In the last tutorial I showed you how you can speed up a web application by caching database queries that are executed very often. However, there is a problem. Application will cache the resulted dataset, and in the future this dataset will be served. You need to tell your application to flush the cache every time when certain Model is updated. That way every time you edit, delete or add new Model item, old cache will be flushed, and query will be executed again. Afterwards, resulting new dataset will be cached and served until Model is updated once again.

Here Laravel Model Observers come in rescue. You can use them for numerous things. One of practical examples for using Model Observers is cache flushing.

Eloquent model

Let’s start with the model, here is basic Department model:

Department Model <?php namespace Bosnadev\Models; class Department extends \Eloquent { protected $table = 'departments'; protected $primaryKey = 'dept_no'; public $timestamps = false; } 1 2 3 4 5 6 7 8 9 <?php namespace Bosnadev \ Models ; class Department extends \ Eloquent { protected $table = 'departments' ; protected $primaryKey = 'dept_no' ; public $timestamps = false ; }

One of the places where you can put your observers is Eloquent boot method. We need to override the default boot method to add our observers:

Model Observer <?php namespace Bosnadev\Models; use Bosnadev\Observers\DepartmentObserver; class Department extends \Eloquent { protected $table = 'departments'; protected $primaryKey = 'dept_no'; public $timestamps = false; public static function table() { $model = new static; return $model->getTable(); } public static function boot() { parent::boot(); Department::observe(new DepartmentObserver()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php namespace Bosnadev \ Models ; use Bosnadev \ Observers \ DepartmentObserver ; class Department extends \ Eloquent { protected $table = 'departments' ; protected $primaryKey = 'dept_no' ; public $timestamps = false ; public static function table ( ) { $model = new static ; return $model -> getTable ( ) ; } public static function boot ( ) { parent :: boot ( ) ; Department:: observe ( new DepartmentObserver ( ) ) ; } }

Laravel Model Observers

Some of the code can be abstracted for later implementation, so we create AbstractionObserver class first:

Abstract Observer <?php namespace Bosnadev\Observers; use \Cache; abstract class AbstractObserver { protected function clearCacheTags($tags) { Cache::tags($tags)->flush(); } protected function clearCacheSections($section) { Cache::section($section)->flush(); } abstract public function saved($model); abstract public function saving($model); abstract public function deleted($model); abstract public function deleting($model); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php namespace Bosnadev \ Observers ; use \ Cache ; abstract class AbstractObserver { protected function clearCacheTags ( $tags ) { Cache:: tags ( $tags ) -> flush ( ) ; } protected function clearCacheSections ( $section ) { Cache:: section ( $section ) -> flush ( ) ; } abstract public function saved ( $model ) ; abstract public function saving ( $model ) ; abstract public function deleted ( $model ) ; abstract public function deleting ( $model ) ; }

Now we can implement this class to create any observer we need. For this purpose, let’s create one Department observer which will flush cache every time when Department model is updated.

Department Observer <?php namespace Bosnadev\Observers; class DepartmentObserver extends AbstractObserver { public function saved($model) { $this->clearCacheSections($model->getTable()); } public function deleted($model) { $this->clearCacheSections($model->getTable()); } } 1 2 3 4 5 6 7 8 9 10 11 <?php namespace Bosnadev \ Observers ; class DepartmentObserver extends AbstractObserver { public function saved ( $model ) { $this -> clearCacheSections ( $model -> getTable ( ) ) ; } public function deleted ( $model ) { $this -> clearCacheSections ( $model -> getTable ( ) ) ; } }

Only thing left now is to cache some data. Here is a simple controller which handles IT Department resources, employees and everything else related to it:

IT Department Controller <?php namespace Bosnadev\Controllers; use BaseController; use Bosnadev\Models\Department; use DB; class ITDepartmentController extends BaseController { public function index() { $it_department = \Cache::section(Department::table())->remember("departments-it", 60, function() { $sql = " SELECT employees.emp_no, employees.first_name, employees.last_name, employees.birth_date, employees.gender, employees.hire_date, departments.dept_name, departments.dept_no FROM employees INNER JOIN dept_emp ON employees.emp_no = dept_emp.emp_no AND dept_emp.dept_no = 'd005' INNER JOIN departments ON dept_emp.dept_no = departments.dept_no LIMIT 10 OFFSET 0; "; return DB::select(DB::raw($sql)); }); // Here you can do whatever you want with $it_department, but let's check the cache var_dump(\Cache::section(Department::table())->get("departments-it")); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php namespace Bosnadev \ Controllers ; use BaseController ; use Bosnadev \ Models \ Department ; use DB ; class ITDepartmentController extends BaseController { public function index ( ) { $it_department = \ Cache:: section ( Department:: table ( ) ) -> remember ( "departments-it" , 60 , function ( ) { $sql = " SELECT employees.emp_no, employees.first_name, employees.last_name, employees.birth_date, employees.gender, employees.hire_date, departments.dept_name, departments.dept_no FROM employees INNER JOIN dept_emp ON employees.emp_no = dept_emp.emp_no AND dept_emp.dept_no = 'd005' INNER JOIN departments ON dept_emp.dept_no = departments.dept_no LIMIT 10 OFFSET 0; " ; return DB:: select ( DB:: raw ( $sql ) ) ; } ) ; // Here you can do whatever you want with $it_department, but let's check the cache var_dump ( \ Cache:: section ( Department:: table ( ) ) -> get ( "departments-it" ) ) ; } }

By opening route departments/it we can see that one query is executed:

Refreshing the same page gives the same result, but no query is executed.

OK. Now when we know that caching is working, how about making some changes in the Department model. What if we want to rename Development into, let’s say – DevOps.

I created DepartmentsApiController with basic CRUD operations, here you can see how update() method looks like, and what is result of PUT request:

Let’s refresh our departments/it route once again:

Query is executed once again and new dataset is cached for later use. This query will not be executed until new change in Department model triggers DepartmentObserver or until cache expires, which is for 1 hour.

I hope you now understand basic principle behind Model Observers and how this powerful feature can be used for other things that require notification on Model update.

Share this: Facebook

Google

Twitter

Reddit

Pocket

Email

Print



Like this: Like Loading...