In this tutorial we’ll look into a couple of important functions to manipulate or filter out data from a Laravel collection along with their impact on pagination. First, we’ll setup a Laravel 9 app with relevant data, routes and view, then we’ll use collection’s map() and filter() functions to demonstrate their functionality and pagination behavior thereof.
Laravel 9.x requires a minimum PHP version of 8.0. This tutorial assumes that you are using Linux, MacOS or WSL on Windows and Node.js, Composer and PHP 8 along with required modules i.e bcmatch, sqlite, mbstring, xml, zip, gd, mcrypt are properly installed. Also make sure that SQLite is installed as we’ll be using SQLite to keep things simple and quick; you may use MySQL or PostgreSQL as you like, however make sure the corresponding PHP module is installed too.
This tutorial is built upon the previous tutorial for generating fake data; therefore, if you’ve gone through the previous tutorial, you can continue from where we ended it. The initial instructions given hereunder are just repetition up to the creation of employees’ records. If you haven’t gone through the previous tutorial, then just follow each instruction as given below.
Using Composer, enter following command in terminal to install Laravel 9;
composer create-project laravel/laravel app 9.*
Now go inside newly created app directory by executing;
cd app
Then, setup database and Laravel environment file. Here, I’m configuring SQLite database for simplicity purposes.
Being in parent directory (app), execute following command in order to create new SQLite database file;
touch database/database.sqlite
Then find .env config file in the parent directory, open it and amend DB_CONNECTION line as follows;
DB_CONNECTION = sqlite
You can remove DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD from .env when using SQLite database.
Now, enter the following command to create Employee model along with the migration:
php artisan make:model Employee -m
Open newly created migration file from database/migrations/ which is named something like …create_employees_table.php and then insert the desired table fields;
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('employees', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->text('address');
$table->integer('age');
$table->integer('salary');
$table->enum("department", ["Finance", "Admin", "Marketing","Production"]);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('employees');
}
};
Enter following command in parent directory to run the above migration;
php artisan migrate
Now, create factory for generating dummy records by executing following command;
php artisan make:factory EmployeeFactory
Then, insert following code inside database/factories/EmployeeFactory.php file;
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class EmployeeFactory extends Factory
{
public function definition()
{
return [
"name" => $this->faker->name(),
"email" => $this->faker->unique()->safeEmail,
"address" => $this->faker->address,
"age" => $this->faker->numberBetween(25, 65),
"salary" => $this->faker->numberBetween(25000, 100000),
"department" => $this->faker->randomElement([
"Finance", "Admin", "Marketing", "Production"
]),
];
}
}
In order to populate database table quickly, let’s enter following command to invoke tinker;
php artisan tinker
And then enter following command to create 50 employees;
Employee::factory(50)->create()
Type ‘exit’ and press Enter in order to exit tinker.
Now, for client-side action first install Breeze starter kit by entering following command;
composer require laravel/breeze:* --dev
Then, run following command to install Breeze (this will also install required npm modules and compile UI assets);
php artisan breeze:install
Now, open routes/web.php and change it as follows;
<?php
use Illuminate\Support\Facades\Route;
use App\Models\Employee;
Route::get('/', function () {
$employees = Employee::paginate(10);
return view('empview', compact('employees'));
// return view('welcome');
});
Route::get('/map', function () {
$employees = Employee::all()
->map(function ($item, $key) {
$item->salary = round(($item->salary / 12) , 2);
return $item;
})->paginate(10);
return view('empview', compact('employees'));
});
Route::get('/filter', function () {
$employees = Employee::all()
->filter(function ($item, $key) {
return $item->age > 45;
})->paginate(10);
return view('empview', compact('employees'));
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
require __DIR__.'/auth.php';
Let’s stop for a while and analyze above routes. We have modified default route ‘/‘ in order to render different view i.e., empview. Then, we defined two other routes i.e., ‘/map‘ and ‘/filter‘ which demonstrate the functionality of map() and filter() methods of Collection respectively.
Map() method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items. Whereas the filter() method filters the collection using the given callback, keeping only those items that pass a given truth test.
Next, create a view file inside resources/views folder by entering following command;
touch resources/views/empview.blade.php
Insert following code inside empview.blade.php;
<!DOCTYPE html>
<html>
<head>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
<div class="p-4">
<table class="shadow-lg">
<thead>
<tr class="bg-gray-400 text-white font-extrabold" >
<td class="px-4 py-1 text-center" >ID</td>
<td class="px-4 py-1">Name</td>
<td class="px-4 py-1">Email</td>
<td class="px-4 py-1 w-30">Address</td>
<td class="px-4 py-1">Age</td>
<td class="px-4 py-1">Salary</td>
<td class="px-4 py-1">Department</td>
</tr>
</thead>
<tbody>
@foreach($employees as $employee)
<tr>
<td class="border px-4 py-1 text-center">{{ $employee->id }}</td>
<td class="border px-4 py-1">{{ $employee->name }}</td>
<td class="border px-4 py-1">{{ $employee->email }}</td>
<td class="border px-4 py-1">{{ $employee->address }}</td>
<td class="border px-4 py-1">{{ $employee->age }}</td>
<td class="border px-4 py-1">{{ $employee->salary }}</td>
<td class="border px-4 py-1">{{ $employee->department }}</td>
</tr>
@endforeach
</tbody>
</table>
<div class="p-4">
{{$employees->links()}}
</div>
</div>
</body>
</html>
Then execute following command in parent directory to compile UI assets:
npm run build
Finally, execute following command in parent directory to run PHP development server;
php artisan serv
Open browser and enter following URL in address bar;
http://localhost:8000
The above URL will trigger the default route ‘/‘ whereof all the records inside employees table will be retrieved then paginated and then sent to empview to display. Now enter following URL in address bar;
http://localhost:8000/map
You’ll get strange error something like ‘Collection::paginate does not exist‘. That’s what frustrates many developers. The reason is that the Collection class of Laravel does not have paginate() method. Map() and filter() methods actually return new Collection with the resulting data and you can’t call paginate() on a Collection. Since map() and filter() are so crucial in Laravel app development, it’s fitting that we fix this shortcoming at framework level. So, let’s stop server for a while and insert following package to add more powers to Laravel Collection including paginate() function;
composer require spatie/laravel-collection-macros
Now restart server and then enter http://localhost:8000/map or http://localhost:8000/filter and they are functioning just fine. I strongly believe that the above laravel-collection-macros should come prepackaged with Laravel.
1 thought on “Laravel collection manipulation, filtering and pagination”