Laravel Verifica se esiste un modello correlato


151

Ho un modello eloquente che ha un modello correlato:

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

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

Quando creo il modello, non ha necessariamente un modello correlato. Quando lo aggiorno, potrei aggiungere un'opzione o no.

Quindi devo controllare se esiste il modello correlato, per aggiornarlo o crearlo, rispettivamente:

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

Dov'è <related_model_exists>il codice che sto cercando.


3
Fantastica domanda grazie! E ottime risposte ai ragazzi qui sotto. Mi ha fatto risparmiare tempo sul mio progetto.
Rafael,

Risposte:


197

In php 7.2+ non è possibile utilizzare countl'oggetto relazione, quindi non esiste un metodo completo per tutte le relazioni. Utilizzare invece il metodo di query come @tremby fornito di seguito:

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

soluzione generica che funziona su tutti i tipi di relazione ( pre php 7.2 ):

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

Questo funzionerà per ogni relazione poiché restituiscono proprietà dinamiche Modelo Collection. Entrambi implementano ArrayAccess.

Quindi va così:

relazioni singole: 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

a molte relazioni: 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

1
Leggi tutto. count($relation)è una soluzione generale per tutte le relazioni. Funzionerà per Modele Collection, mentre Modelnon ha alcun ->count()metodo.
Jarek Tkaczyk,

7
@CurvianVynes No, non è così. Collectionha il suo metodo isEmpty, ma la emptyfunzione generica restituisce false per un oggetto (quindi non funzionerà per una raccolta vuota).
Jarek Tkaczyk,

1
count($model->relation)non ha funzionato morphToquando la relazione non aveva ancora alcuna associazione. ID e tipo esterni sono nulli e la query db creata da Laravel è falsa e genera un'eccezione. Ho usato $model->relation()->getOtherKey()come soluzione alternativa.
Jocelyn,

1
@Jocelyn Sì, è un bug eloquente. Sfortunatamente ce ne sono almeno alcuni per le relazioni polimorfiche, quindi ovviamente non puoi fare affidamento su di essi in alcun modo.
Jarek Tkaczyk,

2
Si interromperà su PHP 7.2, restituendo:count(): Parameter must be an array or an object that implements Countable
CodeGodie il

81

Un oggetto Relation passa chiamate di metodo sconosciute a un generatore di query Eloquent , impostato per selezionare solo gli oggetti correlati. A sua volta, quel Builder passa chiamate di metodo sconosciute alla sua query Builder sottostante .

Ciò significa che è possibile utilizzare i metodi exists()o count()direttamente da un oggetto relazione:

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

Nota le parentesi dopo relation: ->relation()è una chiamata di funzione (ottenere l'oggetto relazione), al contrario della ->relationquale un getter di proprietà magiche impostato per te da Laravel (ottenere l'oggetto / gli oggetti correlati).

L'utilizzo del countmetodo sull'oggetto relazione (ovvero l'uso delle parentesi) sarà molto più rapido rispetto a fare $model->relation->count()o count($model->relation)((a meno che la relazione non sia già stata caricata con entusiasmo) poiché esegue una query di conteggio anziché estrarre tutti i dati per eventuali oggetti correlati dal database, solo per contarli. Allo stesso modo, l'utilizzo existsnon richiede neanche di estrarre i dati del modello.

Sia exists()e count()il lavoro su tutti i tipi di relazione che ho provato, così almeno belongsTo, hasOne, hasMany, e belongsToMany.


esiste non è disponibile in lumen, non so perché.
briankip,

@briankip - dovrebbe. Sei sicuro di ottenere l'oggetto relazione (chiamando il metodo) piuttosto che la raccolta (usando la proprietà magic)?
tremby,

18

Preferisco usare il existsmetodo:

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

per verificare se esiste un modello correlato o meno. Funziona bene su Laravel 5.2


1
+1; il conteggio ($ modello-> relazione) stava tornando vero per me in Laravel 5.2 anche se non c'era alcun elemento nella tabella delle relazioni. -> esiste () fa il trucco.
Ben Wilson,

9

Dopo Php 7.1 , la risposta accettata non funzionerà per tutti i tipi di relazioni.

Perché a seconda del tipo di relazione, Eloquent restituirà a Collection, a Modelo Null. E in Php 7.1 count(null) lancerà un error.

Quindi, per verificare l'esistenza della relazione è possibile utilizzare:

Per relazioni singole: ad esempio hasOneebelongsTo

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

Per relazioni multiple: Ad esempio: hasManyebelongsToMany

if ($model->relation->isNotEmpty()) {
   ....
}

4

Non sono sicuro che questo sia cambiato in Laravel 5, ma la risposta accettata usando count($data->$relation)non ha funzionato per me, poiché l'atto stesso di accedere alla proprietà della relazione ha causato il suo caricamento.

Alla fine, un semplice ha isset($data->$relation)fatto il trucco per me.


Credo che sia $data->relationsenza $(impossibile modificare, a causa del limite di 6 caratteri)
Zanshin13

2
Ah, $relationsarebbe il nome della tua relazione, come $data->postso simile. Scusate se era confuso, volevo chiarire che relationnon era una proprietà del modello concreto: P
Dave Stewart

Funzionava da un po ', ma ha smesso di funzionare dopo aver aggiornato Laravel dalla 5.2.29 alla 5.2.45. Qualche idea sul perché o come risolverlo? Ora sta causando il caricamento dei dati relazionali per qualche motivo.
Anthony,

Ho aggiunto una risposta che ha una correzione per questo.
Anthony,


2

Come ha già detto Hemerson Varela in Php 7.1 count(null), lancerà errore hasOneritorna nullse non esiste alcuna riga. Dato che hai una hasOnerelazione, utilizzerei il emptymetodo per verificare:

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

Ma questo è superfluo. Non è necessario verificare l'esistenza della relazione, per determinare se è necessario effettuare una updateo una createchiamata. Usa semplicemente il metodo updateOrCreate . Ciò equivale a quanto sopra:

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

0

Ho dovuto refactificare completamente il mio codice quando ho aggiornato la mia versione di PHP alla versione 7.2+ a causa del cattivo utilizzo della funzione count ($ x). Questo è un vero dolore ed è anche estremamente spaventoso in quanto ci sono centinaia di usi, in diversi scenari e non esiste una regola adatta a tutti.

Regole che ho seguito per refactoring tutto, esempi:

$ x = Auth :: user () -> posts-> find (6); (controlla se l'utente ha un post id = 6 usando -> find ())

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

$ x = Auth :: user () -> profilo-> dipartimenti; (controlla se il profilo ha alcuni dipartimenti, possono esserci molti dipartimenti)

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

$ x = Auth :: user () -> profile-> get (); (controlla se l'utente ha un profilo dopo aver usato un -> get ())

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

Spero che questo possa aiutare, anche 5 anni dopo che la domanda è stata posta, questo post di StackOverflow mi ha aiutato molto!

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.