Как избавиться от большого количества запросов к бд, при попытке вытянуть связи через expand в Yii2?

90
06 июля 2021, 09:20

Разрабатываю RESTful API на фреймворке Yii2. На данный момент делаю вторую версию API v2. Задача следующая:

Сократить количество запросов к бд, при выборке.

Отправляется запрос на URL: api.com/v2/indicators&expand=tickers.

В expand я передаю связи, которые хочу вытянуть. Вообще в expand можно предать до 20 связей. Данных в базе достаточно много, и поэтому Yii2 делает очень много запросов, для того чтобы получить все tickers для indicators. Связь MANY_TO_MANY.

Пример: В базе есть 300 индикаторов, у каждого индикатора есть много тикеров. Связь идет через junction table, в модели Indicator выглядит так:

public function getTickers(): ActiveQuery
{
    return $this->hasMany(Ticker::class, ['id' => 'tickerId'])
        ->viaTable(RelationIndicatorTicker::tableName(), ['indicatorId' => 'id']);
}

Используется дефолтный ActiveController и его дефолтный IndexAction. Связи все тянет правильно, но делает примерно 300*20 = 6000 запросов, если у каждого индикатора будет по 20 тикеров(что так и есть).

Сейчас Yii2 в базу делает такие пустые запросы, которые исполняются по 6000 раз:

SELECT * FROM `ticker` WHERE 0=1

Вот скрин из NeorProfiler

Видно что у indicator с id = 8 есть два тикера с id IN(40, 51). Т.е. все остальные запросы просто сплошной мусор. Как от этого можно избавиться? Есть ли какие-то варианты переопределить expand?

Пробовал я еще переопределять дефолтный IndexAction, и там переопределял prepareDataProvider, писал свой метод для этого в котором получал:

$query = Indicator::find();
$expand = Yii::$app->getRequest->getQueryParam('expand');
$expand = explode(',', $expand);
foreach($expand as $relationName) {
    //  тут можно сделать проверку на реляцию, но сейчас не суть
    $query = $query->joinWith($relationName);
}

Таким способом все работало и в базе не было "мусора", и вместо 6000 запросов было всего 3, разница очень большая. Есть ли какие-то другие, более "элегантные" способы решения данной проблемы?

Answer 1
<?php 
return $this->hasMany(Ticker::class, ['id' => 'tickerId'])
        ->with('ticker') // по идее эта строчка должна помочь - это жадный запрос
        ->viaTable(RelationIndicatorTicker::tableName(), ['indicatorId' => 'id']);
READ ALSO
Перевод разницы во времени в часы PHP

Перевод разницы во времени в часы PHP

Как посчитать разницу $gotime - $cometime в (int) часах?

108
Работа с preg_replace

Работа с preg_replace

Прошу помочь с использованием функции preg_replace

100
Добавить изображение в анонсы элемента

Добавить изображение в анонсы элемента

Доброго времени суток Подскажите пожалуйста как добавить image в Анонсы элемента? Я делаю так:

115
Оптимизировать функцию Laravel

Оптимизировать функцию Laravel

Всем приветДелаю небольшой проект на Ларавел, где нужно сгенерировать 4 случайные дни недели , включая сегодняшний

93