styde relaciones relacion que hasone hacer entidades ejemplo belongstomany belongsto php laravel laravel-5 eloquent

php - hasone - Patrón de objeto nulo con relaciones Eloquent



que es belongsto laravel (3)

Puedes lograrlo usando fábricas modelo.

Defina una fábrica de autor dentro de su ModelFactory.php

$factory->define(App/Author::class, function (Faker/Generator $faker) { return [ ''name'' => $faker->firstName, //or null ''avatar'' => $faker->imageUrl() //or null ]; });

agregue valores para todos los atributos necesarios. Estoy usando valores ficticios de Faker, pero puede usar cualquier cosa que desee.

Luego, dentro de su modelo de libro puede devolver una instancia de autor como esta:

public function getAuthorAttribute($author) { return $author ?: factory(App/Author::class)->make(); }

A menudo ocurre que una determinada relación de modelo elocuente no está establecida (es decir, en una tabla de libros, author_id es nulo) y, por lo tanto, llama a algo como $ model-> relation returns null.

Por ejemplo, un modelo de libro tiene una relación de autor () (hasOne) que podría querer hacer

$author = Book::find(1)->author->name;

Si el Libro 1 no tiene un autor establecido arrojará un error "tratando de obtener la propiedad de un objeto no". ¿Hay alguna forma de evitar esto y de forma predeterminada con un Author blanco, así que siempre podré invocar el name independientemente de si la relación se ha establecido para el modelo específico?

Básicamente, quiero evitar condicionales para comprobar si $book->author es un Author real antes de llamar a otros métodos / propiedades en él. Debería establecerse por defecto a una nueva instancia de Autor si la relación no está establecida.

Intenté algo como:

public function getAuthorAttribute($author) { return $author ?: new Author; }

sin embargo, esto no funciona; $author se pasa como nulo, incluso si está configurado en el modelo. Presumiblemente porque es una relación más que una propiedad directa de un libro. Necesitaría algo como

public function getAuthorAttribute() { return $this->author()->first() ?: new Author; }

que parece muy poco elegante y parece que anularía cualquier carga ansiosa que produjera un rendimiento deficiente.


Tuve el mismo problema en mi proyecto. En mi opinión, hay algunas filas que acceden a las propiedades dinámicas de las relaciones nulas, pero en lugar de devolver un campo vacío, la aplicación fue trepidante y una excepción.

Acabo de agregar un bucle foreach en mi controlador como una solución temporal que verifica en cada valor de la colección si la relación es nula. Si este caso es verdadero, asigna una nueva instancia del modelo de deseo a ese valor.

foreach ($shifts as $shift) { if (is_null($shift->productivity)) { $shift->productivity = new Productivity(); } }

De esta forma, cuando tengo acceso a $this->productivity->something en mi opinión cuando la relación no está establecida, obtengo un valor vacío en lugar de una excepción sin poner ninguna lógica en mis vistas ni reemplazar los métodos.

Esperando una mejor solución para hacer esto automáticamente.


Actualizar

A partir de Laravel 5.3.23, ahora hay una forma integrada para lograr esto (al menos para HasOne relaciones HasOne ). Se withDefault() un método withDefault() a la relación HasOne . En el caso de su ejemplo de Book / Author , su código se vería así:

public function author() { return $this->hasOne(Author::class)->withDefault(); }

Esta relación ahora devolverá un modelo de Author bastante vacío (se establecen las claves) si no se encuentra ningún registro en la base de datos. Además, puede pasar una serie de atributos si desea rellenar su modelo vacío con algunos datos adicionales, o puede pasar un Cierre que devuelve lo que le gustaría tener su configuración predeterminada (no tiene ser un modelo de Author ).

Hasta que esto llegue a la documentación un día, para obtener más información, puede consultar las solicitudes de extracción relacionadas con el cambio: 16198 y 16382 .

En el momento de escribir esto, esto solo se ha implementado para la relación HasOne . Eventualmente puede migrar a las relaciones BelongsTo , MorphOne y MorphTo , pero no puedo asegurarlo.

Original

No hay una forma de construir que conozca para hacer esto, pero hay un par de soluciones.

Usando un accesorio

El problema con el uso de un descriptor de acceso, como descubriste, es que el $value pasa al descriptor de acceso siempre será null , ya que se completa desde la matriz de atributos en el modelo. Esta matriz de atributos no incluye las relaciones, ya estén cargadas o no.

Si desea intentar resolver esto con un descriptor de acceso, simplemente ignorará el valor que se transfiere y comprobará la relación usted mismo.

public function getAuthorAttribute($value) { $key = ''author''; /** * If the relationship is already loaded, get the value. Otherwise, attempt * to load the value from the relationship method. This will also set the * key in $this->relations so that subsequent calls will find the key. */ if (array_key_exists($key, $this->relations)) { $value = $this->relations[$key]; } elseif (method_exists($this, $key)) { $value = $this->getRelationshipFromMethod($key); } $value = $value ?: new Author(); /** * This line is optional. Do you want to set the relationship value to be * the new Author, or do you want to keep it null? Think of what you''d * want in your toArray/toJson output... */ $this->setRelation($key, $value); return $value; }

Ahora, el problema al hacer esto en el acceso es que necesita definir un acceso para cada relación hasOne / belongsTo en cada modelo.

Un segundo problema, más pequeño, es que el descriptor de acceso solo se usa al acceder al atributo. Entonces, por ejemplo, si tuvieras que cargar la relación, y luego dd() o toArray / toJson el modelo, aún mostraría null para el relatioinship, en lugar de un Autor vacío.

Reemplazar los métodos del modelo

Una segunda opción, en lugar de usar acceso de atributos, sería anular algunos métodos en el Model . Esto resuelve los dos problemas al usar un acceso de atributo.

Puede crear su propia clase de Model base que amplíe el Model Laravel y anule estos métodos, y luego todos sus otros modelos extenderán su clase de Model base, en lugar de la clase de Model de Laravel.

Para manejar relaciones cargadas ansiosas, necesitaría anular el método setRelation() . Si usa Laravel> = 5.2.30, esto también manejará relaciones cargadas perezosas. Si usa Laravel <5.2.30, también tendrá que anular el método getRelationshipFromMethod() para las relaciones de carga getRelationshipFromMethod() .

MyModel.php

class MyModel extends Model { /** * Handle eager loaded relationships. Call chain: * Model::with() => Builder::with(): sets builder eager loads * Model::get() => Builder::get() => Builder::eagerLoadRelations() => Builder::loadRelation() * =>Relation::initRelation() => Model::setRelation() * =>Relation::match() =>Relation::matchOneOrMany() => Model::setRelation() */ public function setRelation($relation, $value) { /** * Relationships to many records will always be a Collection, even when empty. * Relationships to one record will either be a Model or null. When attempting * to set to null, override with a new instance of the expected model. */ if (is_null($value)) { // set the value to a new instance of the related model $value = $this->$relation()->getRelated()->newInstance(); } $this->relations[$relation] = $value; return $this; } /** * This override is only needed in Laravel < 5.2.30. In Laravel * >= 5.2.30, this method calls the setRelation method, which * is already overridden and contains our logic above. * * Handle lazy loaded relationships. Call chain: * Model::__get() => Model::getAttribute() => Model::getRelationshipFromMethod(); */ protected function getRelationshipFromMethod($method) { $results = parent::getRelationshipFromMethod($method); /** * Relationships to many records will always be a Collection, even when empty. * Relationships to one record will either be a Model or null. When the * result is null, override with a new instance of the related model. */ if (is_null($results)) { $results = $this->$method()->getRelated()->newInstance(); } return $this->relations[$method] = $results; } }

Book.php

class Book extends MyModel { // }