Laravel Filters: How to Create Filters in Laravel 9

11
9027
Laravel Filters

This tutorial will show you how to filter your data based on the type and categories. For example, if you are making an E-Commerce type of website, then Product Filtering is the essential part of your application. So, we will see how we can go for Filters in Laravel.

Filters in Laravel

Laravel ships with some filters already built-in for you to use in your applications. Route filters are an excellent method to limit access to a given route.

Step 1: Install Laravel.

I am using Laravel Valet to install using the following command.

laravel new filters

If you are not using Valet, you can pull it through the following command.

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

Go into that project.

cd filters

Open the project in your editor; I am using VSCode.

code .

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

Step 2: Create migration and dummy data.

Now, 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

Okay, the next step is to generate the factory’s fake data. For example, to create a factory using 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)],   
    ];
});

So, we have written the code for Factory to generate the random data. It is time to create the data from the factory. There are plenty of ways to do that, but I prefer the tinker console. Go to your terminal and type the following command.

php artisan tinker

Now, 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, and 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.

 Now, 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 we get the result according to that. We code this function later.

Now, 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 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);
    }
}

So, 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);
    }
}

Step 5: Explanation

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

Now, 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 it 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 there are more filters 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];
    }

So, 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 is 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 is a bit 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

So this is our API endpoint that you can consume at the frontend.

That’s it for this tutorial.

11 Comments

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

  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?

  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

  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.

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

      • 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

Leave A Reply

Please enter your comment!
Please enter your name here

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