依赖于其他 table/model 的虚拟字段
Virtual Field with dependency on other table/model
几年前,我用 CakePHP 2.2 开发了一些东西。在我的 Bill
模型中,我有一个 virtualField
计算给定账单是否已经“完成”:
scheduled_payment = 1
&& (payed_beihilfe IS NOT NULL && payed_beihilfe > 0)
&& (payed_pkv IS NOT NULL && payed_pkv > 0)
这在出现 payed_beihilfe
始终为零的情况之前一直运行良好,因为这部分不再发挥任何作用。有关分布的信息在字段 Person.beihilfe_part
中,范围从 0 到 100。如果它是极端之一,我不想考虑那里的数量。所以我首先尝试了这个:
scheduled_payment = 1
&& ((payed_beihilfe IS NOT NULL && payed_beihilfe > 0) || Person.beihilfe_part = 0)
&& ((payed_pkv IS NOT NULL && payed_pkv > 0) || Person.beihilfe_part = 100)
这在查看单个 Bill
对象时有效。但是,一旦有人查看 Person
,我就会得到 SQL 错误 are mentioned in the manual。基本上这就是说不能有需要 JOIN
操作的 virtualFields
。这是一个无赖,因为我真的需要扩展我拥有的这个“完成”属性。
这是我的 Bill
class:
class Bill extends AppModel {
public $displayField = 'amount';
public $virtualFields = array(
# XXX There is a limitation in the `virtualFields` that it cannot
# use any sort of `JOIN`. Therefore I have to make due with the
# data that is available in the `bills` table. This is a real
# kludge but it seems to work.
"done" =>
"scheduled_payment = 1
&& ((payed_beihilfe IS NOT NULL && payed_beihilfe > 0) || (person_id = 7 && date > '2016-06-01'))
&& (payed_pkv IS NOT NULL && payed_pkv > 0)"
);
public $validate = array(
'amount' => array(
"rule" => array("comparison", ">", 0),
"message" => "Der Rechnungsbetrag muss größer als € 0 sein."
)
);
public $belongsTo = array(
'Doctor' => array(
'className' => 'Doctor',
'foreignKey' => 'doctor_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Person' => array(
'className' => 'Person',
'foreignKey' => 'person_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
这是Person
型号:
class Person extends AppModel {
public $displayField = 'name';
public $hasMany = array(
'Bill' => array(
'className' => 'Bill',
'foreignKey' => 'person_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
查询发生在 PeopleController::view
函数中:
public function view($id = null) {
$this->Person->id = $id;
if (!$this->Person->exists()) {
throw new NotFoundException(__('Invalid person'));
}
$this->set('person', $this->Person->read(null, $id));
}
这也将绘制相关的 Bill
对象,并在那里崩溃,因为 Person
table 没有 JOIN
进入获得 [= 的查询16=] 与给定 Person
相关的对象。这在 CakePHP 中隐式发生,我只是使用了脚手架。
我已经有很长一段时间没有使用 CakePHP 了,因为我已经远离了 Web 开发。在一个非常小的 Django 项目中,我看到来自 SQL table 的行被实例化为真正的 Python 对象。在那里添加一个函数或字段真的很容易。在 CakePHP 中,所有内容都存储在关联数组中。因此,我真的不知道如何添加一个复杂的函数来计算这个“完成”属性。
处理此问题的明智方法是什么?
不幸的是,没有非常干净的方法来克服这个限制。没有关联的 Model.beforeFind
事件,可以在其中修改查询,以及构建关联数据查询的点,如果不完全重新实现部分数据源逻辑 (DboSource::generateAssociationQuery()
).
我过去解决了类似的问题,方法是将自定义数据源与重写的 buildStatement()
方法结合使用,该方法会发送 Model.beforeBuildStatement
事件。该事件将为所有查询分派,因此关联模型可以收听它并在必要时修改查询。
像这样:
app/Model/Datasource/Database/AppMysql.php
<?php
App::uses('CakeEvent', 'Event');
App::uses('Mysql', 'Model/Datasource/Database');
class AppMysql extends Mysql
{
public function buildStatement($query, Model $Model)
{
$targetModel = $Model;
$linkedModel = $Model->{$query['alias']};
if ($linkedModel !== null) {
$targetModel = $linkedModel;
}
$event = new CakeEvent('Model.beforeBuildStatement', $this, array($query, $Model));
$targetModel->getEventManager()->dispatch($event);
if (is_array($event->result)) {
$query = $event->result;
}
return parent::buildStatement($query, $Model);
}
}
然后在模型中可以添加联接,以便虚拟字段可以访问它们。在您的情况下,它可能类似于
class Bill extends AppModel
{
// ...
public function implementedEvents()
{
return parent::implementedEvents() + array(
'Model.beforeBuildStatement' => array(
'callable' => 'beforeBuildStatement',
'passParams' => true
)
);
}
public function beforeBuildStatement(array $query, Model $Model)
{
if ($Model->name === 'Person') {
$query['joins'][] = array(
'table' => 'persons',
'alias' => 'Person',
'type' => 'LEFT',
'conditions' => array(
'Person.id = Bill.person_id',
)
);
}
return $query;
}
}
如果$Model
不是当前模型,这表明当前模型正在作为关联查询的一部分进行查询。每当作为 Person
模型的关联查询的一部分查询账单 table 时,此片段将加入 persons
table。
需要注意的是,这个例子可能存在缺陷,因为唯一的条件是是否 "parent model = Person model"。您可能需要实施进一步的措施,以避免 duplicate/non-unique 连接等问题,具体取决于您的应用中发生的情况。
另见
几年前,我用 CakePHP 2.2 开发了一些东西。在我的 Bill
模型中,我有一个 virtualField
计算给定账单是否已经“完成”:
scheduled_payment = 1
&& (payed_beihilfe IS NOT NULL && payed_beihilfe > 0)
&& (payed_pkv IS NOT NULL && payed_pkv > 0)
这在出现 payed_beihilfe
始终为零的情况之前一直运行良好,因为这部分不再发挥任何作用。有关分布的信息在字段 Person.beihilfe_part
中,范围从 0 到 100。如果它是极端之一,我不想考虑那里的数量。所以我首先尝试了这个:
scheduled_payment = 1
&& ((payed_beihilfe IS NOT NULL && payed_beihilfe > 0) || Person.beihilfe_part = 0)
&& ((payed_pkv IS NOT NULL && payed_pkv > 0) || Person.beihilfe_part = 100)
这在查看单个 Bill
对象时有效。但是,一旦有人查看 Person
,我就会得到 SQL 错误 are mentioned in the manual。基本上这就是说不能有需要 JOIN
操作的 virtualFields
。这是一个无赖,因为我真的需要扩展我拥有的这个“完成”属性。
这是我的 Bill
class:
class Bill extends AppModel {
public $displayField = 'amount';
public $virtualFields = array(
# XXX There is a limitation in the `virtualFields` that it cannot
# use any sort of `JOIN`. Therefore I have to make due with the
# data that is available in the `bills` table. This is a real
# kludge but it seems to work.
"done" =>
"scheduled_payment = 1
&& ((payed_beihilfe IS NOT NULL && payed_beihilfe > 0) || (person_id = 7 && date > '2016-06-01'))
&& (payed_pkv IS NOT NULL && payed_pkv > 0)"
);
public $validate = array(
'amount' => array(
"rule" => array("comparison", ">", 0),
"message" => "Der Rechnungsbetrag muss größer als € 0 sein."
)
);
public $belongsTo = array(
'Doctor' => array(
'className' => 'Doctor',
'foreignKey' => 'doctor_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Person' => array(
'className' => 'Person',
'foreignKey' => 'person_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
这是Person
型号:
class Person extends AppModel {
public $displayField = 'name';
public $hasMany = array(
'Bill' => array(
'className' => 'Bill',
'foreignKey' => 'person_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
查询发生在 PeopleController::view
函数中:
public function view($id = null) {
$this->Person->id = $id;
if (!$this->Person->exists()) {
throw new NotFoundException(__('Invalid person'));
}
$this->set('person', $this->Person->read(null, $id));
}
这也将绘制相关的 Bill
对象,并在那里崩溃,因为 Person
table 没有 JOIN
进入获得 [= 的查询16=] 与给定 Person
相关的对象。这在 CakePHP 中隐式发生,我只是使用了脚手架。
我已经有很长一段时间没有使用 CakePHP 了,因为我已经远离了 Web 开发。在一个非常小的 Django 项目中,我看到来自 SQL table 的行被实例化为真正的 Python 对象。在那里添加一个函数或字段真的很容易。在 CakePHP 中,所有内容都存储在关联数组中。因此,我真的不知道如何添加一个复杂的函数来计算这个“完成”属性。
处理此问题的明智方法是什么?
不幸的是,没有非常干净的方法来克服这个限制。没有关联的 Model.beforeFind
事件,可以在其中修改查询,以及构建关联数据查询的点,如果不完全重新实现部分数据源逻辑 (DboSource::generateAssociationQuery()
).
我过去解决了类似的问题,方法是将自定义数据源与重写的 buildStatement()
方法结合使用,该方法会发送 Model.beforeBuildStatement
事件。该事件将为所有查询分派,因此关联模型可以收听它并在必要时修改查询。
像这样:
app/Model/Datasource/Database/AppMysql.php
<?php
App::uses('CakeEvent', 'Event');
App::uses('Mysql', 'Model/Datasource/Database');
class AppMysql extends Mysql
{
public function buildStatement($query, Model $Model)
{
$targetModel = $Model;
$linkedModel = $Model->{$query['alias']};
if ($linkedModel !== null) {
$targetModel = $linkedModel;
}
$event = new CakeEvent('Model.beforeBuildStatement', $this, array($query, $Model));
$targetModel->getEventManager()->dispatch($event);
if (is_array($event->result)) {
$query = $event->result;
}
return parent::buildStatement($query, $Model);
}
}
然后在模型中可以添加联接,以便虚拟字段可以访问它们。在您的情况下,它可能类似于
class Bill extends AppModel
{
// ...
public function implementedEvents()
{
return parent::implementedEvents() + array(
'Model.beforeBuildStatement' => array(
'callable' => 'beforeBuildStatement',
'passParams' => true
)
);
}
public function beforeBuildStatement(array $query, Model $Model)
{
if ($Model->name === 'Person') {
$query['joins'][] = array(
'table' => 'persons',
'alias' => 'Person',
'type' => 'LEFT',
'conditions' => array(
'Person.id = Bill.person_id',
)
);
}
return $query;
}
}
如果$Model
不是当前模型,这表明当前模型正在作为关联查询的一部分进行查询。每当作为 Person
模型的关联查询的一部分查询账单 table 时,此片段将加入 persons
table。
需要注意的是,这个例子可能存在缺陷,因为唯一的条件是是否 "parent model = Person model"。您可能需要实施进一步的措施,以避免 duplicate/non-unique 连接等问题,具体取决于您的应用中发生的情况。
另见