ActiveRecord where和order on via表

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'

我有三个数据库表:

产品(id、名称)

产品具有高级(产品、优势、分类、重要)

优势(id、文本)

在ProductModel中,我定义了以下内容:

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是:选择*FROM
advantages
where(
product\u has\u advantage
important
=1)和(
id
IN('1'、'2'、'4'、'14'))ORDER BY
product\u具有优势
sort
DESC为什么这是可接受的答案?我有同样的问题(需要在'viatable'中定义的表上排序),并且将列移动到另一个表是不可接受的。->orderBy(['sort'=>sort\u DESC]);只有要排序的字段将被称为“sort”为什么OP“试试这个”?一个好的答案总是会有一个解释,说明做了什么以及为什么这样做,不仅是为了OP,而且是为了未来的访客,这样他们可能会发现这个问题并阅读你的答案。请看我的答案:谢谢你。我个人认为这更清楚,即使更详细,还有一个额外的优势,那就是它避免了很多问题…->所有()难题。