• Время чтения ~4 мин
  • 15.11.2023

Вы, вероятно, кэшировали некоторые данные модели в контроллере раньше, но я собираюсь показать вам технику кэширования моделей Laravel, которая немного более детализирована с использованием моделей Active Record. Это техника, о которой я впервые узнал на RailsCasts.

Используя уникальный ключ кэша в модели, можно кэшировать свойства и связи в моделях, которые автоматически обновляются (и кэш становится недействительным) при обновлении модели (или связанной модели). Дополнительным преимуществом является то, что доступ к кэшированным данным является более переносимым, чем кэширование данных в контроллере, поскольку они находятся в модели, а не в одном методе контроллера.

Вот суть методики:

Допустим, у вас есть Article модель, которая имеет много Comment моделей. Учитывая следующий шаблон blade Laravel, вы можете получить количество комментариев следующим образом в своем /article/:id маршруте:

<h3>{{ str_plural('Comment', $article->comments->count()) }}</h3>

Вы можете кэшировать количество комментариев в контроллере, но контроллер может стать довольно уродливым, когда у вас есть несколько одноразовых запросов и данных, которые вам нужно кэшировать. Использование контроллера для доступа к кэшированным данным также не очень портативно.

Мы можем создать шаблон, который будет попадать в базу данных только при обновлении статьи, и любой код, имеющий доступ к модели, может получить кэшированное значение:

<h3>{{ str_plural('Comment', $article->cached_comments_count) }}</h3>

Используя метод доступа к модели, мы будем кэшировать количество комментариев на основе последнего обновления статьи.

Итак, как мы обновляем колонку статьи updated_at при добавлении или удалении нового комментария?

Введите сенсорный метод.

Касание моделей

Используя метод моделиtouch(), мы можем обновить столбец статьи:

$ php artisan tinker

>>> $article = \App\Article::first();
=> App\Article {#746
     id: 1,
     title: "Hello World",
     body: "The Body",
     created_at: "2018-01-11 05:16:51",
     updated_at: "2018-01-11 05:51:07",
   }
>>> $article->updated_at->timestamp
=> 1515649867
>>> $article->touch();
=> true
>>> $article->updated_at->timestamp
=> 1515650910

Мы можем использовать обновленную метку времени, чтобы сделать кэш недействительным, но как мы можем повлиять на updated_at поле статьиupdated_at, когда мы добавляем или удаляем комментарий?

Так уж вышло, что модели Eloquent имеют свойство под названием $touches. Вот как может выглядеть наша модель комментариев:

namespace App;

use App\Article;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $guarded = [];

    protected $touches = ['article'];

    public function article()
    {
        return $this->belongsTo(Article::class);
    }
}

Свойство $touches представляет собой массив, содержащий ассоциацию, которая будет «затронута» при создании, сохранении или удалении комментария.

Кэшированный атрибут

Вернемся к методу доступа$article->cached_comments_count. Реализация модели может выглядеть следующим образомApp\Article:

public function getCachedCommentsCountAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
        return $this->comments->count();
    });
}

Мы кэшируем модель в течение пятнадцати минут, используя уникальный cacheKey() метод и просто возвращая количество комментариев внутри замыкания.

Обратите внимание, что мы также можем использовать этот Cache::rememberForever() метод и полагаться на сборку мусора нашим механизмом кэширования для удаления устаревших ключей. Я установил таймер таким образом, чтобы кэш попадал большую часть времени, а новый кэш появлялся каждые пятнадцать минут.

Метод cacheKey() должен сделать модель уникальной и сделать кэш недействительным при обновлении модели. Вот моя cacheKey реализация:Пример выходных данных для метода модели может возвращать следующее строковое представление:

public function cacheKey()
{
    return sprintf(
        "%s/%s-%s",
        $this->getTable(),
        $this->getKey(),
        $this->updated_at->timestamp
    );
}

articles/1-1515650910

Ключ — это имя таблицы, идентификатор модели cacheKey() и текущая updated_at метка времени. Как только мы коснемся модели, метка времени будет обновлена, и наш кэш модели будет соответствующим образом признан недействительным.

Article Вот модель, если она полная:И связанная Comment модель:

namespace App;

use App\Comment;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    public function cacheKey()
    {
        return sprintf(
            "%s/%s-%s",
            $this->getTable(),
            $this->getKey(),
            $this->updated_at->timestamp
        );
    }
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
    public function getCachedCommentsCountAttribute()
    {
        return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
            return $this->comments->count();
        });
    }
}

namespace App;

use App\Article;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $guarded = [];

    protected $touches = ['article'];

    public function article()
    {
        return $this->belongsTo(Article::class);
    }
}

Что дальше?

Я показал вам, как кэшировать простое количество комментариев, но как насчет кэширования всех комментариев?

public function getCachedCommentsAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments', 15, function () {
        return $this->comments;
    });
}

Вы также можете преобразовать комментарии в массив вместо сериализации моделей, чтобы разрешить только простой доступ к массиву данных на фронтенде:Наконец, я определил cacheKey() метод в модели, но вы захотите определить этот метод с помощью типажа, называемого чем-то вроде ProvidesModelCacheKey того, что вы можете использовать в нескольких моделях или определить метод в Article базовой модели,

public function getCachedCommentsAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments', 15, function () {
        return $this->comments->toArray();
    });
}

которую расширяют все наши модели. Возможно, вы даже захотите использовать контракт (интерфейс) для моделей, реализующих cacheKey() метод.

Надеюсь, эта простая техника оказалась для вас полезной!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297