Разрабатываю 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, разница очень большая. Есть ли какие-то другие, более "элегантные" способы решения данной проблемы?
<?php
return $this->hasMany(Ticker::class, ['id' => 'tickerId'])
->with('ticker') // по идее эта строчка должна помочь - это жадный запрос
->viaTable(RelationIndicatorTicker::tableName(), ['indicatorId' => 'id']);
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости