Кэширование запроса всего одним методом

Кэширование запроса всего одним методом

Мощь макросов в Laravel велика, так как позволяет расширять класс во время выполнения своими собственными методами. Вы можете добавить полезные методы, которые помогут преобразовать длинный код в выразительный однострочный. Пример из коробки — Валидация Запроса:

public function (Request $request)
{
    $request->validate([
        'email' => 'required|unique:users',
    ]);    // ...
}

Среди списка классов, реализующих трейт Macroable, обеспечивающий функциональность макросов в классе, есть Query Builder и Eloquent Builder. Оба используются для создания запросов непосредственно к базе данных.

Когда мне нужны ресурсоемкие SQL вычисления, то первое что я делаю — сохраняю результат в Cache, что сильно раздувает код:

$votes = 100;

$users = Cache::remember('users', 60, function () use ($votes) {
        return Users::has('comments')
            ->where('votes', '>', $votes)
            ->get();
    });

Было бы неплохо всё это уместить в одну строку вместо того, чтобы угадывать, что я, черт возьми, здесь понаписал? Разумеется!

План, Класс и Макрос

Я хочу запомнить результаты, используя простой синтаксис — вот как я хочу писать в своем приложении:

$results = User::where('something')->remember(60)->get();

Чтобы это заработало, добавление метода remember() в качестве макроса в Query Builder будет недостаточно. Если мы сделаем только это, то используя другой метод, например get() (или любой другой метод извлечения) — вернется результат запроса в обход любой логики кэширования.

Другими словами: кеш и результат запроса не связаны.

Поэтому я сделал очень простой пакет, согласно схеме ниже. Есть крошечный класс, который, как только вы вызовете remember(), будет промежуточным звеном между Builder и Cache.

Схема работы пакета Rememberable Queries

Попытаюсь объяснить лучше: когда вы используете метод remember(), экземпляр Eloquent или Query Builder будет инкапсулирован внутри класса, который передаст все ваши методы в Builder… за исключением случаев, когда Builder возвращает результат базы данных — в основном все, что угодно, кроме самого экземпляра Builder.

Это очень хороший способ делать ресурсоемкие вызовы в базу данных, без сложной логики и замыканий. Давайте вернемся к нашему первому примеру:

$votes = 100;

$users = Cache::remember('users', 60, function () use ($votes) {
        return Users::has('comments')
            ->where('votes', '>', $votes)
            ->get();
    });

Ок, мы знаем, что используем кэш. Замыкание находится внутри логики Cache, которая фактически вызывает базу данных, при импорте переменной извне. Теперь сравните это с этой выразительной строкой:

$users = Users::has('comments')->where('votes', '>', $votes)
    ->remember()
    ->get();

Работа с ключами кэша выполняется автоматически. По сути, хэшируется SQL-оператор запроса вместе с привязками, делая каждый оператор уникальным. Другими словами, логика может быть такой же, но если вы измените переменную, например количество голосов со 100 до 99, то хеш будет другим, и результат будет выполнен и сохранен в кэше.

Вот и все. Попробуйте и расскажите, что вы об этом думаете.

Пакет: DarkGhostHunter/RememberableQuery

Автор: Italo Baeza
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.


Задать вопросы по урокам можно на нашем форуме.