Yii2 Activerecord 从联结表中获取字段并根据它们排序
Posted
技术标签:
【中文标题】Yii2 Activerecord 从联结表中获取字段并根据它们排序【英文标题】:Yii2 Activerecord get fields from junction table and order by according to them 【发布时间】:2016-02-01 15:40:08 【问题描述】:我有 items
和 units
表有多对多的关系。换句话说,项目有很多单元,单元有很多项目。我通过 junction 表 item_units
管理关系。联结表除了item_id
和unit_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
表中的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',
];
但是,我暂时无法从联结表中获取 price
字段值,我将其设置为当前模型 ID 以防止产生错误。另外,我仍然无法通过在该联结表中使用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;
【问题讨论】:
【参考方案1】:终于找到了解决办法。这取决于findBySql
方法。我将使用 Update 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);
【讨论】:
【参考方案2】:您需要在带有asArray
的ActiveRecord 模型中使用fields 或extraFields。
例子:
/**
* @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
【讨论】:
您能否在用法中添加更多详细信息?【参考方案3】:默认情况下,yii\base\Model::fields() 将所有模型属性作为字段返回,而 yii\db\ActiveRecord::fields() 只返回从数据库中填充的属性。
您可以覆盖 fields() 来添加、删除、重命名或重新定义字段。 fields() 的返回值应该是一个数组。数组键是字段名称,数组值是相应的字段定义,可以是属性/属性名称,也可以是返回相应字段值的匿名函数。
【讨论】:
我在哪里可以覆盖它?在Items
模型中?以及如何获取return $this->hasMany(Units::className(), ['id'=> 'unit_id'])->select(['id','title'])->via('itemUnits');
中的字段值,即..->select(['id','title','itemUnits.price'])...
以上是关于Yii2 Activerecord 从联结表中获取字段并根据它们排序的主要内容,如果未能解决你的问题,请参考以下文章
Yii2 Select2 - 更新时选择的值 - 联结表(多对多)