【问题标题】:Yii2 GridView SearchModel for count of relationYii2 GridView SearchModel 用于关系计数
【发布时间】:2018-12-17 11:39:16
【问题描述】:

我有一个模型 item,它与 image 模型有 hasMany 关系。在GridView 项目中,我有一列显示item 有多少图像。我希望能够按此列排序,并且可能能够使用复选框进行过滤以仅显示 items 而没有 images。

我尝试在搜索模型中添加检查以查询 withimages 关系,我想我会添加一个 andFilterWhere 以用于 image 计数小于输入值的位置。但是我不确定拥有该逻辑的最佳方法,是否有更好的 Yii 方法来按该计数进行过滤?

更新 根据this question 的信息,我更新了我的item 模型,使其具有返回关系计数的方法。

然后我像这样更新我的搜索模型:

class ItemSearch extends Item
{
    public $image_count;

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['id', 'product_id', 'manufacturer_id', 'scale_id', 'owner_id', 'quantity'], 'integer'],
            [['sku_number', 'description', 'details', 'condition'], 'safe'],
            [['price', 'sale_price', 'image_count'], 'number'],
        ];
    }

    /**
     * @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 = Item::find();
        $subQuery = Image::find()->select('item_id, COUNT(id) as image_count')->groupBy('item_id');
        $query->leftJoin(['imageCount' => $subQuery], 'imageCount.item_id = id');

        // add conditions that should always apply here

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

        $dataProvider->setSort([
            'attributes' => [
                'id',
                'product_id',
                'manufacturer_id',
                'scale_id',
                'owner_id',
                'image_count' => [
                    'asc' => ['imageCount.image_count' => SORT_ASC],
                    'desc' => ['imageCount.image_count' => SORT_DESC],
                    'label' => 'Images'
                ]
            ]
        ]); 

        $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;
        }

        // grid filtering conditions
        if (!empty($this->id)) {
            $query->andFilterWhere([
                'id' => $this->id,
            ]);
        }
        if (!empty($this->product_id)) {
            $query->andFilterWhere([
                'product_id' => $this->product_id,
            ]);
        }
        if (!empty($this->manufacturer_id)) {
            $query->andFilterWhere([
                'manufacturer_id' => $this->manufacturer_id,
            ]);
        }
        if (!empty($this->scale_id)) {
            $query->andFilterWhere([
                'scale_id' => $this->scale_id,
            ]);
        }
        if (!empty($this->owner_id)) {
            $query->andFilterWhere([
                'owner_id' => $this->owner_id,
            ]);
        }
        if (!empty($this->image_count)) {
            $query->andWhere(['is','imageCount.image_count',new \yii\db\Expression('null')]);
         }

        $query->andFilterWhere(['like', 'sku_number', $this->sku_number])
            ->andFilterWhere(['like', 'description', $this->description])
            ->andFilterWhere(['like', 'details', $this->details])
            ->andFilterWhere(['like', 'condition', $this->condition]);

        return $dataProvider;
    }
}

现在的问题是上面会产生一个类似这样的查询:

SELECT `item`.* FROM `item` LEFT JOIN (SELECT `item_id`, COUNT(id) as image_count FROM `image` GROUP BY `item_id`) `imageCount` ON imageCount.item_id = id WHERE `imageCount`.`image_count`=0

不再显示具有 0 个 images 的 items 的问题(并且选中该框不会显示任何内容),因为联接在 image 表中找不到该 ID 的任何内容

【问题讨论】:

  • 您可能需要添加您的搜索模型和/或与您的问题相关的任何其他代码。这样如果你犯了任何错误,可以指出来。
  • @MuhammadOmerAslam 我还没有修改 Gii 生成的搜索模型。我去添加了andFilterWhere,但不知道该放什么。
  • @MuhammadOmerAslam 这真的很接近,我会更新我的问题。现在问题是MySQL 一个
  • 请参阅我的回答中的 Update 部分。它现在应该不惜一切代价工作。

标签: gridview yii2


【解决方案1】:

您应该检查WHERE imageCount.image_count IS NULL 而不是WHERE imageCount.image_count=0,因为那些没有任何相关图像的行将在image_count 下显示为null,并将条件从

$query->andFilterWhere(['imageCount.image_count' => 0]); 

$query->andWhere(['is','imageCount.image_count',new \yii\db\Expression('null')]);`

您的查询应该像这样生成

SELECT `item`.* FROM `item` 
LEFT JOIN
(
   SELECT `item_id`, COUNT(id) as image_count 
   FROM `image` 
   GROUP BY `item_id`
) `imageCount` 

ON imageCount.item_id = id 

WHERE `imageCount`.`image_count` is NULL

更新

由于您仍然面临问题,所以我想我有一些空闲时间来发现这个问题,我想出了以下内容

您在过滤图像计数为 0 的项目时遇到问题我没有您所拥有的确切架构,但我将在下面说明一个示例,其中我有 Shoots 及其相关的类似场景联结表中的标签ShootTag 模型

  • Shoots

以下是架构和数据的示例

+----+------------+--------+------------+
| id | name       | active | shoot_type |
+----+------------+--------+------------+
|  1 | aslam omer |      1 | modeling   |
|  2 | asif       |      1 | modeling   |
|  3 | saleem     |      1 | modeling   |
|  4 | sajid      |      1 | modeling   |
|  5 | tasleem    |      1 | modeling   |
|  6 | tehseen    |      1 | modeling   |
|  7 | amjad      |      1 | modeling   |
|  8 | shaban     |      1 | modeling   |
|  9 | irfan      |      1 | modeling   |
+----+------------+--------+------------+
  • ShootTags

以下是架构和数据的示例

+----------+--------+
| shoot_id | tag_id |
+----------+--------+
|        1 |      1 |
|        1 |      2 |
|        1 |      3 |
|        1 |      4 |
|        2 |      1 |
|        2 |      2 |
|        2 |      3 |
|        3 |      1 |
|        4 |      1 |
|        4 |      4 |
+----------+--------+

现在考虑上面的表格和数据,我有一个名为 ShootsSearch 的搜索模型,我用它来显示所有的拍摄,我想针对保存在 ShootTag 中的每个拍摄显示标签的数量型号。

我没有添加 GridView 的代码,因为它不相关,我的搜索模型 ShootsSearch 具有以下搜索方法,该方法适用于任何计数拍摄标签模型中的拍摄。

  • 与您的代码不同的是,我使用 ShootsSearch 模型进行第一个查询,而不是 Shoots 模型,因为您使用 Item::find(); 应该是 ItemSearch::find(); 而不是别名您正在使用 image_count 在搜索模型中声明,

  • 然后主查询中的new Expression('if(st.totalTags is NOT NULL,st.totalTags,0) as totalTags') 行需要将空值显示为0,以便在此处使用条件选择。

  • 然后您需要检查if ($this->totalTags == '0') { 以应用$query->andWhere(['IS', 'totalTags', new Expression('null')]);,因为totalTags 的实际值将是null,其中对于任何Shoot 都没有可用的标签,而在其他部分您将使用query->andFilterWhere(['=', 'totalTags', $this->totalTags]);

这在您希望看到下面的图像的所有三种情况下都能正常工作

第一次默认查看

搜索总标签数为 4 的芽

搜索 TotalTags 计数为 0 的芽

您应该在代码中替换以下内容

  • public $totalTagspublic $image_count
  • ShootTagsImage
  • shoot_iditem_id
  • ShootsSearchItemSearch/self

这是搜索模型,代码已经过测试并可以运行。

class ShootsSearch extends Shoots
{
    /**
     * @var mixed
     */
    public $totalTags;

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['id', 'active', 'totalTags'], 'integer'],
            [['name', 'shoot_type', 'description', 'totalTags'], '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)
    {
        $subQuery = ShootTag::find()->select(
            [
                new Expression('shoot_id, COUNT(shoot_id) as totalTags')
            ]
        )->groupBy('shoot_id');
        $query = ShootsSearch::find()->alias('s')
            ->select(
                [
                    's.*',
                    new Expression('if(st.totalTags is NOT NULL,st.totalTags,0) as totalTags')
                ]
            )->leftJoin(['st' => $subQuery], 'st.shoot_id=s.id');

        // add conditions that should always apply here

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

        $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;
        }

        // grid filtering conditions
        $query->andFilterWhere([
            'id' => $this->id,
            'active' => $this->active
        ]);

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

        if ($this->totalTags == '0') {
            $query->andWhere(['IS', 'totalTags', new Expression('null')]);
        } else {
            $query->andFilterWhere(['=', 'totalTags', $this->totalTags]);
        }

        return $dataProvider;
    }
}

【讨论】:

  • 这并不能解决我的问题。对于初学者,当没有提供过滤器时,根本不会出现没有图像的项目,然后按 null 过滤显示没有记录,因为没有图像的项目在最初的原始查询中。
  • @sharf 我没有看到您的问题中的更新代码与旧代码相同
  • 我没有打扰,因为它并不真正相关。我只是这样做了。问题是初始查询,而不是过滤器。如果没有应用过滤器,它不会返回正确的条目。
  • 嗯,它可以帮助我排除故障并在可能的情况下通过在我的末尾复制 @sharf 来找到修复程序
  • 由于每个图像只能属于一个项目,所以我没有链接表,而是在图像表上有外键。我会看看你的例子,看看我能想出什么。我最近没有机会看它。感谢您抽出宝贵时间!
【解决方案2】:

我的理解是,你需要统计关联表中的数据量,并根据统计数据进行排序和过滤。

那么你的ItemSearch::search() 方法应该可以改成这样:

public function search($params)
{
    $query = Item::find()->select('*, (select count(*) from image where image.item_id = item.id) as image_count'); // If the table name of your Image table is image, if the table name of your Item table is item

    // add conditions that should always apply here

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

    $dataProvider->setSort([
        'attributes' => [
            'id',
            'product_id',
            'manufacturer_id',
            'scale_id',
            'owner_id',
            'image_count'
        ]
    ]); 

    $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;
    }

    // grid filtering conditions
    if (!empty($this->id)) {
        $query->andFilterWhere([
            'id' => $this->id,
        ]);
    }
    if (!empty($this->product_id)) {
        $query->andFilterWhere([
            'product_id' => $this->product_id,
        ]);
    }
    if (!empty($this->manufacturer_id)) {
        $query->andFilterWhere([
            'manufacturer_id' => $this->manufacturer_id,
        ]);
    }
    if (!empty($this->scale_id)) {
        $query->andFilterWhere([
            'scale_id' => $this->scale_id,
        ]);
    }
    if (!empty($this->owner_id)) {
        $query->andFilterWhere([
            'owner_id' => $this->owner_id,
        ]);
    }
    if (!empty($this->image_count)) {
        $query->andWhere([
            'image_count' => $this->image_count,
        ]);
     }

    $query->andFilterWhere(['like', 'sku_number', $this->sku_number])
        ->andFilterWhere(['like', 'description', $this->description])
        ->andFilterWhere(['like', 'details', $this->details])
        ->andFilterWhere(['like', 'condition', $this->condition]);

    return $dataProvider;
}

但是如果你的image_count经常需要计算和统计,而且你的数据量很大,那么建议你在item表中重新定义这个字段。

更新:

我的错,聚合字段无法通过 where 条件过滤,但您可以使用:

$query->andFilterHaving([
    'image_count' => $this->image_count,
]);

【讨论】:

  • 这适用于显示和排序,但是当尝试通过将 image_count 设置为 1(或任何值)进行过滤时,我得到 1054 Unknown column 'image_count' in 'where clause' The SQL being executed was: SELECT COUNT(*) FROM item` WHERE image_count='1'`
  • @sharf 查看更新
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-05
  • 2019-12-10
相关资源
最近更新 更多