DEV Community

loading...
Cover image for Laravel Pipeline Design Pattern Example

Laravel Pipeline Design Pattern Example

abdallhsamy profile image Abdallah Samy ・4 min read

Using laravel pipelines you can pass an object between several classes in a fluid way to perform any type of task and finally return the resulting value once all the “tasks” have been executed.

Larval uses the Pipeline Design Pattern in a couple of places throughout the framework. This means everything we need to implement this pattern is already part of the foundation of your application!

In today’s tutorial we will be looking at the Pipeline Design Pattern and how we can filter data using laravel pipeline, in this tutorial we are going to see that.

laravel-pipeline-example

What is the Pipeline Design Pattern?
The Pipeline Design Pattern is where a complex process is broken down into individual tasks. Each individual task is reusable and so the tasks can be composed into complex processes.

This allows you to break up monolithic processes into smaller tasks that process data and then pass that data to the next step.

Each task within the pipeline should receive and emit the same type of data. This allows tasks to be added, removed, or replaced from the pipeline without disturbing the other tasks.

Now let's start our tutorial. In this tutorial we will create a post table, and filter them using active or inactive, asc or desc or will filter them order by title or name. That mean we are going to filter data using Laravel pipeline. Let's see.

Step 1 : Create Route

Now we have to create a route to return our data, so create it.

Route::get('post', [FilterController::class, 'index']);
Enter fullscreen mode Exit fullscreen mode

Step 2 : Create Post Model

To create Post model, paste this code.

php artisan make:model Post -m
Enter fullscreen mode Exit fullscreen mode

Now open our newly created database table and paste the following code.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{

    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->boolean('active')->comment('active or inactive');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Generate some dummy data

To generate some dummy data, open PostFactory and paste below code

<?php

namespace Database\Factories;

use App\Models\Post;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    protected $model = Post::class;

    public function definition()
    {
        return [
            'title' => $this->faker->sentence,
            'active' => $this->faker->boolean
        ];
    }
}

Enter fullscreen mode Exit fullscreen mode

and then run below command to generate fake data using faker.

php artisan tinker
\App\Models\Post::factory(5)->create();
exit
Enter fullscreen mode Exit fullscreen mode

Step 4: Add Controller Method

Now at last we have to add new controller method index() in your Home Controller. So let's add index() method on HomeController.php file.

app/Http/Controllers/FilterController.php

<?php

namespace App\Http\Controllers;

use App\Models\Post;

class FilterController extends Controller
{

    public function index()
    {
        $posts = Post::filtered()->paginate(2);

        return view('post',compact('posts'));
    }

}
Enter fullscreen mode Exit fullscreen mode

Step 5: Setup Model

Now open Post model, and paste this following code.

<?php

namespace App\Models;

use Illuminate\Pipeline\Pipeline;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public static function filtered()
    {
       return app(Pipeline::class)
                ->send(self::query())
                ->through([
                    \App\QueryFilters\Active::class,
                    \App\QueryFilters\Sort::class,
                    \App\QueryFilters\MaxCount::class,
                ])
                ->thenReturn();
    }
}
Enter fullscreen mode Exit fullscreen mode

If you see the pipeline class source code which i mentioned link above, you will understand clearly what is happening here. Pipeline class define some classes like, you pass in the object that you want to send through the pipeline:

$pipeline->send($request); 
Enter fullscreen mode Exit fullscreen mode

Next you pass an array of tasks that should accept and process the request:

$pipeline->through($middleware);  
Enter fullscreen mode Exit fullscreen mode

Finally you run the pipeline with a destination callback:

$pipeline->then(function ($request) {  
// Do something  
});  
Enter fullscreen mode Exit fullscreen mode

it also uses this below method which i used my code above.

    /**
     * Run the pipeline and return the result.
     *
     * @return mixed
     */
    public function thenReturn()
    {
       //
    }
Enter fullscreen mode Exit fullscreen mode

Now we have to create our own custom class to filter our database data.

app/QueryFilters/Filter.php

<?php

namespace App\QueryFilters;

use Closure;
use Illuminate\Support\Str;

abstract class Filter 
{
    public function handle($request, Closure $next)
    {  

        if( ! request()->has($this->filterName())){
            return $next($request);
        }

        $builder = $next($request);

        return $this->applyFilters($builder);
    }

    abstract protected function applyFilters($builder);

    protected function filterName()
    {
       return Str::snake(class_basename($this));
    }
}
Enter fullscreen mode Exit fullscreen mode

app/QueryFilters/Active.php

<?php
namespace App\QueryFilters;

class Active extends Filter
{

    protected function applyFilters($builder)
    {
        return $builder->where('active',request($this->filterName()));
    }

}
Enter fullscreen mode Exit fullscreen mode

app/QueryFilters/Sort.php

<?php

namespace App\QueryFilters;

class Sort extends Filter
{

    protected function applyFilters($builder)
    {
       return $builder->orderBy('title',request($this->filterName()));
    }

}
Enter fullscreen mode Exit fullscreen mode

app/QueryFilters/MaxCount.php

<?php

namespace App\QueryFilters;

class MaxCount extends Filter
{

    protected function applyFilters($builder)
    {
       return $builder->take(request($this->filterName()));
    }

}
Enter fullscreen mode Exit fullscreen mode

Now if you visit this url, you will find our filter data

http://127.0.0.1:8000/post?active=1&sort=desc&max_count=1

Now you should the below output. Here we can see our all active post with descending order. make sure that paginate and max_count can not work together.

If you get data from model without out using paginate method then max_count also work for filtering. See below code to understand. Hope you will understand.

$posts = app(Pipeline::class)
                ->send(\App\Post::query())
                ->through([
                    \App\QueryFilters\Active::class,
                    \App\QueryFilters\Sort::class,
                    \App\QueryFilters\MaxCount::class,
                ])
                ->thenReturn()
                ->paginate(2); //just change here like below code


$posts = app(Pipeline::class)
                ->send(\App\Post::query())
                ->through([
                    \App\QueryFilters\Active::class,
                    \App\QueryFilters\Sort::class,
                    \App\QueryFilters\MaxCount::class,
                ])
                ->thenReturn()
                ->get();
Enter fullscreen mode Exit fullscreen mode

1

http://127.0.0.1:8000/post?active=0&sort=desc&max_count=1
Enter fullscreen mode Exit fullscreen mode

2

Now you should the below output. Here we can see our all inactive post with descending order.

Step 6 : Create View File

Now paste this following code to this post.blade.php file.

resources/views/post.blade.php

    <div class="card-body">
       @foreach ($posts as $item)
           {{ $item->title }} 
           {{ $item->active }} <br>
       @endforeach
    </div>
 {!! $posts->appends(request()->input())->links()  !!}
Enter fullscreen mode Exit fullscreen mode

Laravel makes good use of this design pattern internally in the framework. But you can use Laravel’s Pipeline functionality for your own projects.

In next week’s tutorial we’re going to be looking at how you can use the Pipeline functionality to deal with complex processes. Hope it can help you to gather new knowledge.

Discussion (0)

Forem Open with the Forem app