Containable should keep assoc conditions
Reported by cricket | October 29th, 2010 @ 10:46 PM | in RFC
(1.3.5)
In beforeFind(), Containable unbinds associated models briefly. If an association has been declared with conditions, those will not be used unless those conditions are also included in the contain array. I think it would be better to merge the association conditions (if any) with those (if any) included in 'contain'.
scenario:
// Book
public $hasMany = array(
'Image' => array(
'className' => 'Image',
'foreignKey' => 'foreign_key',
'conditions' => array('Image.model' => 'Book'),
'dependent' => true
)
);
// Image
public $belongsTo = array(
'Book' => array(
'className' => 'Book',
'foreignKey' => false,
'conditions' => array(
'Image.model' => 'Book',
'Image.foreign_key' => 'Book.id'
),
'dependent' => true
),
'Journal' => array(
'className' => 'Site',
'foreignKey' => false,
'conditions' => array(
'Image.model' => 'Journal',
'Image.foreign_key' => 'Journal.id'
),
'dependent' => true
)
);
// BooksController
public $paginate = array(
'fields' => array('*'),
'contain' => array(
'Image' => array(
'conditions' => array('Image.sort_order' => 1),
'Thumbnail'
)
)
//, etc ...
);
What I'm after here is to limit the number of Images returned to just one when paginating a list of Books (yeah, I guess that's obvious). Because we can't have limit inside of contain the condition is used to do effectively the same thing.
However, Containable will use only the condition in $paginate, not also 'Image.model' => 'Book', resulting in Images from other models (that have sort_order == 1) being returned. This can be remedied by including the conditions before calling paginate():
$this->paginate['contain']['Image']['conditions'] = array_merge(
$this->Book->hasMany['Image']['conditions'],
array('Image.sort_order' => 1)
);
// or, keep the original conditions in $paginate, and:
$this->paginate['contain']['Image']['conditions'] = array_merge(
$this->Book->hasMany['Image']['conditions'],
$this->paginate['contain']['Image']['conditions']
);
$this->set('data', $this->paginate());
This works but it's inelegant. A (possible) fix for Containable::beforeFind() ...
/* FIX
*/
if (isset($instance->__backAssociation[$type][$assoc]['conditions']))
{
if (!isset($model['keep'][$assoc]['conditions'])) {
$model['keep'][$assoc]['conditions'] = array();
}
$model['keep'][$assoc]['conditions'] = array_merge(
$model['keep'][$assoc]['conditions'],
$instance->__backAssociation[$type][$assoc]['conditions']
);
}
/* END FIX
*/
$instance->{$type}[$assoc] = array_merge($instance->{$type}[$assoc], $model['keep'][$assoc]);
I say "possible" for the same reason I'm not including a test. While I understand $backAssociation, I'm a bit hazy on $reset, $backContainableAssociation, $backOriginalAssociation, and $backInnerAssociation (getting hazier), so I wouldn't be surprised if a little more work would be necessary.
Comments and changes to this ticket
-

cricket October 29th, 2010 @ 11:03 PM
Ugh! What kind of boneheaded formatting is this? It even created frigging textareas out of I-don't-know-what! And I can't edit. Nice. I'll attach a text file with the relevant code.
Seriously, this is pretty crappy for a site like this. Some "formatting help" that was.
-

Mark Story October 29th, 2010 @ 11:11 PM
There's an edit ticket at the top, at least I have an edit.
-

Mark Story October 29th, 2010 @ 11:17 PM
- → Milestone set to RFC
- → Tag set to conditions, containable
If the behaviour was changed to do merges, how would you do overwrites?
-

cricket October 29th, 2010 @ 11:31 PM
Ah, i see the barely-visible, badly-positioned link now. I think I'll leave it as-is for now; one wrong character might add blink tags for all I know. Please go ahead and edit if you like.
And I think I've just figured out how to label a ticket with a milestone. Not very obvious, either. Sorry for the hassle.
As for overwrites, good point. We could instead loop through the assoc. conditions and keep only those that aren't in the contain array. See attached. Works for me. But, as I said, I'm under no illusions that this is all that's required.
-

cricket October 29th, 2010 @ 11:36 PM
No, wait. That was boneheaded. I dropped the 2nd test in if(). Please see attached.
-

cricket October 29th, 2010 @ 11:39 PM
Hmm ... there'd need to be some normalisation of the keys, actually. The conditions--in the model assoc array or contain--may, or may not, include Model-alias.
-

cricket October 29th, 2010 @ 11:53 PM
Normalised the keys in both conditions arrays to include model name. You guys run into this all the time so there may be a cleaner way. And I'm wondering if there may be edge cases where Model-alias should not be prepended.
Attached.
-

cricket October 30th, 2010 @ 12:21 AM
Bah! The association conditions may be null, of course. Update attached.
-

Mark Story November 15th, 2010 @ 10:19 PM
- → Tag changed from conditions, containable to conditions, containablebehavior
Please Sign in or create a free account to add a new ticket.
With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
Source available from github
Repository is at http://github.com/cakephp/cakephp
Creating a bug report
When creating a bug report, please include as much relevant information as possible. Please include code to reproduce the issue. Or even better, make a unit test. Either change an existing test or add a new test to show that the expected behavior is not occuring.