How to Create Filters in Laravel

Here are the steps to create a filter in Laravel.

Step 1: Install Laravel.

composer create-project laravel/laravel filters --prefer-dist

Now, set up the database in your .env file.

Step 2: Create migration and dummy data.

We are filtering products based on type and categories. So let us 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\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->increments('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.
     *
     * @return void
     */
    public function down()
    {
        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.

<?php

use Faker\Generator as Faker;

$factory->define(App\Product::class, function (Faker $faker) {
    return [
        'name' => $name = $faker->sentence,
        '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 Factory to generate the random data. It is time to create the data from the factory. There are many ways to do that, but I prefer the Tinker console. Go to your terminal and type the following command.

php artisan tinker

Write the following code to generate the data.

factory(\App\Product::class, 20)->create()

See the products table inside the database.

How To Create Filters In Laravel

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\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function index()
    {
        return Product::get();
    }
}

Define the route inside the web.php file.

Route::get('/products', 'ProductController@index')->name('products');

So, now go to the /products path; in my case, it is http://filter.test/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

// ProductController.php

namespace App\Http\Controllers;

use App\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, and then a different filter class can extend this class so that 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;

use App\Filters\ProductFilter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class Product extends Model
{
    public function scopeFilter(Builder $builder, $request)
    {
        return (new ProductFilter($request))->filter($builder);
    }
}

Now our final file will be AbstractFilter.php 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://filter.test/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 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 are iterating the filters and mapping 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, and it gets a $builder and query string’s value as an argument. In our case, value isGift.

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 is complicated and easy for you, but once you realize how the flow w you. For this example, you need to have strong Object Oriented knowledge. Otherwise, it is hard to understand.

Laravel Filters example

That’s it.

13 thoughts on “How to Create Filters in Laravel”

  1. About ProductFacory
    PHP Notice: Undefined variable: name in …
    tinker crashed about var $name
    What is this: ‘name’ => $name = $faker->sentence, ??

    Reply
  2. 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?

    Reply
  3. 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

    Reply
  4. 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.

    Reply
    • 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
      ];
      }

      Reply
      • 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

        Reply
  5. 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.

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.