Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Charkhan's avatar

[RESTful API] Filtering data the clean way ?

Hi,

I'm building an Laravel 5 API with the following :

  • Repositories
  • Transformers (with the Fractal package)
  • Easy & re-usable filter function

The Fractal docs talks about a ParamBag class that get parameters in a array but theses are only usable in the include functions.

But what if I wanted to filter the current requested resource by its name, creation date etc... ? Or I want some resource depending on a relation ?

I'm not sure about where to put the filtering logic since I want my repos to be used in another laravel project which will not need thoses HTTP query filters....

Any help is welcome ! :)

0 likes
14 replies
Charkhan's avatar

Thanks for the reply,

I would try to avoid AngularJS in a first time because I'm dealing with massive datas and if I have to filter through nested resource, my app will not be as reactive as it should be.

For the server-side , I'm not too sure about where to put my logic (In controller ? Repos (but I think this is a bad idea : reason above) ? Elsewhere ? And how to link my filtering class with the rest of my app ?

NoorDeen's avatar

maybe you need to make some service class as filter and then filter the data throw this class methods

Charkhan's avatar

@alnour altegani I thought about it but I need to directly convert the filters into where SQL like.

Ex: I want to filter some news by its title , its author or even more -> like some RSS feeds I could be looking for.

Let's say I want to search some news that starts by or contains "Breaking news", well I want my final repo request to be like :

<?php

$news = $newsRepo->where('title', 'like', '%Breaking News%')->get();

This is a simple exemple just to let you get the idea.

The first approch I found was to have a BaseRepository class that all my repos will extends, but it's really a mess dealing with Dependency injection...

DbNewsRepository.php

<?php

namespace Api\Repositories\News;

use Api\Repositories\News\News;
use Api\Repositories\BaseRepository;

class DbNewsRepository extends BaseReposiroty implements NewsRepositoryInterface {


    public function __construct(News $model)
    {
        $this->model = $model;
        parent::__construct();
    }

    public function paginate($per_page = 20)
    {
        return $this->model->paginate($per_page);
    }

   ....
}

BaseReposytory.php

<?php

namespace Api\Repositories;

use Illuminate\Support\Facades\Input;

class BaseRepository {
    public function __construct()
    {
        //Parse the inputs thanks to the Input facade ....
    }
}

DbNewsRepository is resolved first so I have to force the parent resolver's call. And it's not the place to just inject the Request object.

Got a lot of thinkings and ideas but nothing really fits best practices or fit my aim ...

thepsion5's avatar

You shouldn't be using the Input facade or injecting the Request object in your repositories, because that means your repository is incapable of being used in any context outside an HTTP request.

Regarding filtering/sanitizing input, I'd use a service class that has the filter and repository injected into it. The service is then injected into your controller.

Alternatively, if you're using a command bus architecture, you can use a decorator to do the same thing before the input ever reaches the command handler.

Charkhan's avatar

@thepsion5 Ok, I'm trying your solution but still facing problems :s

Like how does the service class know which repo to inject ? Do I have to replicate the service for each repo ?

InputHandlerService.php

<?php

namespace Api\Services;

use Api\Filters\InputFilter;
use Api\Repositories\BaseRepository;

class InputHandlerService {

    /**
     * @var InputFilter
     */
    protected $inputFilter;
    /**
     * @var BaseRepository
     */
    protected $repository ;

    public function __construct(InputFilter $inputFilter, BaseRepository $repository)
    {
        $this->inputFilter = $inputFilter;
        $this->repository = $repository;
    }
} 

InputFilter.php

<?php
namespace Api\Filters;

use Illuminate\Http\Request;

class InputFilter {

    /**
     * @var Request
     */
    protected $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
        //$this->filterFields();
    }

NewsController.php

namespace Api\Http\Controllers\Common;

use Api\Repositories\News\NewsRepositoryInterface;
use Api\Services\InputHandlerService;
use Api\Transformers\NewsTransformer;

class NewsController extends ApiController {

    /**
     * @var NewsRepositoryInterface
     */
    protected $newsRepository;

    /**
     * @var NewsTransformer
     */
    protected $newsTransformer;

    /**
     * @param NewsRepositoryInterface $newsRepository
     * @param NewsTransformer $newsTransformer
     */
    public function __construct(NewsRepositoryInterface $newsRepository, NewsTransformer $newsTransformer)
    {
        $this->newsRepository = $newsRepository;
        $this->newsTransformer = $newsTransformer;
    }

    /**
     * Display a listing of the resource.
     *
     * @param InputHandlerService $inputHandlerService
     * @return Response
     */
    public function index(InputHandlerService $inputHandlerService)
   {
        dd($inputHandlerService);
   }
}

The InputProviderService returns me a BaseRepository instance as expected , but this class doestn't know which model to use (my model is null). I want my service to ignore which Repo concretion its using, but I also want the good repo concretion to be loaded in order to manipulate my SQL output...

I'm so confused... :/

Thanks for the replies anyway !

thepsion5's avatar

Oh, given that you're using Laravel 5, that makes things a bit different. I apologize for the confusion, I missed that in the initial post and I don't blame you for the confusion.

In this case, the solution is actually a lot simpler. I'd extend the FormRequest class to create a FilteredFormRequest, where you would have optional rule to filter or sanitize input. The form request class even has a formatInput method you can extend to add the filtering.

Charkhan's avatar

In fact, I'm the one to apologize because I don't think I'm explaining it the right way...

I'm aware of the new FormRequest class, but my purpose is slightly different (but I keep what you say in mind for future functionnality :))

At first, I want the fields I'm passing through the API to be reverberated on the Repo query.

Exemple :

API call : http://my.api.com/news/?filters=(title|like|%Breaking news%)&amp;sorters=(date|desc)

SQL code output expected : SELECT * FROM news WHERE title LIKE "%Breaking news%" ORDER BY date DESC

  • filters will contain all the fields I want to filter with (Generate the WHERE title LIKE "%Breaking news%" part)
  • sorters will contain all the fields I want to order by with (Generate the ORDER BY date DESC)

And that's why I'm talking about a "link" between my class, which convert the query string into a more processable variable like an array, and my Repo.

Once I get the array, I have to tell my repo about this new conditions.

In fact, I tried like you said and it worked. the only downside to it is that I have to c/c the Input handler service for each resource I want to be alterable (in order to inject the correct repo).

See what the service is getting injected InputConverter -> NewsInputHandlerService <- NewsRepository

I think there is a better way, but I don't really see one , I'm a beginner trying to play with abstractions :(

Thanks for the help :)

thepsion5's avatar

Something you could look into is using the Specification Pattern: You basically turn each sort/filter criteria into a data transfer object that you can then pass to your repository. For example:

class FilterSpecification
{

    public $field;

    public $value;

    public $comparisonType;

    protected $validComparisons = ['=', '!=', '>', '<', 'like'];

    public function __construct($field, $value, $comparisonType = '=')
    {
        $field = trim($field);
        $value = trim($value);
        $comparisonType = strtolower( trim($comparisonType) );

        $this->validate($field, $value, $type);

        $this->field = $field;
        $this->value = $value;
        $this->comparisonType = $comparisonType;
    }

    protected function validate($field, $value, $comparisonType)
    {
        if( empty( $field)  || empty( $value ) ) {
            throw new ApiException('Filters must have a non-empty field and value');
        }
        if( ! in_array( $comparison, $this->validComparisons ) ) {
            throw new ApiException('Comparison types must be among the following: ' . implode(',', $this->validComparisons );
        }
    }
}

Then you have a method in your repository like addFilter(FilterSpecification $spec) that you turn into query builder function calls, and a method in your controller that turns the query string into a series of FilterSpecification instances.

Charkhan's avatar

Oh ! I didn't know about this pattern and it seems promising :)

Just one more question : By putting the addFilter(FilterSpecification $spec) method into the Repo, don't the specification too tighted with the Repo ?

Like if I put my Repo on another project, wouldn't it needs the FilterSpec to run properly ? Just wandering...

Thanks for your research and your help !

thepsion5's avatar
Level 25

@Charkhan Since I forgot to reply when you originally posted this.

Depends. You can have the specification exist mostly as a Data Transfer Object, in which case it's too "dumb" to be tightly-coupled to anything really. The alternative is a specification that has a method like applyToQueryBuilder($queryBuilder), which does make it tightly-coupled to Eloquent. From what I've seen and read, both are viable options - either way, changing repository implementations requires re-implementing the filtering logic.

Charkhan's avatar

Welcome back ! :)

Thanks for the answer, I got the idea. I'll try it ASAP !

Please or to participate in this conversation.