3つのデータベーステーブルがあります。
製品(ID、名前)
product_has_adv(製品、利点、ソート、重要)
利点(ID、テキスト)
ProductModelでこれを定義しました:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id']);
}
問題なく利点を得ることができます。
しかし、ここで、where product_has_advantage.important = 1句を追加し、product_has_advantage-tableのsort-columenで利点をソートする必要があります。
どのように、どこでそれを実現する必要がありますか?
リレーションでvia
メソッドとviaTable
メソッドを使用すると、2つの別個のクエリが発生します。
次のように、3番目のパラメーターで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]);
}
リレーションでviaTable
メソッドを使用すると、2つの別個のクエリが発生しますが、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 hasMany関係としてこのメソッドを使用できることに注意してください。
参考のために https://github.com/yiisoft/yii2/issues/10174ORDER BY viaTable()
カラムはほとんど不可能です。 Yii 2.0.7の場合、viaTable()
クエリからIDのセットを返し、final/top query IN()
句は順序を無視します。
最初に、CRUDを使用して、ジャンクションテーブル(product_has_adv
)のProductHasAdv
という名前のモデルを作成する必要があります。
次に、product
モデルでリレーションを作成し、並べ替えます。
public function getAdvRels()
{
return $this->hasMany(ProductHasAdv::className(), ['product' => 'id'])->
orderBy(['sort' => SORT_ASC]);;
}
次に、次のような2番目の関係を作成します。
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;
行頭。
しばらくしてここに来て、上記の解決策が気に入らない人のために、フィルター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
は2つの別個のクエリを使用し、中間のorderBy
を廃止します
@MartinMと同様のソリューションで、次のようにviaTable
をinnerJoin
に置き換えることができます。
_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
IDを介して結合できます
hasMany
の2番目のパラメーターlinkは、_[ query.column => $this->attribute ]
_として表示できます。これは、結合された_product_has_advantage
_とそのproduct
アイデンティティーを介してサポートできるようになりました。
注、viaTable
を使用する場合、リンクパラメータは中間クエリが完了し、そこから開始しているように表示できます。 _[ query.column => viaTable.column ]
_したがって、あなたの質問で_['id', 'advantage']
_