Добавить пользовательский атрибут в модель Laravel / Eloquent при загрузке?

219

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

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

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

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

Я попытался использовать некоторые из событий модели, описанных в документации, чтобы присоединить это свойство при загрузке объекта, но пока безуспешно.

Ноты:

  • 'available' не является полем в базовой таблице.
  • $sessionsвозвращается как объект JSON как часть API, и поэтому вызов чего-то похожего $session->available()на шаблон не является опцией
coatesap
источник

Ответы:

317

Проблема вызвана тем фактом, что метод Models toArray()игнорирует любые методы доступа, которые не имеют прямого отношения к столбцу в базовой таблице.

Как Тейлор Отвелл упомянул здесь : «Это сделано намеренно и по причинам производительности». Однако для этого есть простой способ:

class EventSession extends Eloquent {

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

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

Любые атрибуты, перечисленные в свойстве $ appends, будут автоматически включены в массив или форму JSON модели, если вы добавили соответствующий метод доступа.

Старый ответ (для версий Laravel <4.08):

Лучшее решение, которое я нашел, это переопределить toArray()метод и установить явный атрибут explicity:

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);    
    }

}
coatesap
источник
Лучший вопрос и ответ на сегодня. Я работал над различными реализациями того, как этого добиться, и перед тем, как опубликовать вопрос на laravel.io, я нашел это! ура !!!
Гаян Хева
И есть ли способ не добавлять их в JSON только для некоторых случаев?
Jordi Puigdellívol
3
Эти таможенные атрибуты, кажется, не появляются при вызове модели через отношения. (Например: Models \ Company :: with ('people')). Любая идея?
Андрей
@ JordiPuigdellívol В Laravel 5 вы можете использовать, protected $hidden = []чтобы добавить исключаемые столбцы / методы доступа.
junkystu
124

Последнее , что на странице дока Laravel красноречивой является:

protected $appends = array('is_admin');

Это может быть использовано автоматически для добавления новых методов доступа к модели без какой-либо дополнительной работы, такой как изменение методов, таких как ::toArray().

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

trm42
источник
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

РЕДАКТИРОВАТЬ: Поскольку ваш атрибут является «виртуальным», он не включен по умолчанию в JSON-представление вашего объекта.

Но я обнаружил следующее: пользовательские методы доступа к моделям не обрабатываются при вызове -> toJson ()?

Чтобы заставить ваш атрибут возвращаться в массив, добавьте его в качестве ключа в массив $ attribute.

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

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

Я не проверял это, но для вас должно быть довольно тривиально попробовать в вашей текущей настройке.

Александр Дано
источник
Это работает настолько, насколько это ->availableдоступно для $sessionобъекта, но так как оно $sessionsпреобразуется непосредственно в строку JSON (это часть API), у вас нет шансов использовать это.
Коутесап
Я не уверен, что понимаю, как работает ваш материал. Если EventSession::all()возвращает объект JSON из API, вы на самом деле не используете модель Laravel, верно? Извините, я запутался в том, как вы реализовали свою модель.
Александр Дано
EventSession - это стандартный объект Eloquent ( class EventSession extends Eloquent). ::all()это просто в качестве примера. EventSession::find(170071)было бы другое. Они вызываются в различных точках SessionController ( SessionController extends \BaseController), которые будут вызываться через URL-адреса, такие как «/ session / 170071».
Коутесап
Я думаю, что ключ может лежать во встроенном объекте Eloquent в преобразование JSON. Даже если вы добавите пользовательский атрибут, например, public $availableв модель, он не будет отображаться при преобразовании объекта.
Коутесап
3
Это доступно с момента выпуска Laravel 4.0.8 8 октября 2013 года. См. Официальные документы: laravel.com/docs/eloquent#converting-to-arrays-or-json (ищите protected $appends = array('is_admin');)
Рональд Халсхоф
23

У меня было что-то похожее: в моей модели есть изображение атрибута, в котором содержится местоположение файла в папке «Хранилище». Изображение должно быть возвращено в кодировке 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
Это должно быть picture_data, а не pictureData в $ attribute & $ append. И вы можете пропустить переменную $ attribute.
Мадушан Перера
16

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

Цзяньфэн
источник
9

Допустим, у вас есть 2 столбца с именами first_name и last_name в вашей таблице пользователей, и вы хотите получить полное имя. Вы можете достичь с помощью следующего кода:

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

в моем случае создание пустого столбца и установка его метода доступа работали нормально. мой аксессор заполняет возраст пользователя из столбца доб. Функция toArray () тоже сработала.

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
Ханиф Рифа'и
источник