Cake HABTM Query, Order By Rand()

thesunneversets picture thesunneversets · Nov 8, 2010 · Viewed 8.8k times · Source

I'm aware that Cake HABTM associations are tricky at the best of times, but I seem to be making life even harder for myself...

If I want to return a random Item from the db, I can do it as follows in the Item model:

$random = $this->find('first', array(
    'order' => 'rand()'
));

and if I want to find all the Items that are in a certain Category (where Item has a HABTM relationship to Categories), I know I can get a result set through $this->Categories->find.

My question is: how can I combine the two, so I can return a random Item that belongs to a specified Category? Is there any easy way? (If not, I'll gladly take any suggestions for a laborious way, as long as it works ;)

ETA: I can get some of the way with Containable, maybe: say I add the line

'contain' => array('Categories'=>array('conditions'=>array('Categories.id'=>1))),

then the Item results that I don't want come back with an empty Categories array, to distinguish them from "good" items. But really I don't want said Item results to be returned at all...

ETA(2): I can get a workaround going by unsetting my results in the afterFind if the Categories array is empty (thanks to http://nuts-and-bolts-of-cakephp.com/2008/08/06/filtering-results-returned-by-containable-behavior/ for the tip), and then having my random find function not give up until it gets a result:

while (!is_array($item)) {
    $item = $this->random($cat);
}

but ugh, could this be any clunkier? Anyway, time for me to stop editing my question, and to go away and sleep on it instead!

Answer

pawelmysior picture pawelmysior · Nov 8, 2010

Try this:

<?php
$this->Item->bindModel(array('hasOne' => array('ItemsCategory')));
$random = $this->Item->find('all', array(
  'order' => 'rand()',
  'conditions' => array('ItemsCategory.category_id' => '1')
  ));
?>