在 CakePHP 中使用 Containable 行为后获取原始关联
Get original associations after using Containable behavior in CakePHP
背景: CakePHP 2.6.3。一个相当稳定的应用程序。创建新行为 (MyCustomBehavior
) 以输出一些额外信息。
我有一个模型 MyModel
作为 Containable
(在 AppModel
中定义),然后是 MyCustom
(在 MyModel
中定义)。 MyCustomBehavior
的编写方式需要与我的应用程序中模型与其他模型的关联一起工作。
问题: 每当我在 MyModel
的 find()
调用中包含相关模型时,我无法获得 MyModel
关联的完整列表因为 Containable
行为会解除绑定未包含的模型。但是,如果我没有在我的 find()
选项中设置 contain
或设置 'contain' => false
一切都会按预期工作。
样本MyModel->belongsTo
public $belongsTo = array(
'MyAnotherModel' => array(
'className' => 'MyAnotherModel',
'foreignKey' => 'my_another_model_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Creator' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Approver' => array(
'className' => 'User',
'foreignKey' => 'approver_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Status' => array(
'className' => 'Status',
'foreignKey' => 'status_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
);
样本find()
$this->MyModel->find('all', array(
'fields' => array(...),
'conditions' => array(...),
'contain' => array('Approver', 'Status')
));
MyModel->belongsTo
在 MyCustomBehavior::beforeFind()
中的结果
$belongsTo = array(
'Approver' => array(
...
),
'Status' => array(
...
),
);
预期 MyModel->belongsTo
MyCustomBehavior::beforeFind()
$belongsTo = array(
'MyAnotherModel' => array(
...
),
'Creator' => array(
...
),
'Approver' => array(
...
),
'Status' => array(
...
),
);
明显的解决方案: 解决问题的一种愚蠢方法是简单地在 MyModel
中设置 Containable
行为而不是 AppModel
来控制加载行为的顺序,即 public $actsAs = ['MyCustom', 'Containable']
。这个解决方案不是最好的,因为其他模型中可能还有其他依赖于 Containable
的行为,所以 Containable
的顺序需要在应用程序中的每个模型中明确设置,希望我没有破坏某处的应用程序。
有人在 SO here 上提出了类似(相关)的问题,但没有答案。
需要更强大的解决方案来满足 MyCustomBehavior
的需求,而无需更改应用程序的其余部分并留意任何意外行为。
尝试 1 (不完美,容易出错):
恢复所有原始关联的一种方法是调用
$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo; // will now have original belongsTo assoc
但是,如果我在查找调用中使用了 joins
(使用默认 alias
),这种方法可能会失败(SQL 错误 1066 Not unique table/alias
)显式加入一个已经关联的模型。这是因为 Containable
还将尝试连接所有这些由 resetBindings()
调用恢复的表,导致使用相同的别名执行两次连接。
尝试 2 (完美#,无已知副作用##):
进一步挖掘核心 Containable
行为和 docs 让我反对 $MyModel->__backOriginalAssociation
和 $MyModel->__backAssociation
(奇怪的是 ContainableBehavior
从未使用 $__backContainableAssociation
作为变量名称建议),此行为创建并使用它来执行 resetBindings()
。因此,我的最终解决方案是简单地检查我的模式是否启用了 Containable
(在我的情况下是冗余的,因为它附加在 AppModel
中并且在整个应用程序中从未被禁用或分离)并检查对象是否设置在模型上。
// somewhere in MyCustomBehavior
private function __getOriginalAssociations(Model $Model, $type = 'belongsTo') {
if(isset($Model->__backAssociation['belongsTo']) && !empty($Model->__backAssociation['belongsTo'])) { // do an additional test for $Model->Behaviors->enabled('Containable') if you need
return $Model->__backAssociation[$type];
}
return $Model->$type;
}
public function beforeFind(Model $Model, $query) {
// somewhere in MyCustomBehavior::beforeFind()
...
$belongsToAssoc = $this->__getOriginalAssociations($MyModel, 'belongsTo'); // will now have original belongsTo assoc
...
return $query;
}
$__backAssociation
临时保存模型关联以允许动态(取消)绑定。 通过合并 $Model->belongsTo
和 $Model->__backAssociation['belongsTo']
(或 hasMany
、hasOne
、hasAndBelongsToMany
)的结果以包含任何动态绑定的模型。我不需要它,所以我将跳过合并代码。
# 完美 适合我自己的用例和我的应用设置。
## 受限于我的 expertise/skill.
水平,我的测试没有发现副作用
免责声明:我在 post 中的工作是根据 WTF Public License(WTFPL). So, do what the f**k you want with the material. Additionally, I claim no responsibility for any financial, physical or mental loss of any kind whatsoever due to the use of above material. Use at your own risk and do your own f**king research before attempting a copy/paste. Don't forget to take a look at cc by-sa 3.0 获得许可的,因为 SO 说 "user contributions licensed under cc by-sa 3.0 with attribution required."(检查此页面的页脚。我知道你从未注意到今天之前!:p)
背景: CakePHP 2.6.3。一个相当稳定的应用程序。创建新行为 (MyCustomBehavior
) 以输出一些额外信息。
我有一个模型 MyModel
作为 Containable
(在 AppModel
中定义),然后是 MyCustom
(在 MyModel
中定义)。 MyCustomBehavior
的编写方式需要与我的应用程序中模型与其他模型的关联一起工作。
问题: 每当我在 MyModel
的 find()
调用中包含相关模型时,我无法获得 MyModel
关联的完整列表因为 Containable
行为会解除绑定未包含的模型。但是,如果我没有在我的 find()
选项中设置 contain
或设置 'contain' => false
一切都会按预期工作。
样本MyModel->belongsTo
public $belongsTo = array(
'MyAnotherModel' => array(
'className' => 'MyAnotherModel',
'foreignKey' => 'my_another_model_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Creator' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Approver' => array(
'className' => 'User',
'foreignKey' => 'approver_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Status' => array(
'className' => 'Status',
'foreignKey' => 'status_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
);
样本find()
$this->MyModel->find('all', array(
'fields' => array(...),
'conditions' => array(...),
'contain' => array('Approver', 'Status')
));
MyModel->belongsTo
在 MyCustomBehavior::beforeFind()
$belongsTo = array(
'Approver' => array(
...
),
'Status' => array(
...
),
);
预期 MyModel->belongsTo
MyCustomBehavior::beforeFind()
$belongsTo = array(
'MyAnotherModel' => array(
...
),
'Creator' => array(
...
),
'Approver' => array(
...
),
'Status' => array(
...
),
);
明显的解决方案: 解决问题的一种愚蠢方法是简单地在 MyModel
中设置 Containable
行为而不是 AppModel
来控制加载行为的顺序,即 public $actsAs = ['MyCustom', 'Containable']
。这个解决方案不是最好的,因为其他模型中可能还有其他依赖于 Containable
的行为,所以 Containable
的顺序需要在应用程序中的每个模型中明确设置,希望我没有破坏某处的应用程序。
有人在 SO here 上提出了类似(相关)的问题,但没有答案。
需要更强大的解决方案来满足 MyCustomBehavior
的需求,而无需更改应用程序的其余部分并留意任何意外行为。
尝试 1 (不完美,容易出错):
恢复所有原始关联的一种方法是调用
$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo; // will now have original belongsTo assoc
但是,如果我在查找调用中使用了 joins
(使用默认 alias
),这种方法可能会失败(SQL 错误 1066 Not unique table/alias
)显式加入一个已经关联的模型。这是因为 Containable
还将尝试连接所有这些由 resetBindings()
调用恢复的表,导致使用相同的别名执行两次连接。
尝试 2 (完美#,无已知副作用##):
进一步挖掘核心 Containable
行为和 docs 让我反对 $MyModel->__backOriginalAssociation
和 $MyModel->__backAssociation
(奇怪的是 ContainableBehavior
从未使用 $__backContainableAssociation
作为变量名称建议),此行为创建并使用它来执行 resetBindings()
。因此,我的最终解决方案是简单地检查我的模式是否启用了 Containable
(在我的情况下是冗余的,因为它附加在 AppModel
中并且在整个应用程序中从未被禁用或分离)并检查对象是否设置在模型上。
// somewhere in MyCustomBehavior
private function __getOriginalAssociations(Model $Model, $type = 'belongsTo') {
if(isset($Model->__backAssociation['belongsTo']) && !empty($Model->__backAssociation['belongsTo'])) { // do an additional test for $Model->Behaviors->enabled('Containable') if you need
return $Model->__backAssociation[$type];
}
return $Model->$type;
}
public function beforeFind(Model $Model, $query) {
// somewhere in MyCustomBehavior::beforeFind()
...
$belongsToAssoc = $this->__getOriginalAssociations($MyModel, 'belongsTo'); // will now have original belongsTo assoc
...
return $query;
}
$__backAssociation
临时保存模型关联以允许动态(取消)绑定。 通过合并 $Model->belongsTo
和 $Model->__backAssociation['belongsTo']
(或 hasMany
、hasOne
、hasAndBelongsToMany
)的结果以包含任何动态绑定的模型。我不需要它,所以我将跳过合并代码。
# 完美 适合我自己的用例和我的应用设置。
## 受限于我的 expertise/skill.
水平,我的测试没有发现副作用
免责声明:我在 post 中的工作是根据 WTF Public License(WTFPL). So, do what the f**k you want with the material. Additionally, I claim no responsibility for any financial, physical or mental loss of any kind whatsoever due to the use of above material. Use at your own risk and do your own f**king research before attempting a copy/paste. Don't forget to take a look at cc by-sa 3.0 获得许可的,因为 SO 说 "user contributions licensed under cc by-sa 3.0 with attribution required."(检查此页面的页脚。我知道你从未注意到今天之前!:p)