I have items
and units
table that have many to many relationship. In other words, the item has many units and the unit has many items. I managed the relation through a junction table item_units
. The junction table has some extra field more than item_id
and unit_id
, i.e it has price, and weight (it is an integer to manage the order of units for each item for display purposes).
I managed the relations in the models as follows:
//In Items model
/**
* @return \yii\db\ActiveQuery
*/
public function getItemUnits()
{
return $this->hasMany(ItemUnits::className(), ['item_id' => 'id'])->orderBy(['item_units.weight' => SORT_DESC]);
}
public function getUnits()
{
return $this->hasMany(Units::className(), ['id'=> 'unit_id'])->select(['id','title'])->via('itemUnits');
}
//
//In Units model
public function getItemUnits()
{
return $this->hasMany(ItemUnits::className(), ['unit_id' => 'id'])->orderBy(['price' => SORT_DESC]);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getItems()
{
return $this->hasMany(Items::className(), ['id' => 'item_id'])->via('itemUnits');
}
//
//In ItemUnits model
public function getItem()
{
return $this->hasOne(Items::className(), ['id' => 'item_id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getUnit()
{
return $this->hasOne(Units::className(), ['id' => 'unit_id']);
}
In the controller I'm able to get the data of all related units to an item by something like the following:
$item = Items::findOne($id);
return Json::encode($item->units);
The following is a demo of the JSON object obtained:
[{"id":"4","title":"قرص"},{"id":"5","title":"شريط 10"},{"id":"6","title":"علبة 2 شريط"}]
However, I could not able to order the results according to the weight
field in item_units
table and also I could not able to include the price
field there in the demo result above -JSON Object-.
I only able to get data in item_units
as a separate result like the following:
return Json::encode($item->itemUnits);
Update
According to the two answers (@Александр Шалаев & @Onedev.Link) , I have overridden the fields
method in Units model as follows:
public function fields() {
parent::fields();
return [
'price' => function($model){
return $model->id; //Here I could not able to get the corresponding price field value from item_units -junction table-
},
'id',
'title',
];
}
However, I could not able to get the price
field value from the junction table, temporary, I set it to current model id to prevent error generation. Also, I still has no any mean to set order by using weight
field in that junction table.
Update 2
In other words, how could Yii2 Activerecords perform the following SQL query:
SELECT units.id UnitID, units.title Unit, iu.weight, iu.price
FROM units
Left JOIN item_units AS iu
ON iu.item_id = 1 AND iu.unit_id = units.id
WHERE
units.id = iu.unit_id
ORDER BY iu.weight;
By default, yii\base\Model::fields() returns all model attributes as fields, while yii\db\ActiveRecord::fields() only returns the attributes which have been populated from DB.
You can override fields() to add, remove, rename or redefine fields. The return value of fields() should be an array. The array keys are the field names, and the array values are the corresponding field definitions which can be either property/attribute names or anonymous functions returning the corresponding field values.
You need fields or extraFields in your ActiveRecord model with
asArray
.Example:
Usage:
Finally I have found a solution. It depends on
findBySql
method. I'm going to use the above SQL query regarded in Update 2 -just I have removed some selected fields to be suitable for my current task-.