问题描述
背景:CakePHP 2.6.3。一个相当稳定的应用程序。创建了新行为( MyCustomBehavior
)以输出一些额外的信息。
我有一个模型 MyModel
充当 Containable
(在 AppModel中定义
),然后是 MyCustom
(在 MyModel
中定义)。 MyCustomBehavior
的编写方式需要与应用程序中模型与其他模型的关联一起使用。
Background: CakePHP 2.6.3. A pretty stable app. New behavior (MyCustomBehavior
) created to output some extra info.I have a Model MyModel
acting as Containable
(defined in AppModel
) and then MyCustom
(defined in MyModel
). MyCustomBehavior
is written in a way that it needs to work with the Model's associations with other Models in my app.
问题:每当我在 find()
调用中包含相关模型时, code> MyModel ,我无法获得 MyModel
关联的完整列表,因为 Containable
行为将未包含的模型解除绑定。但是,如果我没有在 find()
选项中设置 contain
或设置'包含'=> false
一切正常。
Problem: Whenever I contain related models in my find()
call of MyModel
, I cannot get a complete list of MyModel
associations because Containable
behavior unbinds the models that are not contained. However, if I don't set contain
in my find()
options or set 'contain' => false
everything works as expected.
示例 MyModel->属于
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->的结果属于 MyCustomBehavior :: beforeFind()
Result of MyModel->belongsTo
in MyCustomBehavior::beforeFind()
$belongsTo = array(
'Approver' => array(
...
),
'Status' => array(
...
),
);
期望 MyModel->属于 MyCustomBehavior :: beforeFind()
Expected MyModel->belongsTo
in MyCustomBehavior::beforeFind()
$belongsTo = array(
'MyAnotherModel' => array(
...
),
'Creator' => array(
...
),
'Approver' => array(
...
),
'Status' => array(
...
),
);
显而易见的解决方案:解决问题的一种愚蠢方法是简单地设置 MyModel
中的 Containable
行为而不是 AppModel
中的行为来控制顺序加载行为,即 public $ actsAs = ['MyCustom','Containable']
。此解决方案不是最佳解决方案,因为在其他模型中可能还有其他行为依赖于 Containable
,因此 Containable 需要在应用程序中的每个模型中明确设置,并希望我不会在某个地方破坏应用程序。
Obvious solution: One dumb way to solve the problem is to simply set
Containable
behavior in MyModel
instead of AppModel
to control the order of loading the behaviors, i.e., public $actsAs = ['MyCustom', 'Containable']
. This solution is not the best because there may be other behaviors in other models that depend on Containable
, so the order of Containable
needs to set in each model in app explicitly and hope that I didn't break the app somewhere.
有人问了一个类似(相关)的问题,所以,但没有答案。
A similar(related) question was asked on SO here but has no answers.
需要一个更强大的解决方案,可以解决
MyCustomBehavior
的需求,而无需在应用程序的其余部分进行更改
Need a more robust solution that can address the needs of
MyCustomBehavior
without having to make changes in rest of the app and looking out for any unexpected behavior.
推荐答案
尝试1个(不完美,容易出错):
Attempt 1 (Imperfect, error prone):
恢复所有原始关联的一种方法是致电
One way to recover all the original associations is to call
$MyModel->resetBindings($MyModel->alias);
$belongsToAssoc = $MyModel->belongsTo; // will now have original belongsTo assoc
但是,这种方法可能会失败(SQL错误
1066不是唯一的表/别名
)如果我在查找调用中使用了 joins
(使用默认的 alias
)以显式加入已关联的模型。这是因为 Containable
还将尝试联接所有通过 resetBindings()
调用还原的表,从而导致两次执行联接
However, this approach it may fail (SQL error
1066 Not unique table/alias
) to work correctly if I had used joins
in my find call (using default alias
) to explicitly join to an already associated model. This is because Containable
will also attempt to join all these tables restored by resetBindings()
call resulting in join being performed twice with same alias.
尝试2 (perfect ,没有已知的副作用, no known side effects):
Further digging through the core Containable
behavior and docs led me to object $MyModel->__backOriginalAssociation
and $MyModel->__backAssociation
(weird enough that ContainableBehavior
never used $__backContainableAssociation
as the variable name suggests) that was created and used by this behavior to perform resetBindings()
. So, my final solution was to simply check if Containable
is enabled on my modal (redundant in my case because it is attached in AppModel
and is never disabled or detached throughout the app) and check if the object is set on the model.
// 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
)包含所有在运行中绑定的模型。我不需要它,因此我将跳过合并代码。
$__backAssociation
holds model associations temporarily to allow for dynamic (un)binding. This solution can definitely be further improved by merging results of $Model->belongsTo
and $Model->__backAssociation['belongsTo']
(or hasMany
, hasOne
, hasAndBelongsToMany
) to include any models that were bound on the fly. I don't need it, so I will skip the code for merging.
完美,适合我自己的用例和应用设置。
在我的测试中未发现副作用,受我的专业知识/技能水平的限制。
免责声明:我的工作本帖子中的内容已获得 的许可。因此,用您想要的材料做些什么。此外,对于因使用上述材料而造成的任何形式的经济,身体或精神损失,我不承担任何责任。使用前需要您自担风险,并自行进行复制研究。不要忘了看,因为根据CC by-sa 3.0许可的用户贡献,需要提供出处。 (请检查此页脚上的页脚。我知道您从未在今天之前注意到它!:p)
Perfect for my own use case and my app setup.
No side effects were found in my testing that is limited by my level of expertise/skill.
Disclaimer: My work in this post is licensed under 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 because SO says "user contributions licensed under cc by-sa 3.0 with attribution required." (check the footer on this page. I know you never noticed it before today! :p)
这篇关于在CakePHP中使用Containable行为后获取原始关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!