简体   繁体   English

Yii2:在GridView中对关系计数列进行排序

[英]Yii2: sort a relational count column in GridView

[EDITED 2] [编辑2]

I'm having hard time to sort by the 'topicCount' which is defined as a relational getter on a model 'Tag'. 我很难按'topicCount'进行排序,'topicCount'被定义为模型'Tag'上的关系获取器。 A Topic can have a lots of Tag, and wish to sort the Tags by how many Topics containing that Tag. 一个主题可以有很多标签,并希望按照包含该标签的主题数对标签进行排序。

In my models/Tag.php: 在我的模型/Tag.php中:

public function getTopicCount()
{
    return TopicTag::find()->where(['tag_id' => $this->id])->count();
}

And in my views/tag/index.php: 在我的观点/ tag / index.php中:

<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        'id',
        'name',
        [
             'attribute'=>'topicCount',
             'value' => 'topicCount',
        ],
        'created_at',

        ['class' => 'yii\grid\ActionColumn','template' => '{view}',],
    ],
]); ?>

And in my controllers/TagController.php: 在我的controllers / TagController.php中:

public function actionIndex()
{
    $dataProvider = new ActiveDataProvider([
        'query' => Tag::find(),
        'sort'=> [
            'defaultOrder' => ['id'=>SORT_DESC],
            'attributes' => ['id','topicCount'],
        ],
        'pagination' => [
            'pageSize' => 100,
        ],
    ]);

    return $this->render('index', [
        'dataProvider' => $dataProvider,
    ]);
}

And in my models/TagSearch.php: 在我的模型/TagSearch.php中:

<?php

namespace common\models;

use Yii;

/**
 * This is the model class for table "tags".
 *
 * @property integer $id
 * @property string $name
 * @property string $created_at
 * @property string $updated_at
 */
class TagSearch extends Tag
{

public $topicCount;

/**
 * @inheritdoc
 */
public function rules()
{
    return [
        [['topicCount'], 'safe']
    ];
}

public function search($params)
{
    // create ActiveQuery
    $query = Tag::find();
    $query->joinWith(['topicCount']);

    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);

    $dataProvider->sort->attributes['topicCount'] = [
        'asc' => ['topicCount' => SORT_ASC],
        'desc' => ['topicCount' => SORT_DESC],
    ];

    if (!($this->load($params) && $this->validate())) {
        return $dataProvider;
    }

    $query->andFilterWhere([
        //... other searched attributes here
    ])
    ->andFilterWhere(['=', 'topicCount', $this->topicCount]);

    return $dataProvider;
}


}

And in the index view I can see the correct topicCount: 在索引视图中,我可以看到正确的topicCount:

在此处输入图片说明

but on clicking the topicCount column I get the error: 但是在单击topicCount列时出现错误:

exception 'PDOException' with message 'SQLSTATE[42703]: Undefined column: 7 ERROR: column "topicCount" does not exist LINE 1: SELECT * FROM "tags" ORDER BY "topicCount" LIMIT 100

Thanks for any guidance..! 感谢您的指导。


[EDIT] [编辑]

Following Lucas' advice, I've set my dataProvider query in my $dataProvider like this: 遵循卢卡斯的建议,我在$ dataProvider中设置了dataProvider查询,如下所示:

'query' => $query->select(['tags.*','(select count(topic_tags.id) from topic_tags where topic_tags.tag_id=tags.id) topicCount'])->groupBy('tags.id'),

and I got error: 我得到了错误:

exception 'PDOException' with message 'SQLSTATE[42P01]: Undefined table: 7 ERROR: missing FROM-clause entry for table "tags"

so I reformulated like this: 所以我这样改写:

        'query' => $query->from('tags')->leftJoin('topic_tags','topic_tags.tag_id = tags.id')->select(['tags.*','(select count(topic_tags.id) from topic_tags where topic_tags.tag_id=tags.id) topicCount'])->groupBy('tags.id'),

and now I get the result: 现在我得到结果:

在此处输入图片说明

apparently the topicCount column is not set, so when I try to sort by it, it returns the error: 显然没有设置topicCount列,因此当我尝试按其排序时,它返回错误:

exception 'PDOException' with message 'SQLSTATE[42703]: Undefined column: 7 ERROR: column "topicCount" does not exist

but when I try the SQL directly on the DB, it works fine: 但是当我直接在数据库上尝试SQL时,它可以正常工作:

在此处输入图片说明

so I suppose the problem is in the way Yii handles the alias 'topicCount'? 所以我想问题出在Yii处理别名'topicCount'的方式上吗?


2nd EDIT 第二次编辑

Still the same result without the topicCount set in the Grid view. 没有在Grid视图中设置topicCount的结果仍然相同。 I show my TagSearch model, TagController and views/tag/index view file below: 我在下面显示我的TagSearch模型,TagController和views / tag / index视图文件:

TagSearch 标签搜索

<?php

namespace common\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use common\models\Tag;

/**
 * TagSearch represents the model behind the search form about `common\models\Tag`.
 */
class TagSearch extends Tag
{

    public $topicCount;

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['id', 'topicCount'], 'integer'],
            [['name', 'created_at', 'updated_at', 'topicCount'], 'safe'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params)
    {
        $query = Tag::find();

        $dataProvider = new ActiveDataProvider([
            'query' => $query->from("tags")->select(["tags.*","(select count(topic_tags.id) from topic_tags where topic_tags.tag_id=tags.id) topicCount"])->groupBy("tags.id"),
        ]);

        $this->load($params);

        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            $query->where('0=1');
            return $dataProvider;
        }

        $query->andFilterWhere([
            'id' => $this->id,
            'topicCount' => $this->topicCount,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ]);

        $query->andFilterWhere(['like', 'name', $this->name]);

        return $dataProvider;
    }
}

Tag model 标签模型

<?php

namespace common\models;

use Yii;

/**
 * This is the model class for table "tags".
 *
 * @property integer $id
 * @property integer $topicCount
 * @property string $name
 * @property string $created_at
 * @property string $updated_at
 */
class Tag extends \yii\db\ActiveRecord
{

    public $topicCount;

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'tags';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['topicCount'], 'integer'],
            [['name'], 'string'],
            [['created_at', 'updated_at'], 'required'],
            [['created_at', 'updated_at'], 'safe']
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'name' => 'Name',
            'topicCount' => 'TC',
            'created_at' => 'Created At',
            'updated_at' => 'Updated At',
        ];
    }

}

TagController 标签控制器

public function actionIndex()
{

    $searchModel = new TagSearch();
    $myModels = $searchModel->search([]);

    return $this->render('index', [
        'dataProvider' => $myModels,
    ]);
}

tags/index 标签/索引

<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        'id',
        'name',
        'topicCount',
        'created_at',
        'updated_at',
        ['class' => 'yii\grid\ActionColumn','template' => '{view}',],
    ],
]); ?>

What am I missing? 我想念什么?

So resolved following this wiki : 所以解决了这个维基

Since in my case I don't use SUM('amount'), I changed to the following and works perfectly: 由于在我的情况下,我不使用SUM('amount'),因此我将其更改为以下内容并可以完美地工作:

Tag model: 标签型号:

public function getTopicCount() 
{
    return $this->hasMany(TopicTag::className(), ["tag_id" => "id"])->count();

}

TagSearch model: TagSearch模型:

    $query = Tag::find();
    $subQuery = TopicTag::find()->select('tag_id, COUNT(tag_id) as topic_count')->groupBy('tag_id');        
    $query->leftJoin(["topicSum" => $subQuery], '"topicSum".tag_id = id');

Just encountered a problem with the generated SQL: 刚遇到生成的SQL问题:

exception 'PDOException' with message 'SQLSTATE[42P01]: Undefined table: 7 ERROR:  missing FROM-clause entry for table "topicsum"

This might be a Postgres-specific issue, had to arrange the code so that the generated SQL becomes like this: 这可能是特定于Postgres的问题,必须安排代码以使生成的SQL变为:

SELECT COUNT(*) FROM "tags" 
LEFT JOIN (SELECT "tag_id", COUNT(*) as topic_count FROM "topic_tags" GROUP BY "tag_id") "topicSum" 
ON "topicSum".tag_id = id

note the double-quotation in "topicSum".tag_id part. 请注意"topicSum".tag_id部分中的双引号。

Hope this might be of help for someone using Postgres on Yii2. 希望这对在Yii2上使用Postgres的人有所帮助。

You should alter your query to group and select the count instead of working with relations. 您应该将查询更改为分组并选择计数,而不是使用关系。

$query->groupBy('tags.id')->select(['tags.*','(select count(topic_tag.id) from topic_tag where topic_tag.tag.id=tags.id) topicCount']);

This will add topicCount as a result object in your query, which will make it behave like an ordinary column. 这会将topicCount添加为查询中的结果对象,这将使其行为像普通列一样。

Also as a side note, for a method to act a relation in Yii2, it must return an ActiveQuery object. 另外,对于在Yii2中执行关系的方法,它必须返回ActiveQuery对象。 Your getTopicCount() is returning the count as an int, instead of the query, therefore Yii2 will not treat it like a relation. 您的getTopicCount()会将计数返回为int而不是查询,因此Yii2不会将其视为关系。

Based on this Wiki and @arogachev's answer. 基于此Wiki和@arogachev的答案。 I put select property to get tags count 我放了select属性来获取标签计数

public function search($params)
{ 
$query = SomeModels::find()
         ->select('subQueryName.field_count, someModels.*');
// ....

so it will give SQL like this SELECT subQuery.field_count, someModels.* ... 因此它将像这样的SQL给SELECT subQuery.field_count, someModels.* ...

at view (grid), 在视图(网格)上,

[
  'attribute'=> 'field_count',
],

Thank you @arogachev , you saved me :) 谢谢@arogachev,您救了我:)

light solution is just reate view in PostgreSQL 解决方案只是PostgreSQL中的view

and generate model via gii generator using as model and order & find work. 并通过gii生成器生成模型并用作模型并订购和查找工作。

For update & delete use table model for search & index use view model. 对于更新和删除,使用table模型进行搜索和索引,使用view模型。

For example 例如

for actions update & delete use Tag model 动作updatedelete使用Tag模型

for actions index & view use TagView model. 对于动作indexview使用TagView模型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM