ActiveRecord在via-table的位置和顺序

时间:2014-12-29 14:07:18

标签: activerecord yii2

我有三个数据库表:

产品(身份证,姓名)

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对优势进行排序。

我必须如何以及在哪里实现它?

8 个答案:

答案 0 :(得分:34)

对关系使用viaviaTable方法会导致两个单独的查询。

您可以在第三个参数中指定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顺序将会更改。

根据您的数据库逻辑,最好将importantsort列移至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]);
}