You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于1:n关联表的GridView多规格联合排序实现问题

问题背景

我有三张数据表:

  • product_tbl:存储产品数据
  • specification_tbl:存储产品可拥有的所有规格类型
  • product_specification_tbl:关联表,以[$name => $value]格式存储各产品的规格值,与product_tbl为1:n关系

表结构示例:

product-idspecification-idnamevalue
110Tire Width250
111Aspect Ratio50
112Rim Size16
210Tire Width195
211Aspect Ratio70
212Rim Size14

需求是在Yii的GridView中新增拼接格式为[Tire Width]/[Aspect Ratio]R[Rim Size]的轮胎规格列,并且实现点击该列时,按「轮胎宽度→扁平比→轮毂尺寸」的顺序统一升/降序排序。

现有模型已通过hasOne关联了三个规格字段,可单独对每个规格列排序,但无法实现三者联合排序。


解决方案

1. 调整控制器的DataProvider查询与排序配置

在获取$dataProvider的代码中,关联三个规格表并配置自定义排序规则:

use yii\data\ActiveDataProvider;
use yii\db\Expression;

// 初始化Product查询,关联三个规格表并设置别名
$query = \app\models\Product::find();
$query->joinWith([
    'productTreadWidth as tw',
    'productAspectRatio as ar',
    'productDiameter as rd'
]);

// 可选:将拼接后的轮胎规格作为计算字段加入查询,方便视图直接调用
$query->addSelect([
    '*',
    'tire_size' => new Expression("CONCAT(tw.value_en, '/', ar.value_en, 'R', rd.value_en)")
]);

// 配置DataProvider,添加自定义排序规则
$dataProvider = new ActiveDataProvider([
    'query' => $query,
    'sort' => [
        'attributes' => [
            // 保留原有可排序字段
            'brand.name_en',
            'family.name_en',
            'product_id',
            // 新增轮胎尺寸的联合排序规则
            'tire_size' => [
                'asc' => [
                    new Expression('CAST(tw.value_en AS UNSIGNED) ASC'),
                    new Expression('CAST(ar.value_en AS UNSIGNED) ASC'),
                    new Expression('CAST(rd.value_en AS UNSIGNED) ASC'),
                ],
                'desc' => [
                    new Expression('CAST(tw.value_en AS UNSIGNED) DESC'),
                    new Expression('CAST(ar.value_en AS UNSIGNED) DESC'),
                    new Expression('CAST(rd.value_en AS UNSIGNED) DESC'),
                ],
                'label' => Yii::t('app', '轮胎尺寸')
            ],
            // 保留单独规格的排序(如果需要)
            'productTreadWidth.value_en' => [
                'asc' => [new Expression('CAST(tw.value_en AS UNSIGNED) ASC')],
                'desc' => [new Expression('CAST(tw.value_en AS UNSIGNED) DESC')],
            ],
            'productAspectRatio.value_en' => [
                'asc' => [new Expression('CAST(ar.value_en AS UNSIGNED) ASC')],
                'desc' => [new Expression('CAST(ar.value_en AS UNSIGNED) DESC')],
            ],
            'productDiameter.value_en' => [
                'asc' => [new Expression('CAST(rd.value_en AS UNSIGNED) ASC')],
                'desc' => [new Expression('CAST(rd.value_en AS UNSIGNED) DESC')],
            ],
        ]
    ]
]);

2. 在GridView中添加轮胎规格列

修改GridView的columns配置,新增显示拼接后轮胎规格的列:

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],

        [
            'attribute' => 'brand.name_en',
            'label' => 'Brand'
        ],
        [
            'attribute' => 'family.name_en',
            'label' => 'Model',
        ],
        // 新增轮胎尺寸列
        [
            'attribute' => 'tire_size',
            'label' => Yii::t('app', '轮胎尺寸'),
            // 如果query中已添加tire_size字段,直接返回$model->tire_size即可
            'content' => function($model) {
                return $model->productTreadWidth['value_en'] . '/' . $model->productAspectRatio['value_en'] . 'R' . $model->productDiameter['value_en'];
            },
            'sortable' => true, // 开启排序功能
        ],
        // 保留原有单独规格列(可选)
        [
            'attribute' => 'productTreadWidth.value_en',
            'label' => Yii::t('app' , 'Tire Width'),
            'content' => function($model){
                return $model->productTreadWidth['value_en'];
            }
        ],
        [
            'attribute' => 'productAspectRatio.value_en',
            'label' => Yii::t('app' , 'Aspect Ratio'),
            'content' => function($model){
                return $model->productAspectRatio['value_en'];
            }
        ],
        [
            'attribute' => 'productDiameter.value_en',
            'label' => Yii::t('app' , 'Diameter'),
            'content' => function($model){
                return $model->productDiameter['value_en'];
            }
        ],                    
        'name_en',
        'product_id',
    ],
]);

关键说明

  • 使用CAST(... AS UNSIGNED)是为了避免字符串排序的问题:如果规格值存储为字符串,直接排序会出现"8"排在"100"之后的错误,转成无符号整数后可按数值大小排序;若规格值本身是数值类型,可去掉CAST表达式,直接使用字段名排序。
  • joinWith中的别名tw、ar、rd需与模型中关联方法的specification_id条件对应,确保关联到正确的规格数据。

内容的提问来源于stack exchange,提问作者Narek Abedian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 13:30:41