ActiveRecord where和order on via表
我有三个数据库表: 产品(id、名称) 产品具有高级(产品、优势、分类、重要) 优势(id、文本) 在ProductModel中,我定义了以下内容:ActiveRecord where和order on via表,activerecord,yii2,Activerecord,Yii2,我有三个数据库表: 产品(id、名称) 产品具有高级(产品、优势、分类、重要) 优势(id、文本) 在ProductModel中,我定义了以下内容: public function getAdvantages() { return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage']) ->viaTable('product_has_advantage'
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id']);
}
我毫无问题地获得了优势
但是现在我需要添加一个product\u has\u advantage.important=1子句,并在product\u has\u advantage-table中按排序列对优势进行排序
如何以及在何处实现它?使用带有关系的
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]);
});
}
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) . ')')]);
}
将应用按important
进行的筛选,但不会应用排序,因为它发生在第一次查询中。因此,语句中中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]);
}
仅供参考
几乎不可能通过viaTable()对列进行排序。
对于Yii 2.0.7,它从viaTable()
query返回一组ID,
(
子句中的final/top query忽略顺序。首先需要使用CRUD为联接表(product\u has\u adv
)创建一个名为producthasaadv
的模型
public function getAdvantages()
{
return $this
->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'])
->andWhere(['important' => 1])
->orderBy(['sort' => SORT_DESC]);
}
然后在产品模型中创建关系并对其排序:
public function getAdvRels()
{
return $this->hasMany(ProductHasAdv::className(), ['product' => 'id'])->
orderBy(['sort' => SORT_ASC]);;
}
然后创建第二个关系,如下所示:
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]);
});
}
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) . ')')]);
}
这将使用按字段排序技术对最终结果进行排序
不要忘记添加:
use yii\db\Expression;
行到头。使用带有关系的viaTable
方法将导致两个单独的查询,但如果不需要link()
方法,则可以通过以下方式使用innerJoin按产品对表进行排序:
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;
}
注意:than$query->multiple=true
允许您使用此方法,因为Yii2有很多关系。对于稍后来到这里但不喜欢上述解决方案的人,我通过在过滤器via表之后连接回via表来实现
上述代码的示例:
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。我已经设法做到了这一点。。。但它需要额外的工作后。
关键是,您必须首先从源模型查询多对多关系,然后在闭包中查询目标模型
$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..",
}
},
正如@arogachev所解释的,viaTable
使用两个单独的查询,这使得任何中间的orderBy
都过时了
您可以用innerJoin
替换viaTable
,方法与@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()
<代码>产品\u拥有\u优势
可以通过优势
身份加入
hasMany
的第二个参数link可以被视为[query.column=>$this->attribute]
,您现在可以通过加入的product\u has\u advantage
及其product
标识来支持它
注意,当使用viaTable
时,可以将链接参数视为中间查询已完成,我们从那里开始<代码>[query.column=>viaTable.column]
因此,在您的问题中['id','advantage']
这会导致以下错误:SQLSTATE[42S22]:未找到列:1054未知列“product\u has\u advantage”。在“where子句”中执行的SQL是:选择*FROMadvantages
where(product\u has\u advantage
important
=1)和(id
IN('1'、'2'、'4'、'14'))ORDER BYproduct\u具有优势
sort
DESC为什么这是可接受的答案?我有同样的问题(需要在'viatable'中定义的表上排序),并且将列移动到另一个表是不可接受的。->orderBy(['sort'=>sort\u DESC]);只有要排序的字段将被称为“sort”为什么OP“试试这个”?一个好的答案总是会有一个解释,说明做了什么以及为什么这样做,不仅是为了OP,而且是为了未来的访客,这样他们可能会发现这个问题并阅读你的答案。请看我的答案:谢谢你。我个人认为这更清楚,即使更详细,还有一个额外的优势,那就是它避免了很多问题…->所有()难题。