Achraf Khouadja Achraf Khouadja - 6 months ago 20
PHP Question

Caching an eloquent querys and dependency inversion

Im learning more about dependency inversion principle in laravel and i'm just wondering !
which is the best approach to cache querys?

exemple

In the controller

public function edit($id)
{
$user = Cache::remember('users', 60, function() {
return $this->repo->findorfail($id);
});
return view('admin.users.edit', compact('user'));
}


But this way i will have to do this in every function

in the abstract class

public function findorfail($id)
{
return Cache::remember('users', 60, function() {
return $this->model->findOrFail($id);
});
}


I think (be easy on me) this way this will happen for classes that extends it so i wont to type it lots of time

is there a better approach? is this right?

Answer

Depending on the size of your application, doing this all in the controller may be fine. As it grows, you may want to pull the caching out to its own spot, like a base model or repository.

A couple of things to think about when using a Cache.

  • Each cache object needs to have a key. You can use that key later to check if there is a cache and invalidate where necessary
  • You're responsible for invalidating the cache when data changes. For example, in your example, if something about this User changes, you'll likely want to invalidate and then re-cache. As your application grows this is more difficult than it sounds. Just be mindful of this.

An example might be:

// /app/BaseModel.php
<?php

namespace App;

use Cache;
use Illuminate\Database\Eloquent\Model;

class BaseModel extends Model 
{
    public function find($id)
    {
        $key = get_class($this->model) . '_find_' . $id;

        return Cache::remember($key, 60, function() { // the first argument is the key. you'll need to know this to invalidate the cache
            return $this->model->findOrFail($id);
        });
    }

    public function purgeOne($id, $method)
    {
        $key = get_class($this->model) . '_' . $method . '_' . $id ;

        return Cache::forget($key);
    }

    ... maybe you have other cache-related methods
}

// /app/User.php
<?php

namespace App;

use BaseModel;

class User extends BaseModel 
{
   ... stuff specific to your user
}

Another idea rather than putting the Cache functionality in a BaseModel is to use a Trait. The principle is basically the same, but instead you may have a trait named something like HandlesCache or Cacheable. Use that in any models that are "Cacheable" and you're good to go.

// /app/Traits/Cacheable.php
<?php

namespace App\Traits;

use Cache;

trait Cacheable
{
    public function find($id)
    {
        $key = get_class($this->model) . '_find_' . $id;

        return Cache::remember($key, 60, function() { // the first argument is the key. you'll need to know this to invalidate the cache
            return $this->model->findOrFail($id);
        });
    }

    public function purgeOne($id, $method)
    {
        $key = get_class($this->model) . '_' . $method . '_' . $id ;

        return Cache::forget($key);
    }

    ... maybe you have other cache-related methods
}

// /app/User.php
<?php

namespace App;

use App\Traits\Cacheable;
use Illuminate\Database\Eloquent\Model;

class User extends Model 
{
    use Cacheable;
    ... stuff specific to your user
}
Comments