paulzi / yii2-nested-intervals
为Yii2实现的嵌套区间行为
v1.1.1
2018-08-06 08:37 UTC
Requires
- php: >=5.4.0
- yiisoft/yii2: ~2.0.0
Requires (Dev)
- phpunit/dbunit: ~1.0
- phpunit/phpunit: ~4.0
README
实现嵌套区间算法,用于在数据库表中存储树。
安装
通过Composer安装
composer require paulzi/yii2-nested-intervals
或添加
"paulzi/yii2-nested-intervals" : "^1.1"
到您的composer.json
文件的require
部分。
迁移示例
单个树迁移
class m150722_150000_single_tree extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('{{%single_tree}}', [ 'id' => Schema::TYPE_PK, 'lft' => Schema::TYPE_INTEGER . ' NOT NULL', 'rgt' => Schema::TYPE_INTEGER . ' NOT NULL', 'depth' => Schema::TYPE_INTEGER . ' NOT NULL', 'name' => Schema::TYPE_STRING . ' NOT NULL', // example field ], $tableOptions); $this->createIndex('lft', '{{%single_tree}}', ['lft', 'rgt']); $this->createIndex('rgt', '{{%single_tree}}', ['rgt']); } }
多个树迁移
class m150722_150100_multiple_tree extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('{{%multiple_tree}}', [ 'id' => Schema::TYPE_PK, 'tree' => Schema::TYPE_INTEGER . ' NULL', 'lft' => Schema::TYPE_INTEGER . ' NOT NULL', 'rgt' => Schema::TYPE_INTEGER . ' NOT NULL', 'depth' => Schema::TYPE_INTEGER . ' NOT NULL', 'name' => Schema::TYPE_STRING . ' NOT NULL', // example field ], $tableOptions); $this->createIndex('lft', '{{%multiple_tree}}', ['tree', 'lft', 'rgt']); $this->createIndex('rgt', '{{%multiple_tree}}', ['tree', 'rgt']); } }
配置
use paulzi\nestedintervals\NestedIntervalsBehavior; class Sample extends \yii\db\ActiveRecord { public function behaviors() { return [ [ 'class' => NestedIntervalsBehavior::className(), // 'treeAttribute' => 'tree', ], ]; } public function transactions() { return [ self::SCENARIO_DEFAULT => self::OP_ALL, ]; } }
您可以设置查询以查找根节点(可选)
class Sample extends \yii\db\ActiveRecord { public static function find() { return new SampleQuery(get_called_class()); } }
查询类
use paulzi\nestedintervals\NestedIntervalsQueryTrait; class SampleQuery extends \yii\db\ActiveQuery { use NestedIntervalsQueryTrait; }
选项
$treeAttribute = null
- 在表架构中设置多树树属性。$leftAttribute = 'lft'
- 表架构中的左属性。$rightAttribute = 'rgt'
- 表架构中的右属性。$depthAttribute = 'depth'
- 表架构中的深度属性(注意:它必须是带符号的整数)。$range = [0, 2147483647]
- 区间大小。默认值是32位PHP和标准带符号整数字段的最大值。如果您有BIGINT左和右列,支持64位表达式数据库和64位PHP版本,您可以使用[0, 9223372036854775807]
(SQLite不支持此)。$amountOptimize = 10
- 插入优化 - 每层的平均子节点数。该值可以是整数或表示每级值的数组。如果级别比属性中指定的深,则从数组中的最后一个级别取值。$reserveFactor = 1
- 决定节点之间间隙大小的系数。默认为1,表示区间大小等于元素本身的大小。如果您经常使用insertBefore()
和insertAfter()
方法,您可以尝试增加此系数以提高效率。$noPrepend = false
- 如果为真,则当向空节点插入时将使用间隙的初始位置。$noAppend = false
- 如果为真,则当向空节点插入时将使用间隙的最终位置。$noInsert = false
- 如果为真,则相邻节点之间不会有间隙。
用法
选择
获取根节点
如果您连接NestedIntervalsQueryTrait
,您可以获取所有根节点
$roots = Sample::find()->roots()->all();
获取节点的祖先
获取节点的祖先
$node11 = Sample::findOne(['name' => 'node 1.1']); $parents = $node11->parents; // via relation $parents = $node11->getParents()->all(); // via query $parents = $node11->getParents(2)->all(); // get 2 levels of ancestors
获取节点的父节点
$node11 = Sample::findOne(['name' => 'node 1.1']); $parent = $node11->parent; // via relation $parent = $node11->getParent()->one(); // via query
获取节点的根节点
$node11 = Sample::findOne(['name' => 'node 1.1']); $root = $node11->root; // via relation $root = $node11->getRoot()->one(); // via query
获取节点的后代
获取节点的所有后代
$node11 = Sample::findOne(['name' => 'node 1.1']); $descendants = $node11->descendants; // via relation $descendants = $node11->getDescendants()->all(); // via query $descendants = $node11->getDescendants(2, true)->all(); // get 2 levels of descendants and self node $descendants = $node11->getDescendants(3, false, true)->all(); // get 3 levels of descendants in back order
为节点及其后代填充children
关系
$node11 = Sample::findOne(['name' => 'node 1.1']); $tree = $node11->populateTree(); // populate all levels $tree = $node11->populateTree(2); // populate 2 levels of descendants
获取节点的子节点
$node11 = Sample::findOne(['name' => 'node 1.1']); $children = $node11->children; // via relation $children = $node11->getChildren()->all(); // via query
获取叶节点
获取节点的所有叶节点
$node11 = Sample::findOne(['name' => 'node 1.1']); $leaves = $node11->leaves; // via relation $leaves = $node11->getLeaves(2)->all(); // get 2 levels of leaves via query
获取相邻节点
获取下一个节点
$node11 = Sample::findOne(['name' => 'node 1.1']); $next = $node11->next; // via relation $next = $node11->getNext()->one(); // via query
获取上一个节点
$node11 = Sample::findOne(['name' => 'node 1.1']); $prev = $node11->prev; // via relation $prev = $node11->getPrev()->one(); // via query
一些检查
$node1 = Sample::findOne(['name' => 'node 1']); $node11 = Sample::findOne(['name' => 'node 1.1']); $node11->isRoot() - return true, if node is root $node11->isLeaf() - return true, if node is leaf $node11->isChildOf($node1) - return true, if node11 is child of $node1
修改
创建根节点
$node11 = new Sample(); $node11->name = 'node 1.1'; $node11->makeRoot()->save();
注意:如果您允许多树且未设置属性tree
,它将自动采用主键值。
将节点作为另一个节点的第一个子节点插入
$node1 = Sample::findOne(['name' => 'node 1']); $node11 = new Sample(); $node11->name = 'node 1.1'; $node11->prependTo($node1)->save(); // inserting new node
将节点作为另一个节点的最后一个子节点插入
$node11 = Sample::findOne(['name' => 'node 1.1']); $node12 = Sample::findOne(['name' => 'node 1.2']); $node12->appendTo($node11)->save(); // move existing node
在另一个节点之前插入节点
$node13 = Sample::findOne(['name' => 'node 1.3']); $node12 = new Sample(); $node12->name = 'node 1.2'; $node12->insertBefore($node13)->save(); // inserting new node
在另一个节点之后插入节点
$node13 = Sample::findOne(['name' => 'node 1.3']); $node14 = Sample::findOne(['name' => 'node 1.4']); $node14->insertAfter($node13)->save(); // move existing node
删除有后代的节点
$node11 = Sample::findOne(['name' => 'node 1.1']); $node11->delete(); // delete node, children come up to the parent $node11->deleteWithChildren(); // delete node and all descendants
优化
对于节点在区间上的均匀分布(慢!)
$node11 = Sample::findOne(['name' => 'node 1.1']); $node11->optimize();