Unrivaled Creations Stories

Internet Website Design and Bespoke Software Application Development with PHP and the Laravel Stack.

2 Ways to Code Laravel Query Scopes, Part I

1 year ago · 4 MIN READ
#development  #laravel 

Laravel query scopes

This is Part I of a two-part series about Laravel query scopes which will cover local query scopes. Be sure to visit Part II of this series to learn about global query scopes.

Laravel Eloquent Query Scopes: Part I

Query Scopes

By definition, a query scope takes a database query and limits its scope in some way. For example, you may wish to create a scope representing a range of dates and pull records that conform to that limit. The MySQL where() instruction certainly works to limit a scope; but with Eloquent, there’s a more streamlined, readable and less verbose way to express these scopes. This article will help you grasp that.

As of Laravel 5.4, there are two ways to apply a scope: locally (applied only when the scope is explicitly called); or globally (applied to every query for a given model). Let’s look at the local query scope in this part of the series, because that’s the easiest to understand. We’ll explore the second way, using a global query scope, in Part II.

The Local Query Scope

Example: You are writing an online takeout ordering app for FooBar Bakery. You want the app to automatically put donuts on sale that are at least 1-day old, but fresher than 3-days old:

$donuts = new App\Donut;
$onSaleDonuts = $donuts->excludeFresh()->excludeStale()->get();

This code first creates a new App\Donut Model instance and then uses PHP method chaining to apply two scopes to it: excludeFresh() and excludeStale().

As you can see, the code above is simple and elegant. It says, almost in plain language, exactly what it intends to do: “exclude brand new fresh donuts from the list of automatic sale items” and “exclude stale donuts, too.” If you ever have to come back to maintain this code several months or several years down the road, you will immediately see the tremendous advantage it is to use an Eloquent scope to access your data.

Implementing Local Query Scope Methods

With Laravel, you create local query scopes by adding specially–named methods to your model class (that is, the database access class). Just prefix the method name with 'scope', specify a object name (such as $query) as an argument, and be sure to return that same argument:

namespace App;

use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;

class Donut extends Model
{
    public function scopeExcludeFresh($query)
    {
        $now = Carbon::now();
        return $query->where('baked_at', '<=', $now->subDay());
    }

    public function scopeExcludeStale($query)
    {
        $now = Carbon::now();
        return $query->where('baked_at', '>=', $now->subDays(3));
    }

}

_I even threw in some sample code for Carbon, a simple PHP API extension for DateTime which is integrated into Laravel by default. Yes, Laravel is full of nice surprises!_

At run time, Laravel will create a new App\Donut class instance for you (but only if you use it in your program). Then, if you have called a query scope method of that class (such as excludeFresh() and excludeStale() in the above example), Laravel automatically instantiates an Illuminate\Database\Eloquent\Builder object, connects it to its appropriate database and table and so forth. Laravel will then call scopeExcludeFresh($query) and scopeExcludeStale($query), automatically injecting the Illuminate\Database\Eloquent\Builder object (called $query) for you. This degree of automation in itself is a huge savings of time and effort, and because these objects are only instantiated when they’re actually used, your Laravel app is intrinsically fast and optimized.

Method Chaining

It’s important to note that Laravel’s query scopes help you write more streamlined, efficient code by using PHP “method chaining” (using the -> symbols) to link multiple App\Donut class methods into a single elegant, readable line of code. It internally handles things like class instantiation and dependency injection which relieves you of having to manually deal with all that overhead.

Try It Yourself!

Give local query scopes a spin! All of the source code you see in this example is available for viewing or downloading from the Laravel Query Scopes Example Github repository.

(1.) Install Laravel and create a scratch project to experiment with. (I’ve created a 5-minute Laravel Setup guide for Apple Mac users that might be helpful if you’ve never done it before or don’t already have Laravel ready to go on your system.)

laravel new donut

(2.) Clone the Github repository:

git clone https://github.com/unrivaledcreations/laravel-query-scopes-example.git

(3.) Copy the relevant repository files into their proper locations for your new Laravel app:

cp -r laravel-query-scopes-example/app/* donut/app
cp -r laravel-query-scopes-example/database/* donut/database

(4.) Initialize the database tables:

cd donut
composer update
composer dump-autoload
php artisan migrate --seed

(5.) Use php artisan tinker to play with the query scopes (using the example from this article):

php artisan tinker
>>> $donuts = new App\Donut;
=> App\Donut {#683}
>>> $onSaleDonuts = $donuts->excludeFresh()->excludeStale()->get();
=> Illuminate\Database\Eloquent\Collection {#689...results are shown...}
>>>

You may optionally implement routes and views to take your exploration to a more practical level, but tinker is very nice for learning.

Summary

Laravel’s Eloquent query scopes give you powerful tools to create readable, maintainable code for accessing data. This article explored local query scopes, which is one of two ways you can use query scopes to clean up your database access code in a flexible, reusable and efficient way. Part II of this series will explore global query scopes which are intended to accomplish the same thing in a more automated, all-encompassing way.

Need Help?

Comment down below if you have any questions about query scopes or need help setting up the examples in this article. I will be happy to help you as best I can.

···

Michael Hall

Hi! I am a digital product designer and website/web application developer always seeking a better version of myself. Follow my journey as I share my story (and expertise) through the mutable, ever-changing, ever-growing world of design, web development and technology.
comments powered by Disqus


Copyright © 2017 by Michael Hall. All rights reserved. · Sign In