evercode
(Ikon321)
April 4, 2015, 5:56pm
1
n Grideview, I need to sort a column that is the result of a method that calculates an average, not a value from a DB column.
On my search model, I’m used to doing my $dataProvider->setSort like so:
'faq_weight' => [
'asc' => ['faq.faq_weight' => SORT_ASC],
'desc' => ['faq.faq_weight' => SORT_DESC],
'label' => 'Weight'
],
Super easy to understand. But now I have a method that returns a result, which is an average of ratings, not a value from a DB column. I added it to my Gridview like so:
['attribute'=>'Rating', 'value' => function($model){
return $model->getFaqRatings($model->id);
}],
This works perfectly, so far no problem. But what I don’t know how to do is make it a sortable column in gridview. I tried adding the following to $dataProvider->setSort in my search model:
'Rating' => [
'asc' => [$this->getFaqRatings($this->id) => SORT_ASC],
'desc' => [$this->getFaqRatings($this->id) => SORT_DESC],
'label' => 'Rating'
],
But that doesn’t work because it looks for a column that obviously it will not find.
Is there anyway to make my rating attribute sortable in gridview?
Vojtech
(Vojtech Horak)
April 4, 2015, 10:32pm
2
I’d say you can’t use ActiveDataProvider for this purpose because it returns rows in the same order as the underlying SQL query.
You can either use ArrayDataProvider:
http://www.yiiframework.com/doc-2.0/yii-data-arraydataprovider.html
$provider = new ArrayDataProvider([
'allModels' => $query->from('faq')->all(),
'sort' => [
'attributes' => [..., 'faqRatings', ...],
],
]);
Or you can extend the ActiveDataProvider and override the prepareModels() method:
public function init()
{
parent::init();
if (is_string($this->db)) {
$this->db = Instance::ensure($this->db, Connection::className());
}
}
/**
* {@inheritdoc}
*/
protected function prepareModels()
{
if (!$this->query instanceof QueryInterface) {
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
}
$query = clone $this->query;
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
if ($pagination->totalCount === 0) {
return [];
You would need to bypass pagination when executing the Query and add sorting that would be executed after retrieving models from DB.
Vojtech
(Vojtech Horak)
April 4, 2015, 10:43pm
3
Anyway, if you work with large sets of data you may run into some performance issues (as hinted in the ArrayDataProvider docs).
evercode
(Ikon321)
April 5, 2015, 1:07am
4
Thanks @vojtech
The array data provider is not an option at this point. I will look into your other suggestion about extending ActiveDataProvider. I wish there were a simpler solution. It seems to me that this would be a fairly common scenario.