Покращення JSON-відповідей за допомогою Laravel Model Appends

Перекладено ШІ
Оригінал: Laravel News
Оновлено: 10 липня, 2025
Завдяки функції додавання моделей у Laravel ви можете включати обчислювані атрибути в JSON-відповіді, збагачуючи ваш API. Чи знаєте ви, як створити ці атрибути за допомогою класу Attribute? Читайте далі, щоб дізнатися, як максимально використати цю потужну функцію

Функція додавання атрибутів у Laravel дозволяє включати обчислені значення в JSON-відповіді, збагачуючи ваш API результатами без зміни структури бази даних

Визначте доступи за допомогою класу Attribute для створення обчислених властивостей:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
 
class Order extends Model
{
    protected function totalWithTax(): Attribute
    {
        return new Attribute(
            get: fn () => $this->subtotal * (1 + $this->tax_rate),
        );
    }
}

Щоб включити ці обчислені значення у JSON-вихід, додайте їх до властивості $appends

class Order extends Model
{
    protected $appends = ['total_with_tax', 'status_label'];
 
    protected function statusLabel(): Attribute
    {
        return new Attribute(
            get: fn () => match($this->status) {
                'pending' => 'Очікує оплати',
                'processing' => 'Готується',
                'shipped' => 'В дорозі',
                'delivered' => 'Завершено',
                default => 'Невідомий статус'
            }
        );
    }
}

Ось приклад моделі продукту для електронної комерції, що демонструє різні техніки додавання атрибутів:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
 
class Product extends Model
{
    protected $appends = [
        'display_price',
        'availability_status',
        'discount_percentage',
        'rating_summary'
    ];
 
    protected function displayPrice(): Attribute
    {
        return new Attribute(
            get: function () {
                if ($this->sale_price & $this->sale_price < $this->regular_price) {
                    return [
                        'current' => '$' . number_format($this->sale_price, 2),
                        'original' => '$' . number_format($this->regular_price, 2),
                        'on_sale' => true
                    ];
                }
 
                return [
                    'current' => '$' . number_format($this->regular_price, 2),
                    'original' => null,
                    'on_sale' => false
                ];
            }
        );
    }
 
    protected function availabilityStatus(): Attribute
    {
        return new Attribute(
            get: function () {
                if ($this->inventory_count <= 0) {
                    return 'out_of_stock';
                }
 
                if ($this->inventory_count <= 5) {
                    return 'low_stock';
                }
 
                return 'in_stock';
            }
        );
    }
 
    protected function discountPercentage(): Attribute
    {
        return new Attribute(
            get: function () {
                if (!$this->sale_price || $this->sale_price >= $this->regular_price) {
                    return 0;
                }
 
                return round((($this->regular_price - $this->sale_price) / $this->regular_price) * 100);
            }
        );
    }
 
    protected function ratingSummary(): Attribute
    {
        return new Attribute(
            get: function () {
                $reviewsCount = $this->whenCounted('reviews');
                $averageRating = $this->reviews_avg_rating ?? 0;
 
                return [
                    'average' => round($averageRating, 1),
                    'count' => $reviewsCount,
                    'stars' => $this->generateStarRating($averageRating)
                ];
            }
        );
    }
 
    public function reviews()
    {
        return $this->hasMany(Review::class);
    }
 
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
 
    private function generateStarRating($rating)
    {
        $fullStars = floor($rating);
        $halfStar = ($rating - $fullStars) >= 0.5 ? 1 : 0;
        $emptyStars = 5 - $fullStars - $halfStar;
 
        return [
            'full' => $fullStars,
            'half' => $halfStar,
            'empty' => $emptyStars
        ];
    }
 
    public function scopeWithReviewStats($query)
    {
        return $query->withCount('reviews')
                    ->withAvg('reviews', 'rating');
    }
}

Також можна додавати атрибути умовно, використовуючи метод append для динамічних сценаріїв:

$products = Product::withReviewStats()->get();
 
foreach ($products as $product) {
    if ($user->isAdmin()) {
        $product->append(['cost_analysis', 'profit_margin']);
    }
}
 
return response()->json($products);

Додавання атрибутів у моделі створює багатофункціональні API-відповіді, які надають клієнтам обчислені дані, зберігаючи чистий розподіл між збереженою та виведеною інформацією