mykeels.com

Writing even better Eloquent Filters 🎉

🔥🔥🔥 Including Relationships and Deferred Execution. 🔥🔥🔥

Writing even better Eloquent Filters 🎉

🔥🔥🔥 Including Relationships and Deferred Execution. 🔥🔥🔥

I wonder what these machines do … they look so efficient 😂

If you have been through this article, you might be familiar with Filters, a concept I have learned to use to handle query strings in GET requests to Laravel APIs.

https://medium.com/@mykeels/writing-clean-composable-eloquent-filters-edd242c82cc8

Here are a few things you could do with it, so tell me … good ideas or nah?

1. Loading Relationships

You can actually use ?with_{relationship} as a query string to include a relationship … Here’s an example.

In a sample API, a user has companies and we wish to list all users, include their companies in a GET request.

Where our Company and User model classes are defined as

If you don’t understand laravel relationships, now is a good time to learn it.

As we learned in the last article, we then write our UserFilter.php class as

Note how sleek the with_companies method looks 😍

Once we filter the User model query with our UserFilter as we learned in the previous article, we can make a GET request like:

~/api/users?with_companies

and get a response like:

[
 { 
  "id": 1, 
  "name": "Mykeels", 
  "companies": [ 
   { 
     "id": 1, 
     "name": "The XYZ Company", 
     "user_id": 1 
   } 
  ]
 }
]

The inclusion is done by the query builder, so a single query is made to the DB, to make this happen.

2. Defer Loading Relationships

The previous technique only works when there is a static association between the relationships such as via a belongsTo, belongsToMany, hasMany, or hasManyThrough method.

If we need a model instance to create the relationship, such as in scoped queries, like when attempting to load the staff in a user’s companies in the User class:

public function scopeStaff() {
  $ids = $this->companies()->pluck('id');
  return Staff::whereIn('company_id', $ids);
}

The ->with('staff') query builder method used in the UserFilter.php class would not work, but there’s a workaround for that.

Ladies and Gentlemen, I hereby present QueryFilters.php class 2.0 🎉

If UserFilters extends this, we can have its with_staff method defined as

public function with_staff() {
  return $this->defer(function ($user) {
    $user->staff = $user->staff()->get();
    return $user;
  });
}

then transform the users data we get from our query like

User::get()->map(function ($user) use ($filters) {
  return $filters->transform($user);
});

So, when we load ~/api/users?with_staff we would receive a response like:

[
 { 
  "id": 1, 
  "name": "Mykeels", 
  "staff": [ 
   { 
     "id": 2, 
     "user_id": 2, 
     "company_id": 1
   } 
  ]
 }
]

Which looks so cool! 😍 But, be wary though … Having multiple DB queries such as in deferred executions like this, could potentially slow your requests down.

Enjoy! 💃

Related Articles

Tags