我有三个数据库表:
产品(身份证,姓名)
product_has_adv(产品,优势,排序,重要)
优势(id,text)
在ProductModel中我定义了这个:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id']);
}
我毫无问题地获得了优势。
但是现在我需要添加一个product_has_advantage.important = 1 clausel,并通过product_has_advantage-table中的sort-columen对优势进行排序。
我必须如何以及在哪里实现它?
答案 0 :(得分:34)
对关系使用via
和viaTable
方法会导致两个单独的查询。
您可以在第三个参数中指定callable,如下所示:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'], function ($query) {
/* @var $query \yii\db\ActiveQuery */
$query->andWhere(['important' => 1])
->orderBy(['sort' => SORT_DESC]);
});
}
将应用important
的过滤器,但排序不会,因为它在第一次查询中发生。因此,IN
语句中的ID顺序将会更改。
根据您的数据库逻辑,最好将important
和sort
列移至advantage
表。
然后只需添加条件并对现有方法链进行排序:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'])
->andWhere(['important' => 1])
->orderBy(['sort' => SORT_DESC]);
}
答案 1 :(得分:4)
对关系使用viaTable
方法会导致两个单独的查询,但如果您不需要link()
方法,则可以按以下方式使用innerJoin按product_has_advantage表进行排序:
public function getAdvantages()
{
$query = AdvantageModel::find();
$query->multiple = true;
$query->innerJoin('product_has_advantage','product_has_advantage.advantage = advantage.id');
$query->andWhere(['product_has_advantage.product' => $this->id, 'product_has_advantage.important' => 1]);
$query->orderBy(['product_has_advantage.sort' => SORT_DESC]);
return $query;
}
注意,$query->multiple = true
允许您使用此方法,因为Yii2具有多种关系。
答案 2 :(得分:3)
仅供参考https://github.com/yiisoft/yii2/issues/10174
ORDER BY viaTable()
列几乎不可能。
对于Yii 2.0.7,它从viaTable()
查询返回一组ID,
和final / top query IN()
子句忽略顺序。
答案 3 :(得分:1)
首先,您需要使用CRUD为联结表(ProductHasAdv
)创建名为product_has_adv
的模型。
然后在product
模型中创建关系并对其进行排序:
public function getAdvRels()
{
return $this->hasMany(ProductHasAdv::className(), ['product' => 'id'])->
orderBy(['sort' => SORT_ASC]);;
}
然后创建第二个关系:
public function getAdvantages()
{
$adv_ids = [];
foreach ($this->advRels as $adv_rel)
$adv_ids[] = $adv_rel->advantage;
return $this->hasMany(Advantage::className(), ['id' => 'advantage'])->viaTable('product_has_adv', ['product' => 'id'])->orderBy([new Expression('FIELD (id, ' . implode(',', $adv_ids) . ')')]);
}
这将使用order by FIELD
技术对最终结果进行排序。
不要忘记添加:
use yii\db\Expression;
排队。
答案 4 :(得分:1)
对于过了一段时间来到这里并且不喜欢上述解决方案的人,我通过表格过滤回到过孔表来实现它。
以上代码示例:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'])
->innerJoin('product_has_advantage','XXX')
->orderBy('product_has_advantage.YYY'=> SORT_ASC);
}
注意使用正确的连接路径更改XXX,使用正确的排序列更改YYY。
答案 5 :(得分:0)
我已经对此进行了一些管理...但是此后还需要其他工作。 关键是您必须首先从源模型查询多对多关系,然后在该闭包内查询目标模型。
$query = Product::find();
$query->joinWith([
'product_has_adv' => function ($query)
{
$query->alias('pha');
$query->orderBy('pha.sort ASC');
$query->joinWith(['advantage ' => function ($query){
$query->select([
'a.id',
'a.text',
]);
$query->alias('a');
}]);
},
]);
然后,您只需要根据需要修饰排序的结果即可。 每行的结果看起来像
"product_has_adv": [
{
"product": "875",
"advantage": "true",
"sort": "0",
"important": "1",
"advantage ": {
"id": "875",
"text": "Some text..",
}
},
答案 6 :(得分:0)
正如@arogachev所解释的,viaTable
使用两个单独的查询,这使得任何中间orderBy
都已过时
您可以按照以下方式将viaTable
替换为innerJoin
,类似于@MartinM
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::class, ['pha.product' => 'id'])
->innerJoin('product_has_advantage pha', 'pha.advantage = advantage.id')
->andWhere(['pha.important' => 1])
->orderBy(['pha.sort' => SORT_ASC]);
}
通过调整hasMany
的结果,您正在调整目标类-AdvantageModel::find()
的查询; product_has_advantage
可以通过advantage
身份加入
hasMany
的第二个参数,链接,可以看成[ query.column => $this->attribute ]
,您现在可以通过连接的product_has_advantage
及其product
身份来支持它
注意,当使用viaTable
时,可以将link参数视为中间查询已完成,并且我们从此处开始; [ query.column => viaTable.column ]
因此['id', 'advantage']
是您的问题
答案 7 :(得分:-2)
public function getAdvantages()
{
return $this
->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'])
->andWhere(['important' => 1])
->orderBy(['sort' => SORT_DESC]);
}