本文目录导读:

在 Laravel 中使用 PHP 值对象(Value Object),以下是一些最佳实践和示例:
基础值对象实现
<?php
namespace App\ValueObjects;
use InvalidArgumentException;
class Email
{
private string $value;
public function __construct(string $email)
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email address: {$email}");
}
$this->value = $email;
}
public function value(): string
{
return $this->value;
}
public function equals(Email $other): bool
{
return $this->value === $other->value;
}
public function __toString(): string
{
return $this->value;
}
}
在 Eloquent 模型中使用
使用 Casts 特性(Laravel 7+)
<?php
namespace App\Models;
use App\ValueObjects\Email;
use App\ValueObjects\Money;
use App\ValueObjects\Address;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $casts = [
'email' => Email::class,
'wallet_balance' => Money::class,
'address' => Address::class,
];
// 自定义 cast 方法
protected function asEmail(string $value): Email
{
return new Email($value);
}
}
自定义 Cast 类
<?php
namespace App\Casts;
use App\ValueObjects\Email;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class EmailCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes): ?Email
{
if ($value === null) {
return null;
}
return new Email($value);
}
public function set($model, string $key, $value, array $attributes): ?string
{
if ($value === null) {
return null;
}
if ($value instanceof Email) {
return $value->value();
}
return (new Email($value))->value();
}
}
// 在模型中使用
class User extends Model
{
protected $casts = [
'email' => \App\Casts\EmailCast::class,
];
}
复杂值对象示例
<?php
namespace App\ValueObjects;
class Money
{
private int $amount;
private string $currency;
public function __construct(int $amount, string $currency = 'USD')
{
if ($amount < 0) {
throw new \InvalidArgumentException('Amount cannot be negative');
}
$this->amount = $amount;
$this->currency = strtoupper($currency);
}
public function add(Money $other): self
{
if ($this->currency !== $other->currency) {
throw new \InvalidArgumentException('Cannot add different currencies');
}
return new self($this->amount + $other->amount, $this->currency);
}
public function format(): string
{
return number_format($this->amount / 100, 2) . ' ' . $this->currency;
}
public function getAmount(): int
{
return $this->amount;
}
public function getCurrency(): string
{
return $this->currency;
}
public function equals(Money $other): bool
{
return $this->amount === $other->amount && $this->currency === $other->currency;
}
}
// 在模型中使用
class Order extends Model
{
public function total(): Money
{
return new Money(
$this->items->sum(fn($item) => $item->price->getAmount()),
'USD'
);
}
}
在 Form Request 中使用
<?php
namespace App\Http\Requests;
use App\ValueObjects\Email;
use Illuminate\Foundation\Http\FormRequest;
class CreateUserRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => 'required|email|unique:users',
'name' => 'required|string|max:255',
];
}
public function toEmail(): Email
{
return new Email($this->input('email'));
}
}
// 在控制器中使用
public function store(CreateUserRequest $request)
{
$email = $request->toEmail();
User::create([
'email' => $email->value(),
'name' => $request->input('name'),
]);
}
在 Repository 中使用
<?php
namespace App\Repositories;
use App\Models\User;
use App\ValueObjects\Email;
use App\ValueObjects\UserId;
class UserRepository
{
public function findByEmail(Email $email): ?User
{
return User::where('email', $email->value())->first();
}
public function findById(UserId $id): ?User
{
return User::find($id->value());
}
}
使用 Trait 简化操作
<?php
namespace App\Traits;
use App\ValueObjects\Email;
use App\ValueObjects\Money;
trait HasValueObjects
{
public function getEmailAttribute($value): ?Email
{
return $value ? new Email($value) : null;
}
public function setEmailAttribute($value): void
{
$this->attributes['email'] = $value instanceof Email
? $value->value()
: (new Email($value))->value();
}
public function getWalletBalanceAttribute($value): ?Money
{
return $value ? new Money($value, 'USD') : null;
}
public function setWalletBalanceAttribute($value): void
{
$this->attributes['wallet_balance'] = $value instanceof Money
? $value->getAmount()
: $value;
}
}
// 在模型中使用
class User extends Model
{
use HasValueObjects;
}
验证规则集成
<?php
namespace App\Rules;
use App\ValueObjects\Email;
use Illuminate\Contracts\Validation\Rule;
class ValidEmailValueObject implements Rule
{
public function passes($attribute, $value): bool
{
try {
new Email($value);
return true;
} catch (\Exception $e) {
return false;
}
}
public function message(): string
{
return 'The :attribute is not a valid email address.';
}
}
// 在表单请求中使用
class UpdateUserRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => ['required', new ValidEmailValueObject()],
];
}
}
主要优点:
- 类型安全 - 确保数据始终有效
- 自文档化 - 代码意图清晰
- 可测试性 - 易于单元测试
- 封装逻辑 - 业务规则集中在值对象中
- 不变性 - 减少意外修改
最佳实践:
- 保持值对象不可变
- 实现
equals()方法用于比较 - 考虑使用
__toString()方法 - 在构造函数中进行验证
- 使用命名构造函数提高可读性
这种模式可以显著提高 Laravel 应用的代码质量和可维护性。