Как в GridView использовать вызов функции только один раз?
Сразу упомяну, что использовать чисты array я не могу, только Active Record.
Есть грид и его колонки:
$gridColumns = [
['class' => SerialColumn::class],
[
'attribute' => 'cap',
'label' => 'Всего, Зан., Св.',
'format' => 'raw',
'value' => function($model) {
$data = Yii::$app->db->createCommand("SELECT * FROM dbFunction({$model->point_id}")->queryOne();
$html = <<< HTML
<div class="item">{$data['capacity']}</div>
<div class="item">{$data['occupied']}</div>
<div class="item">{$data['free']}</div>
HTML;
return $html;
},
],
];
echo GridView::widget([
'id' => 'my-table',
'columns' => $gridColumns,
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'rowOptions' => function($model) {
$data = Yii::$app->db->createCommand("SELECT * FROM dbFunction({$model->point_id}")->queryOne();
$class = $data['free'] === 0 ? 'free-point' : '';
if ($data['capacity'] === 0 && $data['occupied'] === 0 && $data['free'] === 0)
$class = 'd-none';
return [
'data-id' => $model->id,
'class' => $class,
];
},
]);
Всё работает, но проблема в том, что функция dbFunction на каждую строчку выполняется по два раза (один раз для ячейки и один раз для определения условий отрисовки самой строки). А функция очень тяжеловесная.
Вопрос: можно ли где-то её вызвать единожды (для итерации) и использовать результат в обоих местах? Не имею понятия как сделать, потому что для columns внутри движка идёт свой цикл, и для rowOptions видимо тоже что-то отдельное. А как это скомпановать?
P.S. На js я могу перенести условия для rowOptions, но тогда получится что написано "отображается 30 записей из 60", а на деле будет отображено 5. Поэтому хотелось бы решить это так же на бэке.
Ответы (1 шт):
Помогли с решением на английском SO.
Добавить функцию в модель и приватную переменную, куда складировать результат:
class MyModel extends ActiveRecord
{
private ?array $capacityData = null;
public function capacityData(): array
{
if ($this->capacityData === null) {
$this->capacityData = $this->getDb()->createCommand(
"SELECT * FROM dbFunction({$model->point_id})"
)->queryOne();
}
return $this->capacityData;
}
// ... other definitions ...
}
Потом её использовать:
'value' => function($model) {
$data = $model->capacityData();
$html = <<< HTML
<div class="item">{$data['capacity']}</div>
<div class="item">{$data['occupied']}</div>
<div class="item">{$data['free']}</div>
HTML;
return $html;
},
и
'rowOptions' => function($model) {
$data = $model->capacityData();
$class = $data['free'] === 0 ? 'free-point' : '';
if ($data['capacity'] === 0 && $data['occupied'] === 0 && $data['free'] === 0)
$class = 'd-none';
return [
'data-id' => $model->id,
'class' => $class,
];
},
SQL выполнится только один раз и положит результат в $capacityData. При других вызовах, если переменная не null - то сразу вернёт значение