If you are looking for an example of Eloquent Global Scope feature, then you’re in the right place!
Check the ready-to-use demo at …
https://github.com/jarektkaczyk/laravel5-global-scope-example
UPDATE – I have just created an abstract GlobalScope
class that deals with removing
the scope constraints for you. That being said, you have to implement only the apply
method in the scope class if you use my package, and all the heavy lifting will be done for you.
Here it is: sofa/laravel-global-scope
Applying global scope requires just 2 simple steps:
- Create a class
AbcScope
that implementsScopeInterface
- Boot that class in your Eloquent model calling
static::addGlobalScope(new AbcScope)
So basically you could just create the scope and then boot it in the model, which should use the scope.
However this is not going to be very reusable, so instead let’s take a bit longer path (and this is how SoftDeleting
is implemented in the Eloquent core):
- Create the scope
- Create a trait that will boot the scope and implement some handy methods
- Add single
use AbcTrait
line to all the models that will use the scope
As an example let’s use Post
with draft/published feature. By default we would like to get only published posts, but when we are logged in to the admin area, we need to see also the drafts:
(In real life I would use whereNull('published_at')
as a constraint, however this would be exactly the same as SoftDeletingTrait
, so instead I’m gonna check for published=1
condition, for this is a bit trickier)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
// app/models/PublishedScope.php <?php namespace Sofa\Eloquent\Scopes; use Illuminate\Database\Query\Builder as BaseBuilder; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ScopeInterface; class PublishedScope implements ScopeInterface { /** * Apply scope on the query. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function apply(Builder $builder, Model $model) { $column = $model->getQualifiedPublishedColumn(); $builder->where($column, '=', 1); $this->addWithDrafts($builder); } /** * Remove scope from the query. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function remove(Builder $builder, Model $model) { $query = $builder->getQuery(); $column = $model->getQualifiedPublishedColumn(); $bindingKey = 0; foreach ((array) $query->wheres as $key => $where) { if ($this->isPublishedConstraint($where, $column)) { $this->removeWhere($query, $key); // Here SoftDeletingScope simply removes the where // but since we use Basic where (not Null type) // we need to get rid of the binding as well $this->removeBinding($query, $bindingKey); } // Check if where is either NULL or NOT NULL type, // if that's the case, don't increment the key // since there is no binding for these types if ( ! in_array($where['type'], ['Null', 'NotNull'])) $bindingKey++; } } /** * Remove scope constraint from the query. * * @param \Illuminate\Database\Query\Builder $builder * @param int $key * @return void */ protected function removeWhere(BaseBuilder $query, $key) { unset($query->wheres[$key]); $query->wheres = array_values($query->wheres); } /** * Remove scope constraint from the query. * * @param \Illuminate\Database\Query\Builder $builder * @param int $key * @return void */ protected function removeBinding(BaseBuilder $query, $key) { $bindings = $query->getRawBindings()['where']; unset($bindings[$key]); $query->setBindings($bindings); } /** * Check if given where is the scope constraint. * * @param array $where * @param string $column * @return boolean */ protected function isPublishedConstraint(array $where, $column) { return ($where['type'] == 'Basic' && $where['column'] == $column && $where['value'] == 1); } /** * Extend Builder with custom method. * * @param \Illuminate\Database\Eloquent\Builder $builder */ protected function addWithDrafts(Builder $builder) { $builder->macro('withDrafts', function(Builder $builder) { $this->remove($builder, $builder->getModel()); return $builder; }); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
// app/models/PublishedTrait.php <?php namespace Sofa\Eloquent\Scopes; trait PublishedTrait { /** * Boot the scope. * * @return void */ public static function bootPublishedTrait() { static::addGlobalScope(new PublishedScope); } /** * Get the name of the column for applying the scope. * * @return string */ public function getPublishedColumn() { return defined('static::PUBLISHED_COLUMN') ? static::PUBLISHED_COLUMN : 'published'; } /** * Get the fully qualified column name for applying the scope. * * @return string */ public function getQualifiedPublishedColumn() { return $this->getTable().'.'.$this->getPublishedColumn(); } /** * Get the query builder without the scope applied. * * @return \Illuminate\Database\Eloquent\Builder */ public static function withDrafts() { return with(new static)->newQueryWithoutScope(new PublishedScope); } } |
Usage
1. Apply the scope for the Post
model:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php namespace App; use Illuminate\Database\Eloquent\Model as Eloquent; use Sofa\Eloquent\Scopes\PublishedTrait; class Post extends Eloquent { use PublishedTrait; } |
2. Use it:
1 2 3 4 5 6 7 8 9 10 |
App\Post::all(); // only published App\Post::first(); // only published App\Post::withDrafts()->get(); // all the posts, using withDrafts static method App\Post::where('some_field', 'some_value')->withDrafts()->get(); // all the posts with some_field=some_value, using Builder extension method |
And that’s all!
Pingback: Laravel – how to define and use Eloquent Global Scopes | SOFTonSOFA()