Laravel-Prüfung, ob ein verwandtes Modell vorhanden ist

151

Ich habe ein eloquentes Modell, das ein verwandtes Modell hat:

public function option() {
    return $this->hasOne('RepairOption', 'repair_item_id');
}

public function setOptionArrayAttribute($values)
{
    $this->option->update($values);
}

Wenn ich das Modell erstelle, muss es nicht unbedingt ein verwandtes Modell haben. Wenn ich es aktualisiere, kann ich eine Option hinzufügen oder nicht.

Ich muss also überprüfen, ob das zugehörige Modell vorhanden ist, um es entweder zu aktualisieren oder zu erstellen:

$model = RepairItem::find($id);
if (Input::has('option')) {
    if (<related_model_exists>) {
        $option = new RepairOption(Input::get('option'));
        $option->repairItem()->associate($model);
        $option->save();
        $model->fill(Input::except('option');
    } else {
       $model->update(Input::all());
    }
};

Wo <related_model_exists>ist der Code, den ich suche?

Tom Macdonald
quelle
3
Super Frage, danke! Und tolle Antworten an die Jungs unten. Spart mir Zeit bei meinem Projekt.
Rafael

Antworten:

197

In PHP 7.2+ können Sie nicht countfür das Beziehungsobjekt verwenden, daher gibt es keine Einheitsmethode für alle Beziehungen. Verwenden Sie stattdessen die unten angegebene Abfragemethode als @tremby:

$model->relation()->exists()

generische lösung für alle beziehungstypen ( vor php 7.2 ):

if (count($model->relation))
{
  // exists
}

Dies funktioniert für jede Beziehung, da dynamische Eigenschaften Modeloder zurückgeben Collection. Beide implementieren ArrayAccess.

So geht es also:

Einzelbeziehungen: hasOne / belongsTo/ morphTo/morphOne

// no related model
$model->relation; // null
count($model->relation); // 0 evaluates to false

// there is one
$model->relation; // Eloquent Model
count($model->relation); // 1 evaluates to true

zu viele Beziehungen: hasMany / belongsToMany/ morphMany/ morphToMany/morphedByMany

// no related collection
$model->relation; // Collection with 0 items evaluates to true
count($model->relation); // 0 evaluates to false

// there are related models
$model->relation; // Collection with 1 or more items, evaluates to true as well
count($model->relation); // int > 0 that evaluates to true
Jarek Tkaczyk
quelle
1
Lesen Sie die ganze Sache. count($relation)ist eine allgemeine Lösung für alle Beziehungen. Es wird für Modelund funktionieren Collection, obwohl Modeles keine ->count()Methode gibt.
Jarek Tkaczyk
7
@CurvianVynes Nicht, tut es nicht. Collectionhat eine eigene Methode isEmpty, aber die generische emptyFunktion gibt false für ein Objekt zurück (funktioniert daher nicht für leere Sammlungen).
Jarek Tkaczyk
1
count($model->relation)hat nicht funktioniert, morphTowenn für die Beziehung noch keine Zuordnung festgelegt wurde. Fremde ID und Typ sind null und die von Laravel erstellte Datenbankabfrage ist falsch und löst eine Ausnahme aus. Ich habe $model->relation()->getOtherKey()als Workaround verwendet.
Jocelyn
1
@ Jocelyn Ja, es ist ein beredter Fehler. Leider gibt es mindestens einige davon für polymorphe Beziehungen, so dass Sie sich offensichtlich in keiner Weise auf sie verlassen können.
Jarek Tkaczyk
2
Es wird auf PHP 7.2 brechen und zurückgeben:count(): Parameter must be an array or an object that implements Countable
CodeGodie
81

Ein Beziehungsobjekt leitet unbekannte Methodenaufrufe an einen Eloquent Query Builder weiter , der so eingerichtet ist, dass nur die zugehörigen Objekte ausgewählt werden. Dieser Builder leitet wiederum unbekannte Methodenaufrufe an den zugrunde liegenden Abfrage-Builder weiter .

Dies bedeutet, dass Sie die Methoden exists()oder count()direkt von einem Beziehungsobjekt aus verwenden können:

$model->relation()->exists(); // bool: true if there is at least one row
$model->relation()->count(); // int: number of related rows

Beachten Sie die Klammern nach relation: ->relation()ist ein Funktionsaufruf (Abrufen des Beziehungsobjekts), im Gegensatz dazu ->relationein von Laravel für Sie eingerichteter Getter für magische Eigenschaften (Abrufen des zugehörigen Objekts / der zugehörigen Objekte).

Die Verwendung der countMethode für das Beziehungsobjekt (dh die Verwendung der Klammern) ist viel schneller als die Ausführung $model->relation->count()oder count($model->relation)(es sei denn, die Beziehung wurde bereits eifrig geladen), da eine Zählabfrage ausgeführt wird, anstatt alle Daten für verwandte Objekte abzurufen aus der Datenbank, nur um sie zu zählen. Ebenso müssen bei der Verwendung existsauch keine Modelldaten abgerufen werden.

Sowohl exists()und count()Arbeit für alle Beziehungstypen habe ich versucht, so zumindest belongsTo, hasOne, hasMany, und belongsToMany.

tremby
quelle
existiert ist nicht im Lumen verfügbar, nicht sicher warum.
Briankip
@briankip - es sollte. Sind Sie sicher, dass Sie das Beziehungsobjekt (durch Aufrufen der Methode) und nicht die Auflistung (mithilfe der Eigenschaft magic) erhalten?
Tremby
18

Ich bevorzuge die existsMethode:

RepairItem::find($id)->option()->exists()

um zu überprüfen, ob ein verwandtes Modell vorhanden ist oder nicht. Es funktioniert gut mit Laravel 5.2

Hafez Divandari
quelle
1
+1; count ($ model-> relation) hat in Laravel 5.2 für mich true zurückgegeben, obwohl es kein Element in der Relationstabelle gab. -> existiert () macht den Trick.
Ben Wilson
9

Nach Php 7.1 funktioniert die akzeptierte Antwort nicht für alle Arten von Beziehungen.

Da Eloquent je nach Typ der Beziehung a Collection, a Modeloder zurückgibt Null. Und in Php 7.1 count(null) wird ein werfen error.

Um zu überprüfen, ob die Beziehung besteht, können Sie Folgendes verwenden:

Für Beziehungen einzeln: Zum Beispiel hasOneundbelongsTo

if(!is_null($model->relation)) {
   ....
}

Für mehrere Beziehungen: Zum Beispiel: hasManyundbelongsToMany

if ($model->relation->isNotEmpty()) {
   ....
}
Hemerson Varela
quelle
4

Ich bin mir nicht sicher, ob sich dies in Laravel 5 geändert hat, aber die akzeptierte Antwort mit count($data->$relation)hat bei mir nicht funktioniert, da der Zugriff auf die Relation-Eigenschaft dazu führte, dass sie geladen wurde.

Am Ende hat ein Unkomplizierter isset($data->$relation)den Trick für mich getan.

Dave Stewart
quelle
Ich glaube, es ist $data->relationohne $(kann nicht bearbeitet werden, da
maximal
2
Ah, das $relationwäre der Name Ihrer Beziehung, so $data->postsoder so. Entschuldigung, wenn das verwirrend war, wollte ich klarstellen, dass es relationsich nicht um eine konkrete Modelleigenschaft handelt: P
Dave Stewart
Dies funktionierte eine Weile, aber es funktionierte nicht mehr, nachdem ich Laravel von 5.2.29 auf 5.2.45 aktualisiert hatte. Irgendeine Idee warum oder wie man es behebt? Es führt jetzt dazu, dass die relationalen Daten aus irgendeinem Grund geladen werden.
Anthony
Ich habe eine Antwort hinzugefügt, die eine Lösung dafür enthält.
Anthony
3

Sie können die RelationLoaded- Methode für das Modellobjekt verwenden. Das hat meinen Speck so hoffentlich gerettet, dass es jemand anderem hilft. Ich erhielt diesen Vorschlag, als ich die gleiche Frage zu Laracasts stellte.

Anthony
quelle
2

Wie Hemerson Varela bereits in Php 7.1 sagte, count(null)wird ein geworfen errorund hasOnezurückgegeben, nullwenn keine Zeile existiert. Da Sie eine hasOneBeziehung haben, würde ich die emptyMethode verwenden, um Folgendes zu überprüfen:

$model = RepairItem::find($id);
if (!empty($temp = $request->input('option'))) {
   $option = $model->option;

   if(empty($option)){
      $option = $model->option()->create();
   }

   $option->someAttribute = temp;
   $option->save();
};

Das ist aber überflüssig. Es ist nicht erforderlich zu überprüfen, ob die Beziehung besteht, um festzustellen, ob Sie einen updateoder einen createAnruf tätigen sollten . Verwenden Sie einfach die updateOrCreate- Methode. Dies entspricht dem oben Gesagten:

$model = RepairItem::find($id);
if (!empty($temp = $request->input('option'))) {  
   $model->option()
         ->updateOrCreate(['repair_item_id' => $model->id],
                          ['option' => $temp]);
}
Adam
quelle
0

Ich musste meinen Code komplett überarbeiten, als ich meine PHP-Version auf 7.2+ aktualisierte, weil die Funktion count ($ x) nicht richtig verwendet wurde. Dies ist ein echter Schmerz und auch äußerst beängstigend, da es Hunderte von Anwendungen in verschiedenen Szenarien gibt und es keine Regeln gibt, die für alle geeignet sind.

Regeln, die ich befolgt habe, um alles umzugestalten, Beispiele:

$ x = Auth :: user () -> posts-> find (6); (Überprüfen Sie mit -> find (), ob der Benutzer eine Post-ID = 6 hat.)

[FAILS] if(count($x)) { return 'Found'; } 
[GOOD] if($x) { return 'Found'; }

$ x = Auth :: user () -> Profil-> Abteilungen; (Überprüfen Sie, ob das Profil einige Abteilungen hat. Es kann viele Abteilungen geben.)

[FAILS] if(count($x)) { return 'Found'; }
[GOOD] if($x->count()) { return 'Found'; }

$ x = Auth :: user () -> profile-> get (); (Überprüfen Sie, ob der Benutzer ein Profil hat, nachdem Sie ein -> get () verwendet haben.)

[FAILS] if(count($x)) { return 'Found'; }
[GOOD] if($x->count()) { return 'Found'; }

Ich hoffe, dies kann helfen, auch 5 Jahre nachdem die Frage gestellt wurde, hat mir dieser Stackoverflow-Beitrag sehr geholfen!

Raphjutras
quelle