Aggiungere un attributo personalizzato a un modello Laravel / Eloquent al caricamento?


219

Mi piacerebbe poter aggiungere un attributo / proprietà personalizzati a un modello Laravel / Eloquent quando viene caricato, in modo simile a come ciò potrebbe essere ottenuto con il $model->open() metodo RedBean .

Ad esempio, al momento, nel mio controller ho:

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

Sarebbe bello poter omettere il loop e avere l'attributo 'disponibile' già impostato e popolato.

Ho provato ad utilizzare alcuni degli eventi del modello descritti nella documentazione per collegare questa proprietà quando l'oggetto viene caricato, ma finora non ci sono riuscito.

Appunti:

  • 'disponibile' non è un campo nella tabella sottostante.
  • $sessionsviene restituito come oggetto JSON come parte di un'API e pertanto chiamare qualcosa come $session->available()un modello non è un'opzione

Risposte:


316

Il problema è causato dal fatto che il metodo di Models toArray()ignora tutti gli accessori che non si riferiscono direttamente a una colonna nella tabella sottostante.

Come Taylor Otwell ha menzionato qui , "Questo è intenzionale e per motivi di prestazioni". Tuttavia, esiste un modo semplice per raggiungere questo obiettivo:

class EventSession extends Eloquent {

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

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

Tutti gli attributi elencati nella proprietà $ accends verranno automaticamente inclusi nell'array o nel modulo JSON del modello, a condizione che tu abbia aggiunto l'accessor appropriato.

Vecchia risposta (per versioni Laravel <4.08):

La migliore soluzione che ho trovato è quella di sovrascrivere il toArray()metodo e impostare esplicitamente l'attributo:

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

}

oppure, se disponi di molti accessori personalizzati, esegui il ciclo tra tutti e applicali:

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

}

La migliore domanda e risposta per oggi. Stavo lavorando su diverse implementazioni su come raggiungere questo obiettivo e poco prima di pubblicare una domanda su laravel.io l'ho trovato! Sìì !!!
Gayan Hewa,

E c'è un modo per non aggiungerli a JSON solo per alcuni casi?
Jordi Puigdellívol,

3
Questi attributi doganali non sembrano apparire quando si chiama il modello attraverso una relazione. (Es: Models \ Company :: with ('people')). Qualche idea?
Andrew,

@ JordiPuigdellívol In Laravel 5, puoi usare il protected $hidden = []per aggiungere colonne / accessori per averlo escluso.
Junkystu,

124

L' ultima cosa nella pagina doc di Laravel Eloquent è:

protected $appends = array('is_admin');

Che può essere utilizzato automaticamente per aggiungere nuovi accessori al modello senza alcun lavoro aggiuntivo come la modifica di metodi come ::toArray().

Basta creare getFooBarAttribute(...)di accesso e aggiungere la foo_bara $appendsmatrice.


4
Ah interessante. Questa funzione è stata aggiunta a Laravel da quando è stata pubblicata la mia domanda ( github.com/laravel/framework/commit/… ). Questa è la risposta giusta per chiunque usi v4.08 o successive.
Coatesap,

3
Questo non sarà disponibile per te se stai utilizzando le relazioni per generare il contenuto per i tuoi utenti. In questo caso, potrebbe essere necessario ricorrere alla sostituzione del toArraymetodo.
gpmcadam,

2
Sembra che la documentazione a cui ti riferivi sia stata spostata qui: https://laravel.com/docs/5.5/eloquent-serialization .
mibbler,

40

Se rinomini il tuo getAvailability()metodo con il getAvailableAttribute()tuo metodo diventa un accedente e sarai in grado di leggerlo usando ->availabledirettamente il tuo modello.

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

EDIT: Poiché il tuo attributo è "virtuale", non è incluso per impostazione predefinita nella rappresentazione JSON del tuo oggetto.

Ma ho trovato questo: Accessor di modelli personalizzati non elaborati quando -> toJson () ha chiamato?

Per forzare la restituzione dell'attributo nell'array, aggiungerlo come chiave all'array $ attributi.

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

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

Non l'ho provato, ma dovresti essere abbastanza banale da provare nella tua configurazione attuale.


Funziona tanto quanto rende ->availabledisponibile $sessionsull'oggetto, ma poiché $sessionsviene convertito direttamente in una stringa JSON (fa parte di un'API), non è possibile utilizzarlo.
Coatesap,

Non sono sicuro di capire come funzionano le tue cose. Se EventSession::all()restituisce un oggetto JSON da un'API, non stai davvero utilizzando un modello Laravel, giusto? Siamo spiacenti, sono confuso su come hai implementato il tuo modello.
Alexandre Danault,

EventSession è un oggetto Eloquent standard ( class EventSession extends Eloquent). ::all()è solo un esempio. EventSession::find(170071)sarebbe un altro. Questi sono chiamati in vari punti in SessionController ( SessionController extends \BaseController) che verrebbero chiamati tramite URL come '/ sessioni / 170071'.
Coatesap,

Penso che la chiave potrebbe risiedere nell'oggetto incorporato di Eloquent alla conversione JSON che ha luogo. Anche se aggiungi un attributo personalizzato come public $availableal modello, questo non viene mostrato quando l'oggetto viene convertito.
Coatesap,

3
È disponibile dalla versione di Laravel 4.0.8 dell'8 ottobre 2013. Consulta i documenti ufficiali: laravel.com/docs/eloquent#converting-to-arrays-or-json (cerca protected $appends = array('is_admin');)
Ronald Hulshof,

23

Ho avuto qualcosa di simile: ho una foto di attributo nel mio modello, questo contiene la posizione del file nella cartella Archiviazione. L'immagine deve essere restituita con codifica 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
Dovrebbe essere picture_data non pictureData in $ attributi e $ accoda. E puoi anche saltare $ variabile variabile.
Madushan Perera,

16

è possibile utilizzare la setAttributefunzione in Modello per aggiungere un attributo personalizzato


9

Supponiamo che tu abbia 2 colonne denominate first_name e last_name nella tabella degli utenti e desideri recuperare il nome completo. puoi ottenere con il seguente codice:

class User extends Eloquent {


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

ora puoi ottenere il nome completo come:

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

7

Passaggio 1: definire gli attributi nel $appends
passaggio 2: definire l'accessor per tali attributi.
Esempio:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

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

3

Nel mio modello di abbonamento, devo sapere che l'abbonamento è in pausa o meno. ecco come l'ho fatto

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

quindi nel modello di visualizzazione, posso usare $subscription->is_pausedper ottenere il risultato.

Il getIsPausedAttributeè il formato per impostare un attributo personalizzato,

e utilizza is_pausedper ottenere o utilizzare l'attributo nella vista.


2

nel mio caso, creare una colonna vuota e impostarne l'accessorio ha funzionato bene. il mio accessor riempie l'età dell'utente dalla colonna dob. Anche la funzione toArray () ha funzionato.

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.