Laravel Check si existe un modelo relacionado

151

Tengo un modelo Eloquent que tiene un modelo relacionado:

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

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

Cuando creo el modelo, no necesariamente tiene un modelo relacionado. Cuando lo actualizo, podría agregar una opción, o no.

Por lo tanto, debo verificar si existe el modelo relacionado, para actualizarlo o crearlo, respectivamente:

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

¿Dónde <related_model_exists>está el código que estoy buscando?

Tom Macdonald
fuente
3
Impresionante pregunta gracias! Y excelentes respuestas para los chicos de abajo. Me ahorró tiempo en mi proyecto.
Rafael

Respuestas:

197

En php 7.2+ no se puede usar counten el objeto de relación, por lo que no hay un método único para todas las relaciones. Utilice el método de consulta en su lugar como @tremby proporcionado a continuación:

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

solución genérica que funciona en todos los tipos de relación ( pre php 7.2 ):

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

Esto funcionará para cada relación ya que las propiedades dinámicas regresan Modelo Collection. Ambos implementan ArrayAccess.

Entonces va así:

relaciones individuales: 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

relaciones de muchos: 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
fuente
1
Lee todo. count($relation)Es una solución general para todas las relaciones. Funcionará para Modely Collection, aunque Modelno tiene ningún ->count()método.
Jarek Tkaczyk
77
@CurvianVynes No, no lo hace. Collectiontiene su propio método isEmpty, pero la emptyfunción genérica devuelve falso para un objeto (por lo tanto, no funcionará para una colección vacía).
Jarek Tkaczyk
1
count($model->relation)no funcionó morphTocuando la relación aún no tenía una asociación establecida. La identificación y el tipo externos son nulos y la consulta de db creada por Laravel es falsa y genera una excepción. Solía $model->relation()->getOtherKey()como una solución alternativa.
Jocelyn
1
@Jocelyn Sí, es un error elocuente. Desafortunadamente, hay al menos algunos de ellos para las relaciones polimórficas, por lo que obviamente no puede confiar en ellos de ninguna manera.
Jarek Tkaczyk
2
Se romperá en PHP 7.2, regresando:count(): Parameter must be an array or an object that implements Countable
CodeGodie
81

Un objeto de relación pasa llamadas de métodos desconocidos a un generador de consultas elocuente , que está configurado para seleccionar solo los objetos relacionados. Ese generador a su vez pasa llamadas de métodos desconocidos a su generador de consultas subyacente .

Esto significa que puede usar los métodos exists()o count()directamente desde un objeto de relación:

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

Tenga en cuenta los paréntesis después de relation: ->relation()es una llamada a la función (obtener el objeto de relación), en lugar de ->relationque Laravel configure un captador de propiedades mágicas para usted (obtener el objeto / objetos relacionados).

Usar el countmétodo en el objeto de relación (es decir, usar los paréntesis) será mucho más rápido que hacerlo $model->relation->count()o count($model->relation)(a menos que la relación ya se haya cargado con entusiasmo) ya que ejecuta una consulta de conteo en lugar de extraer todos los datos de cualquier objeto relacionado de la base de datos, solo para contarlos. Del mismo modo, el uso existstampoco necesita extraer datos del modelo.

Tanto exists()y count()el trabajo en todos los tipos de relación que he probado, así que al menos belongsTo, hasOne, hasMany, y belongsToMany.

tembloroso
fuente
existe no está disponible en lumen, no estoy seguro de por qué.
briankip
@briankip: debería. ¿Estás seguro de que estás obteniendo el objeto de relación (llamando al método) en lugar de la colección (usando la propiedad mágica)?
temblor
18

Prefiero usar el existsmétodo:

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

para verificar si el modelo relacionado existe o no. Está funcionando bien en Laravel 5.2

Hafez Divandari
fuente
1
+1; count ($ modelo-> relación) estaba volviendo verdadero para mí en Laravel 5.2 a pesar de que no había ningún elemento en la tabla de relaciones. -> existe () hace el truco.
Ben Wilson
9

Después de Php 7.1 , la respuesta aceptada no funcionará para todo tipo de relaciones.

Porque dependiendo del tipo de relación, Eloquent devolverá a Collection, a Modelo Null. Y en Php 7.1 count(null) lanzará un error.

Entonces, para verificar si existe la relación, puede usar:

Para relaciones individuales: por ejemplo hasOneybelongsTo

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

Para relaciones múltiples: Por ejemplo: hasManyybelongsToMany

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

No estoy seguro de si esto ha cambiado en Laravel 5, pero la respuesta aceptada usando count($data->$relation)no funcionó para mí, ya que el solo hecho de acceder a la propiedad de relación causó que se cargara.

Al final, un sencillo me isset($data->$relation)sirvió.

Dave Stewart
fuente
Creo que es $data->relationsin $(no se puede editar, debido al límite de 6 caracteres)
Zanshin13
2
Ah $relation, sería el nombre de su relación, tal $data->postso cual. Lo siento si eso fue confuso, quería dejar en claro que relationno era una propiedad modelo concreta: P
Dave Stewart
Esto funcionó por un tiempo, pero dejó de funcionar después de actualizar Laravel de 5.2.29 a 5.2.45. ¿Alguna idea de por qué o cómo solucionarlo? Ahora está causando que los datos relacionales se carguen por alguna razón.
Anthony
Agregué una respuesta que tiene una solución para esto.
Anthony
3

Se puede utilizar el relationLoaded método en el objeto del modelo. Esto salvó mi tocino, así que espero que ayude a alguien más. Me dieron esta sugerencia cuando hice la misma pregunta sobre Laracasts.

Antonio
fuente
2

Como Hemerson Varela ya dijo en Php 7.1 count(null)arrojará un errory hasOneregresa nullsi no existe una fila. Ya que tienes unhasOne relación, usaría el emptymétodo para verificar:

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

Pero esto es superfluo. No es necesario verificar si la relación existe, para determinar si debe hacer una updateo una createllamada. Simplemente use el método updateOrCreate . Esto es equivalente a lo anterior:

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

Tuve que refactorizar completamente mi código cuando actualicé mi versión de PHP a 7.2+ debido al mal uso de la función de conteo ($ x). Esto es un verdadero dolor y también es extremadamente aterrador, ya que hay cientos de usos, en diferentes escenarios y no hay reglas para todos.

Reglas que seguí para refactorizar todo, ejemplos:

$ x = Auth :: user () -> posts-> find (6); (verifique si el usuario tiene un id de publicación = 6 usando -> find ())

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

$ x = Auth :: usuario () -> perfil-> departamentos; (verifique si el perfil tiene algunos departamentos, puede haber muchos departamentos)

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

$ x = Auth :: user () -> perfil-> get (); (verifique si el usuario tiene un perfil después de usar un -> get ())

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

Espero que esto pueda ayudar, incluso 5 años después de que se haya hecho la pregunta, ¡esta publicación de stackoverflow me ha ayudado mucho!

raphjutras
fuente