Оптимизация Laravel Eloquent запросов

Источник: «Optimizing Laravel Eloquent queries»
Вот несколько советов и приёмов, которые я узнал на своём пути, помогающих оптимизировать запросы к базе данных в Laravel. Иногда небольшие изменения могут быть очень существенными.

Выбирайте только те поля, которые вам необходимы

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

Вместо:

$users = User::get();

Вы можете выбрать только нужные поля, например, id, name и email:

$users = User::query()
->select('id', 'name', 'email')
->get();

Спецификация оператора select

Выберите нужное поле

Если вам нужно только одно поле из моделей, то вы можете использовать функцию pluck(), чтобы вернуть только это поле. При этом вместо коллекции моделей возвращается массив.

$userNames = User::query()->pluck('name');

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

$userNames = User::query()->pluck('name', 'id');

Получение списка значений столбцов

Выбор конкретных полей в отношениях

При выборке отношений можно указать, какие поля вы хотите вернуть в отношение. Здесь мы указываем, что нам нужно вернуть только идентификатор и имя роли для моделей связанных ролей.

User::query()->with('roles:id,name')->get();

Нетерпеливая загрузка определённых колонок

Получение только последней связанной записи

Допустим, у вас есть таблица аудита, в которой фиксируется каждый раз, когда пользователь входит в систему. Чтобы получить последний аудит, можно попытаться использовать существующее отношение, например, User has many audits, а затем отфильтровать их, чтобы получить последний элемент. Вместо этого в Laravel есть отношение Has One Of Many, которое можно использовать для возврата самого последнего или самого старого элемента.

public function latestAudit()
{
return $this->hasOne(Audit::class)->latestOfMany();
}

Затем его можно использовать следующим образом:

$user = User::query()
->with('latestAudit')
->first();
$user->latestAudit; // A single Audit model.

Has one of many

Создание индексов

Рассмотрите возможность добавления индексов к столбцам, по которым часто выполняется поиск. В качестве примера можно привести поиск существующих пользователей в приложении Laravel по их именам, чтобы можно было отметить их в комментариях.

Мы можем создать индекс для столбца name в таблице users, когда он будет определён в процессе миграции:

$table->string('name')->index();

Если мы добавляем индекс к существующему столбцу, то это можно сделать следующим образом:

$table->index('name');

Другой похожий пример — наличие двух часто используемых полей. Например, вместо name в таблице users могут быть first_name и last_name. Для добавления составного индекса мы можем сделать следующее в миграции:

$table->index(['first_name', 'last_name']);

Тогда, когда мы используем функцию where с обоими полями, она должна использовать этот составной индекс:

$users = User::query()
->where('first_name', 'like', "{$search}%")
->orWhere('last_name', 'like', "{$search}%")
->get();

Создание индексов

where like поиск и индексирование

Заманчиво писать операторы where, которые ищут все строки, содержащие строку. Например, получить всех пользователей, имя которых содержит заданный поисковый термин, используя % в качестве подстановочного знака при поиске:

$users = User::query()
->where('name', 'like', "%{$search}%")
->get();

Это означает, что хотя индекс для name существует, база данных не может его использовать.

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

$users = User::query()
->where('name', 'like', "{$search}%")
->get();

Полнотекстовый индекс

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

$table->fullText('biography');

После получения полнотекстового индекса можно использовать функцию whereFullText() вместо запроса типа where.

$users = User::query()
->whereFullText('biography', 'developer')
->get();

Полнотекстовые условия where

Пагинация

В большинстве предыдущих примеров следует обратить внимание на то, что все результаты мы получаем с помощью функции get().

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

Обычно требуется возвращать только набор результатов за один раз. Это можно сделать следующим образом, используя функции skip() и take() для ограничения результатов:

$users = User::query()
->skip(10)
->take(10)
->get();

Но это может быстро стать громоздким, если приходится следить за тем, сколько из них пропустить.

Вместо этого можно использовать функцию paginate() для возврата набора результатов. Laravel автоматически определяет номер страницы из строки запроса, поэтому он знает, какой набор результатов следует отобразить за один раз. Мы можем передать целое число в метод paginate(), чтобы определить, сколько результатов нужно вернуть, например, в этом случае будет возвращено 10 результатов на страницу.

$users = User::query()
->paginate(10);

Пагинация базы данных

Дополнительные материалы

Предыдущая Статья

Как CSS @scope может заменить БЭМ

Следующая Статья

9 распространённых ошибок в CSS, которые мы совершаем