Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a polymorphic relation in a child model causes an infinite loop?

This question was already asked here but it received no answer. Now I face the same problem but in laravel 5.4. I have a model Book, a model ReadingSession and a model Comment. A book has many reading sessions and has many comments but the reading session can also have comments. So I have my relations defined like this:

Book.php

protected $with = [
    'author',
    'readingSessions',
    'userRating',
    'ratings',
    'comments'
];

public function users()
{
    return $this->belongsToMany(User::class, 'user_book');
}

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

public function allReadingSessions()
{
    return $this->hasMany(ReadingSession::class);
}

public function readingSessions()
{
    return $this->hasMany(ReadingSession::class)
                ->where('user_id', Auth::user()->id);
}

public function ratings()
{
    return $this->hasMany(Rating::class);
}

public function userRating()
{
    return $this->hasMany(Rating::class)
                ->where('user_id', Auth::user()->id);
}

public function comments()
{
    return $this->morphMany('App\Models\Comment', 'commentable');
}

ReadingSession.php

protected $with = ['comments'];

public function user()
{
    return $this->belongsTo(User::class);
}

public function book()
{
    return $this->belongsTo(Book::class);
}

public function comments()
{
    return $this->morphMany('App\Models\Comment', 'commentable');
}

Comment.php

public function commentable()
{
    return $this->morphTo();
}

These seems to create an infinite loop. Can anyone hint me on what I'm doing wrong?

like image 966
António Quadrado Avatar asked Oct 18 '22 06:10

António Quadrado


1 Answers

The main reason you might have an infinite loop there is if you are trying to load automatically a relationship that in turn tries to do the same with the previous model.

Putting it into an example:

Book.php

protected $with = [
    'author',
];

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

Author.php

protected $with = [
    'books',
];

public function books()
{
    return $this->hasMany(Book::class);
}

In this case, every time you fetch an author it will fetch automatically his books that in turn will try to fetch the author and on and on...

One other thing that might happen and it's harder to realize is when using the $appends property on some accessors. If you are trying automatically had a variable into a model through the $appends and if that accessor fetches a relation or uses a relation in some way you might get an infinite loop again.

Example: Author.php

protected $appends = [
    'AllBooks',
];

public function books()
{
    return $this->hasMany(Book::class);
}

public function getAllBooksAttribute() {
    return $this->books->something...
}

In this case, every time the app tries to resolve your Author model it will fetch the books, that in turn will fetch the Author, that in turn will fetch the books again and on and on...

From your snippets, is not clear what is causing the problem but this answer might give some leads where to search for it.

To solve it, you might remove the relation from the $with and load it manually: $author->load('books') or Author::with('books')->where... You can also load a relation of a relation in this way, for example: $author->load('books', 'books.comments') or Author::with('books', 'books.comments')->where...

It all comes down what you are trying to achieve. So you have to evaluate what and what not you should auto-load.

Be careful when loading automatically relations on your models and when adding accessors to $appends, especially if they use relations. It is an awesome feature but can bite hard sometimes.

like image 68
flipjms Avatar answered Oct 27 '22 11:10

flipjms