Yii2 Activerecord 从 junction table 获取字段并根据它们排序

Yii2 Activerecord get fields from junction table and order by according to them

我有 itemsunits table 有多对多的关系。换句话说,物品有很多单位,单位有很多物品。我通过 junction table item_units 管理关系。结 table 比 item_idunit_id 有一些额外的字段,即它有价格和重量(它是一个整数,用于管理每个项目的单位顺序以供显示) .

我管理模型中的关系如下:

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

在控制器中,我可以通过以下方式获取项目的所有相关单元的数据:

$item = Items::findOne($id);
return Json::encode($item->units);

下面是获取JSON对象的demo:

[{"id":"4","title":"قرص"},{"id":"5","title":"شريط 10"},{"id":"6","title":"علبة 2 شريط"}]

但是,我无法根据 item_units table 中的 weight 字段对结果进行排序,而且我无法包含 price 字段在上面的演示结果中 -JSON Object-.

我只能在 item_units 中获取数据作为单独的结果,如下所示:

return Json::encode($item->itemUnits);

更新

根据这两个答案 (@Александр Шалаев & @Onedev.Link) ,我重写了 Units 模型中的 fields 方法,如下所示:

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',                

      ];
    }

但是,我无法从连接点 table 获取 price 字段值,暂时,我将其设置为当前模型 ID 以防止产生错误。此外,我仍然无法通过在该连接点 table.

中使用 weight 字段来设置顺序

更新 2

换句话说,Yii2 Activerecords 如何执行以下 SQL 查询:

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;

您的 ActiveRecord 模型中需要 fields or extraFields asArray

示例:

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

or

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

用法:

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

默认情况下,yii\base\Model::fields() returns 所有模型属性作为字段,而 yii\db\ActiveRecord::fields() 仅 returns 属性已从数据库中填充。

您可以覆盖 fields() 以添加、删除、重命名或重新定义字段。 fields() 的 return 值应该是一个数组。数组键是字段名称,数组值是相应的字段定义,可以是 property/attribute 名称或匿名函数 returning 相应的字段值。

终于找到解决办法了。这取决于 findBySql 方法。我将使用 更新 2 中考虑的上述 SQL 查询 - 只是我删除了一些选定的字段以适合我当前的任务-.

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