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:
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:
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:
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.
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:
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.
Latest posts by Mirza Pasic (see all)
- Quick tip: How to delete a tag from a Git repository? - August 20, 2016
- Laravel Accessors and Mutators - December 17, 2015
- How to allow remote connections to PostgreSQL database server - December 15, 2015