📄 tree.php
字号:
<?php/* SVN FILE: $Id: tree.php 7118 2008-06-04 20:49:29Z gwoo $ *//** * Tree behavior class. * * Enables a model object to act as a node-based tree. * * PHP versions 4 and 5 * * CakePHP : Rapid Development Framework <http://www.cakephp.org/> * Copyright 2006-2008, Cake Software Foundation, Inc. * 1785 E. Sahara Avenue, Suite 490-204 * Las Vegas, Nevada 89104 * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * * @filesource * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project * @package cake * @subpackage cake.cake.libs.model.behaviors * @since CakePHP v 1.2.0.4487 * @version $Revision: 7118 $ * @modifiedby $LastChangedBy: gwoo $ * @lastmodified $Date: 2008-06-04 13:49:29 -0700 (Wed, 04 Jun 2008) $ * @license http://www.opensource.org/licenses/mit-license.php The MIT License *//** * Short description for file * * Long description for file * * @package cake * @subpackage cake.cake.libs.model.behaviors */class TreeBehavior extends ModelBehavior { var $errors = array(); var $_defaults = array( 'parent' => 'parent_id', 'left' => 'lft', 'right' => 'rght', 'scope' => '1 = 1', 'type' => 'nested', '__parentChange' => false, 'recursive' => -1 ); function setup(&$model, $config = array()) { if (!is_array($config)) { $config = array('type' => $config); } $settings = array_merge($this->_defaults, $config); if (in_array($settings['scope'], $model->getAssociated('belongsTo'))) { $data = $model->getAssociated($settings['scope']); $parent =& $model->{$settings['scope']}; $settings['scope'] = $model->alias . '.' . $data['foreignKey'] . ' = ' . $parent->alias . '.' . $parent->primaryKey; $settings['recursive'] = 0; } $this->settings[$model->alias] = $settings; }/** * @deprecated */ function setScope(&$model, $scope) { trigger_error(__('(TreeBehavior::setScope) Deprecated - Use BehaviorCollection::attach() to re-attach with new settings', true), E_USER_WARNING); $this->settings[$model->alias]['scope'] = $scope; if ($this->settings[$model->alias]['recursive'] < 0) { $this->settings[$model->alias]['recursive'] = 0; } }/** * After save method. Called after all saves * * Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the * parameters to be saved. * * @param AppModel $model * @param boolean $created indicates whether the node just saved was created or updated * @return boolean true on success, false on failure */ function afterSave(&$model, $created) { extract($this->settings[$model->alias]); if ($created) { if ((isset($model->data[$model->alias][$parent])) && $model->data[$model->alias][$parent]) { return $this->_setParent($model, $model->data[$model->alias][$parent], $created); } } elseif ($__parentChange) { $this->settings[$model->alias]['__parentChange'] = false; return $this->_setParent($model, $model->data[$model->alias][$parent]); } }/** * Before delete method. Called before all deletes * * Will delete the current node and all children using the deleteAll method and sync the table * * @param AppModel $model * @return boolean true to continue, false to abort the delete */ function beforeDelete(&$model) { extract($this->settings[$model->alias]); list($name, $data) = array($model->alias, $model->read()); $data = $data[$name]; if (!$data[$right] || !$data[$left]) { return true; } $diff = $data[$right] - $data[$left] + 1; if ($diff > 2) { if (is_string($scope)) { $scope = array($scope); } $scope[]["{$model->alias}.{$left} BETWEEN ? AND ?"] = array($data[$left] + 1, $data[$right] - 1); $model->deleteAll($scope); } $this->__sync($model, $diff, '-', '> ' . $data[$right]); return true; }/** * Before save method. Called before all saves * * Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the * parameters to be saved. For newly created nodes with NO parent the left and right field values are set directly by * this method bypassing the setParent logic. * * @since 1.2 * @param AppModel $model * @return boolean true to continue, false to abort the save */ function beforeSave(&$model) { extract($this->settings[$model->alias]); if (isset($model->data[$model->alias][$model->primaryKey])) { if ($model->data[$model->alias][$model->primaryKey]) { if (!$model->id) { $model->id = $model->data[$model->alias][$model->primaryKey]; } } unset($model->data[$model->alias][$model->primaryKey]); } $this->_addToWhitelist($model, array($left, $right)); if (!$model->id) { if (array_key_exists($parent, $model->data[$model->alias]) && $model->data[$model->alias][$parent]) { $parentNode = $model->find('first', array( 'conditions' => array($scope, $model->escapeField() => $model->data[$model->alias][$parent]), 'fields' => array($model->primaryKey, $right), 'recursive' => $recursive )); if (!$parentNode) { return false; } list($parentNode) = array_values($parentNode); $model->data[$model->alias][$left] = 0; //$parentNode[$right]; $model->data[$model->alias][$right] = 0; //$parentNode[$right] + 1; } else { $edge = $this->__getMax($model, $scope, $right, $recursive); $model->data[$model->alias][$left] = $edge + 1; $model->data[$model->alias][$right] = $edge + 2; } } elseif (array_key_exists($parent, $model->data[$model->alias])) { if ($model->data[$model->alias][$parent] != $model->field($parent)) { $this->settings[$model->alias]['__parentChange'] = true; } if (!$model->data[$model->alias][$parent]) { $model->data[$model->alias][$parent] = null; $this->_addToWhitelist($model, $parent); } else { list($node) = array_values($model->find('first', array( 'conditions' => array($scope,$model->escapeField() => $model->id), 'fields' => array($model->primaryKey, $parent, $left, $right ), 'recursive' => $recursive) )); $parentNode = $model->find('first', array( 'conditions' => array($scope, $model->escapeField() => $model->data[$model->alias][$parent]), 'fields' => array($model->primaryKey, $left, $right), 'recursive' => $recursive )); if (!$parentNode) { return false; } list($parentNode) = array_values($parentNode); if (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) { return false; } elseif ($node[$model->primaryKey] == $parentNode[$model->primaryKey]) { return false; } } } return true; }/** * Get the number of child nodes * * If the direct parameter is set to true, only the direct children are counted (based upon the parent_id field) * If false is passed for the id parameter, all top level nodes are counted, or all nodes are counted. * * @param AppModel $model * @param mixed $id The ID of the record to read or false to read all top level nodes * @param boolean $direct whether to count direct, or all, children * @return integer number of child nodes * @access public */ function childcount(&$model, $id = null, $direct = false) { if (is_array($id)) { extract (array_merge(array('id' => null), $id)); } if ($id === null && $model->id) { $id = $model->id; } elseif (!$id) { $id = null; } extract($this->settings[$model->alias]); if ($direct) { return $model->find('count', array('conditions' => array($scope, $model->escapeField($parent) => $id))); } if ($id === null) { return $model->find('count', array('conditions' => $scope)); } elseif (!empty ($model->data)) { $data = $model->data[$model->alias]; } else { list($data) = array_values($model->find('first', array('conditions' => array($scope, $model->escapeField() => $id), 'recursive' => $recursive))); } return ($data[$right] - $data[$left] - 1) / 2; }/** * Get the child nodes of the current model * * If the direct parameter is set to true, only the direct children are returned (based upon the parent_id field) * If false is passed for the id parameter, top level, or all (depending on direct parameter appropriate) are counted. * * @param AppModel $model * @param mixed $id The ID of the record to read * @param boolean $direct whether to return only the direct, or all, children * @param mixed $fields Either a single string of a field name, or an array of field names * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") defaults to the tree order * @param integer $limit SQL LIMIT clause, for calculating items per page. * @param integer $page Page number, for accessing paged data * @param integer $recursive The number of levels deep to fetch associated records * @return array Array of child nodes * @access public */ function children(&$model, $id = null, $direct = false, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) { if (is_array($id)) { extract (array_merge(array('id' => null), $id)); } $overrideRecursive = $recursive; if ($id === null && $model->id) { $id = $model->id; } elseif (!$id) { $id = null; } $name = $model->alias; extract($this->settings[$model->alias]); if (!is_null($overrideRecursive)) { $recursive = $overrideRecursive; } if (!$order) { $order = $model->alias . '.' . $left . ' asc'; } if ($direct) { $conditions = array($scope, $model->escapeField($parent) => $id); return $model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive')); } if (!$id) { $constraint = $scope; } else { $result = array_values($model->find('first', array('conditions' => array($scope, $model->escapeField() => $id), 'fields' => array($left, $right), 'recursive' => $recursive))); if (!empty($result) && isset($result[0])) { $item = $result[0]; } else { return array(); } $constraint = array($scope, $model->escapeField($right) . ' <' . $item[$right], $model->escapeField($left) . ' >' => $item[$left]); } return $model->find('all', array('conditions' => $constraint, 'fields' => $fields, 'order' => $order, 'limit' => $limit, 'page' => $page, 'recursive' => $recursive)); }/** * A convenience method for returning a hierarchical array used for HTML select boxes * * @param AppModel $model * @param mixed $conditions SQL conditions as a string or as an array('field' =>'value',...) * @param string $keyPath A string path to the key, i.e. "{n}.Post.id" * @param string $valuePath A string path to the value, i.e. "{n}.Post.title" * @param string $spacer The character or characters which will be repeated * @param integer $recursive The number of levels deep to fetch associated records * @return array An associative array of records, where the id is the key, and the display field is the value * @access public */ function generatetreelist(&$model, $conditions = null, $keyPath = null, $valuePath = null, $spacer = '_', $recursive = null) { $overrideRecursive = $recursive; extract($this->settings[$model->alias]); if (!is_null($overrideRecursive)) { $recursive = $overrideRecursive; } if ($keyPath == null && $valuePath == null && $model->hasField($model->displayField)) { $fields = array($model->primaryKey, $model->displayField, $left, $right); } else { $fields = null; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -