Спеціалізоване кастування об'єктів у Laravel дозволяє перетворювати атрибути бази даних у багаті об'єкти значень, що спрощує обробку даних та зберігає чистоту інтерфейсів моделей. Ця функція заповнює прогалину між плоским зберіганням даних у базі та складною логікою додатка.
Щоб створити кастування, реалізуйте інтерфейс CastsAttributes
:
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class Money implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return new MoneyValueObject($value, $attributes['currency'] ?? 'USD');
}
public function set($model, $key, $value, $attributes)
{
return [
'amount' => $value->amount,
'currency' => $value->currency,
];
}
}
Об'єкти значень інкапсулюють пов'язані дані та поведінку, забезпечуючи зрозумілий інтерфейс для складних операцій.
class MoneyValueObject implements JsonSerializable
{
public function __construct(
public float $amount,
public string $currency = 'USD'
) {}
public function formatted(): string
{
return match ($this->currency) {
'USD' => '$' . number_format($this->amount, 2),
'EUR' => '€' . number_format($this->amount, 2),
'GBP' => '£' . number_format($this->amount, 2),
default => $this->currency . ' ' . number_format($this->amount, 2),
};
}
public function add(MoneyValueObject $other): self
{
if ($this->currency !== $other->currency) {
throw new InvalidArgumentException('Неможливо додати різні валюти');
}
return new self($this->amount + $other->amount, $this->currency);
}
public function jsonSerialize(): array
{
return [
'amount' => $this->amount,
'currency' => $this->currency,
'formatted' => $this->formatted(),
];
}
}
Ось приклад комплексної системи рахунків-фактур, що демонструє просунуте кастування:
class Invoice extends Model
{
protected $casts = [
'billing_address' => ContactInfo::class,
'shipping_address' => ContactInfo::class,
'total_amount' => Money::class,
'tax_amount' => Money::class,
'discount' => Money::class,
];
public function calculateTotal(): MoneyValueObject
{
$subtotal = $this->lineItems->sum(fn($item) => $item->total_price->amount);
$taxTotal = $this->tax_amount->amount;
$discountTotal = $this->discount->amount;
return new MoneyValueObject($subtotal + $taxTotal - $discountTotal, $this->total_amount->currency);
}
public function lineItems()
{
return $this->hasMany(InvoiceLineItem::class);
}
}
class ContactInfo implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
$prefix = $key === 'billing_address' ? 'billing_' : 'shipping_';
return new ContactInfoValueObject(
$attributes["{$prefix}name"] ?? '',
$attributes["{$prefix}company"] ?? '',
$attributes["{$prefix}address_line_1"] ?? '',
$attributes["{$prefix}address_line_2"] ?? '',
$attributes["{$prefix}city"] ?? '',
$attributes["{$prefix}state"] ?? '',
$attributes["{$prefix}postal_code"] ?? '',
$attributes["{$prefix}country"] ?? ''
);
}
public function set($model, $key, $value, $attributes)
{
$prefix = $key === 'billing_address' ? 'billing_' : 'shipping_';
return [
"{$prefix}name" => $value->name,
"{$prefix}company" => $value->company,
"{$prefix}address_line_1" => $value->addressLine1,
"{$prefix}address_line_2" => $value->addressLine2,
"{$prefix}city" => $value->city,
"{$prefix}state" => $value->state,
"{$prefix}postal_code" => $value->postalCode,
"{$prefix}country" => $value->country,
];
}
}
class ContactInfoValueObject implements JsonSerializable
{
public function __construct(
public string $name,
public string $company,
public string $addressLine1,
public string $addressLine2,
public string $city,
public string $state,
public string $postalCode,
public string $country
) {}
public function fullAddress(): string
{
$parts = array_filter([
$this->addressLine1,
$this->addressLine2,
$this->city,
$this->state . ' ' . $this->postalCode,
$this->country,
]);
return implode(', ', $parts);
}
public function displayName(): string
{
return $this->company ? "{$this->name} ({$this->company})" : $this->name;
}
public function jsonSerialize(): array
{
return get_object_vars($this);
}
}
class InvoiceController extends Controller
{
public function store(Request $request)
{
$invoice = new Invoice();
$invoice->billing_address = new ContactInfoValueObject(
$request->input('billing.name'),
$request->input('billing.company', ''),
$request->input('billing.address_line_1'),
$request->input('billing.address_line_2', ''),
$request->input('billing.city'),
$request->input('billing.state'),
$request->input('billing.postal_code'),
$request->input('billing.country')
);
$invoice->total_amount = new MoneyValueObject(
$request->input('total'),
$request->input('currency', 'USD')
);
$invoice->save();
return response()->json([
'invoice_id' => $invoice->id,
'formatted_total' => $invoice->total_amount->formatted(),
'billing_contact' => $invoice->billing_address->displayName(),
]);
}
public function show(Invoice $invoice)
{
return view('invoices.show', [
'invoice' => $invoice,
'calculatedTotal' => $invoice->calculateTotal()->formatted(),
'billingDisplay' => $invoice->billing_address->fullAddress(),
]);
}
}
Кастування об'єктів дозволяє створювати багаті доменні моделі, спрощуючи при цьому взаємодію з базою даних у ваших Laravel-додатках
```