在CakePHP中使用Containable行为后获取原始关联
背景:CakePHP 2.6.3。一个相当稳定的应用程序。创建新行为(在CakePHP中使用Containable行为后获取原始关联,cakephp,cakephp-2.x,Cakephp,Cakephp 2.x,背景:CakePHP 2.6.3。一个相当稳定的应用程序。创建新行为(MyCustomBehavior)以输出一些额外信息。 我有一个模型MyModel充当Containable(在AppModel中定义),然后是MyCustom(在MyModel中定义)MyCustomBehavior的编写方式需要处理模型与我的应用程序中其他模型的关联 问题:每当我在MyModel的find()调用中包含相关模型时,我都无法获得MyModel关联的完整列表,因为Containable行为会解除未包含的模型的绑
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' => ''
),
);
$this->MyModel->find('all', array(
'fields' => array(...),
'conditions' => array(...),
'contain' => array('Approver', 'Status')
));
$belongsTo = array(
'Approver' => array(
...
),
'Status' => array(
...
),
);
$belongsTo = array(
'MyAnotherModel' => array(
...
),
'Creator' => array(
...
),
'Approver' => array(
...
),
'Status' => array(
...
),
);
示例find()
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' => ''
),
);
$this->MyModel->find('all', array(
'fields' => array(...),
'conditions' => array(...),
'contain' => array('Approver', 'Status')
));
$belongsTo = array(
'Approver' => array(
...
),
'Status' => array(
...
),
);
$belongsTo = array(
'MyAnotherModel' => array(
...
),
'Creator' => array(
...
),
'Approver' => array(
...
),
'Status' => array(
...
),
);
在MyCustomBehavior::beforeFind()中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' => ''
),
);
$this->MyModel->find('all', array(
'fields' => array(...),
'conditions' => array(...),
'contain' => array('Approver', 'Status')
));
$belongsTo = array(
'Approver' => array(
...
),
'Status' => array(
...
),
);
$belongsTo = array(
'MyAnotherModel' => array(
...
),
'Creator' => array(
...
),
'Approver' => array(
...
),
'Status' => array(
...
),
);
在MyCustomBehavior::beforeFind()中,预期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' => ''
),
);
$this->MyModel->find('all', array(
'fields' => array(...),
'conditions' => array(...),
'contain' => array('Approver', 'Status')
));
$belongsTo = array(
'Approver' => array(
...
),
'Status' => array(
...
),
);
$belongsTo = array(
'MyAnotherModel' => array(
...
),
'Creator' => array(
...
),
'Approver' => array(
...
),
'Status' => array(
...
),
);
显而易见的解决方案:解决问题的一个愚蠢方法是在MyModel
中简单地设置Containable
行为,而不是AppModel
来控制加载行为的顺序,即public$actsAs=['MyCustom',Containable']
。这个解决方案不是最好的,因为在依赖于Containable
的其他模型中可能存在其他行为,所以Containable
的顺序需要在应用程序中的每个模型中明确设置,希望我没有在某个地方破坏应用程序
有人问了一个类似的(相关的)问题,但没有答案
需要一个更强大的解决方案,可以满足MyCustomBehavior
的需求,而无需对应用程序的其余部分进行更改,也无需注意任何意外行为。尝试1(不完美,容易出错):
恢复所有原始关联的一种方法是调用
$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo; // will now have original belongsTo assoc
但是,如果我在find调用中使用了连接
(使用默认的别名
)显式连接到已经关联的模型,这种方法可能会失败(SQL错误1066非唯一表/别名
)。这是因为Containable
还将尝试联接由resetBindings()
调用还原的所有这些表,从而使用相同的别名执行两次联接
尝试2(完美,无已知副作用):
进一步挖掘核心Containable
行为,我发现object$MyModel->\uuuu backOriginalAssociation
和$MyModel->\uu backAssociation
(奇怪的是ContainableBehavior
从未使用变量名所示的$\uu backOriginalAssociation
)此行为创建并使用它来执行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;
}
$\uuuu backAssociation
暂时保留模型关联以允许动态(取消)绑定通过将$Model->belongsTo
和$Model->\uu backAssociation['belongsTo']
(或hasMany
,hasOne
,hasandblongstomy
)的结果合并以包含任何动态绑定的模型,此解决方案肯定会得到进一步改进。我不需要它,因此,我将跳过合并的代码
#非常适合我自己的用例和我的应用程序设置。
###在我的测试中没有发现受我的专业知识/技能水平限制的副作用。
免责声明:我在这篇文章中的作品是根据授权的。所以,对材料做你想做的事。此外,我对因使用上述材料而造成的任何经济、身体或精神损失不承担任何责任。在尝试复制/粘贴之前,请自行承担使用风险并进行深入研究。别忘了看一看,因为上面写着“根据sa 3.0抄送授权的用户贡献,需要署名。”(查看此页的页脚。我知道你今天之前从未注意到过它!:p)好吧,我想有人会发布一个比我的答案更好的答案,或者至少告诉我一个我认为完美的解决方案可能存在的问题。因为在这篇帖子上没有太多的活动,我将我的解决方案标记为已被接受,直到有人能够提供一个具有良好推理能力的更好的解决方案。如果值得的话,我不介意改变我接受的解决方案。