Laravel Filter Scope on a Model

Posted: 2018-08-20 11:25:33

So I find myself writing searchable pages that, via an API or what not, someone can filter the page by different fields on the model.

This can end up being a Controller class that does a lot of filters off the Request. Or it can be a Repository class that abstracts the business logic into one place for several related models.

But in many cases it really is just one Model being filtered. And if I add a Scope scopeFilter and pass in the Request as an array I can iterate over that array and call to other existing scopes on the model.

    /**
     * Return only active models
     *
     * @param object $query
     * @param array $filters
     * @return Builder
     */
    public function scopeFilters($query, $filters)
    {
        unset($filters['page']);
        foreach ($filters as $scope => $value) {
            $scope = camel_case($scope);
            $query = $this->{$scope}($value);
        }
        return $query;
    }

This allows me to use those scopes in other places as well.

For example a request to active policies for repo_name "foo":

curl https://foo.test/api/policies?active=1&repo_name=foo

The Controller can do something simple like this:

    public function __invoke(Request $request)
    {
        try {
            $results = Subscriber::filters($request->all())->orderBy("repo_name")->paginate(20);
            return response()->json($results, 200);
        } catch (\Exception $e) {
             \Log::error($e);
            return response()->json(null, 400);
        }
    }

And the model can handle it with these other scopes, that can be used by themselves as well:

    /**
     * Scope to get by repo_name
     *
     * @param object $query
     * @param string $repo_name
     * @return Builder
     */
    public function scopeRepoName($query, $repo_name)
    {
        return $query->where("repo_name", $repo_name);
    }

         /**
     * Return only active models
     *
     * @param object $query
     * @return Builder
     */
    public function scopeActive($query, $state = 1)
    {
        if ($state == 'all') {
            $state = [0,1];
        }

        $state = array_wrap($state);
        return $query->whereIn("active", $state);
    }

For some time I kept this logic out of models but recently I been trying to use models as more of a place to store this sort of model related logic.

If two models need to work together then maybe a Business class could help out to glue them together but still something to consider.

I will show in the next post one model subscribing another model.