Reputation: 109
I have 2 tables :
id | opt_name | active_flag
------------------------------
1 | Cook level | 0
id | optgrp_id | list_name | active_flag
------------------------------------------
1 | 1 | 25 | 0
2 | 1 | 50 | 1
3 | 1 | 75 | 0
4 | 1 | 100 | 0
public function getOptList()
{
return $this->hasMany(ProdOptlist::className(),['optgrp_id'=>'id']);
}
public function getOptGrp()
{
return $this->hasOne(ProdOptgrp::className(),['id'=>'optgrp_id']);
}
public function actionUpdate($id)
{
$model = $this->findModel($id);
if($model->load(Yii::$app->request->post()) && $model->validate())
{
...
}
else
return $this->render('update', ['model'=>$model]);
}
protected function findModel($id)
{
if (($model = ProdOptgrp::find()
->joinWith('optList')
->where([ProdOptgrp::tableName().'.id'=>$id,
ProdOptgrp::tableName().'.active_flag'=>0,
ProdOptlist::tableName().'.active_flag'=>0])
->one()) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
Expected output for print_r($model->optList) :
{
[id] => 1
[optgrp_id] => 1
[list_name] => 25
[active_flag] => 0
},
{
[id] => 3
[optgrp_id] => 1
[list_name] => 75
[active_flag] => 0
},
{
[id] => 4
[optgrp_id] => 1
[optList_name] => 100
[active_flag] => 0
}
Actual output :
{
[id] => 1
[optgrp_id] => 1
[list_name] => 25
[active_flag] => 0
},
{
[id] => 2
[optgrp_id] => 1
[list_name] => 50
[active_flag] => 1
},
{
[id] => 3
[optgrp_id] => 1
[list_name] => 75
[active_flag] => 0
},
{
[id] => 4
[optgrp_id] => 1
[optList_name] => 100
[active_flag] => 0
}
Yii2 debugger showing correct query but output still consist of all 4 elements. Kindly advice if there is any mistake, thank you in advance :)
Upvotes: 1
Views: 890
Reputation: 6169
Even if you use the joinWith()
the where()
part of your $model = ProdOptgrp::find()...
code only limits the result that is returned by the query executed by one()
method call. It doesn't affect what is loaded for relations.
If you want to limit what is loaded for relations you can do it:
1) By modifing the existing relation
This is the solution you've come to. You add the where condition directly to the getOptList()
method of your ProdOptgrp
model. If you do it this way, the $model->optList
will always return filtered relation. Depending on case that might be advantage or disadvantage.
2) By adding second relation
You can create another method in your ProdOptgrp
model that will define the filtered relation while keeping the original getOptList()
unfiltered.
For example like this:
class ProdOptgrp extends \yii\db\ActiveRecord
{
public function getOptList()
{
return $this->hasMany(ProdOptlist::className(),['optgrp_id'=>'id']);
}
public function getFilteredOptList()
{
return $this->getOptList()->where([
ProdOptlist::tableName() . '.active_flag' => 0
]);
}
}
In this case the $model->optList
will still contain unfiltered OptLists and the $model->filteredOptList
will contain filtered. This solution is good when you need to use both at different spots of your application.
3) By using callback syntax in joinWith() or with()
In case you want to filter the relation only in one particular case you don't need to modify your model. You can modify the relation with callback that is called before loading the data.
$model = ProdOptgrp::find()
->joinWith([
'optList' => function(\yii\db\ActiveQuery $query) {
$query->where([
ProdOptlist::tableName() . '.active_flag' => 0
]);
}
])->where([
ProdOptgrp::tableName().'.id'=>$id,
ProdOptgrp::tableName().'.active_flag'=>0,
ProdOptlist::tableName() . '.active_flag'=>0,
])->one();
The last condition in where()
will cause the $model to be null when the active_flag is 0 in optgrp table but 1 in all related records in optlist table. I'm not sure if that is inteded behavior.
Upvotes: 3
Reputation: 3507
You can do it this way too:
//Controller
protected function findModel($id)
{
if (($model = ProdOptgrp::findOne($id)) !== null && $model->isActive()) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
//Model
/*
*Check if model is active
*/
public function isActive(){
return ($this->active_flag == 0) ? true: false;
}
public function getOptList()
{
return $this->hasMany(ProdOptlist::className(),['optgrp_id'=>'id'])->where([ProdOptgrp::tableName().'.active_flag'=>0]);
}
//View
foreach($model->optList as optItem): // you get filtered result
Upvotes: 1