Je souhaite attirer votre attention sur le concept d'organisation du filtrage par demande d'URL. Par exemple, j'utiliserai le langage PHP et le framework Laravel.
Concept
L'idée est de créer une classe 
QueryFilter universelle pour travailler avec des filtres.
GET /posts?title=source&status=active 
En utilisant cet exemple, nous filtrerons les 
publications (modèle de publication) selon les critères suivants:
- La présence du mot "source" dans le champ titre ;
- La valeur "publier" dans le champ d' état ;
Exemple d'application
Modèle de 
poste <?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model {  protected $fillable = [ 'name', 'id', 'title', 'slug', 'status', 'type', 'published_at', 'updated_at', ]; } 
Ajouter un itinéraire:
 Route::get('/posts', 'PostController@index'); 
Créez un fichier 
Resource \ Post pour la sortie au 
format JSON :
 namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class Post extends JsonResource {  public function toArray($request) { return [ 'id' => $this->ID, 'title' => $this->post_title, 'slug' => $this->post_name, 'status' => $this->post_status, 'type' => $this->post_type, 'published_at' => $this->post_date, 'updated_at' => $this->post_modified, ]; } } 
Et le contrôleur lui-même avec une seule action:
 namespace App\Http\Controllers; use App\Http\Resources\Post as PostResource; use App\Post; class PostController extends Controller {  public function index() { $posts = Post::limit(10)->get(); return PostResource::collection($posts); } } 
Le filtrage standard est organisé par le code suivant:
  public function index(Request $request) { $query = Post::limit(10); if ($request->filled('status')) { $query->where('post_status', $request->get('status')); } if ($request->filled('title')) { $title = $request->get('title'); $query->where('post_title', 'like', "%$title%"); } $posts = $query->get(); return PostResource::collection($posts); } 
Avec cette approche, nous sommes confrontés à la croissance du contrôleur, ce qui n'est pas souhaitable.
Implémentation de QueryFilter
La signification d'un tel concept est d'utiliser une classe distincte pour chaque entité qui mappe les méthodes à chaque champ pour le filtrage.
Filtrer par demande:
 GET /posts?title=source&status=publish 
Pour le filtrage, nous aurons la classe 
PostFilter et les méthodes 
title () et 
status () . 
PostFilter étendra la classe abstraite 
QueryFiler qui est responsable de la correspondance des méthodes de classe avec les paramètres passés.
Méthode applicable ()La classe 
QueryFIlter possède une méthode 
apply () , qui est chargée d' 
appeler les filtres qui se trouvent dans la classe enfant 
PostFilter .
 namespace App\Http\Filters; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; abstract class QueryFilter {  protected $request;  protected $builder;  public function __construct(Request $request) { $this->request = $request; }  public function apply(Builder $builder) { $this->builder = $builder; foreach ($this->fields() as $field => $value) { $method = camel_case($field); if (method_exists($this, $method)) { call_user_func_array([$this, $method], (array)$value); } } }  protected function fields(): array { return array_filter( array_map('trim', $this->request->all()) ); } } 
L'essentiel est que pour chaque champ passé par 
Request, nous avons une méthode distincte dans la classe de filtre enfant (classe PostFilter). Cela nous permet de personnaliser la logique de chaque champ de filtre.
Classe PostFilterPassons maintenant à la création d'une classe 
PostFilter qui étend 
QueryFilter . Comme mentionné précédemment, cette classe doit contenir des méthodes pour chaque champ par lequel nous devons filtrer. Dans notre cas, les méthodes 
title ($ value) et 
status ($ value) namespace App\Http\Filters; use Illuminate\Database\Eloquent\Builder; class PostFilter extends QueryFilter {  public function status(string $status) { $this->builder->where('post_status', strtolower($status)); }  public function title(string $title) { $words = array_filter(explode(' ', $title)); $this->builder->where(function (Builder $query) use ($words) { foreach ($words as $word) { $query->where('post_title', 'like', "%$word%"); } }); } } 
Ici, je ne vois aucune raison pour une analyse détaillée de chacune des méthodes, des requêtes assez standard. Le fait est que nous avons maintenant une méthode distincte pour chaque champ et que nous pouvons utiliser toute logique dont nous avons besoin pour former la demande.
Créer scopeFilter ()Maintenant, nous devons lier le concepteur de modèle et de requête
  public function scopeFilter(Builder $builder, QueryFilter $filter) { $filter->apply($builder); } 
Pour effectuer une recherche, nous devons appeler la méthode 
filter () et passer une instance de 
QueryFilter , dans notre cas 
PostFilter .
 $filteredPosts = Post::filter($postFilter)->get(); 
Ainsi, toute la logique de filtrage est traitée en appelant la méthode de 
filtrage ($ postFilter) , sauvant le contrôleur d'une logique inutile.
Pour faciliter la réutilisation, vous pouvez mettre la méthode 
scopeFilter dans le trait et l'utiliser pour chaque modèle qui doit être filtré.
 namespace App\Http\Filters; use Illuminate\Database\Eloquent\Builder; trait Filterable {  public function scopeFilter(Builder $builder, QueryFilter $filter) { $filter->apply($builder); } } 
Dans la 
publication, ajoutez:
 class Post extends CorcelPost { use Filterable; 
Il reste à ajouter 
index () à la méthode du contrôleur en tant 
que paramètre 
PostFilter et appeler la méthode du modèle 
filter () .
 class PostController extends Controller {  public function index(PostFilter $filter) { $posts = Post::filter($filter)->limit(10)->get(); return PostResource::collection($posts); } } 
C’est tout. Nous avons déplacé toute la logique de filtrage vers la classe appropriée, en respectant le principe de responsabilité unique (S dans le système de principes SOLID)
Conclusion
Cette approche de la mise en œuvre des filtres vous permet de vous en tenir au contrôleur fin de remorque, et facilite également la rédaction des tests.
Voici un exemple utilisant PHP et Laravel. Mais comme je l'ai dit, c'est un concept qui peut fonctionner avec n'importe quel langage ou framework.
Les références