Laravel Model Caching — Performance Boost
How to reduce 1002 MySQL queries in one page to 0 (zero) queries? In this tutorial, I will show how to use laravel model caching. This great library allows to fully cache entire eloquent objects and reduce queries to 0. Self-invalidating feature allows to regenerate data from database on model save, update, create etc..
You can read article with code highlight at Laravel-BAP.com
Where to use model caching?
If You have menu on each page, complex form with many drop-downs populated from database, lot data loaded in background (Language, Date Format, Time Format , Dictionaries etc) Cache it.
1. Create new application
Create a new application with composer. (Note: I have composer installed on Ubuntu, if you are using windows download composer.phar)
composer create-project — prefer-dist laravel/laravel modelcache
cd into project and install DebugBar and laravel-model-caching.
2. Installing required libs
Use artisan composer command to download libs. Don’t forget to publish vendors at the end.
— DebugBar will allow us to see queries to MySQL
composer require barryvdh/laravel-debugbar –dev— Laravel Model Caching will allow us to cache entire models
composer require genealabs/laravel-model-caching
publish vendors
php artisan vendor:publish
3. Creating models
I will create 2 models Company and City. Company will have City as belongTo relation.
php artisan make:model Company
php artisan make:model City
<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class City extends Model{public $table = ‘city’;public $fillable = [‘name’,‘desc’];public function companies(){return $this->hasMany(City::class);}}
4. Creating Migrations
We also need to create migrations for database tables.
php artisan make:migration create_entities<?php
use Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;class CreateEntities extends Migration{/*** Run the migrations.** @return void*/public function up(){Schema::create(‘company’, function (Blueprint $table) {$table->increments(‘id’);$table->string(‘name’);$table->string(‘desc’);$table->integer(‘city_id’);$table->timestamps();});Schema::create(‘city’, function (Blueprint $table) {$table->increments(‘id’);$table->string(‘name’);$table->string(‘desc’);$table->timestamps();});}/*** Reverse the migrations.** @return void*/public function down(){Schema::drop(‘company’);Schema::drop(‘city’);}}
5. Creating Seeders
We need some data to run tests
php artian make:seeder DemoSeeder<?php
use Illuminate\Database\Seeder;class DemoSeeder extends Seeder{/*** Run the database seeds.** @return void*/public function run(){\Illuminate\Database\Eloquent\Model::unguard();\App\Company::truncate();\App\City::truncate();for ($i = 0; $i <= 100; $i++) {$faker = \Faker\Factory::create();$city = new \App\City();$city->name = $faker->city;$city->desc = $faker->sentence();$city->save();}for ($i = 0; $i <= 1000; $i++) {$faker = \Faker\Factory::create();$company = new \App\Company();$company->name = $faker->company;$company->desc = $faker->sentence();$company->city()->associate(rand(1,100));$company->save();}}}
Update DatabaseSeeder.php and add our newly created seeder to default Laravel seeder.
<?php
use Illuminate\Database\Seeder;class DatabaseSeeder extends Seeder{/*** Run the database seeds.** @return void*/public function run(){$this->call(DemoSeeder::class);}}
6. Controller, Views, and Routing
Create new controller, update view and route.
php artisan make:controller ModelCacheController<?php
namespace App\Http\Controllers;use App\Company;class ModelCacheController extends Controller{/*** @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View*/public function index(){$view = view(‘welcome’);$companies = Company::all();$view->with(‘companies’,$companies);return $view;}
Yes, ugly AF this should be done with(‘city’) eager-loading but this example shows how to use model cache.
Update Route
<?php
/*Route::get(‘/’,[‘as’=>’welcome’,’uses’=>’ModelCacheController@index’]);
Update view
<!doctype html><html lang=”{{ app()->getLocale() }}”><head><meta charset=”utf-8"><meta http-equiv=”X-UA-Compatible” content=”IE=edge”><meta name=”viewport” content=”width=device-width, initial-scale=1"><title>Laravel</title><! — Fonts →<link href=”https://fonts.googleapis.com/css?family=Raleway:100,600" rel=”stylesheet” type=”text/css”></head><body><ul>@foreach($companies as $company)<li>{{ $company->name }}{{ $company->city->name }}</li>@endforeach</ul></body></html>
7. Testing
php artisan serve
Now what will happen when You will load localhost:8000 in the browser ?
What is happening? Because Laravel uses “lazy load” approach page is generating 1002 queries. (Yes, yes You can use “eager load” as I described in the previous article about performance but here I will show what we can do with caching).
8. Updating model
What I did. I added Cachable trait — with this all data from database will be stored in cache. Now if You will run again Your web app You will see 0 database queries.
<?php
namespace App;use GeneaLabs\LaravelModelCaching\Traits\Cachable;use Illuminate\Database\Eloquent\Model;class City extends Model{use Cachable;public $table = ‘city’;public $fillable = [‘name’,‘desc’];public function companies(){return $this->hasMany(City::class);}}<?php
namespace App;use GeneaLabs\LaravelModelCaching\Traits\Cachable;use Illuminate\Database\Eloquent\Model;class Company extends Model{use Cachable;public $table = ‘company’;public $fillable = [‘name’,‘desc’];public function city(){return $this->belongsTo(City::class);}}
Now when you will run localhost:8000 again.
Quite amazing, right?
9. Summary
- I use this library for all my Laravel project where I need to get data from database and those data are mostly static. For example all dictionaries (Date Format, Time Format, Countries etc).
- This library is self-invalidating models this means its refreshing cache when you will create, update, delete record via eloquent.
- This library requires PHP 7.1.*
- It’s better to use Redis in production.
You can read article with code highlight at Laravel-BAP.com