基于1:n关联表的GridView多规格联合排序实现问题
问题背景
我有三张数据表:
product_tbl:存储产品数据specification_tbl:存储产品可拥有的所有规格类型product_specification_tbl:关联表,以[$name => $value]格式存储各产品的规格值,与product_tbl为1:n关系
表结构示例:
| product-id | specification-id | name | value |
|---|---|---|---|
| 1 | 10 | Tire Width | 250 |
| 1 | 11 | Aspect Ratio | 50 |
| 1 | 12 | Rim Size | 16 |
| 2 | 10 | Tire Width | 195 |
| 2 | 11 | Aspect Ratio | 70 |
| 2 | 12 | Rim Size | 14 |
需求是在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
相关产品推荐
相关产品推荐

