matthijsbreijer / oaktree
Requires
- php: >=7.0
Requires (Dev)
- phpunit/phpunit: ^6.5
This package is auto-updated.
Last update: 2024-09-28 23:50:53 UTC
README
OakTree 是一种支持基于键遍历和自定义(反)序列化的树数据结构实现。有用的实现可能包括产品树、文件/文件夹结构、路由树等。作为一个包,它从头开始编写,大部分灵感来源于 nicmart/Tree
树结构和树遍历方法
节点创建
在实例化过程中,节点可以传递任何类型的值。
use MatthijsBreijer\OakTree\Node; $node = new Node('data');
获取和设置值
可以使用 Node::getValue()
和 Node::setValue()
来检索和修改节点值。请注意,Node::setValue()
是流畅的,可以链式调用。
var_dump( $node->getValue() ); // string(4) "data" $node->setValue('new data'); var_dump( $node->getValue() ); // string(8) "new data"
向节点添加子节点
可以使用 Node::addChild()
添加一个或多个子节点。该方法流畅,可以链式调用。
$child1 = new Node('child1'); $child2 = new Node('child2'); $node->addChild($child1) ->addChild($child2);
使用特定子键添加子节点到节点
OakTree 跟踪树中节点的键。因此,可以为子节点分配一个键。
$child1 = new Node('child1'); $child2 = new Node('child2'); $node->addChild($child1, 0) ->addChild($child2, 'customKey');
删除子节点
从 $node
的子节点中删除 $child1
和 $child2
实例。Node::removeChild()
是流畅的,可以链式调用。
$node->removeChild($child1) ->removeChild($child2);
获取节点的所有直接子节点
$children = $node->getChildren(); // array(2) [0 => $child1, 'customKey' => $child2]
获取直接子节点节点的所有键
$children = $node->getChildrenKeys(); // array(2) [0, 'customKey']
通过其键获取子节点
OakTree 保持树中节点的键完整。因此,可以通过键获取子节点。
// when $child1 is located at array key '1' $child1 = $node->getChildByKey(1); // when $child1 is located at array key 'customKey' $child1 = $node->getChildByKey('customKey'); // requesting a non-existent key throws \OutOfBoundsException $node->getChildByKey('nonExistentKey');
获取子节点并从其节点树中删除子节点
使用 Node::pop()
可以从树中删除/分离一个节点(及其后代),并独立修改它。
$child1 = new Node('child1'); $child2 = new Node('child2'); $node->addChild($child1) ->addChild($child2); // all examples below produce same result $child1 = $child1->pop(); $child1 = $node->getChildren()[0]->pop(); $child1 = $node->getChildByKey(0)->pop(); // using one of the above examples $node would look as follows var_dump($node->getChildren()); // array(1) [1 => $child2]
设置子节点
请注意,此方法会删除之前存在的子节点。 Node::setChildren()
设置一个(新)子节点数组。该方法流畅,可以链式调用。
$node->setChildren([new Node('a'), new Node('b')]); // or with keys $node->setChildren([0 => new Node('a'), 'customKey' => new Node('b')]);
获取子节点的父节点
$childNode->getParent(); // returns $parent Node $root->getParent(); // $root has no parent Node and returns NULL
获取树的根节点
// all return $root Node when part of the same tree $root->getRoot(); $child1->getRoot(); $grandChild1->getRoot();
树上下文
节点是叶子吗?
叶子是一个没有子节点的节点。
$node->isLeaf(); // bool(true)
节点是子节点吗?
子节点是一个有父节点的节点。
$node->isChild(); // bool(true)
节点是根节点吗?
根节点没有父节点。
$node->isRoot(); // bool(true)
树变更
OakTree 使用 访问者模式 来遍历树。访问者包含应用于树中节点的逻辑。访问者必须实现 MatthijsBreijer\OakTree\Visitor\VisitorInterface
接口。访问者根据其目的返回混合内容。
LeafVisitor
LeafVisitor
返回一个包含树($node
的一个数组,其中 Node::isLeaf()
返回 true)的叶子。
$tree = new Node('root'); $child1 = new Node('child1'); $child2 = new Node('child2'); $tree->addChild($child1) ->addChild($child2); $visitor = new LeafVisitor(); $leafs = $tree->accept($visitor); // array(2) [$child1, $child2]
ClosureVisitor
ClosureVisitor 可以传递一个函数或 \Closure
参数来定义其行为。下面的示例模仿了 LeafVisitor。
$tree = new Node('root'); $child1 = new Node('child1'); $child2 = new Node('child2'); $tree->addChild($child1) ->addChild($child2); $closure = function(NodeInterface $node, VisitorInterface $visitor) { $return = $node->isLeaf() ? [$node] : []; foreach ($node->getChildren() as $key => $child) { $return = array_merge($return, $child->accept($visitor)); } return $return; }; $visitor = new ClosureVisitor($closure); $leafs = $tree->accept($visitor); // array(2) [$child1, $child2]
序列化/反序列化
OakTree 节点具有 Node::toArray()
和 Node::fromArray()
方法,允许对树进行自定义(反)序列化。这可以用于缓存数据,快速在树与 API 或反之之间传递信息。节点还实现了 PHP 的 \JsonSerializable
接口。
基本树到数组转换
// Expose product catalog to a view $product = new Node('Product 1'); $option1 = new Node('Extended package option 1'); $option2 = new Node('Extended package option 2'); $product->addChild($option1) ->addChild($option2); // array(2) [ // 'value' => 'Product 1', // 'children' => array(2) [ // array(3) [ // 'value' => 'Extended package option 1', // 'children' => [] // ], // array(3) [ // 'value' => 'Extended package option 2', // 'children' => [] // ] // ] // ] $array = $product->toArray();
基本数组到树转换
使用上面的示例,可以将结果数组 $array
转换回树,如下所示。
$tree = Node::fromArray($array);
基于闭包的树到数组转换
Node::toArray()
方法接受第二个参数,用于基于闭包转换为数组,这可以进一步进行序列化。
// Build a fictive product catalog tree $product = new Node( new Product('Product 1') ); $option1 = new Node( new Option('Extended package option 1') ); $option2 = new Node( new Option('Extended package option 2') ); $product->addChild($option1) ->addChild($option2); $closure = function($nodeValue) { return [ 'name' => $nodeValue->getName(), 'type' => get_class($nodeValue) ]; }; // array(2) [ // 'value' => array(2) [ // 'name' => 'Product 1', // 'type' => 'Product' // ], // 'children' => array(2) [ // array(2) [ // 'value' => array(2) [ // 'name' => 'Extended package option 1', // 'type' => 'Option' // ], // 'children' => [] // ], // array(2) [ // 'value' => array(2) [ // 'name' => 'Extended package option 2', // 'type' => 'Option' // ], // 'children' => [] // ] // ] // ] $array = $product->toArray($closure);
基于闭包的数组到树转换
使用前一个示例中创建的 $array
变量,可以将数组结果转换回树,如下所示。
$closure = function($value) { $type = $value['type']; $name = $value['name']; return new $type($name); }; $tree = Node::fromArray($array, $closure);
安装
Composer
可以使用以下命令使用 PHP Composer 包管理器安装 OakTree:
composer require matthijsbreijer/oaktree
运行测试
cd vendor/matthijsbreijer/oaktree/tests
phpunit