Вы когда-нибудь ждали, пока страница загрузится, и думали: "Ну почему так долго?" Чаще всего проблема в базе данных. Yii2 даёт мощный инструмент — ActiveQuery. Но если использовать его неправильно, можно получить тормоза. В этой статье я расскажу, как сделать запросы быстрыми и не сойти с ума.

Почему медленные запросы — это зло

Представьте, что вы заказываете пиццу. Один звонок — и всё готово. А если звонить по одному ингредиенту? То же самое с базой данных. Каждый запрос — это соединение, обработка, передача данных. Чем больше запросов, тем дольше ждёшь. Yii2 ActiveQuery может генерировать сотни запросов, если не настроить его правильно. Особенно это заметно на списках товаров, пользователей или постов.

Основы ActiveQuery: что это и как работает

ActiveQuery — это объект, который строит SQL-запрос. Вы вызываете методы, а он собирает строку. Например:

$users = User::find()
    ->where(['status' => 1])
    ->orderBy(['created_at' => SORT_DESC])
    ->all();

Это просто. Но когда появляются связи (relations), начинаются проблемы. Yii2 по умолчанию лениво загружает связанные данные. То есть при обращении к $user->profile делается отдельный запрос. Если у вас 100 пользователей, будет 101 запрос. Это называется проблема N+1.

Проблема N+1 и как её победить

Проблема N+1 — это когда для каждого элемента списка делается дополнительный запрос. Например, выводим список пользователей с их профилями:

$users = User::find()->all();
foreach ($users as $user) {
    echo $user->profile->bio;
}

Здесь сначала один запрос на всех пользователей, потом для каждого — запрос на профиль. Итог: 1 + N запросов. Решение — жадная загрузка (eager loading). Используйте with() или joinWith():

$users = User::find()
    ->with('profile')
    ->all();
foreach ($users as $user) {
    echo $user->profile->bio;
}

Теперь всего два запроса: один на пользователей, второй на профили. Yii2 сам свяжет их в памяти. Это база, но многие о ней забывают.

Правило: Всегда используйте with() или joinWith(), когда вам нужны связанные данные. Это сократит количество запросов в разы.

Фильтрация и сортировка: не делайте лишнего

Часто разработчики загружают все данные, а потом фильтруют в PHP. Это убивает производительность. Пусть база данных делает свою работу. Используйте where(), andWhere(), orWhere() прямо в ActiveQuery. Пример:

$query = User::find()
    ->where(['status' => 1])
    ->andWhere(['>=', 'created_at', '2023-01-01'])
    ->orderBy(['id' => SORT_ASC])
    ->limit(20);
$users = $query->all();

Только нужные строки. Только нужные колонки. Если вам нужны только имена, используйте select():

$names = User::find()
    ->select(['username'])
    ->column();

Это вернёт массив строк, а не объекты. Быстро и экономно.

Создание своих методов в ActiveQuery

Чтобы не повторять одни и те же условия, создавайте собственные методы в классе ActiveQuery. Например, у вас есть модель UserQuery:

namespace app\models;

use yii\db\ActiveQuery;

class UserQuery extends ActiveQuery
{
    public function active()
    {
        return $this->andWhere(['status' => 1]);
    }

    public function recent($days = 30)
    {
        return $this->andWhere(['>=', 'created_at', date('Y-m-d', strtotime("-$days days"))]);
    }
}

Теперь в модели User укажите:

public static function find()
{
    return new UserQuery(get_called_class());
}

Используйте цепочки:

$users = User::find()->active()->recent(7)->all();

Код становится чище, а запросы — понятнее. Плюс легко тестировать.

МетодОписаниеПример
with()Жадная загрузка связей->with('profile')
joinWith()Жадная загрузка с JOIN->joinWith('profile')
select()Выборка только нужных колонок->select(['id', 'name'])
limit()Ограничение количества записей->limit(10)
indexBy()Индексация результата по полю->indexBy('id')

Кэширование запросов: когда данные редко меняются

Если одни и те же данные запрашиваются часто, кэшируйте результат. Yii2 поддерживает кэширование через query caching. Просто добавьте:

$users = User::find()
    ->where(['status' => 1])
    ->cache(60) // кэш на 60 секунд
    ->all();

Это работает, если настроен компонент cache в конфиге. Например, используйте файловое кэширование или Redis. Но будьте осторожны: если данные часто обновляются, кэш может показать устаревшую информацию.

Итоги: что запомнить

Оптимизация запросов в Yii2 с ActiveQuery — это не магия, а набор простых правил. Используйте жадную загрузку, фильтруйте на уровне базы данных, создавайте свои методы для повторяющихся условий и кэшируйте, где нужно. Это сократит время загрузки страниц и спасёт нервы. Не ленитесь профилировать запросы с помощью Yii Debug Toolbar — он покажет, что тормозит. И помните: быстрый сайт — это счастливые пользователи.

Студия WNDER