我以为我同时了解Yii2的各个方面,但这让我头疼。
情况
两个表:客户和帐单。客户表包含一个常规的客户列表。计费表为每个客户有多个条目(1:n)。
问题
我想获取计算的DB字段以及行本身,并通过模型的虚拟属性访问它。
关键是它与行本身一起被计算和选择。我知道我可以用一个常规的虚拟吸气剂来计算出类似的数量……但这与选择本身不同。
我的计划
在客户端模型的查询对象中,我尝试添加一个附加选择(addSelect-Method)并为该字段提供别名。然后,我将该选择的别名与模型的属性方法一起添加。不知何故,这行不通。
我的问题
你们当中有人知道实现此目标的正确方法吗?因为这是一个非常普遍的问题,所以我无法想象这件事太难了。我只是以某种方式找不到解决方案。
示例代码:
echo $client->sumOfBillings应在客户端模型中输出相应属性的内容。该属性的内容应在获取客户端行本身时填充,而不是在调用该属性时填充。
我实际上自己找到了答案。这是您的操作方式:
查询对象
所有Yii2-Model的获取均通过其相应的Query-Object完成。通过模型find()-Method 检索此对象。如果重写此方法,则可以为该类返回自己的查询对象。在上面的示例中,我的模型如下所示:
class Client extends \yii\db\ActiveRecord
{
//...
public static function find()
{
return new ClientQuery(get_called_class());
}
//...
}
Run Code Online (Sandbox Code Playgroud)
现在,在Query-Objects init()-Method中,我们可以添加相应的其他选择:
public class ClientQuery extends \yii\db\ActiveQuery
{
public function init()
{
parent::init();
//prepare subquery for calculation
$sub = (new Query())
->select('SUM(billing_amount)')
->from('billing')
->where('billing.client_id = client.id');
$this->addSelect(['client.*', 'sumBillings'=>$sub]);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我们完成了查询对象。我们现在做了什么?选择客户时,总和也会被计算和加载。但是,我们如何访问它呢?这是我努力奋斗的难点。解决方案位于ActiveRecord-class之内。
用计算的数据填充模型的可能性
有多种可能性将此数据加载到模型类中。要了解我们拥有哪些选项,我们可以查看-class 的populateRecord($record, $row)-method BaseActiveRecord:
/**
* Populates an active record object using a row of data from the database/storage.
*
* This is an internal method meant to be called to create active record objects after
* fetching data from the database. It is mainly used by [[ActiveQuery]] to populate
* the query results into active records.
*
* When calling this method manually you should call [[afterFind()]] on the created
* record to trigger the [[EVENT_AFTER_FIND|afterFind Event]].
*
* @param BaseActiveRecord $record the record to be populated. In most cases this will be an instance
* created by [[instantiate()]] beforehand.
* @param array $row attribute values (name => value)
*/
public static function populateRecord($record, $row)
{
$columns = array_flip($record->attributes());
foreach ($row as $name => $value) {
if (isset($columns[$name])) {
$record->_attributes[$name] = $value;
} elseif ($record->canSetProperty($name)) {
$record->$name = $value;
}
}
$record->_oldAttributes = $record->_attributes;
}
Run Code Online (Sandbox Code Playgroud)
如您所见,该方法采用原始数据($row)并填充模型实例($record)。如果模型具有与计算字段同名的属性或设置方法,则将填充数据。
客户模型的最终代码
这是客户端模型的最终代码:
class Client extends \yii\db\ActiveRecord
{
private $_sumBillings;
//...
public static function find()
{
return new ClientQuery(get_called_class());
}
public function getSumBillings()
{
return $this->_sumBillings;
}
protected function setSumBillings($val)
{
$this->_sumBillings = $val;
}
//...
}
Run Code Online (Sandbox Code Playgroud)
本populateRecord()-方法会发现setter方法的方法($record->canSetProperty($name))并调用它来填充计算值。由于受保护,因此它是只读的。
Voilà...实际上并不难,而且绝对有用!
| 归档时间: |
|
| 查看次数: |
3004 次 |
| 最近记录: |