Laravel不可变对象怎么创建

wen PHP项目 1

本文目录导读:

Laravel不可变对象怎么创建

  1. 使用 PHP 8.1+ readonly 属性(推荐)
  2. 使用 Laravel 的 Immutable Trait
  3. 基于 Laravel Collections 的不可变模式
  4. 使用 Spatie Data Transfer Object
  5. 结合 Laravel Eloquent 的不可变模型
  6. 使用 PHP 的 __set 魔术方法
  7. 最佳实践建议

在 Laravel 中创建不可变对象(Immutable Objects),有几种常见的方法:

使用 PHP 8.1+ readonly 属性(推荐)

<?php
namespace App\ValueObjects;
use Carbon\CarbonImmutable;
class UserProfile
{
    public readonly string $name;
    public readonly string $email;
    public readonly CarbonImmutable $createdAt;
    public function __construct(
        string $name,
        string $email,
        ?CarbonImmutable $createdAt = null
    ) {
        $this->name = $name;
        $this->email = $email;
        $this->createdAt = $createdAt ?? CarbonImmutable::now();
    }
    // 通过 with 方法返回新实例来"修改"属性
    public function withName(string $name): self
    {
        return new self($name, $this->email, $this->createdAt);
    }
    public function withEmail(string $email): self
    {
        return new self($this->name, $email, $this->createdAt);
    }
}

使用 Laravel 的 Immutable Trait

<?php
namespace App\ValueObjects;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Support\Traits\Tappable;
class Configuration
{
    use Conditionable, Macroable, Tappable;
    private array $data;
    public function __construct(array $data)
    {
        $this->data = $data;
    }
    public function get(string $key, mixed $default = null): mixed
    {
        return $this->data[$key] ?? $default;
    }
    // 返回新实例而非修改原对象
    public function merge(array $data): self
    {
        return new self(array_merge($this->data, $data));
    }
    public function toArray(): array
    {
        return $this->data;
    }
}

基于 Laravel Collections 的不可变模式

<?php
namespace App\Support;
use Illuminate\Support\Collection;
class ImmutableCollection
{
    private Collection $items;
    public function __construct(array $items = [])
    {
        $this->items = collect($items);
    }
    public function add(mixed $item): self
    {
        return new self($this->items->push($item)->all());
    }
    public function remove(int $index): self
    {
        return new self(
            $this->items->reject(fn($value, $key) => $key === $index)->all()
        );
    }
    public function map(callable $callback): self
    {
        return new self($this->items->map($callback)->all());
    }
    public function all(): array
    {
        return $this->items->all();
    }
}

使用 Spatie Data Transfer Object

<?php
namespace App\DataTransferObjects;
use Spatie\DataTransferObject\DataTransferObject;
class OrderDTO extends DataTransferObject
{
    public string $orderNumber;
    public float $total;
    public array $items;
    public ?string $customerNote;
    // 所有属性都是只读的
    protected bool $immutable = true;
}
// 使用示例
$order = new OrderDTO([
    'orderNumber' => 'ORD-001',
    'total' => 150.00,
    'items' => ['Product A', 'Product B'],
]);
// 获取副本
$modifiedOrder = $order->with('total', 200.00);

结合 Laravel Eloquent 的不可变模型

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
class Product extends Model
{
    // 禁用批量赋值修改
    protected $guarded = ['*'];
    // 返回不可变的 Carbon 实例
    protected function serializeDate(\DateTimeInterface $date): string
    {
        return Carbon::instance($date)->toImmutable()->toISOString();
    }
    // 通过只读访问器返回副本
    public function getPriceWithTaxAttribute(): \Money\Money
    {
        return $this->price->multiply('1.2');
    }
    // 修改时返回新实例
    public function replicateWith(array $attributes = []): self
    {
        return tap($this->replicate(), function ($instance) use ($attributes) {
            foreach ($attributes as $key => $value) {
                $instance->$key = $value;
            }
        });
    }
}

使用 PHP 的 __set 魔术方法

<?php
namespace App\ValueObjects;
class Address
{
    private string $street;
    private string $city;
    private string $country;
    public function __construct(string $street, string $city, string $country)
    {
        $this->street = $street;
        $this->city = $city;
        $this->country = $country;
    }
    // 禁止动态修改属性
    public function __set(string $name, mixed $value): void
    {
        throw new \LogicException("Cannot modify immutable object's property: {$name}");
    }
    // 允许通过方法创建修改后的副本
    public function withStreet(string $street): self
    {
        return new self($street, $this->city, $this->country);
    }
    public function toArray(): array
    {
        return [
            'street' => $this->street,
            'city' => $this->city,
            'country' => $this->country,
        ];
    }
}

最佳实践建议

  1. 优先使用 PHP 8.1+ readonly 属性 - 最简洁且性能最好
  2. *使用 `with` 方法模式** - 返回新实例而不是修改原对象
  3. 配合 CarbonImmutable - 处理日期时间时使用不可变版本
  4. 避免继承 - 不可变类最好保持 final
  5. 使用显式的 getter 方法 - 而不是直接访问属性
// 完整示例
final readonly class Money
{
    public function __construct(
        private int $amount,
        private string $currency = 'USD'
    ) {}
    public function getAmount(): int
    {
        return $this->amount;
    }
    public function getCurrency(): string
    {
        return $this->currency;
    }
    public function add(Money $money): self
    {
        if ($this->currency !== $money->currency) {
            throw new \InvalidArgumentException('Currency mismatch');
        }
        return new self($this->amount + $money->amount, $this->currency);
    }
    public function multiply(float $multiplier): self
    {
        return new self((int) ($this->amount * $multiplier), $this->currency);
    }
}

通过这些方法,你可以创建真正不可变的对象,提高代码的可预测性和安全性。

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