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

160

Я бы хотел добавить пользовательский атрибут/свойство в модель 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() в шаблоне не является опцией
Теги:
eloquent
orm

8 ответов

258
Лучший ответ

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

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

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

}
  • 0
    Лучший вопрос и ответ на сегодня. Я работал над различными реализациями того, как этого добиться, и перед тем, как опубликовать вопрос на laravel.io, я нашел это! ура !!!
  • 0
    И есть ли способ не добавлять их в JSON только для некоторых случаев?
Показать ещё 3 комментария
106

Последнее, что есть на странице документации Laravel Eloquent:

protected $appends = array('is_admin');

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

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

  • 4
    Ах, интересно. Эта функция была добавлена в Laravel с момента публикации моего вопроса ( github.com/laravel/framework/commit/… ). Это правильный ответ для тех, кто использует v4.08 или выше.
  • 3
    Это не будет доступно для вас, если вы используете отношения для создания контента для ваших аксессоров. В этом случае вам, возможно, придется прибегнуть к переопределению метода toArray .
Показать ещё 1 комментарий
33

Если вы переименуете свой 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 ....
    }
}

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

  • 0
    Это работает так же , как это делает ->available доступны на $session объекта, но $sessions преобразуется непосредственно в строку JSON (это часть API), есть не шанс использовать это.
  • 0
    Я не уверен, что понимаю, как работает ваш материал. Если EventSession::all() возвращает объект JSON из API, вы на самом деле не используете модель Laravel, верно? Извините, я запутался в том, как вы реализовали свою модель.
Показать ещё 5 комментариев
13

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

11

У меня было что-то симулятивное: У меня есть изображение атрибута в моей модели, это содержит расположение файла в папке "Хранение". Изображение должно быть возвращено в кодировке base64

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

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

//implement the attribute
public function getPictureDataAttribute()
{
    $file = Storage::get($this->picture);
    $type = Storage::mimeType($this->picture);
    return "data:" . $type . ";base64," . base64_encode($file);
}
  • 0
    Это должно быть picture_data, а не pictureData в $ attribute & $ append. И вы можете пропустить переменную $ attribute.
1

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

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
0

Допустим, у вас есть 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;
0

В моей модели подписки мне нужно знать, что подписка приостановлена ​​или нет. вот как я это сделал   public function getIsPausedAttribute() { $isPaused = false; if (!$this->is_active) { $isPaused = true; } }

то в шаблоне просмотра я могу использовать $subscription->is_paused, чтобы получить результат.

getIsPausedAttribute - это формат для установки пользовательского атрибута,

и использует is_paused для получения или использования атрибута в вашем представлении.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню