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']);
Step 2 : Create Post Model To create Post model, paste this code.
php artisan make:model Post -m
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');
}
}
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
];
}
}
and then run below command to generate fake data using faker.
php artisan tinker
\App\Models\Post::factory(5)->create();
exit
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'));
}
}
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();
}
}
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);
Next you pass an array of tasks that should accept and process the request:
$pipeline->through($middleware);
Finally you run the pipeline with a destination callback:
$pipeline->then(function ($request) {
// Do something
});
it also uses this below method which i used my code above.
/**
* Run the pipeline and return the result.
*
* @return mixed
*/
public function thenReturn()
{
//
}
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));
}
}
app/QueryFilters/Active.php
<?php
namespace App\QueryFilters;
class Active extends Filter
{
protected function applyFilters($builder)
{
return $builder->where('active',request($this->filterName()));
}
}
app/QueryFilters/Sort.php
<?php
namespace App\QueryFilters;
class Sort extends Filter
{
protected function applyFilters($builder)
{
return $builder->orderBy('title',request($this->filterName()));
}
}
# app/QueryFilters/MaxCount.php
<?php
namespace App\QueryFilters;
class MaxCount extends Filter
{
protected function applyFilters($builder)
{
return $builder->take(request($this->filterName()));
}
}
Now if you visit this url, you will find our filter data
127.0.0.1:8000/post?active=1&sort=desc&..
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();
127.0.0.1:8000/post?active=0&sort=desc&..
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() !!}
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.