Skip to content

Instantly share code, notes, and snippets.

@redoPop
Created April 28, 2011 13:48
Show Gist options
  • Save redoPop/946379 to your computer and use it in GitHub Desktop.
Save redoPop/946379 to your computer and use it in GitHub Desktop.
CakePHP Behavior that makes ContainableBehavior respect a Model's default order
<?php
/**
* Behavior to enhance CakePHP ContainableBehavior by respecting default Model order
*
* Makes ContainableBehavior respect Model::order when no other order is specified in
* the containment. Must be placed before ContainableBehavior in the actsAs array of
* the Model being queried, like so:
*
* var $actsAs = array('OrderedContainable', 'Containable');
*
* @author joe bartlett ([email protected])
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @package OrderedContainableBehavior
*/
class OrderedContainableBehavior extends ModelBehavior {
/**
* Runs before a Model::find() operation. Examines the query's containments
* and supplements them with each Model's default order property if no other
* is set.
*
* @param object $Model Model using this ModelBehavior
* @param array $query Query parameters
* @return array
* @access public
*/
public function beforeFind(&$Model, $query) {
if (!empty($query['contain'])) {
$query['contain'] = (array)$query['contain'];
$containments = $Model->Behaviors->Containable->containments($Model, $query['contain']);
$containmentModelNames = array_keys($containments['models']);
$this->__addOrdersToContainments($Model, $query['contain'], $containmentModelNames);
}
return $query;
}
/**
* Called by ::beforeFind() to recursively examine a containment and add
* order information where appropriate.
*
* @param object $Model Model $contain applies to
* @param array $contain Containment from the query being examined
* @param array $containmentModelNames List of Model names contained in the query
* @access private
*/
private function __addOrdersToContainments(&$Model, &$contain, &$containmentModelNames) {
foreach ($contain as $modelName => $containment) {
if (!is_array($containment) && in_array($containment, $containmentModelNames)) {
unset($contain[$modelName]);
$contain[$containment] = array();
}
}
foreach ($contain as $modelName => &$containment) {
if (is_array($containment) && in_array($modelName, $containmentModelNames)) {
if (!array_key_exists('order', $containment) && !empty($Model->$modelName->order)) {
$containment['order'] = $Model->$modelName->order;
}
$lookDeeper = FALSE;
foreach ($containmentModelNames as $containmentModelName) {
if (in_array($containmentModelName, $containment)) {
$lookDeeper = TRUE;
break;
}
if (array_key_exists($containmentModelName, $containment)) {
$lookDeeper = TRUE;
break;
}
}
if ($lookDeeper) {
$this->__addOrdersToContainments($Model->$modelName, $containment, $containmentModelNames);
}
}
}
}
}
@josegonzalez
Copy link

Wouldn't it be better to implement this in Containable itself?

@redoPop
Copy link
Author

redoPop commented Apr 28, 2011

Yes! I assumed there was a reason Containable doesn't already do this (e.g., it can produce undesirably long query times for deep containments on large data sets when the 'order' isn't set to false), and didn't really want to maintain a fork of the entire behavior (if it still exists as a separate repo -- it's been absorbed into the core). Where's the best place to suggest a patch for Containable?

@josegonzalez
Copy link

You can fork the cake core, add your patches, add test cases, and then issue a pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment