Ein benutzerdefiniertes Attribut zu einem Laravel / Eloquent-Modell beim Laden hinzufügen?

219

Ich möchte in der Lage sein, einem Laravel / Eloquent-Modell beim Laden ein benutzerdefiniertes Attribut / eine benutzerdefinierte Eigenschaft hinzuzufügen, ähnlich wie dies mit der RedBean- $model->open() Methode erreicht werden kann.

Zum Beispiel habe ich im Moment in meinem Controller:

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

Es wäre schön, die Schleife weglassen zu können und das Attribut 'verfügbar' bereits festgelegt und ausgefüllt zu haben.

Ich habe versucht, einige der in der Dokumentation beschriebenen Modellereignisse zu verwenden, um diese Eigenschaft beim Laden des Objekts anzuhängen, aber bisher ohne Erfolg.

Anmerkungen:

  • 'verfügbar' ist kein Feld in der zugrunde liegenden Tabelle.
  • $sessionswird als JSON-Objekt als Teil einer API zurückgegeben, und daher ist das Aufrufen von Elementen wie $session->available()in einer Vorlage keine Option
Coatesap
quelle

Antworten:

316

Das Problem wird durch die Tatsache verursacht, dass die Methode von Model's toArray()alle Accessoren ignoriert, die sich nicht direkt auf eine Spalte in der zugrunde liegenden Tabelle beziehen.

Wie Taylor Otwell hier erwähnte : "Dies ist beabsichtigt und aus Leistungsgründen." Es gibt jedoch einen einfachen Weg, dies zu erreichen:

class EventSession extends Eloquent {

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

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

Alle in der Eigenschaft $ appends aufgeführten Attribute werden automatisch in das Array- oder JSON-Formular des Modells aufgenommen, sofern Sie den entsprechenden Accessor hinzugefügt haben.

Alte Antwort (für Laravel-Versionen <4.08):

Die beste Lösung, die ich gefunden habe, besteht darin, die toArray()Methode zu überschreiben und entweder explizit das Attribut festzulegen:

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

}

Wenn Sie viele benutzerdefinierte Accessoren haben, durchlaufen Sie alle und wenden Sie sie an:

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
quelle
Beste Frage und Antwort für heute. Ich habe an verschiedenen Implementierungen gearbeitet, um dies zu erreichen, und kurz bevor ich eine Frage auf laravel.io gestellt habe, habe ich dies gefunden! Yay !!!
Gayan Hewa
Und gibt es eine Möglichkeit, sie nicht nur für einige Fälle zu json hinzuzufügen?
Jordi Puigdellívol
3
Diese Zollattribute scheinen nicht zu erscheinen, wenn das Modell über eine Beziehung aufgerufen wird. (Beispiel: Models \ Company :: with ('people')). Irgendeine Idee?
Andrew
@ JordiPuigdellívol In Laravel 5 können Sie das verwenden protected $hidden = [], um Spalten / Accessoren hinzuzufügen, die ausgeschlossen wurden.
Junkystu
124

Das Letzte auf der Laravel Eloquent-Dokumentenseite ist:

protected $appends = array('is_admin');

Dies kann automatisch verwendet werden, um neue Accessoren zum Modell hinzuzufügen, ohne zusätzliche Arbeit wie das Ändern von Methoden wie ::toArray().

Erstellen Sie einfach getFooBarAttribute(...)Accessor und fügen Sie das foo_barzu $appendsArray.

trm42
quelle
4
Ah interessant. Diese Funktion wurde Laravel hinzugefügt, seit meine Frage veröffentlicht wurde ( github.com/laravel/framework/commit/… ). Dies ist die richtige Antwort für alle, die v4.08 oder höher verwenden.
Coatesap
3
Dies steht Ihnen nicht zur Verfügung, wenn Sie Beziehungen verwenden, um den Inhalt für Ihre Accessoren zu generieren. In diesem Fall müssen Sie möglicherweise die toArrayMethode überschreiben .
gpmcadam
2
Es scheint, als ob die Dokumentation, auf die Sie verwiesen haben, hierher verschoben wurde: https://laravel.com/docs/5.5/eloquent-serialization .
Mibbler
40

Wenn Sie Ihre getAvailability()Methode in getAvailableAttribute()Ihre Methode umbenennen , wird sie zu einem Accessor, und Sie können sie ->availabledirekt auf Ihrem Modell lesen .

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

BEARBEITEN: Da Ihr Attribut "virtuell" ist, ist es standardmäßig nicht in der JSON-Darstellung Ihres Objekts enthalten.

Aber ich habe folgendes gefunden: Benutzerdefinierte Modell-Accessoren werden nicht verarbeitet, wenn -> toJson () aufgerufen wird?

Um die Rückgabe Ihres Attributs im Array zu erzwingen, fügen Sie es als Schlüssel zum Array $ attribute hinzu.

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

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

Ich habe es nicht getestet, sollte aber für Sie ziemlich trivial sein, um es in Ihrem aktuellen Setup zu versuchen.

Alexandre Danault
quelle
Dies funktioniert so weit, wie es ->availablefür das $sessionObjekt verfügbar ist. $sessionsDa es jedoch direkt in eine JSON-Zeichenfolge konvertiert wird (es ist Teil einer API), besteht keine Möglichkeit, dies zu verwenden.
Coatesap
Ich bin mir nicht sicher, ob ich verstehe, wie deine Sachen funktionieren. Wenn Sie EventSession::all()ein JSON-Objekt von einer API zurückgeben, verwenden Sie nicht wirklich ein Laravel-Modell, oder? Entschuldigung, ich bin verwirrt darüber, wie Sie Ihr Modell implementiert haben.
Alexandre Danault
EventSession ist ein eloquentes Standardobjekt ( class EventSession extends Eloquent). ::all()ist nur als Beispiel. EventSession::find(170071)wäre ein anderer. Diese werden an verschiedenen Stellen in SessionController ( SessionController extends \BaseController) aufgerufen, die über URLs wie '/ session / 170071' aufgerufen werden.
Coatesap
Ich denke, der Schlüssel könnte in Eloquents integriertem Objekt zur JSON-Konvertierung liegen, das stattfindet. Selbst wenn Sie public $availabledem Modell ein benutzerdefiniertes Attribut hinzufügen , wird dieses beim Konvertieren des Objekts nicht angezeigt.
Coatesap
3
Dies ist seit der Veröffentlichung von Laravel 4.0.8 am 8. Oktober 2013 verfügbar. Siehe die offiziellen Dokumente: laravel.com/docs/eloquent#converting-to-arrays-or-json (suchen nach protected $appends = array('is_admin');)
Ronald Hulshof
23

Ich hatte etwas Ähnliches: Ich habe ein Attributbild in meinem Modell, das den Speicherort der Datei im Speicherordner enthält. Das Bild muss base64-codiert zurückgegeben werden

//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
quelle
1
Es sollte picture_data sein, nicht pictureData in $ attribute & $ appends. Sie können auch die Variable $ attribute überspringen.
Madushan Perera
16

Sie können die setAttributeFunktion in Model verwenden, um ein benutzerdefiniertes Attribut hinzuzufügen

jianfeng
quelle
9

Angenommen, Sie haben 2 Spalten mit den Namen Vorname und Nachname in Ihrer Benutzertabelle und möchten den vollständigen Namen abrufen. Sie können mit dem folgenden Code erreichen:

class User extends Eloquent {


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

Jetzt können Sie den vollständigen Namen erhalten als:

$user = User::find(1);
$user->full_name;
ShuBham GuPta
quelle
7

Schritt 1: Definieren Sie Attribute in $appends
Schritt 2: Definieren Sie den Accessor für diese Attribute.
Beispiel:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

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

In meinem Abonnementmodell muss ich wissen, dass das Abonnement angehalten ist oder nicht. Hier ist, wie ich es gemacht habe

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

Dann kann ich in der Ansichtsvorlage $subscription->is_pauseddas Ergebnis abrufen.

Das getIsPausedAttributeist das Format zum Festlegen eines benutzerdefinierten Attributs.

und verwendet is_paused, um das Attribut in Ihrer Ansicht abzurufen oder zu verwenden.

li bing zhao
quelle
2

In meinem Fall hat das Erstellen einer leeren Spalte und das Festlegen des Accessors einwandfrei funktioniert. Mein Accessor füllt das Alter des Benutzers aus der Dob-Spalte. Die Funktion toArray () funktionierte ebenfalls.

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