Yii2 Activerecord get fields from junction table a

2019-06-08 12:54发布

问题:

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;

回答1:

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-.

public function actionUnitsJson($id){    
$sql = 'SELECT units.id, units.title 
    FROM units
     Left JOIN item_units AS iu
      ON iu.item_id = :id AND iu.unit_id = units.id 
    WHERE 
        units.id = iu.unit_id 
    ORDER BY iu.weight DESC;';

          $units = \common\models\Units::findBySql($sql,[':id' => $id])->asArray()->all();
    return Json::encode($units);
}


回答2:

You need fields or extraFields in your ActiveRecord model with asArray.

Example:

/**
 * @return array
 */
public function fields()
{
    return [
        'itemUnit', //will get getItemUnit method
    ];
}

or

/**
 * @return array
 */
public function extraFields()
{
    return [
        'itemUnits', //it is relation name
    ];
}

Usage:

$model->toArray(); //will contains fields and extra fields relations
... sort array & return


回答3:

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.