Question

I have a model for book and one for author. The book model has a belongsTo author relationship, and author hasMany books.

I have a sql query that gives me some authors and now i want all those authors books together in one list.

I thought I could write:

$authors->books

But this gives me the error

Undefined property: Illuminate\Database\Eloquent\Collection::$books 

I know I can call $author->books if it is a single author I have, but what if I like in this case a collection of authors, how can I get all their books together?

Was it helpful?

Solution

Since you're retrieving multiple Authors, Laravel returns it as a Collection. You cannot query on a collection, which means you have to run through it with a foreach for example.

By iterating through the collection

$allBooks = array();

foreach ($authors as $author){
    $authorBooks = $author->books->get()->toArray();
    $allBooks = array_merge($allBooks, $authorBooks);
}

I do recommend using scopes though.

An other solution might be working with scopes:

Create an array with your author id's. You can use ->lists('id') for this when using an object. (In this example, I take all authors, but you can restrict with ->where or a scope.)

$authorids= Author::all()->lists('id');

Create a scopeAuthorIds in your Book model, something like this:

public function scopeAuthorIds($query, $ids)
{
return $query->whereIn('authorId', $ids);
}

You should be able to filter your books on the author id's now, by doing

$allBooks = Book::AuthorIds($authorids)->get();

OTHER TIPS

Just use with('books') eager loading:

$authors = Author::with('books')->get();

Make sure you have following function in Author model:

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

Using of ::with('Book') will solves (less queries) the n+ query (known as eager loading) problem. Next, you can use a loop like this:

foreach($authors as $author) {
    // Assumed you have a name property in authors table
    echo $author->name; // in blade use {{ $author->name }} to echo
    foreach($author->books as $book) {
        // Assumed you have a title property in books table
        echo $book->title; // in blade use {{ $book->title }} to echo
    }
}

Without the loop you may get any model from a collection using this:

// Assumed 1 is the id here in both cases
$author->find(1)->books->find(1)->title;

Or you may use something like this:

$author->first()->books->first()->title;
$author->first()->books->find(2)->title;

You may use get(index) to get an item from the colection using the index of that item like this:

// First author model and first book model
$author->get(0)->books->get(0)->title;

Just remember, $author is a collection and $author->books is also a collection. You may like this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top