Filter Eloquent relationships “on-the-fly”, when you need it

I find more and more Eloquent functions that are rarely used, so continue posting these short tips on the blog. Here’s the next one.

Simple Eloquent relationship goes like this.

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

And then you get the data in the Controller:

$authors = Author::all();
foreach ($authors as $author) {
  foreach ($author->books as $book) {
  // .. do something
  }
}

But what if, in some case, you need to get only books that were written this year? Of course, you can change the original relationship, like this:

  public function books()
  {
    return $this->hasMany(Book::class)->whereYear('books.created_at', date('Y'));
  }

Notice: did you know about whereYear() function?

Another option is to have a separate relationship function for this:

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

  public function booksThisYear()
  {
    return $this->hasMany(Book::class)->whereYear('books.created_at', date('Y'));
  }

And then load it like this:

foreach ($author->booksThisYear() as $book) ...

But you probably don’t want to do that because it’s not flexible, so another option is to add filtering query at the time of getting the data.

$authors = Author::with(['books' => function($query) {
  $query->whereYear('created_at', date('Y'));
}])->get();

This is probably the most flexible way – original relationship stays the same, and you use filtering only when you actually needed.

Another related tip to use those internal functions – if you want to use external variables, don’t forget to add use statement. For example, if you want $year to be a variable, this won’t work:

$year = 2018; // Feels weird writing it on January 2, still getting used to 2018
$authors = Author::with(['books' => function($query) {
  $query->whereYear('created_at', $year);
}])->get();

It will throw error and won’t recognize $year.
This is the correct way:

$year = 2018; 
$authors = Author::with(['books' => function($query) use ($year) {
  $query->whereYear('created_at', $year);
}])->get();
Like our articles?
Check out our Laravel online courses!

3 COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here