Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord where and order on via-table

I have three database table:

product (id, name)

product_has_adv (product,advantage,sort,important)

advantage (id, text)

In ProductModel I defined this:

public function getAdvantages()
    {
        return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
            ->viaTable('product_has_advantage', ['product' => 'id']);
    }

I get the advantages without any problems.

But now I need to add a where product_has_advantage.important = 1 clausel and also sort the advantages by the sort-columen in the product_has_advantage-table.

How and where I have to realize it?

like image 538
rakete Avatar asked Dec 29 '14 14:12

rakete


3 Answers

Using via and viaTable methods with relations will cause two separate queries.

You can specify callable in third parameter like this:

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]);
        });
}

The filter by important will be applied, but the sort won't since it happens in first query. As a result the order of ids in IN statement will be changed.

Depending on your database logic maybe it's better to move important and sort columns to advantage table.

Then just add condition and sort to the existing method chain:

public function getAdvantages()
{
    return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
        ->viaTable('product_has_advantage', ['product' => 'id'])
        ->andWhere(['important' => 1])
        ->orderBy(['sort' => SORT_DESC]);
}
like image 66
arogachev Avatar answered Sep 18 '22 19:09

arogachev


Using viaTable methods with relations will cause two separate queries, but if you don't need link() method you can use innerJoin in the following way to sort by product_has_advantage table:

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;
}

Note than $query->multiple = true allows you to use this method as Yii2 hasMany relation.

like image 41
Martín M Avatar answered Sep 20 '22 19:09

Martín M


Just for reference https://github.com/yiisoft/yii2/issues/10174 It's near impossible to ORDER BY viaTable() columns. For Yii 2.0.7 it returns set of ID's from viaTable() query, and final/top query IN() clause ignores the order.

like image 45
shikotanorama Avatar answered Sep 19 '22 19:09

shikotanorama