Reputation: 3
I want to warn: I can't use array
, only Active Record
.
So I have a GridView and columns:
$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,
];
},
]);
It works correctly, but dbFunction
is executing two times for each row (first time for a cell and the second - for row options). This function is a very heavyweight.
The question is: is there a way to execute this function only once (for each iteration) and use the result of the function in both places? Don't know how to do it, because under the hood Yii2 uses loop for columns
and, I guess, separate invoke for rowOptions. How can I combine it?
P.S. Of course I can write conditions for rowOptions
using js, but then will be a bug: it will be written "shows 30 of 60 records", but in facts it will be shown only 5 (or else). That's why I want to solve the problem on the backend.
Upvotes: 0
Views: 110
Reputation: 174
I think you can use try using query cache for this purpose. Something like this:
Yii::$app->db->cache(function ($db, $model) {
return $db->createCommand(
"SELECT * FROM dbFunction({$model->point_id})"
)->queryOne();
});
This way the second call will return cached results.
P.S. You would also need to configure Cache component and query cache in db config.
Upvotes: 0
Reputation: 6179
I would suggest moving the function that loads data into model and store the result in private property of model.
For example something like this:
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 ...
}
Then you can use it in multiple places like this:
'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;
},
Or like this:
'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,
];
},
The SQL query will be executed only during first call of capacityData()
function. During next call the data will be already available in $capacityData
property.
Btw. adding class in $rowOptions
property that hides the row won't solve the problem with wrong number in text "shows 30 of 60 records". The only way that text will show correct numbers will be if the data provider doesn't contain rows that shouldn't be displayed.
Upvotes: 1