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)