Опубликовано:

Laravel ViewComposers дублируют MySQL запросы

Laravel ViewComposers — отличный инструмент при WEBразработке. Однако, существует небольшая сложность, которую могут “упустить“ начинающие разработчики. В документации к феймворку описан способ связать класс ViewComposer с несколькими шаблонами. Именно в этот момент может возникнуть такая сложность, как дублирование MySQL запросов в ViewComposers.

Повторные вызовы MySQL-запроса в файле ViewComposer

В частности, в документации используется способ привязки класса ViewComposer к нескольким шаблонам с помощью массивов или паттерна ‘*’. Например так:

View::composer('*', 'App\ViewComposers\AllComposerView');Code language: PHP (php)

И если на проекте используется “сложная“ стрeктура Blade-шаблонов, то возникнет ситуация, когда каждый шаблон, к которому был привязан класс ViewComposer начнет вызывать его метод compose. Если в методе compose содержится процесс извлечения данных из MySQL, то при каждом вызове метода compose будет выполняться запрос к MySQL.

На что повлияют множественные запросы к БД?

Все это может привести к сложностям с производительностью сайта. Если предположить, что этот запрос к MySQL не был оптимизирован, то это не только приведет к “долгой“ загрузке страниц сайта, но и к медленной обработке других MySQL-запросов, которые будут выполняться другими пользователями сайта или в фоновом режиме.

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

Как это исправить?

Чтобы избавиться от повторяющихся MySQL запросов в классах ViewComposers, мы можем использовать одно из решений, которые я опишу ниже.

Способ №1. Проверять наличие подгруженных ранее данных

Самый простой способ в данном случае — это проверить, что нужные данные уже были подгружены и переданы в объект $view ранее. Это позволит обращаться к MySQL и другим источникам данных только единожды в процессе формирования запрошенной пользователем страницы.


class AllComposerView
{
    protected UserRepository $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function compose(View $view)
    {
        $viewData = $view->getData();
                 
        $view->with([
          'current_user'  = $this->getCurrentUser( $viewData ),
        ]);
    }

    protected function getCurrentUser( $viewData ) : array
    {
        # если current_user уже установлено
        if( ! empty($viewData['current_user'])) return $viewData['current_user'];
        
        $userId = auth()->id();
        
        if(empty($userId)) return null;
        
        return $this->userRepository->findWithRelated( $userId );
    }

}Code language: PHP (php)

Способ №2. Кэширование данных внутри ViewComposers

В некоторых случаях есть смысл позаботиться о кэшировании данных, которые запрашиваются внутри ViewComposers. Этот способ может показаться значительно более сложным для некоторых разработчиков, поскольку приводит к необходимости управлять кэшем. Позже будет отдельная статья о том, как управлять кэшем и о возможных побочных явлениях. Сейчас покажу примитивный способ обращения к кэше непосредственно в ViewComposer.

Нужно сразу оговориться, что для управления кэшем необходимо выбирать слой приложения поближе к Базе данных. То есть использовать методы кэширования непосредственно в классах ComposerView не самое правильное и удобное место.

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


class AllComposerView
{
    protected UserRepository $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function compose(View $view)
    {
        $viewData = $view->getData();
                 
        $view->with([
          'current_user'  = $this->getCurrentUser( $viewData ),
        ]);
    }

    protected function getCurrentUser( $viewData ) : array
    {
        $userId = auth()->id();
        
        if(empty($userId)) return null;
        
        $cacheTtl = 60*60*3*24*5;
        $cacheKey = 'users||id||' . $userId;
        
        return cache()->remember( $cacheKey, $cacheTtl, function() use(userId) {
            return $this->userRepository->findWithRelated( $userId );
        });
    }

}
Code language: PHP (php)
Опубликовано:
Присоединяйся!

Подписывайся на блог и развивайся вместе со мной.

Внимание! Чтобы получать письма, необходимо подтвердить подписку. Для этого перейдите по ссылке в письме, которое я вам отправил на указанный Email.

Ваши комментарии