Laravel Sprawdź, czy istnieje powiązany model

151

Mam model Eloquent, który ma powiązany model:

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

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

Kiedy tworzę model, niekoniecznie ma on powiązany model. Kiedy ją aktualizuję, mogę dodać opcję lub nie.

Muszę więc sprawdzić, czy powiązany model istnieje, odpowiednio go zaktualizować lub utworzyć:

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

Gdzie <related_model_exists>jest kod, którego szukam.

Tom Macdonald
źródło
3
Niesamowite pytanie, dziękuję! I świetne odpowiedzi dla facetów poniżej. Zaoszczędził mi czas nad projektem.
Rafael

Odpowiedzi:

197

W php 7.2+ nie możesz używać countobiektu relacji, więc nie ma jednej metody dla wszystkich relacji. Zamiast tego użyj metody zapytania, jak podano poniżej @tremby:

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

ogólne rozwiązanie działające na wszystkich typach relacji ( pre php 7.2 ):

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

To zadziała dla każdej relacji, ponieważ właściwości dynamiczne zwracają Modellub Collection. Obie implementują ArrayAccess.

A więc wygląda to tak:

pojedyncze relacje: 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

relacje do wielu: 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
źródło
1
Przeczytaj całość. count($relation)to ogólne rozwiązanie dla wszystkich relacji. Będzie działać dla Modeli Collectionchociaż Modelnie ma ->count()metody.
Jarek Tkaczyk
7
@CurvianVynes Nie, tak nie jest. Collectionma swoją własną metodę isEmpty, ale emptyfunkcja ogólna zwraca wartość false dla obiektu (dlatego nie będzie działać dla pustej kolekcji).
Jarek Tkaczyk
1
count($model->relation)nie działał, morphTogdy związek nie miał jeszcze ustawionego skojarzenia. Zagraniczny identyfikator i typ są zerowe, a zapytanie db zbudowane przez Laravela jest fałszywe i rodzi wyjątek. Użyłem $model->relation()->getOtherKey()jako obejścia.
Jocelyn
1
@Jocelyn Tak, to błąd elokwentny. Niestety jest ich co najmniej kilka dla relacji polimorficznych, więc oczywiście nie można na nich w żaden sposób polegać.
Jarek Tkaczyk
2
count(): Parameter must be an array or an object that implements Countable
Zepsuje się
81

Obiekt Relation przekazuje nieznane wywołania metod do elokwentnego konstruktora zapytań , który jest skonfigurowany do wybierania tylko powiązanych obiektów. Ten Konstruktor z kolei przekazuje nieznane wywołania metod do swojego podstawowego Konstruktora zapytań .

Oznacza to, że możesz użyć metod exists()lub count()bezpośrednio z obiektu relacji:

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

Zwróć uwagę na nawiasy po relation: ->relation()to wywołanie funkcji (pobieranie obiektu relacji), w przeciwieństwie do tego, ->relationktóry pobiera magiczną właściwość ustawioną dla ciebie przez Laravela (pobieranie powiązanego obiektu / obiektów).

Użycie countmetody na obiekcie relacji (to znaczy użycie nawiasów) będzie znacznie szybsze niż wykonanie $model->relation->count()lub count($model->relation)(chyba że relacja została już chętnie załadowana), ponieważ wykonuje zapytanie zliczające, zamiast pobierać wszystkie dane dla dowolnych powiązanych obiektów z bazy danych, żeby je policzyć. Podobnie używanie existsnie wymaga również pobierania danych modelu.

Zarówno exists()i count()praca na wszystkich typach relacji próbowałem, więc przynajmniej belongsTo, hasOne, hasMany, i belongsToMany.

drżenie
źródło
istnieje nie jest dostępny w lumenach, nie wiem dlaczego.
briankip
@briankip - powinno. Jesteś pewien, że otrzymujesz obiekt relacji (przez wywołanie metody), a nie kolekcję (za pomocą właściwości magic)?
drżenie
18

Wolę używać existsmetody:

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

aby sprawdzić, czy powiązany model istnieje, czy nie. Działa dobrze na Laravel 5.2

Hafez Divandari
źródło
1
+1; count ($ model-> relations) zwracało dla mnie prawdę w Laravel 5.2, mimo że w tabeli relacji nie było pozycji. -> exist () załatwia sprawę.
Ben Wilson,
9

Po PHP 7.1 Zaakceptowana odpowiedź nie będzie działać dla wszystkich typów relacji.

Ponieważ w zależności od typu relacji, Eloquent zwróci a Collection, a Modellub Null. A w PHP 7.1 count(null) wyrzuci plik error.

Aby więc sprawdzić, czy relacja istnieje, możesz użyć:

W przypadku relacji singiel: na przykład hasOneibelongsTo

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

Dla wielu relacji: Na przykład: hasManyibelongsToMany

if ($model->relation->isNotEmpty()) {
   ....
}
Hemerson Varela
źródło
4

Nie jestem pewien, czy zmieniło się to w Laravel 5, ale zaakceptowana odpowiedź count($data->$relation)nie zadziałała dla mnie, ponieważ sam akt uzyskania dostępu do właściwości relacji spowodował jej załadowanie.

W końcu prosty isset($data->$relation)załatwił mi sprawę.

Dave Stewart
źródło
$data->relation$
Wydaje
2
Ach, to $relationbyłaby nazwa twojego związku, taka $data->postslub taka. Przepraszam, jeśli to było mylące, chciałem wyjaśnić, że relationnie jest to konkretna właściwość modelu: P
Dave Stewart
To działało przez jakiś czas, ale przestało działać po zaktualizowaniu Laravel z 5.2.29 do 5.2.45. Masz jakiś pomysł, dlaczego lub jak to naprawić? Z jakiegoś powodu powoduje teraz ładowanie danych relacyjnych.
Anthony
Dodałem odpowiedź, która ma na to poprawkę.
Anthony
3

Możesz użyć metody relationsLoaded na obiekcie modelu. To uratowało mój bekon, więc mam nadzieję, że pomoże to komuś innemu. Byłem biorąc pod uwagę tę sugestię , kiedy to samo pytanie na Laracasts.

Anthony
źródło
2

Jak już powiedział Hemerson Varela w Php 7.1 count(null), wyrzuci errori hasOnezwróci, nulljeśli nie ma żadnego wiersza. Ponieważ masz hasOnerelację, użyłbym emptymetody do sprawdzenia:

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

Ale to jest zbyteczne. Nie ma potrzeby sprawdzania, czy relacja istnieje, aby określić, czy należy wykonać wywołanie, updateczy createwywołanie. Po prostu użyj metody updateOrCreate . Jest to równoważne z powyższym:

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

Musiałem całkowicie zmienić kod, kiedy zaktualizowałem moją wersję PHP do 7.2+ z powodu złego użycia funkcji count ($ x). Jest to prawdziwy ból, a także niezwykle przerażający, ponieważ istnieją setki zastosowań, w różnych scenariuszach i nie ma jednej reguły dla wszystkich.

Zasady, których przestrzegałem, aby wszystko refaktoryzować, przykłady:

$ x = Auth :: user () -> posts-> find (6); (sprawdź, czy użytkownik ma post id = 6 używając -> find ())

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

$ x = Auth :: user () -> profile-> departments; (sprawdź, czy profil ma kilka działów, może mieć wiele działów)

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

$ x = Auth :: user () -> profile-> get (); (sprawdź, czy użytkownik ma profil po użyciu a -> get ())

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

Mam nadzieję, że to pomoże, nawet 5 lat po zadaniu pytania ten post o przepływie stosów bardzo mi pomógł!

raphjutras
źródło