Оптимизация Laravel Eloquent запросов
Выбирайте только те поля, которые вам необходимы
Добавление select()
в запрос означает, что вы получаете только те поля, которые собираетесь использовать, а не возвращаете все поля модели. Если представление не нуждается в данном поле, то можно сэкономить немного памяти, получая и возвращая только необходимые поля.
Вместо:
$users = User::get();
Вы можете выбрать только нужные поля, например, id
, name
и email
:
$users = User::query()
->select('id', 'name', 'email')
->get();
Выберите нужное поле
Если вам нужно только одно поле из моделей, то вы можете использовать функцию 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.
Создание индексов
Рассмотрите возможность добавления индексов к столбцам, по которым часто выполняется поиск. В качестве примера можно привести поиск существующих пользователей в приложении 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();
Пагинация
В большинстве предыдущих примеров следует обратить внимание на то, что все результаты мы получаем с помощью функции get()
.
Это может быть нормально, когда в базе данных есть несколько строк, но когда у вас сотни или тысячи строк, то попытка получить все строки из таблицы станет медленной.
Обычно требуется возвращать только набор результатов за один раз. Это можно сделать следующим образом, используя функции skip()
и take()
для ограничения результатов:
$users = User::query()
->skip(10)
->take(10)
->get();
Но это может быстро стать громоздким, если приходится следить за тем, сколько из них пропустить.
Вместо этого можно использовать функцию paginate()
для возврата набора результатов. Laravel автоматически определяет номер страницы из строки запроса, поэтому он знает, какой набор результатов следует отобразить за один раз. Мы можем передать целое число в метод paginate()
, чтобы определить, сколько результатов нужно вернуть, например, в этом случае будет возвращено 10 результатов на страницу.
$users = User::query()
->paginate(10);