Dodać niestandardowy atrybut do modelu Laravel / Eloquent podczas ładowania?

219

Chciałbym móc dodać niestandardowy atrybut / właściwość do modelu Laravel / Eloquent, gdy jest on ładowany, podobnie jak w przypadku $model->open() metody RedBean .

Na przykład w tym momencie w moim kontrolerze mam:

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

Byłoby miło móc pominąć pętlę i mieć ustawiony i wypełniony atrybut „dostępny”.

Próbowałem użyć niektórych zdarzeń modelowych opisanych w dokumentacji, aby dołączyć tę właściwość podczas ładowania obiektu, ale jak dotąd nie powiodło się.

Uwagi:

  • „dostępny” nie jest polem w podstawowej tabeli.
  • $sessionsjest zwracany jako obiekt JSON jako część API, a zatem wywołanie czegoś takiego jak $session->available()w szablonie nie jest opcją
coatesap
źródło

Odpowiedzi:

316

Problem jest spowodowany faktem, że Modeljest toArray()metoda ignoruje wszelkie akcesorów, które nie odnoszą się bezpośrednio do kolumny w tabeli bazowej.

Jak wspomniał tutaj Taylor Otwell: „Jest to celowe i ze względu na wydajność”. Istnieje jednak prosty sposób na osiągnięcie tego:

class EventSession extends Eloquent {

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

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

Wszelkie atrybuty wymienione we właściwości $ appends zostaną automatycznie uwzględnione w tablicy lub formie JSON modelu, pod warunkiem, że dodano odpowiedni akcesor.

Stara odpowiedź (dla wersji Laravel <4.08):

Najlepszym rozwiązaniem, jakie znalazłem, jest zastąpienie toArray()metody i albo jawność ustawić atrybut:

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

}

lub, jeśli masz wiele niestandardowych akcesoriów, przejrzyj je wszystkie i zastosuj:

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
źródło
Najlepsze pytanie i odpowiedź na dziś. Pracowałem nad różnymi implementacjami, jak to osiągnąć i tuż przed opublikowaniem pytania na laravel.io znalazłem to! tak !!!
Gayan Hewa
I czy istnieje sposób, aby nie dodawać ich do JSona tylko w niektórych przypadkach?
Jordi Puigdellívol,
3
Te atrybuty celne nie pojawiają się podczas wywoływania modelu przez relację. (Np .: Models \ Company :: with ('people')). Dowolny pomysł?
Andrew
@ JordiPuigdellívol W Laravel 5 możesz użyć, protected $hidden = []aby dodać kolumny / akcesoria, aby wykluczyć.
junkystu,
124

Ostatnią rzeczą, na stronie doc laravel wymowny jest:

protected $appends = array('is_admin');

Można tego użyć automatycznie, aby dodać nowe akcesoria do modelu bez dodatkowej pracy, takiej jak modyfikowanie metod takich jak ::toArray().

Wystarczy utworzyć getFooBarAttribute(...)akcesor i dodać foo_bardo $appendstablicy.

trm42
źródło
4
Ach ciekawe. Ta funkcja została dodana do Laravela od czasu opublikowania mojego pytania ( github.com/laravel/framework/commit/… ). To właściwa odpowiedź dla każdego, kto używa v4.08 lub wyższej.
coatesap
3
Nie będzie to dla ciebie dostępne, jeśli używasz relacji do generowania treści dla swoich akcesorów. W takim przypadku konieczne może być zastąpienie toArraymetody.
gpmcadam
2
Wygląda na to, że dokumentacja, o której mówiłeś, została przeniesiona tutaj: https://laravel.com/docs/5.5/eloquent-serialization .
mibbler
40

Jeśli zmienisz nazwę swojej getAvailability()metody na getAvailableAttribute()metodę, staniesz się akcesorium i będziesz mógł odczytać ją ->availablebezpośrednio na swoim modelu.

Dokumenty: https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators

EDYCJA: Ponieważ twój atrybut jest „wirtualny”, nie jest domyślnie uwzględniony w reprezentacji JSON twojego obiektu.

Ale znalazłem to: akcesoria modelu niestandardowego nie są przetwarzane, gdy wywoływane jest -> toJson ()?

Aby wymusić zwrócenie atrybutu w tablicy, dodaj go jako klucz do tablicy $ atrybuty.

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

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

Nie przetestowałem tego, ale powinno być dość trywialne, aby wypróbować w obecnej konfiguracji.

Alexandre Danault
źródło
Działa to w takim stopniu, w jakim ->availableudostępnia się na $sessionobiekcie, ale ponieważ $sessionsjest konwertowane bezpośrednio na ciąg JSON (jest to część interfejsu API), nie ma szansy na użycie tego.
coatesap
Nie jestem pewien, czy rozumiem, jak działają twoje rzeczy. Jeśli EventSession::all()zwraca obiekt JSON z interfejsu API, tak naprawdę nie używasz modelu Laravela, prawda? Przepraszam, nie jestem pewien, jak wdrożyłeś swój model.
Alexandre Danault
EventSession to standardowy obiekt Eloquent ( class EventSession extends Eloquent). ::all()jest tylko przykładem. EventSession::find(170071)byłby inny. Są one wywoływane w różnych punktach w całej SessionController ( SessionController extends \BaseController), które byłyby wywoływane za pomocą adresów URL, takich jak „/ session / 170071”.
coatesap
Myślę, że kluczem może być wbudowany obiekt Eloquent do konwersji JSON, która ma miejsce. Nawet jeśli dodasz niestandardowy atrybut, taki jak public $availabledo modelu, nie zostanie to pokazane podczas konwersji obiektu.
coatesap
3
Jest to dostępne od wydania Laravel 4.0.8 8 października 2013 r. Zobacz oficjalne dokumenty: laravel.com/docs/eloquent#converting-to-arrays-or-json (poszukaj protected $appends = array('is_admin');)
Ronald Hulshof
23

Miałem coś podobnego: w moim modelu mam obraz atrybutu, który zawiera lokalizację pliku w folderze Storage. Obraz musi zostać zwrócony w formacie 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);
}
Patrique
źródło
1
Powinno to być picture_data, a nie pictureData w $ atrybuty i $ appends. Możesz także pominąć zmienną $ atrybuty.
Madushan Perera
16

możesz użyć setAttributefunkcji w Modelu, aby dodać niestandardowy atrybut

jianfeng
źródło
9

Załóżmy, że masz 2 kolumny o nazwie imię i nazwisko w tabeli użytkowników i chcesz odzyskać pełną nazwę. możesz osiągnąć za pomocą następującego kodu:

class User extends Eloquent {


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

teraz możesz uzyskać pełną nazwę jako:

$user = User::find(1);
$user->full_name;
ShuBham GuPta
źródło
7

Krok 1: Zdefiniuj atrybuty w $appends
kroku 2: Zdefiniuj akcesorium dla tych atrybutów.
Przykład:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

    //define accessor
    public function getCoverAttribute()
    {
        return json_decode($this->InJson)->cover;
    }
Bedram Tamang
źródło
3

W moim modelu subskrypcji muszę wiedzieć, że subskrypcja jest wstrzymana, czy nie. oto jak to zrobiłem

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

następnie w szablonie widoku mogę użyć, $subscription->is_pausedaby uzyskać wynik.

Jest getIsPausedAttributeto format ustawienia niestandardowego atrybutu,

i używa, is_pausedaby uzyskać lub użyć atrybutu w Twoim widoku.

li bing zhao
źródło
2

w moim przypadku utworzenie pustej kolumny i ustawienie jej akcesorium działało poprawnie. mój akcesorium wypełnia wiek użytkownika z kolumny dob. Działa również funkcja toArray ().

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
Hanif Rifa'i
źródło