本文目录导读:

- 使用 PHP 8.1+ readonly 属性(推荐)
- 使用 Laravel 的
ImmutableTrait - 基于 Laravel Collections 的不可变模式
- 使用 Spatie Data Transfer Object
- 结合 Laravel Eloquent 的不可变模型
- 使用 PHP 的 __set 魔术方法
- 最佳实践建议
在 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,
];
}
}
最佳实践建议
- 优先使用 PHP 8.1+ readonly 属性 - 最简洁且性能最好
- *使用 `with` 方法模式** - 返回新实例而不是修改原对象
- 配合 CarbonImmutable - 处理日期时间时使用不可变版本
- 避免继承 - 不可变类最好保持 final
- 使用显式的 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);
}
}
通过这些方法,你可以创建真正不可变的对象,提高代码的可预测性和安全性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播