CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.6 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.6
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • AclBehavior
  • ContainableBehavior
  • TranslateBehavior
  • TreeBehavior
   1: <?php
   2: /**
   3:  * Tree behavior class.
   4:  *
   5:  * Enables a model object to act as a node-based tree.
   6:  *
   7:  * CakePHP :  Rapid Development Framework (http://cakephp.org)
   8:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
   9:  *
  10:  * Licensed under The MIT License
  11:  * For full copyright and license information, please see the LICENSE.txt
  12:  * Redistributions of files must retain the above copyright notice.
  13:  *
  14:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  15:  * @link          http://cakephp.org CakePHP Project
  16:  * @package       Cake.Model.Behavior
  17:  * @since         CakePHP v 1.2.0.4487
  18:  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  19:  */
  20: 
  21: App::uses('ModelBehavior', 'Model');
  22: 
  23: /**
  24:  * Tree Behavior.
  25:  *
  26:  * Enables a model object to act as a node-based tree. Using Modified Preorder Tree Traversal
  27:  *
  28:  * @see http://en.wikipedia.org/wiki/Tree_traversal
  29:  * @package       Cake.Model.Behavior
  30:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html
  31:  */
  32: class TreeBehavior extends ModelBehavior {
  33: 
  34: /**
  35:  * Errors
  36:  *
  37:  * @var array
  38:  */
  39:     public $errors = array();
  40: 
  41: /**
  42:  * Defaults
  43:  *
  44:  * @var array
  45:  */
  46:     protected $_defaults = array(
  47:         'parent' => 'parent_id', 'left' => 'lft', 'right' => 'rght',
  48:         'scope' => '1 = 1', 'type' => 'nested', '__parentChange' => false, 'recursive' => -1
  49:     );
  50: 
  51: /**
  52:  * Used to preserve state between delete callbacks.
  53:  *
  54:  * @var array
  55:  */
  56:     protected $_deletedRow = array();
  57: 
  58: /**
  59:  * Initiate Tree behavior
  60:  *
  61:  * @param Model $Model using this behavior of model
  62:  * @param array $config array of configuration settings.
  63:  * @return void
  64:  */
  65:     public function setup(Model $Model, $config = array()) {
  66:         if (isset($config[0])) {
  67:             $config['type'] = $config[0];
  68:             unset($config[0]);
  69:         }
  70:         $settings = $config + $this->_defaults;
  71: 
  72:         if (in_array($settings['scope'], $Model->getAssociated('belongsTo'))) {
  73:             $data = $Model->getAssociated($settings['scope']);
  74:             $Parent = $Model->{$settings['scope']};
  75:             $settings['scope'] = $Model->escapeField($data['foreignKey']) . ' = ' . $Parent->escapeField();
  76:             $settings['recursive'] = 0;
  77:         }
  78:         $this->settings[$Model->alias] = $settings;
  79:     }
  80: 
  81: /**
  82:  * After save method. Called after all saves
  83:  *
  84:  * Overridden to transparently manage setting the lft and rght fields if and only if the parent field is included in the
  85:  * parameters to be saved.
  86:  *
  87:  * @param Model $Model Model using this behavior.
  88:  * @param bool $created indicates whether the node just saved was created or updated
  89:  * @param array $options Options passed from Model::save().
  90:  * @return bool true on success, false on failure
  91:  */
  92:     public function afterSave(Model $Model, $created, $options = array()) {
  93:         extract($this->settings[$Model->alias]);
  94:         if ($created) {
  95:             if ((isset($Model->data[$Model->alias][$parent])) && $Model->data[$Model->alias][$parent]) {
  96:                 return $this->_setParent($Model, $Model->data[$Model->alias][$parent], $created);
  97:             }
  98:         } elseif ($this->settings[$Model->alias]['__parentChange']) {
  99:             $this->settings[$Model->alias]['__parentChange'] = false;
 100:             return $this->_setParent($Model, $Model->data[$Model->alias][$parent]);
 101:         }
 102:     }
 103: 
 104: /**
 105:  * Runs before a find() operation
 106:  *
 107:  * @param Model $Model Model using the behavior
 108:  * @param array $query Query parameters as set by cake
 109:  * @return array
 110:  */
 111:     public function beforeFind(Model $Model, $query) {
 112:         if ($Model->findQueryType === 'threaded' && !isset($query['parent'])) {
 113:             $query['parent'] = $this->settings[$Model->alias]['parent'];
 114:         }
 115:         return $query;
 116:     }
 117: 
 118: /**
 119:  * Stores the record about to be deleted.
 120:  *
 121:  * This is used to delete child nodes in the afterDelete.
 122:  *
 123:  * @param Model $Model Model using this behavior.
 124:  * @param bool $cascade If true records that depend on this record will also be deleted
 125:  * @return bool
 126:  */
 127:     public function beforeDelete(Model $Model, $cascade = true) {
 128:         extract($this->settings[$Model->alias]);
 129:         $data = $Model->find('first', array(
 130:             'conditions' => array($Model->escapeField($Model->primaryKey) => $Model->id),
 131:             'fields' => array($Model->escapeField($left), $Model->escapeField($right)),
 132:             'order' => false,
 133:             'recursive' => -1));
 134:         if ($data) {
 135:             $this->_deletedRow[$Model->alias] = current($data);
 136:         }
 137:         return true;
 138:     }
 139: 
 140: /**
 141:  * After delete method.
 142:  *
 143:  * Will delete the current node and all children using the deleteAll method and sync the table
 144:  *
 145:  * @param Model $Model Model using this behavior
 146:  * @return bool true to continue, false to abort the delete
 147:  */
 148:     public function afterDelete(Model $Model) {
 149:         extract($this->settings[$Model->alias]);
 150:         $data = $this->_deletedRow[$Model->alias];
 151:         $this->_deletedRow[$Model->alias] = null;
 152: 
 153:         if (!$data[$right] || !$data[$left]) {
 154:             return true;
 155:         }
 156:         $diff = $data[$right] - $data[$left] + 1;
 157: 
 158:         if ($diff > 2) {
 159:             if (is_string($scope)) {
 160:                 $scope = array($scope);
 161:             }
 162:             $scope[][$Model->escapeField($left) . " BETWEEN ? AND ?"] = array($data[$left] + 1, $data[$right] - 1);
 163:             $Model->deleteAll($scope);
 164:         }
 165:         $this->_sync($Model, $diff, '-', '> ' . $data[$right]);
 166:         return true;
 167:     }
 168: 
 169: /**
 170:  * Before save method. Called before all saves
 171:  *
 172:  * Overridden to transparently manage setting the lft and rght fields if and only if the parent field is included in the
 173:  * parameters to be saved. For newly created nodes with NO parent the left and right field values are set directly by
 174:  * this method bypassing the setParent logic.
 175:  *
 176:  * @param Model $Model Model using this behavior
 177:  * @param array $options Options passed from Model::save().
 178:  * @return bool true to continue, false to abort the save
 179:  * @see Model::save()
 180:  */
 181:     public function beforeSave(Model $Model, $options = array()) {
 182:         extract($this->settings[$Model->alias]);
 183: 
 184:         $this->_addToWhitelist($Model, array($left, $right));
 185:         if (!$Model->id || !$Model->exists()) {
 186:             if (array_key_exists($parent, $Model->data[$Model->alias]) && $Model->data[$Model->alias][$parent]) {
 187:                 $parentNode = $this->_getNode($Model, $Model->data[$Model->alias][$parent]);
 188:                 if (!$parentNode) {
 189:                     return false;
 190:                 }
 191: 
 192:                 $Model->data[$Model->alias][$left] = 0;
 193:                 $Model->data[$Model->alias][$right] = 0;
 194:                 return true;
 195:             }
 196: 
 197:             $edge = $this->_getMax($Model, $scope, $right, $recursive);
 198:             $Model->data[$Model->alias][$left] = $edge + 1;
 199:             $Model->data[$Model->alias][$right] = $edge + 2;
 200:             return true;
 201:         }
 202: 
 203:         if (array_key_exists($parent, $Model->data[$Model->alias])) {
 204:             if ($Model->data[$Model->alias][$parent] != $Model->field($parent)) {
 205:                 $this->settings[$Model->alias]['__parentChange'] = true;
 206:             }
 207:             if (!$Model->data[$Model->alias][$parent]) {
 208:                 $Model->data[$Model->alias][$parent] = null;
 209:                 $this->_addToWhitelist($Model, $parent);
 210:                 return true;
 211:             }
 212: 
 213:             $values = $this->_getNode($Model, $Model->id);
 214:             if (empty($values)) {
 215:                 return false;
 216:             }
 217:             list($node) = array_values($values);
 218: 
 219:             $parentNode = $this->_getNode($Model, $Model->data[$Model->alias][$parent]);
 220:             if (!$parentNode) {
 221:                 return false;
 222:             }
 223:             list($parentNode) = array_values($parentNode);
 224: 
 225:             if (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) {
 226:                 return false;
 227:             }
 228:             if ($node[$Model->primaryKey] === $parentNode[$Model->primaryKey]) {
 229:                 return false;
 230:             }
 231:         }
 232: 
 233:         return true;
 234:     }
 235: 
 236: /**
 237:  * Returns a single node from the tree from its primary key
 238:  *
 239:  * @param Model $Model Model using this behavior
 240:  * @param int|string $id The ID of the record to read
 241:  * @return array|bool The record read or false
 242:  */
 243:     protected function _getNode(Model $Model, $id) {
 244:         $settings = $this->settings[$Model->alias];
 245: 
 246:         return $Model->find('first', array(
 247:             'conditions' => array($Model->escapeField() => $id),
 248:             'fields' => array($Model->primaryKey, $settings['parent'], $settings['left'], $settings['right']),
 249:             'recursive' => $settings['recursive'],
 250:             'order' => false,
 251:         ));
 252:     }
 253: 
 254: /**
 255:  * Get the number of child nodes
 256:  *
 257:  * If the direct parameter is set to true, only the direct children are counted (based upon the parent_id field)
 258:  * If false is passed for the id parameter, all top level nodes are counted, or all nodes are counted.
 259:  *
 260:  * @param Model $Model Model using this behavior
 261:  * @param int|string|bool $id The ID of the record to read or false to read all top level nodes
 262:  * @param bool $direct whether to count direct, or all, children
 263:  * @return int number of child nodes
 264:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::childCount
 265:  */
 266:     public function childCount(Model $Model, $id = null, $direct = false) {
 267:         if (is_array($id)) {
 268:             extract(array_merge(array('id' => null), $id));
 269:         }
 270:         if ($id === null && $Model->id) {
 271:             $id = $Model->id;
 272:         } elseif (!$id) {
 273:             $id = null;
 274:         }
 275:         extract($this->settings[$Model->alias]);
 276: 
 277:         if ($direct) {
 278:             return $Model->find('count', array('conditions' => array($scope, $Model->escapeField($parent) => $id)));
 279:         }
 280: 
 281:         if ($id === null) {
 282:             return $Model->find('count', array('conditions' => $scope));
 283:         } elseif ($Model->id === $id && isset($Model->data[$Model->alias][$left]) && isset($Model->data[$Model->alias][$right])) {
 284:             $data = $Model->data[$Model->alias];
 285:         } else {
 286:             $data = $this->_getNode($Model, $id);
 287:             if (!$data) {
 288:                 return 0;
 289:             }
 290:             $data = $data[$Model->alias];
 291:         }
 292:         return ($data[$right] - $data[$left] - 1) / 2;
 293:     }
 294: 
 295: /**
 296:  * Get the child nodes of the current model
 297:  *
 298:  * If the direct parameter is set to true, only the direct children are returned (based upon the parent_id field)
 299:  * If false is passed for the id parameter, top level, or all (depending on direct parameter appropriate) are counted.
 300:  *
 301:  * @param Model $Model Model using this behavior
 302:  * @param int|string $id The ID of the record to read
 303:  * @param bool $direct whether to return only the direct, or all, children
 304:  * @param string|array $fields Either a single string of a field name, or an array of field names
 305:  * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") defaults to the tree order
 306:  * @param int $limit SQL LIMIT clause, for calculating items per page.
 307:  * @param int $page Page number, for accessing paged data
 308:  * @param int $recursive The number of levels deep to fetch associated records
 309:  * @return array Array of child nodes
 310:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::children
 311:  */
 312:     public function children(Model $Model, $id = null, $direct = false, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
 313:         $options = array();
 314:         if (is_array($id)) {
 315:             $options = $this->_getOptions($id);
 316:             extract(array_merge(array('id' => null), $id));
 317:         }
 318:         $overrideRecursive = $recursive;
 319: 
 320:         if ($id === null && $Model->id) {
 321:             $id = $Model->id;
 322:         } elseif (!$id) {
 323:             $id = null;
 324:         }
 325: 
 326:         extract($this->settings[$Model->alias]);
 327: 
 328:         if ($overrideRecursive !== null) {
 329:             $recursive = $overrideRecursive;
 330:         }
 331:         if (!$order) {
 332:             $order = $Model->escapeField($left) . " asc";
 333:         }
 334:         if ($direct) {
 335:             $conditions = array($scope, $Model->escapeField($parent) => $id);
 336:             return $Model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
 337:         }
 338: 
 339:         if (!$id) {
 340:             $conditions = $scope;
 341:         } else {
 342:             $result = array_values((array)$Model->find('first', array(
 343:                 'conditions' => array($scope, $Model->escapeField() => $id),
 344:                 'fields' => array($left, $right),
 345:                 'recursive' => $recursive,
 346:                 'order' => false,
 347:             )));
 348: 
 349:             if (empty($result) || !isset($result[0])) {
 350:                 return array();
 351:             }
 352:             $conditions = array($scope,
 353:                 $Model->escapeField($right) . ' <' => $result[0][$right],
 354:                 $Model->escapeField($left) . ' >' => $result[0][$left]
 355:             );
 356:         }
 357:         $options = array_merge(compact(
 358:             'conditions', 'fields', 'order', 'limit', 'page', 'recursive'
 359:         ), $options);
 360:         return $Model->find('all', $options);
 361:     }
 362: 
 363: /**
 364:  * A convenience method for returning a hierarchical array used for HTML select boxes
 365:  *
 366:  * @param Model $Model Model using this behavior
 367:  * @param string|array $conditions SQL conditions as a string or as an array('field' =>'value',...)
 368:  * @param string $keyPath A string path to the key, i.e. "{n}.Post.id"
 369:  * @param string $valuePath A string path to the value, i.e. "{n}.Post.title"
 370:  * @param string $spacer The character or characters which will be repeated
 371:  * @param int $recursive The number of levels deep to fetch associated records
 372:  * @return array An associative array of records, where the id is the key, and the display field is the value
 373:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::generateTreeList
 374:  */
 375:     public function generateTreeList(Model $Model, $conditions = null, $keyPath = null, $valuePath = null, $spacer = '_', $recursive = null) {
 376:         $overrideRecursive = $recursive;
 377:         extract($this->settings[$Model->alias]);
 378:         if ($overrideRecursive !== null) {
 379:             $recursive = $overrideRecursive;
 380:         }
 381: 
 382:         $fields = null;
 383:         if (!$keyPath && !$valuePath && $Model->hasField($Model->displayField)) {
 384:             $fields = array($Model->primaryKey, $Model->displayField, $left, $right);
 385:         }
 386: 
 387:         if (!$keyPath) {
 388:             $keyPath = '{n}.' . $Model->alias . '.' . $Model->primaryKey;
 389:         }
 390: 
 391:         if (!$valuePath) {
 392:             $valuePath = array('%s%s', '{n}.tree_prefix', '{n}.' . $Model->alias . '.' . $Model->displayField);
 393: 
 394:         } elseif (is_string($valuePath)) {
 395:             $valuePath = array('%s%s', '{n}.tree_prefix', $valuePath);
 396: 
 397:         } else {
 398:             array_unshift($valuePath, '%s' . $valuePath[0], '{n}.tree_prefix');
 399:         }
 400: 
 401:         $conditions = (array)$conditions;
 402:         if ($scope) {
 403:             $conditions[] = $scope;
 404:         }
 405: 
 406:         $order = $Model->escapeField($left) . " asc";
 407:         $results = $Model->find('all', compact('conditions', 'fields', 'order', 'recursive'));
 408:         $stack = array();
 409: 
 410:         foreach ($results as $i => $result) {
 411:             $count = count($stack);
 412:             while ($stack && ($stack[$count - 1] < $result[$Model->alias][$right])) {
 413:                 array_pop($stack);
 414:                 $count--;
 415:             }
 416:             $results[$i]['tree_prefix'] = str_repeat($spacer, $count);
 417:             $stack[] = $result[$Model->alias][$right];
 418:         }
 419:         if (empty($results)) {
 420:             return array();
 421:         }
 422:         return Hash::combine($results, $keyPath, $valuePath);
 423:     }
 424: 
 425: /**
 426:  * Get the parent node
 427:  *
 428:  * reads the parent id and returns this node
 429:  *
 430:  * @param Model $Model Model using this behavior
 431:  * @param int|string $id The ID of the record to read
 432:  * @param string|array $fields Fields to get
 433:  * @param int $recursive The number of levels deep to fetch associated records
 434:  * @return array|bool Array of data for the parent node
 435:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::getParentNode
 436:  */
 437:     public function getParentNode(Model $Model, $id = null, $fields = null, $recursive = null) {
 438:         $options = array();
 439:         if (is_array($id)) {
 440:             $options = $this->_getOptions($id);
 441:             extract(array_merge(array('id' => null), $id));
 442:         }
 443:         $overrideRecursive = $recursive;
 444:         if (empty($id)) {
 445:             $id = $Model->id;
 446:         }
 447:         extract($this->settings[$Model->alias]);
 448:         if ($overrideRecursive !== null) {
 449:             $recursive = $overrideRecursive;
 450:         }
 451:         $parentId = $Model->find('first', array(
 452:             'conditions' => array($Model->primaryKey => $id),
 453:             'fields' => array($parent),
 454:             'order' => false,
 455:             'recursive' => -1
 456:         ));
 457: 
 458:         if ($parentId) {
 459:             $parentId = $parentId[$Model->alias][$parent];
 460:             $options = array_merge(array(
 461:                 'conditions' => array($Model->escapeField() => $parentId),
 462:                 'fields' => $fields,
 463:                 'order' => false,
 464:                 'recursive' => $recursive
 465:             ), $options);
 466:             $parent = $Model->find('first', $options);
 467: 
 468:             return $parent;
 469:         }
 470:         return false;
 471:     }
 472: 
 473: /**
 474:  * Convenience method to create default find() options from $arg when it is an
 475:  * associative array.
 476:  *
 477:  * @param array $arg Array
 478:  * @return array Options array
 479:  */
 480:     protected function _getOptions($arg) {
 481:         return count(array_filter(array_keys($arg), 'is_string') > 0) ?
 482:             $arg :
 483:             array();
 484:     }
 485: 
 486: /**
 487:  * Get the path to the given node
 488:  *
 489:  * @param Model $Model Model using this behavior
 490:  * @param int|string $id The ID of the record to read
 491:  * @param string|array $fields Either a single string of a field name, or an array of field names
 492:  * @param int $recursive The number of levels deep to fetch associated records
 493:  * @return array Array of nodes from top most parent to current node
 494:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::getPath
 495:  */
 496:     public function getPath(Model $Model, $id = null, $fields = null, $recursive = null) {
 497:         $options = array();
 498:         if (is_array($id)) {
 499:             $options = $this->_getOptions($id);
 500:             extract(array_merge(array('id' => null), $id));
 501:         }
 502: 
 503:         if (!empty($options)) {
 504:             $fields = null;
 505:             if (!empty($options['fields'])) {
 506:                 $fields = $options['fields'];
 507:             }
 508:             if (!empty($options['recursive'])) {
 509:                 $recursive = $options['recursive'];
 510:             }
 511:         }
 512:         $overrideRecursive = $recursive;
 513:         if (empty($id)) {
 514:             $id = $Model->id;
 515:         }
 516:         extract($this->settings[$Model->alias]);
 517:         if ($overrideRecursive !== null) {
 518:             $recursive = $overrideRecursive;
 519:         }
 520:         $result = $Model->find('first', array(
 521:             'conditions' => array($Model->escapeField() => $id),
 522:             'fields' => array($left, $right),
 523:             'order' => false,
 524:             'recursive' => $recursive
 525:         ));
 526:         if ($result) {
 527:             $result = array_values($result);
 528:         } else {
 529:             return array();
 530:         }
 531:         $item = $result[0];
 532:         $options = array_merge(array(
 533:             'conditions' => array(
 534:                 $scope,
 535:                 $Model->escapeField($left) . ' <=' => $item[$left],
 536:                 $Model->escapeField($right) . ' >=' => $item[$right],
 537:             ),
 538:             'fields' => $fields,
 539:             'order' => array($Model->escapeField($left) => 'asc'),
 540:             'recursive' => $recursive
 541:         ), $options);
 542:         $results = $Model->find('all', $options);
 543:         return $results;
 544:     }
 545: 
 546: /**
 547:  * Reorder the node without changing the parent.
 548:  *
 549:  * If the node is the last child, or is a top level node with no subsequent node this method will return false
 550:  *
 551:  * @param Model $Model Model using this behavior
 552:  * @param int|string $id The ID of the record to move
 553:  * @param int|bool $number how many places to move the node or true to move to last position
 554:  * @return bool true on success, false on failure
 555:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::moveDown
 556:  */
 557:     public function moveDown(Model $Model, $id = null, $number = 1) {
 558:         if (is_array($id)) {
 559:             extract(array_merge(array('id' => null), $id));
 560:         }
 561:         if (!$number) {
 562:             return false;
 563:         }
 564:         if (empty($id)) {
 565:             $id = $Model->id;
 566:         }
 567:         extract($this->settings[$Model->alias]);
 568:         list($node) = array_values($this->_getNode($Model, $id));
 569:         if ($node[$parent]) {
 570:             list($parentNode) = array_values($this->_getNode($Model, $node[$parent]));
 571:             if (($node[$right] + 1) == $parentNode[$right]) {
 572:                 return false;
 573:             }
 574:         }
 575:         $nextNode = $Model->find('first', array(
 576:             'conditions' => array($scope, $Model->escapeField($left) => ($node[$right] + 1)),
 577:             'fields' => array($Model->primaryKey, $left, $right),
 578:             'order' => false,
 579:             'recursive' => $recursive)
 580:         );
 581:         if ($nextNode) {
 582:             list($nextNode) = array_values($nextNode);
 583:         } else {
 584:             return false;
 585:         }
 586:         $edge = $this->_getMax($Model, $scope, $right, $recursive);
 587:         $this->_sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right]);
 588:         $this->_sync($Model, $nextNode[$left] - $node[$left], '-', 'BETWEEN ' . $nextNode[$left] . ' AND ' . $nextNode[$right]);
 589:         $this->_sync($Model, $edge - $node[$left] - ($nextNode[$right] - $nextNode[$left]), '-', '> ' . $edge);
 590: 
 591:         if (is_int($number)) {
 592:             $number--;
 593:         }
 594:         if ($number) {
 595:             $this->moveDown($Model, $id, $number);
 596:         }
 597:         return true;
 598:     }
 599: 
 600: /**
 601:  * Reorder the node without changing the parent.
 602:  *
 603:  * If the node is the first child, or is a top level node with no previous node this method will return false
 604:  *
 605:  * @param Model $Model Model using this behavior
 606:  * @param int|string $id The ID of the record to move
 607:  * @param int|bool $number how many places to move the node, or true to move to first position
 608:  * @return bool true on success, false on failure
 609:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::moveUp
 610:  */
 611:     public function moveUp(Model $Model, $id = null, $number = 1) {
 612:         if (is_array($id)) {
 613:             extract(array_merge(array('id' => null), $id));
 614:         }
 615:         if (!$number) {
 616:             return false;
 617:         }
 618:         if (empty($id)) {
 619:             $id = $Model->id;
 620:         }
 621:         extract($this->settings[$Model->alias]);
 622:         list($node) = array_values($this->_getNode($Model, $id));
 623:         if ($node[$parent]) {
 624:             list($parentNode) = array_values($this->_getNode($Model, $node[$parent]));
 625:             if (($node[$left] - 1) == $parentNode[$left]) {
 626:                 return false;
 627:             }
 628:         }
 629:         $previousNode = $Model->find('first', array(
 630:             'conditions' => array($scope, $Model->escapeField($right) => ($node[$left] - 1)),
 631:             'fields' => array($Model->primaryKey, $left, $right),
 632:             'order' => false,
 633:             'recursive' => $recursive
 634:         ));
 635: 
 636:         if ($previousNode) {
 637:             list($previousNode) = array_values($previousNode);
 638:         } else {
 639:             return false;
 640:         }
 641:         $edge = $this->_getMax($Model, $scope, $right, $recursive);
 642:         $this->_sync($Model, $edge - $previousNode[$left] + 1, '+', 'BETWEEN ' . $previousNode[$left] . ' AND ' . $previousNode[$right]);
 643:         $this->_sync($Model, $node[$left] - $previousNode[$left], '-', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right]);
 644:         $this->_sync($Model, $edge - $previousNode[$left] - ($node[$right] - $node[$left]), '-', '> ' . $edge);
 645:         if (is_int($number)) {
 646:             $number--;
 647:         }
 648:         if ($number) {
 649:             $this->moveUp($Model, $id, $number);
 650:         }
 651:         return true;
 652:     }
 653: 
 654: /**
 655:  * Recover a corrupted tree
 656:  *
 657:  * The mode parameter is used to specify the source of info that is valid/correct. The opposite source of data
 658:  * will be populated based upon that source of info. E.g. if the MPTT fields are corrupt or empty, with the $mode
 659:  * 'parent' the values of the parent_id field will be used to populate the left and right fields. The missingParentAction
 660:  * parameter only applies to "parent" mode and determines what to do if the parent field contains an id that is not present.
 661:  *
 662:  * @param Model $Model Model using this behavior
 663:  * @param string $mode parent or tree
 664:  * @param string|int $missingParentAction 'return' to do nothing and return, 'delete' to
 665:  * delete, or the id of the parent to set as the parent_id
 666:  * @return bool true on success, false on failure
 667:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::recover
 668:  */
 669:     public function recover(Model $Model, $mode = 'parent', $missingParentAction = null) {
 670:         if (is_array($mode)) {
 671:             extract(array_merge(array('mode' => 'parent'), $mode));
 672:         }
 673:         extract($this->settings[$Model->alias]);
 674:         $Model->recursive = $recursive;
 675:         if ($mode === 'parent') {
 676:             $Model->bindModel(array('belongsTo' => array('VerifyParent' => array(
 677:                 'className' => $Model->name,
 678:                 'foreignKey' => $parent,
 679:                 'fields' => array($Model->primaryKey, $left, $right, $parent),
 680:             ))));
 681:             $missingParents = $Model->find('list', array(
 682:                 'recursive' => 0,
 683:                 'conditions' => array($scope, array(
 684:                     'NOT' => array($Model->escapeField($parent) => null), $Model->VerifyParent->escapeField() => null
 685:                 )),
 686:                 'order' => false,
 687:             ));
 688:             $Model->unbindModel(array('belongsTo' => array('VerifyParent')));
 689:             if ($missingParents) {
 690:                 if ($missingParentAction === 'return') {
 691:                     foreach ($missingParents as $id => $display) {
 692:                         $this->errors[] = 'cannot find the parent for ' . $Model->alias . ' with id ' . $id . '(' . $display . ')';
 693:                     }
 694:                     return false;
 695:                 } elseif ($missingParentAction === 'delete') {
 696:                     $Model->deleteAll(array($Model->escapeField($Model->primaryKey) => array_flip($missingParents)), false);
 697:                 } else {
 698:                     $Model->updateAll(array($Model->escapeField($parent) => $missingParentAction), array($Model->escapeField($Model->primaryKey) => array_flip($missingParents)));
 699:                 }
 700:             }
 701: 
 702:             $this->_recoverByParentId($Model);
 703:         } else {
 704:             $db = ConnectionManager::getDataSource($Model->useDbConfig);
 705:             foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) {
 706:                 $path = $this->getPath($Model, $array[$Model->alias][$Model->primaryKey]);
 707:                 $parentId = null;
 708:                 if (count($path) > 1) {
 709:                     $parentId = $path[count($path) - 2][$Model->alias][$Model->primaryKey];
 710:                 }
 711:                 $Model->updateAll(array($parent => $db->value($parentId, $parent)), array($Model->escapeField() => $array[$Model->alias][$Model->primaryKey]));
 712:             }
 713:         }
 714:         return true;
 715:     }
 716: 
 717: /**
 718:  * _recoverByParentId
 719:  *
 720:  * Recursive helper function used by recover
 721:  *
 722:  * @param Model $Model Model instance.
 723:  * @param int $counter Counter
 724:  * @param mixed $parentId Parent record Id
 725:  * @return int counter
 726:  */
 727:     protected function _recoverByParentId(Model $Model, $counter = 1, $parentId = null) {
 728:         $params = array(
 729:             'conditions' => array(
 730:                 $this->settings[$Model->alias]['parent'] => $parentId
 731:             ),
 732:             'fields' => array($Model->primaryKey),
 733:             'page' => 1,
 734:             'limit' => 100,
 735:             'order' => array($Model->primaryKey)
 736:         );
 737: 
 738:         $scope = $this->settings[$Model->alias]['scope'];
 739:         if ($scope && ($scope !== '1 = 1' && $scope !== true)) {
 740:             $params['conditions'][] = $scope;
 741:         }
 742: 
 743:         $children = $Model->find('all', $params);
 744:         $hasChildren = (bool)$children;
 745: 
 746:         if ($parentId !== null) {
 747:             if ($hasChildren) {
 748:                 $Model->updateAll(
 749:                     array($this->settings[$Model->alias]['left'] => $counter),
 750:                     array($Model->escapeField() => $parentId)
 751:                 );
 752:                 $counter++;
 753:             } else {
 754:                 $Model->updateAll(
 755:                     array(
 756:                         $this->settings[$Model->alias]['left'] => $counter,
 757:                         $this->settings[$Model->alias]['right'] => $counter + 1
 758:                     ),
 759:                     array($Model->escapeField() => $parentId)
 760:                 );
 761:                 $counter += 2;
 762:             }
 763:         }
 764: 
 765:         while ($children) {
 766:             foreach ($children as $row) {
 767:                 $counter = $this->_recoverByParentId($Model, $counter, $row[$Model->alias][$Model->primaryKey]);
 768:             }
 769: 
 770:             if (count($children) !== $params['limit']) {
 771:                 break;
 772:             }
 773:             $params['page']++;
 774:             $children = $Model->find('all', $params);
 775:         }
 776: 
 777:         if ($parentId !== null && $hasChildren) {
 778:             $Model->updateAll(
 779:                 array($this->settings[$Model->alias]['right'] => $counter),
 780:                 array($Model->escapeField() => $parentId)
 781:             );
 782:             $counter++;
 783:         }
 784: 
 785:         return $counter;
 786:     }
 787: 
 788: /**
 789:  * Reorder method.
 790:  *
 791:  * Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters.
 792:  * This method does not change the parent of any node.
 793:  *
 794:  * Requires a valid tree, by default it verifies the tree before beginning.
 795:  *
 796:  * Options:
 797:  *
 798:  * - 'id' id of record to use as top node for reordering
 799:  * - 'field' Which field to use in reordering defaults to displayField
 800:  * - 'order' Direction to order either DESC or ASC (defaults to ASC)
 801:  * - 'verify' Whether or not to verify the tree before reorder. defaults to true.
 802:  *
 803:  * @param Model $Model Model using this behavior
 804:  * @param array $options array of options to use in reordering.
 805:  * @return bool true on success, false on failure
 806:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::reorder
 807:  */
 808:     public function reorder(Model $Model, $options = array()) {
 809:         $options += array('id' => null, 'field' => $Model->displayField, 'order' => 'ASC', 'verify' => true);
 810:         extract($options);
 811:         if ($verify && !$this->verify($Model)) {
 812:             return false;
 813:         }
 814:         $verify = false;
 815:         extract($this->settings[$Model->alias]);
 816:         $fields = array($Model->primaryKey, $field, $left, $right);
 817:         $sort = $field . ' ' . $order;
 818:         $nodes = $this->children($Model, $id, true, $fields, $sort, null, null, $recursive);
 819: 
 820:         $cacheQueries = $Model->cacheQueries;
 821:         $Model->cacheQueries = false;
 822:         if ($nodes) {
 823:             foreach ($nodes as $node) {
 824:                 $id = $node[$Model->alias][$Model->primaryKey];
 825:                 $this->moveDown($Model, $id, true);
 826:                 if ($node[$Model->alias][$left] != $node[$Model->alias][$right] - 1) {
 827:                     $this->reorder($Model, compact('id', 'field', 'order', 'verify'));
 828:                 }
 829:             }
 830:         }
 831:         $Model->cacheQueries = $cacheQueries;
 832:         return true;
 833:     }
 834: 
 835: /**
 836:  * Remove the current node from the tree, and reparent all children up one level.
 837:  *
 838:  * If the parameter delete is false, the node will become a new top level node. Otherwise the node will be deleted
 839:  * after the children are reparented.
 840:  *
 841:  * @param Model $Model Model using this behavior
 842:  * @param int|string $id The ID of the record to remove
 843:  * @param bool $delete whether to delete the node after reparenting children (if any)
 844:  * @return bool true on success, false on failure
 845:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::removeFromTree
 846:  */
 847:     public function removeFromTree(Model $Model, $id = null, $delete = false) {
 848:         if (is_array($id)) {
 849:             extract(array_merge(array('id' => null), $id));
 850:         }
 851:         extract($this->settings[$Model->alias]);
 852: 
 853:         list($node) = array_values($this->_getNode($Model, $id));
 854: 
 855:         if ($node[$right] == $node[$left] + 1) {
 856:             if ($delete) {
 857:                 return $Model->delete($id);
 858:             }
 859:             $Model->id = $id;
 860:             return $Model->saveField($parent, null);
 861:         } elseif ($node[$parent]) {
 862:             list($parentNode) = array_values($this->_getNode($Model, $node[$parent]));
 863:         } else {
 864:             $parentNode[$right] = $node[$right] + 1;
 865:         }
 866: 
 867:         $db = ConnectionManager::getDataSource($Model->useDbConfig);
 868:         $Model->updateAll(
 869:             array($parent => $db->value($node[$parent], $parent)),
 870:             array($Model->escapeField($parent) => $node[$Model->primaryKey])
 871:         );
 872:         $this->_sync($Model, 1, '-', 'BETWEEN ' . ($node[$left] + 1) . ' AND ' . ($node[$right] - 1));
 873:         $this->_sync($Model, 2, '-', '> ' . ($node[$right]));
 874:         $Model->id = $id;
 875: 
 876:         if ($delete) {
 877:             $Model->updateAll(
 878:                 array(
 879:                     $Model->escapeField($left) => 0,
 880:                     $Model->escapeField($right) => 0,
 881:                     $Model->escapeField($parent) => null
 882:                 ),
 883:                 array($Model->escapeField() => $id)
 884:             );
 885:             return $Model->delete($id);
 886:         }
 887:         $edge = $this->_getMax($Model, $scope, $right, $recursive);
 888:         if ($node[$right] == $edge) {
 889:             $edge = $edge - 2;
 890:         }
 891:         $Model->id = $id;
 892:         return $Model->save(
 893:             array($left => $edge + 1, $right => $edge + 2, $parent => null),
 894:             array('callbacks' => false, 'validate' => false)
 895:         );
 896:     }
 897: 
 898: /**
 899:  * Check if the current tree is valid.
 900:  *
 901:  * Returns true if the tree is valid otherwise an array of (type, incorrect left/right index, message)
 902:  *
 903:  * @param Model $Model Model using this behavior
 904:  * @return mixed true if the tree is valid or empty, otherwise an array of (error type [index, node],
 905:  *  [incorrect left/right index,node id], message)
 906:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::verify
 907:  */
 908:     public function verify(Model $Model) {
 909:         extract($this->settings[$Model->alias]);
 910:         if (!$Model->find('count', array('conditions' => $scope))) {
 911:             return true;
 912:         }
 913:         $min = $this->_getMin($Model, $scope, $left, $recursive);
 914:         $edge = $this->_getMax($Model, $scope, $right, $recursive);
 915:         $errors = array();
 916: 
 917:         for ($i = $min; $i <= $edge; $i++) {
 918:             $count = $Model->find('count', array('conditions' => array(
 919:                 $scope, 'OR' => array($Model->escapeField($left) => $i, $Model->escapeField($right) => $i)
 920:             )));
 921:             if ($count != 1) {
 922:                 if (!$count) {
 923:                     $errors[] = array('index', $i, 'missing');
 924:                 } else {
 925:                     $errors[] = array('index', $i, 'duplicate');
 926:                 }
 927:             }
 928:         }
 929:         $node = $Model->find('first', array(
 930:             'conditions' => array($scope, $Model->escapeField($right) . '< ' . $Model->escapeField($left)),
 931:             'order' => false,
 932:             'recursive' => 0
 933:         ));
 934:         if ($node) {
 935:             $errors[] = array('node', $node[$Model->alias][$Model->primaryKey], 'left greater than right.');
 936:         }
 937: 
 938:         $Model->bindModel(array('belongsTo' => array('VerifyParent' => array(
 939:             'className' => $Model->name,
 940:             'foreignKey' => $parent,
 941:             'fields' => array($Model->primaryKey, $left, $right, $parent)
 942:         ))));
 943: 
 944:         $rows = $Model->find('all', array('conditions' => $scope, 'recursive' => 0));
 945:         foreach ($rows as $instance) {
 946:             if ($instance[$Model->alias][$left] === null || $instance[$Model->alias][$right] === null) {
 947:                 $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
 948:                     'has invalid left or right values');
 949:             } elseif ($instance[$Model->alias][$left] == $instance[$Model->alias][$right]) {
 950:                 $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
 951:                     'left and right values identical');
 952:             } elseif ($instance[$Model->alias][$parent]) {
 953:                 if (!$instance['VerifyParent'][$Model->primaryKey]) {
 954:                     $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
 955:                         'The parent node ' . $instance[$Model->alias][$parent] . ' doesn\'t exist');
 956:                 } elseif ($instance[$Model->alias][$left] < $instance['VerifyParent'][$left]) {
 957:                     $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
 958:                         'left less than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').');
 959:                 } elseif ($instance[$Model->alias][$right] > $instance['VerifyParent'][$right]) {
 960:                     $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
 961:                         'right greater than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').');
 962:                 }
 963:             } elseif ($Model->find('count', array('conditions' => array($scope, $Model->escapeField($left) . ' <' => $instance[$Model->alias][$left], $Model->escapeField($right) . ' >' => $instance[$Model->alias][$right]), 'recursive' => 0))) {
 964:                 $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], 'The parent field is blank, but has a parent');
 965:             }
 966:         }
 967:         if ($errors) {
 968:             return $errors;
 969:         }
 970:         return true;
 971:     }
 972: 
 973: /**
 974:  * Sets the parent of the given node
 975:  *
 976:  * The force parameter is used to override the "don't change the parent to the current parent" logic in the event
 977:  * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this
 978:  * method could be private, since calling save with parent_id set also calls setParent
 979:  *
 980:  * @param Model $Model Model using this behavior
 981:  * @param int|string $parentId Parent record Id
 982:  * @param bool $created True if newly created record else false.
 983:  * @return bool true on success, false on failure
 984:  */
 985:     protected function _setParent(Model $Model, $parentId = null, $created = false) {
 986:         extract($this->settings[$Model->alias]);
 987:         list($node) = array_values($this->_getNode($Model, $Model->id));
 988:         $edge = $this->_getMax($Model, $scope, $right, $recursive, $created);
 989: 
 990:         if (empty($parentId)) {
 991:             $this->_sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
 992:             $this->_sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created);
 993:         } else {
 994:             $values = $this->_getNode($Model, $parentId);
 995: 
 996:             if ($values === false) {
 997:                 return false;
 998:             }
 999:             $parentNode = array_values($values);
1000: 
1001:             if (empty($parentNode) || empty($parentNode[0])) {
1002:                 return false;
1003:             }
1004:             $parentNode = $parentNode[0];
1005: 
1006:             if (($Model->id === $parentId)) {
1007:                 return false;
1008:             } elseif (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) {
1009:                 return false;
1010:             }
1011:             if (empty($node[$left]) && empty($node[$right])) {
1012:                 $this->_sync($Model, 2, '+', '>= ' . $parentNode[$right], $created);
1013:                 $result = $Model->save(
1014:                     array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId),
1015:                     array('validate' => false, 'callbacks' => false)
1016:                 );
1017:                 $Model->data = $result;
1018:             } else {
1019:                 $this->_sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
1020:                 $diff = $node[$right] - $node[$left] + 1;
1021: 
1022:                 if ($node[$left] > $parentNode[$left]) {
1023:                     if ($node[$right] < $parentNode[$right]) {
1024:                         $this->_sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
1025:                         $this->_sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
1026:                     } else {
1027:                         $this->_sync($Model, $diff, '+', 'BETWEEN ' . $parentNode[$right] . ' AND ' . $node[$right], $created);
1028:                         $this->_sync($Model, $edge - $parentNode[$right] + 1, '-', '> ' . $edge, $created);
1029:                     }
1030:                 } else {
1031:                     $this->_sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
1032:                     $this->_sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
1033:                 }
1034:             }
1035:         }
1036:         return true;
1037:     }
1038: 
1039: /**
1040:  * get the maximum index value in the table.
1041:  *
1042:  * @param Model $Model Model Instance.
1043:  * @param string $scope Scoping conditions.
1044:  * @param string $right Right value
1045:  * @param int $recursive Recursive find value.
1046:  * @param bool $created Whether it's a new record.
1047:  * @return int
1048:  */
1049:     protected function _getMax(Model $Model, $scope, $right, $recursive = -1, $created = false) {
1050:         $db = ConnectionManager::getDataSource($Model->useDbConfig);
1051:         if ($created) {
1052:             if (is_string($scope)) {
1053:                 $scope .= " AND " . $Model->escapeField() . " <> ";
1054:                 $scope .= $db->value($Model->id, $Model->getColumnType($Model->primaryKey));
1055:             } else {
1056:                 $scope['NOT'][$Model->alias . '.' . $Model->primaryKey] = $Model->id;
1057:             }
1058:         }
1059:         $name = $Model->escapeField($right);
1060:         list($edge) = array_values($Model->find('first', array(
1061:             'conditions' => $scope,
1062:             'fields' => $db->calculate($Model, 'max', array($name, $right)),
1063:             'recursive' => $recursive,
1064:             'order' => false,
1065:             'callbacks' => false
1066:         )));
1067:         return (empty($edge[$right])) ? 0 : $edge[$right];
1068:     }
1069: 
1070: /**
1071:  * get the minimum index value in the table.
1072:  *
1073:  * @param Model $Model Model instance.
1074:  * @param string $scope Scoping conditions.
1075:  * @param string $left Left value.
1076:  * @param int $recursive Recurursive find value.
1077:  * @return int
1078:  */
1079:     protected function _getMin(Model $Model, $scope, $left, $recursive = -1) {
1080:         $db = ConnectionManager::getDataSource($Model->useDbConfig);
1081:         $name = $Model->escapeField($left);
1082:         list($edge) = array_values($Model->find('first', array(
1083:             'conditions' => $scope,
1084:             'fields' => $db->calculate($Model, 'min', array($name, $left)),
1085:             'recursive' => $recursive,
1086:             'order' => false,
1087:             'callbacks' => false
1088:         )));
1089:         return (empty($edge[$left])) ? 0 : $edge[$left];
1090:     }
1091: 
1092: /**
1093:  * Table sync method.
1094:  *
1095:  * Handles table sync operations, Taking account of the behavior scope.
1096:  *
1097:  * @param Model $Model Model instance.
1098:  * @param int $shift Shift by.
1099:  * @param string $dir Direction.
1100:  * @param array $conditions Conditions.
1101:  * @param bool $created Whether it's a new record.
1102:  * @param string $field Field type.
1103:  * @return void
1104:  */
1105:     protected function _sync(Model $Model, $shift, $dir = '+', $conditions = array(), $created = false, $field = 'both') {
1106:         $ModelRecursive = $Model->recursive;
1107:         extract($this->settings[$Model->alias]);
1108:         $Model->recursive = $recursive;
1109: 
1110:         if ($field === 'both') {
1111:             $this->_sync($Model, $shift, $dir, $conditions, $created, $left);
1112:             $field = $right;
1113:         }
1114:         if (is_string($conditions)) {
1115:             $conditions = array($Model->escapeField($field) . " {$conditions}");
1116:         }
1117:         if (($scope !== '1 = 1' && $scope !== true) && $scope) {
1118:             $conditions[] = $scope;
1119:         }
1120:         if ($created) {
1121:             $conditions['NOT'][$Model->escapeField()] = $Model->id;
1122:         }
1123:         $Model->updateAll(array($Model->escapeField($field) => $Model->escapeField($field) . ' ' . $dir . ' ' . $shift), $conditions);
1124:         $Model->recursive = $ModelRecursive;
1125:     }
1126: 
1127: }
1128: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs