背景:CakePHP 2.6.3。一个相当稳定的应用程序。创建了新的行为(MyCustomBehavior
来输出一些额外的信息。
我有一个模型MyModel
充当Containable
(在AppModel
中定义),然后在MyCustom
(在MyModel
中定义)。 MyCustomBehavior
的编写方式需要与应用程序中模型与其他模型的关联一起使用。
问题:每当我在find()
的{{1}}调用中包含相关模型时,由于{{1}行为将未包含的模型解除绑定。但是,如果我未在MyModel
选项中设置MyModel
或未设置Containable
,则一切正常。
示例contain
find()
示例'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(
...
),
);
来控制加载代码的顺序行为,即Containable
。此解决方案不是最佳解决方案,因为在其他模型中可能还存在其他依赖于MyModel
的行为,因此需要在应用程序的每个模型中明确设置AppModel
的顺序,并希望我不会破坏该应用在某处。
有人在SO here上提出了类似(相关)的问题,但没有答案。
需要一种更强大的解决方案,该解决方案能够满足public $actsAs = ['MyCustom', 'Containable']
的需求,而无需在应用程序的其余部分进行更改,也无需查找任何意外行为。
答案 0 :(得分:0)
尝试1个(不完美,容易出错):
恢复所有原始关联的一种方法是致电
AlarmManager
但是,如果我在find调用中使用$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo; // will now have original belongsTo assoc
(使用默认1066 Not unique table/alias
)显式加入一个已经存在的方法,则此方法可能无法正常工作(SQL错误joins
)相关模型。这是因为alias
还将尝试联接所有通过Containable
调用还原的表,从而导致使用相同的别名执行两次联接。
尝试2个(完美的#,没有已知的副作用 ## ):
进一步挖掘核心resetBindings()
的行为和docs导致我反对Containable
和$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
)的结果以包含任何模型,可以肯定地进一步改进此解决方案。我不需要它,所以我将跳过合并代码。
#完美 适用于我自己的用例和应用设置。
## 在我的测试中未发现副作用,这受我的专业知识/技能水平的限制。
免责声明:我在这篇文章中的工作获得了WTF Public License(WTFPL) 的许可。因此,用您想要的材料做些什么。此外,对于因使用上述材料而造成的任何形式的经济,身体或精神损失,我不承担任何责任。使用前需要您自担风险,并自行进行复制研究。不要忘了看cc by-sa 3.0,因为SO指出“根据cc by-sa 3.0许可的用户贡献需要归因。” (请检查此页脚上的页脚。我知道您从未在今天之前注意到它!:p)