PHP值对象在Laravel中怎么用

wen PHP项目 1

本文目录导读:

PHP值对象在Laravel中怎么用

  1. 基础值对象实现
  2. 在 Eloquent 模型中使用
  3. 复杂值对象示例
  4. 在 Form Request 中使用
  5. 在 Repository 中使用
  6. 使用 Trait 简化操作
  7. 验证规则集成
  8. 主要优点:
  9. 最佳实践:

在 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()],
        ];
    }
}

主要优点:

  1. 类型安全 - 确保数据始终有效
  2. 自文档化 - 代码意图清晰
  3. 可测试性 - 易于单元测试
  4. 封装逻辑 - 业务规则集中在值对象中
  5. 不变性 - 减少意外修改

最佳实践:

  1. 保持值对象不可变
  2. 实现 equals() 方法用于比较
  3. 考虑使用 __toString() 方法
  4. 在构造函数中进行验证
  5. 使用命名构造函数提高可读性

这种模式可以显著提高 Laravel 应用的代码质量和可维护性。

抱歉,评论功能暂时关闭!