Додати навантажений атрибут до моделі Laravel / Eloquent при завантаженні?


219

Я хотів би мати змогу додати спеціальний атрибут / властивість до моделі Laravel / Eloquent, коли вона завантажується, подібно до того, як це можна досягти методом RedBean $model->open() .

Наприклад, на даний момент у своєму контролері я маю:

public function index()
{
    $sessions = EventSession::all();
    foreach ($sessions as $i => $session) {
        $sessions[$i]->available = $session->getAvailability();
    }
    return $sessions;
}

Було б непогано мати можливість пропустити цикл і мати атрибут 'available' вже встановлений і заповнений.

Я намагався використовувати деякі події моделі, описані в документації, щоб прикріпити цю властивість, коли об'єкт завантажується, але поки що без успіху.

Примітки:

  • "Доступно" - це не поле в нижній таблиці.
  • $sessionsповертається як об’єкт JSON як частина API, а тому викликати щось на зразок $session->available()шаблону не є варіантом

Відповіді:


318

Проблема викликана тим, що метод Model's toArray()ігнорує будь-які аксесуари, які безпосередньо не стосуються стовпця в нижній таблиці.

Як уже згадувалося Taylor Otwell тут , «Це навмисне і з міркувань продуктивності.» Однак є простий спосіб досягти цього:

class EventSession extends Eloquent {

    protected $table = 'sessions';
    protected $appends = array('availability');

    public function getAvailabilityAttribute()
    {
        return $this->calculateAvailability();  
    }
}

Будь-які атрибути, перелічені у властивості $ appends, автоматично включаються до масиву або форми JSON моделі за умови, що ви додали відповідний доступ.

Стара відповідь (для версій Laravel <4.08):

Найкраще рішення, яке я знайшов, - це перекрити toArray()метод і встановити атрибут:

class Book extends Eloquent {

    protected $table = 'books';

    public function toArray()
    {
        $array = parent::toArray();
        $array['upper'] = $this->upper;
        return $array;
    }

    public function getUpperAttribute()
    {
        return strtoupper($this->title);    
    }

}

або, якщо у вас багато користувацьких аксесуарів, проведіть їх через усі та застосуйте:

class Book extends Eloquent {

    protected $table = 'books';

    public function toArray()
    {
        $array = parent::toArray();
        foreach ($this->getMutatedAttributes() as $key)
        {
            if ( ! array_key_exists($key, $array)) {
                $array[$key] = $this->{$key};   
            }
        }
        return $array;
    }

    public function getUpperAttribute()
    {
        return strtoupper($this->title);    
    }

}

Найкраще запитання та відповідь на сьогодні. Я працював над різними реалізаціями над тим, як цього досягти, і безпосередньо перед публікацією питання на laravel.io я знайшов це! так !!!
Gayan Hewa

І чи є спосіб не додати їх до json лише для деяких випадків?
Jordi Puigdellívol

3
Ці звичаї, схоже, не з'являються під час виклику моделі через відносини. (Наприклад: Моделі \ Компанія :: з ('люди')). Будь-яка ідея?
Андрій

@ JordiPuigdellívol У Laravel 5 ви можете використовувати protected $hidden = []для додавання стовпців / аксесуарів для виключення.
junkystu

124

Останнє , що на сторінці дока Laravel красномовною є:

protected $appends = array('is_admin');

Це можна використовувати автоматично для додавання нових аксесуарів до моделі без будь-яких додаткових робіт, таких як модифікація методів, таких як ::toArray().

Просто створіть getFooBarAttribute(...)збрую і додайте foo_barв $appendsмасив.


4
Ах цікаво. Ця функція була додана до Laravel з моменту публікації мого питання ( github.com/laravel/framework/commit/… ). Це правильна відповідь для всіх, хто використовує v4.08 або вище.
coatesap

3
Це буде недоступне для вас, якщо ви використовуєте стосунки для створення вмісту для своїх користувачів. У цьому випадку вам, можливо, доведеться вдатися до переосмислення toArrayметоду.
gpmcadam

2
Схоже, документація, на яку ви посилалися, була перенесена сюди: https://laravel.com/docs/5.5/eloquent-serialization .
міблер

40

Якщо перейменувати getAvailability()метод getAvailableAttribute()ваш метод стає збруя , і ви будете мати можливість читати його з допомогою ->availableпрямо на моделі.

Документи: https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators

EDIT: Оскільки ваш атрибут "віртуальний", він за замовчуванням не включається в представлення JSON вашого об'єкта.

Але я виявив це: користувацькі моделі аксесуарів не обробляються, коли -> toJson () викликається?

Щоб змусити ваш атрибут повернути в масив, додайте його як ключ до масиву $ атрибутів.

class User extends Eloquent {
    protected $attributes = array(
        'ZipCode' => '',
    );

    public function getZipCodeAttribute()
    {
        return ....
    }
}

Я не перевіряв цього, але повинен бути досить тривіальним для вас, щоб спробувати у вашому поточному налаштуванні.


Це працює настільки, наскільки це стає ->availableдоступним для $sessionоб'єкта, але, як $sessionsперетворюється безпосередньо в рядок JSON (це частина API), немає шансів використовувати це.
coatesap

Я не впевнений, що розумію, як працюють ваші речі. Якщо EventSession::all()повертає об’єкт JSON з API, ви насправді не використовуєте модель Laravel, правда? Вибачте, я розгублений у тому, як ви реалізували вашу модель.
Олександр Дано

EventSession - це стандартний красномовний об'єкт ( class EventSession extends Eloquent). ::all()є лише прикладом. EventSession::find(170071)було б іншим. Вони викликаються в різних точках по всій SessionController ( SessionController extends \BaseController), які будуть викликані за допомогою таких URL-адрес, як "/ session / 170071".
coatesap

Я думаю, що ключ може лежати в вбудованому красномовному об'єкті в перетворення JSON, яке відбувається. Навіть якщо ви додаєте спеціальний атрибут, такий як public $availableмодель, він не відображається при перетворенні об'єкта.
coatesap

3
Це доступно з моменту виходу Laravel 4.0.8 8 жовтня 2013 року. Дивіться офіційні документи: laravel.com/docs/eloquent#converting-to-arrays-or-json (шукайте protected $appends = array('is_admin');)
Ronald Hulshof

23

У мене було щось симуляційне: у моїй моделі є зображення атрибута, воно містить розташування файлу в папці Storage. Зображення повинно бути повернуто закодованим base64

//Add extra attribute
protected $attributes = ['picture_data'];

//Make it available in the json response
protected $appends = ['picture_data'];

//implement the attribute
public function getPictureDataAttribute()
{
    $file = Storage::get($this->picture);
    $type = Storage::mimeType($this->picture);
    return "data:" . $type . ";base64," . base64_encode($file);
}

1
Це має бути image_data, а не imageData в $ атрибутах & $ додає. І ви також можете пропустити змінну $ атрибутів.
Мадушан Перера

16

ви можете використовувати setAttributeфункцію в моделі, щоб додати спеціальний атрибут


9

Скажімо, у таблиці користувачів у вас є 2 стовпчики з ім'ям ім’я та прізвище, і ви хочете отримати повне ім’я. ви можете досягти наступного коду:

class User extends Eloquent {


    public function getFullNameAttribute()
    {
        return $this->first_name.' '.$this->last_name;
    }
}

тепер ви можете отримати повне ім'я:

$user = User::find(1);
$user->full_name;

7

Крок 1: Визначте атрибути на $appends
кроці 2: Визначте аксесуар для цих атрибутів.
Приклад:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

    //define accessor
    public function getCoverAttribute()
    {
        return json_decode($this->InJson)->cover;
    }

3

У моїй моделі підписки мені потрібно знати, що підписка призупинена чи ні. ось як я це зробив

public function getIsPausedAttribute() {
    $isPaused = false;
    if (!$this->is_active) {
        $isPaused = true;
    }
}

то в шаблоні перегляду я можу використати $subscription->is_pausedдля отримання результату.

Формат getIsPausedAttribute- це встановити спеціальний атрибут,

і використовує is_pausedдля отримання або використання атрибута у вашому перегляді.


2

у моєму випадку створення порожнього стовпця та налаштування його аксесуара спрацювало чудово. мій користувач, що заповнює вік користувача з колонки "dob". функція toArray () також працювала.

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.