Local Scopes in Laravel | Lava Lamp Lab
How we made a build server using webhooks, fastlane and bash (part 1)
How we made a build server using webhooks, fastlane and bash (part 1)
Jan 21, 2019
drupal verses wordpress
What Makes Drupal Awesome Compared To WordPress
Mar 12, 2019

Local Scopes in Laravel

laravel logo

Local Scopes in Laravel

Recently, while working on a project for a client I needed to be able to filter out some results based on a value. After searching the internet for a while, I came across Scopes in the Laravel documentation  I decided to use Local Scopes, instead of Global Scopes, because I don’t want every call to the database to have the scope applied.

Let’s set the scene:

Imagine you are designing a program for a gym that displays a list of Activities a user can sign up for. These Activities can have one or more Schedules. These Schedules should be displayed under an Activity, but only when they are published. I’m using published in my case, but active/show/etc can be used too.

The Activity Model has:

id
name
description

and the Schedule Model has:

activity_id
start_time
end_time
price
address
published

Right now, if we defined a relationship between Activities and Schedules

// App\Schedule.php
public function activity(){
    return $this->belongsTo(Activity::class);
}
// App\Activity.php
public function schedule(){
    return $this->hasMany(Schedule::class);
}

and tried to get the Activities with return Activity::with(‘schedules’)->get(); we would get a result that looks like this:

{  
   "data":{  
      "activity":{  
         "id":1,
         "name":"Yoga in the Woods",
         "description":"Join us this Friday as we learn the basic Yoga poses!",
         "schedules":[  
            {  
               "id":1,
               "activity_id":1,
               "start_time":"12:00",
               "end_time":"13:00",
               "address":"12 Fake Street, Some Suburb",
               "published":false
            },
            {  
               "id":2,
               "activity_id":1,
               "start_time":"13:00",
               "end_time":"15:00",
               "address":"12 Fake Street, Some Suburb",
               "published":true
            }
         ]
      }
   }
}

Notice that on the second schedules objects, the published parameter is false. We don’t want someone seeing a schedule when it’s not published for obvious reasons.

So let’s write a Scope!

What are scopes? Taken shamelessly from the Laravel Docs

Scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered “popular”. To define a scope, prefix an Eloquent model method with scope.Scopes should always return a query builder instance.

Defining a scope is straightforward. Inside
of the Schedule Model we can define one like so:

/**
  * Scope a query to only include published schedules. 
  * 
  * @param \Illuminate\Database\Eloquent\Builder $query 
  * @return \Illuminate\Database\Eloquent\Builder 
  */ 
public function scopePublished($query) {
     return $query->where('published', true);
}

And that’s your scope, pretty simple stuff.In order to utilise the scope all you need to do is apply is to your query. Just remember to not include the scope prefix when doing so:

return Schedule::published()->get();

and you should get a result similar to this:

{  
   "schedules":[  
      {  
         "id":2,
         "activity_id":1,
         "start_time":"13:00",
         "end_time":"15:00",
         "address":"12 Fake Street, Some Suburb",
         "published":true
      }
   ]
}

Notice that this time we only have the schedule that has been published. Now in some cases, you might be happy, but in mine I needed a little more flexibility. I didn’t want any activity to show. I wanted only activities with published schedules to appear on the list.

So let’s define a method on our Activity model that allows us to use the scope in the Schedule model:

public function publishedSchedule() { 
    return $this->hasMany(Schedule::class)->published(); 
    // or this way: 
    // return $this->schedules()->published(); 
}

What this method does is it defines a relationship between Activities and Schedules, but only where the published property is true. If we edit our query a bit we get only the activities with published schedules.

$activity = Activity::whereHas('publishedSchedule')->get();

We can go a little deeper and show the activities with published schedules.

$activity = Activity::whereHas('schedulePublished')->with(['schedule' => function($q) { 
    $q->published();
}])->get();

Result:

{  
   "data":{  
      "activity":{  
         "id":1,
         "name":"Yoga in the Woods",
         "description":"Join us this Friday as we learn the basic Yoga poses!",
         "schedules":[  
            {  
               "id":2,
               "activity_id":1,
               "start_time":"13:00",
               "end_time":"15:00",
               "address":"12 Fake Street, Some Suburb",
               "published":true
            }
         ]
      }
   }
}

And there you have it. Creating a utilising local scopes in a Laravel Project

Leave a Reply

Your email address will not be published.