In our application sometimes we provide the search and filter functionality to the user. In this article, we will see how we can filter the data using the pipeline in Laravel.
What is Pipeline?
Pipeline is a design pattern that allows us to pass an object through a series of stages so that at each stage we can operate on the object in some way. The pipeline is also known as pipes and filters.
Fatty Controller Problem
In the controller, we write the code for the filter and search functionality. The controller becomes fat and it is not a good practice. We should keep the controller thin.
See the below example of the controller.
<?php
namespace App\Http\Controllers;
use App\Models\Book;
use Illuminate\Http\Request;
class BookController extends Controller
{
public function index(Request $request)
{
$books = Book::query();
if ($request->has('title')) {
$books->where('title', 'like', '%' . $request->title . '%');
}
if ($request->has('author')) {
$books->where('author', 'like', '%' . $request->author . '%');
}
if ($request->has('category')) {
$books->where('category', 'like', '%' . $request->category . '%');
}
$books = $books->paginate();
return view('books.index', compact('books'));
}
}
Or maybe something like this.
<?php
namespace App\Http\Controllers;
use App\Models\Book;
use Illuminate\Http\Request;
class BookController extends Controller
{
public function index(Request $request)
{
$books = Book::query();
$books->when($request->has('title'), function ($query) use ($request) {
$query->where('title', 'like', '%' . $request->title . '%');
});
$books->when($request->has('author'), function ($query) use ($request) {
$query->where('author', 'like', '%' . $request->author . '%');
});
$books->when($request->has('category'), function ($query) use ($request) {
$query->where('category', 'like', '%' . $request->category . '%');
});
$books = $books->paginate();
return view('books.index', compact('books'));
}
}
So we can see that the controller is fat and we should keep the controller thin.
Solution
There are many solutions to this problem. We can use the repository pattern or we can use the pipeline. In this article, we will see how we can use the pipeline to filter the data.
Create Pipeline
First, we will create a pipeline class. We will create the classes, we want to filter and search in the app/Pipelines/Filter
directory.
mkdir app/Pipelines
mkdir app/Pipelines/Filter
Now we will create a Filter class for each action.
touch app/Pipelines/Filter/Title.php
touch app/Pipelines/Filter/Author.php
touch app/Pipelines/Filter/Category.php
touch app/Pipelines/Filter/Search.php
So we have created all the classes. Now we will write the code for each class. Each class will have a handle
method. The handle
method will accept the query and the next filter closure. We will filter the data in the handle
method.
app/Pipelines/Filter/Title.php
<?php
namespace App\Pipelines\Filter;
use Closure;
use Illuminate\Database\Eloquent\Builder;
class Title
{
public function handle(Builder $query, Closure $next) {
$search = request()→input('title');
$query = $query->where('title', 'like', "%{$search}%");
return $next($query);
}
}
app/Pipelines/Filter/Author.php
<?php
namespace App\Pipelines\Filter;
use Closure;
use Illuminate\Database\Eloquent\Builder;
class Author
{
public function handle(Builder $query, Closure $next) {
$search = request()→input('author');
$query = $query->where('author', 'like', "%{$search}%");
return $next($query);
}
}
app/Pipelines/Filter/Category.php
<?php
namespace App\Pipelines\Filter;
use Closure;
use Illuminate\Database\Eloquent\Builder;
class Category
{
public function handle(Builder $query, Closure $next) {
$search = request()→input('category');
$query = $query->where('category', 'like', "%{$search}%");
return $next($query);
}
}
app/Pipelines/Filter/Search.php
<?php
namespace App\Pipelines\Filter;
use Closure;
use Illuminate\Database\Eloquent\Builder;
class Search
{
public function handle(Builder $query, Closure $next) {
$search = request()→input('search');
$query = $query->where('title', 'like', "%{$search}%")
->orWhere('author', 'like', "%{$search}%")
->orWhere('category', 'like', "%{$search}%");
return $next($query);
}
}
So our pipeline is ready. Now we will use the pipeline. To use the pipeline we can use the scopeFilter
method in the model or we can use the trait. To make more sense we will use the trait.
touch app/Traits/Filterable.php
Now we will write the code in the trait.
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pipeline\Pipeline;
trait Filterable
{
public function scopeFilter(Builder $query, array $filters)
{
return Pipeline::send($query)
->through($filters)
->thenReturn();
}
}
Now we will use the trait in the model.
<?php
namespace App\Models;
use App\Traits\Filterable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory, Filterable;
// ...
}
Okay our all setup is ready. Now we refactor the controller.
<?php
namespace App\Http\Controllers;
use App\Models\Book;
use Illuminate\Http\Request;
use App\Pipelines\Filter\Title;
use App\Pipelines\Filter\Author;
use App\Pipelines\Filter\Category;
use App\Pipelines\Filter\Search;
class BookController extends Controller
{
public function index(Request $request)
{
$books = Book::query()
->filter([Title::class, Author::class, Category::class, Search::class])
->paginate();
return view('books.index', compact('books'));
}
}
So we can see that our controller is thin now. We can add more filters in the pipeline. We can also add the sort
functionality in the pipeline.
Conclusion
There are so many use cases of pipeline and also we can filter the data in many ways. In this article, we have seen how we can use the pipeline to filter the data. I hope you like this article.
In that case, if you need any help regarding this you can easily contact me from here, I will be happy to help you.