背景:CakePHP 2.6.3。一个非常稳定的应用。创建新行为(MyCustomBehavior
)以输出一些额外信息。我有一个模型MyModel
充当Containable
(在AppModel
中定义),然后MyCustom
(在MyModel
中定义)。 MyCustomBehavior
的编写方式需要与我的应用程序中的模型与其他模型的关联一起使用。
问题:每当我在find()
的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
行为来控制加载行为的顺序,即public $actsAs = ['MyCustom', 'Containable']
。这个解决方案不是最好的,因为在其他模型中可能还有其他行为依赖于Containable
,所以Containable
的顺序需要在app中明确地设置在每个模型中,并希望我没有在某个地方破坏应用程序。
关于SO here的一个类似的(相关的)问题,但没有答案。
需要一个更强大的解决方案,可以满足MyCustomBehavior
的需求,而无需在应用程序的其余部分进行更改,并寻找任何意外的行为。
尝试1(不完美,容易出错):
恢复所有原始关联的一种方法是调用
$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo; // will now have original belongsTo assoc
但是,如果我在查找调用中使用1066 Not unique table/alias
(使用默认的joins
)显式连接到已经关联的模型,这种方法可能会失败(SQL错误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
)的结果,可以进一步改进这种解决方案,以包括任何动态绑定的模型。我不需要它,所以我将跳过代码进行合并。
#非常适合我自己的用例和我的应用程序设置。 ##我的测试中没有发现因我的专业知识/技能水平而受到限制的副作用。 免责声明:我在这篇文章中的工作是根据WTF Public License(WTFPL)许可的。所以,用材料做你想要的f ** k。此外,对于因使用上述材料而导致的任何财务,身体或精神损失,我概不负责。在尝试复制/粘贴之前,请自担风险并进行自己的研究。不要忘记看看cc by-sa 3.0,因为SO说“用户贡献在cc by-sa 3.0下获得许可,需要归属。” (查看此页面上的页脚。我知道你今天之前从未注意到它!:p)