Filters in Laravel are a robust way to manage and manipulate the HTTP requests in your application.
Here is the step-by-step guide:
Step 1: Install Laravel
composer create-project laravel/laravel filters --prefer-dist
Now, set up the database in your .env file.
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=8889 DB_DATABASE=filters DB_USERNAME=root DB_PASSWORD=root
Step 2: Create migration and dummy data.
We are filtering products based on type and categories.
Let’s create the Product model and migration. So generate via the following command.
php artisan make:model Product -m
So it will create both model and migration files.
Inside the create_products_table.php file, write the following schema in it.
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('slug'); $table->enum('type', ['Simple', 'Grouped', 'Variable', 'Gift']); $table->enum('categories', ['Electronics', 'Books', 'Games', 'Garden']); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('products'); } };
Migrate all the tables in the database using the following command.
php artisan migrate
The next step is to generate the factory’s fake data. For example, to create a factory, use the following command.
php artisan make:factory ProductFactory --model=Product
It will create a file inside the database >> factories folder called ProductFactory.php.
Write the below code inside the ProductFactory.php file:
<?php namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; use App\Models\Product; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Product> */ class ProductFactory extends Factory { protected $model = Product::class; /** * Define the model's default state. * * @return array<string, mixed> */ public function definition(): array { $name = $this->faker->sentence; return [ 'name' => $name, 'slug' => Str::slug($name), 'type' => ['Simple', 'Grouped', 'Variable', 'Gift'][rand(0, 3)], 'categories' => ['Electronics', 'Books', 'Games', 'Garden'][rand(0, 3)], ]; } }
We have written the code for the Factory to generate random data. Now, it is time to create the data from the factory. There are many ways to do that, but I prefer the Tinker console.
First, ensure that you have Laravel Tinker installed. Laravel Tinker is typically included by default in Laravel applications, but if it’s not, you can install it via Composer.
composer require laravel/tinker
Once Tinker is installed, you can interact with your Laravel application from the command line.
Open your terminal and navigate to the root directory of your Laravel application.
php artisan tinker
Write the following code to generate the data.
\App\Models\Product::factory()->count(20)->create();
See the products table inside the database.
Step 3: Create an endpoint
The next step is to create a ProductController file. So type the following command.
php artisan make:controller ProductController
Define the index method inside that ProductController.php file.
<?php // ProductController.php namespace App\Http\Controllers; use App\Models\Product; use Illuminate\Http\Request; class ProductController extends Controller { public function index() { return Product::get(); } }
Define the route inside the web.php file.
<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\ProductController; Route::get('/', function () { return view('welcome'); }); Route::get('/products', [ProductController::class, 'index'])->name('products');
Your path might have http://localhost:8000/products
You can see that; we can get the JSON data from our database. Now, we need to apply the filter to that data.
Step 4: Create a filter
Create a Filters folder inside the app directory.
In that folder, create one file called TypeFilter.php.
<?php // TypeFilter.php namespace App\Filters; class TypeFilter { public function filter() { } }
This TypeFilter class is responsible for filtering the data based on the type.
We pass the type in the query string and get the result according to that. We code this function later.
Inside the ProductController.php file, we can write the following code.
<?php namespace App\Http\Controllers; use App\Models\Product; use Illuminate\Http\Request; class ProductController extends Controller { public function index(Request $request) { return Product::filter($request)->get(); } }
Inside the Filters folder, create one abstract class called AbstractFilter.php.
<?php // AbstractFilter.php namespace App\Filters; use Illuminate\Http\Request; use Illuminate\Database\Eloquent\Builder; abstract class AbstractFilter { protected $request; protected $filters = []; public function __construct(Request $request) { $this->request = $request; } public function filter(Builder $builder) { } }
The only reason I have taken the above class as an abstract class is that, in the future, we will have more than one kind of filter, so it is better to use this class as an abstract.
Then, a different filter class can extend this class so we do not repeat each code every time.
The next step is to create a new file called ProductFilter.php inside the Filters directory.
In this file, we will define the actual filter class. I am only using a type filter for this example, but you have more than one filter like age, demographics, price, etc.
<?php // ProductFilter.php namespace App\Filters; use App\Filters\AbstractFilter; use Illuminate\Database\Eloquent\Builder; class ProductFilter extends AbstractFilter { protected $filters = [ 'type' => TypeFilter::class ]; }
Here, I am creating one filter function inside the Product model. So, pass the full request as a parameter.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use App\Filters\ProductFilter; use Illuminate\Database\Eloquent\Builder; class Product extends Model { use HasFactory; public function scopeFilter(Builder $builder, $request) { return (new ProductFilter($request))->filter($builder); } }
Now, our final file will be AbstractFilter.php, which looks like this:
<?php // AbstractFilter.php namespace App\Filters; use Illuminate\Http\Request; use Illuminate\Database\Eloquent\Builder; abstract class AbstractFilter { protected $request; protected $filters = []; public function __construct(Request $request) { $this->request = $request; } public function filter(Builder $builder) { foreach ($this->getFilters() as $filter => $value) { $this->resolveFilter($filter)->filter($builder, $value); } return $builder; } protected function getFilters() { return array_filter($this->request->only(array_keys($this->filters))); } protected function resolveFilter($filter) { return new $this->filters[$filter]; } }
And finally, we write the main filter code inside the TypeFilter.php file.
<?php // TypeFilter.php namespace App\Filters; class TypeFilter { public function filter($builder, $value) { return $builder->where('type', $value); } }
I will give you a brief explanation of what I have done to achieve the filtered result.
First, our Filtered page’s URL looks like this: http://localhost:8000/products?type=Gift
So, the query string is type=Gift.
So the type is the key, and the Gift is value. That is why we have created a filter called TypeFilter.php.
We are mapping that type to the TypeFilter.php class inside the ProductFilter.php file.
So from the ProductController’s index function, we call the model’s filter function and pass the request as an argument.
Inside the Product model, we call the ProductFilter’s filter method and pass the Eloquent query builder as an argument.
Next, ProductFilter does not have a direct filter method but extends the AbstractFilter class, which has the filter method. So we can use that filter method.
But, ProductFilter has an array of Filters. In our case, it is just TypeFilter, but more filters exist in real-time web applications.
The next step is inside the AbstractFilter.php file; we call the filter function.
So, first, we iterate the filters and map the query string key to that filter. Then, if both are matched, we directly instantiate that matched class, call that class’s method, and pass that query string’s key as an argument.
// AbstractFilter.php public function filter(Builder $builder) { foreach($this->getFilters() as $filter => $value) { $this->resolveFilter($filter)->filter($builder, $value); } return $builder; } protected function getFilters() { return array_filter($this->request->only(array_keys($this->filters))); } protected function resolveFilter($filter) { return new $this->filters[$filter]; }
The type is associated with TypeFilter.
That is why the TypeFilter.php file’s filter method is called. It gets a $builder and query string’s value as an argument. In our case, the value is a Gift.
So, it returns only the rows that contain type = Gift.
We create the Filter class based on the query string parameter and then call that class’s filter method to filter the result.
It may seem complicated and easy at first, but once you realize how it flows, you will understand it. For this example, you need to have strong Object-Oriented knowledge. Otherwise, it will be hard to understand.
That’s it.
Robson Tenório
Very nice approach. But if you need a full query builder, just head to
https://github.com/spatie/laravel-query-builder
Ammar
❤️❤️❤️❤️❤️❤️
Aarohi
such a great tutorial
thnx
fouladgar.dev
Nice article!
I suggest check out this package:
https://github.com/mohammad-fouladgar/eloquent-builder
Bob F
Best example of a basic Laravel filter that I have found. Thank you!
bumbar
About ProductFacory
PHP Notice: Undefined variable: name in …
tinker crashed about var $name
What is this: ‘name’ => $name = $faker->sentence, ??
Ostap
quality article! thanks
Ray
Hi there. Thanks for the tutorial. It really helped me creating filters etc for my site. I have one question however. How can I query related models? For instance, “category” has many2many relation. What do i need to change in the CategoryFilter (function filter) to make this work?
genesha
hi,
i just follow your example and succed.
now i want to play with 2 filters, lets say, type.
how i tuned the code ? since i stiil stucked -_-
thank you
genesha
Hi,
i just follow your code, and get succed.
now i want tty, to make 2 kind filter, let say type b..
but iam stucked.. need your advise.
thank you.
genesha
ive got it
1. create another filter class , let say : TypeFilterB.php
2. add that additional class in ProductFilter.php :
class ProductFilter extends AbstractFilter
{
protected $filters = [
‘type’ => TypeFilter::class
‘typeB’ => TypeFilterB::class
];
}
genesha
another question :
in : abstact.php,
youre using : array_filter()
protected function getFilters()
{
// return array_filter($this->request->only(array_keys($this->filters)));
return $this->request->only(array_keys($this->filters));
}
why is that ??
bacause that filter will remove “flag type ” filter
Marcel
nice Tutorial.
But how can I use filters with a many to many or one to many relation without specifying the id but for example the name.