Sample zero object with pronounced relationships

It often happens that some eloquent relation of the model is not defined (i.e., in the table of books it author_idis null) and, thus, calling something like the relation $ model-> returns null.

eg. let's say the book model has an author () (hasOne) relation, which I can do

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

If Book 1 does not have an established author, this will cause the error "try to get the property of a non-object." Is there a way to avoid this and use empty by default Author, so I can always call nameon it regardless of whether the relationship is set for a particular model?

Essentially, I want to avoid conventions in order to check if it is $book->authoractual Authorbefore calling additional methods / properties on it. It should by default use a new instance of the author if the relationship is not established.


I tried something like:

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

however, this does not work; $authorpassed as null, even if it is specified on the model. Presumably because this is an attitude, not a direct property of the book. I need something like

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

which seems rather inelegant and seems to undo any loadable download, resulting in poor performance.

+4
source share
3 answers

Update

Laravel 5.3.23, ( , HasOne). HasOne withDefault(). Book/Author :

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

( ) Author, . , , , , ( Author).

, , , : 16198 16382.

HasOne. BelongsTo, MorphOne MorphTo, .


, , .

, , , $value, , null, . , , .

, , , .

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;
}

, hasOne/belongsTo .

, , . , , , dd() toArray/toJson , null relatioinship .

Model. .

Model, Laravel Model , Model Laravel Model.

, setRelation(). Laravel >= 5.2.30, . Laravel < 5.2.30 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
{
    //
}
+6

. , , , , .

foreach , , null. , .

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

, $this->productivity->something , , , .

.

0

, .

factory ModelFactory.php

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

add values ​​for all necessary attributes I use dummy values ​​from Faker, but you can use whatever you want.

Then, inside your book model, you can return an Author instance as follows:

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

Source: https://habr.com/ru/post/1611306/


All Articles