Laravel vérifie si un modèle associé existe

151

J'ai un modèle Eloquent qui a un modèle connexe:

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

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

Lorsque je crée le modèle, il n'a pas nécessairement de modèle associé. Lorsque je le mets à jour, je peux ajouter une option, ou pas.

Je dois donc vérifier si le modèle associé existe, pour le mettre à jour ou le créer, respectivement:

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

<related_model_exists>est le code que je recherche.

Tom Macdonald
la source
3
Super question merci! Et d'excellentes réponses aux gars ci-dessous. M'a fait gagner du temps sur mon projet.
Rafael

Réponses:

197

Dans php 7.2+, vous ne pouvez pas utiliser countsur l'objet relation, il n'y a donc pas de méthode unique pour toutes les relations. Utilisez plutôt la méthode de requête comme @tremby fourni ci-dessous:

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

solution générique fonctionnant sur tous les types de relations ( pré php 7.2 ):

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

Cela fonctionnera pour chaque relation puisque les propriétés dynamiques renvoient Modelou Collection. Les deux implémentent ArrayAccess.

Donc ça va comme ça:

relations uniques: 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

relations à plusieurs: 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
la source
1
Lisez tout. count($relation)est une solution générale pour toutes les relations. Cela fonctionnera pour Modelet Collection, alors qu'il Modeln'y a pas de ->count()méthode.
Jarek Tkaczyk
7
@CurvianVynes Non, ce n'est pas le cas. Collectiona sa propre méthode isEmpty, mais la emptyfonction générique renvoie false pour un objet (donc ne fonctionnera pas pour une collection vide).
Jarek Tkaczyk
1
count($model->relation)n'a pas fonctionné morphTolorsque la relation n'avait pas encore d'association. L'ID et le type étrangers sont nuls et la requête db construite par Laravel est fausse et augmente l'exception. J'ai utilisé $model->relation()->getOtherKey()comme solution de contournement.
Jocelyn
1
@Jocelyn Oui, c'est un bug éloquent. Malheureusement, il y en a au moins quelques-uns pour les relations polymorphes, vous ne pouvez donc évidemment pas vous y fier.
Jarek Tkaczyk
2
Il cassera sur PHP 7.2, retournant:count(): Parameter must be an array or an object that implements Countable
CodeGodie
81

Un objet Relation transmet des appels de méthode inconnus à un générateur de requêtes Eloquent , qui est configuré pour sélectionner uniquement les objets associés. Ce générateur transmet à son tour les appels de méthode inconnus à son générateur de requêtes sous-jacent .

Cela signifie que vous pouvez utiliser les méthodes exists()ou count()directement à partir d'un objet de relation:

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

Notez les parenthèses après relation: ->relation()est un appel de fonction (obtenant l'objet de relation), par opposition à ->relationun getter de propriété magique configuré pour vous par Laravel (obtenant l'objet / les objets associés).

L'utilisation de la countméthode sur l'objet de relation (c'est-à-dire l'utilisation des parenthèses) sera beaucoup plus rapide que de faire $model->relation->count()ou count($model->relation)(sauf si la relation a déjà été chargée avec impatience) car elle exécute une requête de comptage plutôt que d'extraire toutes les données pour tous les objets liés de la base de données, juste pour les compter. De même, l'utilisation existsn'a pas non plus besoin d'extraire les données du modèle.

Les deux exists()et le count()travail sur tous les types de relations que j'ai essayé, donc au moins belongsTo, hasOne, hasManyet belongsToMany.

tremby
la source
existe n'est pas disponible en lumen, je ne sais pas pourquoi.
briankip
@briankip - ça devrait. Vous êtes sûr d'obtenir l'objet relation (en appelant la méthode) plutôt que la collection (en utilisant la propriété magic)?
tremby
18

Je préfère utiliser la existsméthode:

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

pour vérifier si le modèle associé existe ou non. Cela fonctionne bien sur Laravel 5.2

Hafez Divandari
la source
1
+1; count ($ model-> relation) retournait true pour moi dans Laravel 5.2 même s'il n'y avait aucun élément dans la table de relations. -> exist () fait l'affaire.
Ben Wilson
9

Après Php 7.1 , la réponse acceptée ne fonctionnera pas pour tous les types de relations.

Parce que selon le type de relation, Eloquent retournera un Collection, un Modelou Null. Et dans Php 7.1 count(null) jettera un fichier error.

Donc, pour vérifier si la relation existe, vous pouvez utiliser:

Pour les relations uniques: par exemple hasOneetbelongsTo

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

Pour les relations multiples: Par exemple: hasManyetbelongsToMany

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

Je ne sais pas si cela a changé dans Laravel 5, mais la réponse acceptée en utilisant count($data->$relation)n'a pas fonctionné pour moi, car l'acte même d'accéder à la propriété relation a provoqué son chargement.

En fin de compte, un simple a isset($data->$relation)fait l'affaire pour moi.

Dave Stewart
la source
Je crois que c'est $data->relationsans $(impossible de modifier, à cause de la limite de 6 caractères)
Zanshin13
2
Ah le $relationserait le nom de votre relation, tel $data->postsou tel. Désolé si cela prêtait à confusion, je voulais préciser que ce relationn'était pas une propriété modèle en béton: P
Dave Stewart
Cela fonctionnait pendant un certain temps, mais cela a cessé de fonctionner après la mise à jour de Laravel de 5.2.29 à 5.2.45. Une idée pourquoi ou comment y remédier? Cela provoque maintenant le chargement des données relationnelles pour une raison quelconque.
Anthony
J'ai ajouté une réponse qui a un correctif pour cela.
Anthony
3

Vous pouvez utiliser la méthode relationLoaded sur l'objet de modèle. Cela a sauvé mon bacon alors j'espère que cela aidera quelqu'un d'autre. J'ai reçu cette suggestion lorsque j'ai posé la même question sur les Laracasts.

Anthony
la source
2

Comme Hemerson Varela l'a déjà dit dans Php 7.1 count(null)lancera un erroret hasOneretournera nullsi aucune ligne n'existe. Puisque vous avez une hasOnerelation, j'utiliserais la emptyméthode pour vérifier:

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

Mais c'est superflu. Il n'est pas nécessaire de vérifier si la relation existe, pour déterminer si vous devez faire un updateou un createappel. Utilisez simplement la méthode updateOrCreate . Ceci est équivalent à ce qui précède:

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

J'ai dû refactoriser complètement mon code lorsque j'ai mis à jour ma version PHP vers 7.2+ en raison d'une mauvaise utilisation de la fonction count ($ x). C'est une vraie douleur et c'est aussi extrêmement effrayant car il y a des centaines d'utilisations, dans différents scénarios et il n'y a pas de règles uniques pour tous.

Règles que j'ai suivies pour tout refactoriser, exemples:

$ x = Auth :: user () -> posts-> find (6); (vérifiez si l'utilisateur a un post id = 6 en utilisant -> find ())

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

$ x = Auth :: user () -> profile-> départements; (vérifier si le profil a certains départements, il peut y avoir de nombreux départements)

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

$ x = Auth :: user () -> profile-> get (); (vérifiez si l'utilisateur a un profil après avoir utilisé un -> get ())

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

J'espère que cela peut aider, même 5 ans après que la question ait été posée, ce post de stackoverflow m'a beaucoup aidé!

raphjutras
la source