Skip to content

Commit

Permalink
First pre-release ready.
Browse files Browse the repository at this point in the history
  • Loading branch information
Smoren committed Jan 11, 2023
1 parent b8645db commit 45da421
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 95 deletions.
107 changes: 101 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,25 @@ composer require smoren/tree-tools
| [`traverseDepthFirst`](#Traverse-Depth-First) | Iterates a tree using depth-first search | `TreeWalker::traverseDepthFirst($data, $childrenContainerKey)` |
| [`traverseBreadthFirst`](#Traverse-Breadth-First) | Iterates a tree using breadth-first search | `TreeWalker::traverseBreadthFirst($data, $childrenContainerKey)` |

#### Tree Builder
| Reducer | Description | Code Snippet |
|-------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------|
| [`build`](#Build) | Builds a tree from given flat collection | `TreeBuilder::build($collection, $idField, $parentIdField, $childrenContainerField, $itemContainerField)` |

## Usage

### Tree Walker

#### Traverse-Depth-First
#### Traverse Depth First

Iterates a tree like a flat collection using depth-first traversal.

```TreeWalker::traverseDepthFirst(iterable $data, ?string $childrenContainerKey = null): Generator```

If `$childrenContainerKey` is not null looks for children items using by this key only.

Otherwise, considers any subarray to contain children.

```TreeWalker::traverseDepthFirst(iterable $data, ?string $childrenContainerKey = null): Generator```

```php
use Smoren\TreeTools\TreeWalker;

Expand Down Expand Up @@ -70,16 +75,16 @@ var_dump($result);
// [1, 11, 12, 121, 122, 2, 21, 3]
```

#### Traverse-Breadth-First
#### Traverse Breadth First

Iterates a tree like a flat collection using depth-breadth traversal.

```TreeWalker::traverseBreadthFirst(iterable $data, ?string $childrenContainerKey = null): Generator```

If `$childrenContainerKey` is not null looks for children items using by this key only.

Otherwise, considers any subarray to contain children.

```TreeWalker::traverseBreadthFirst(iterable $data, ?string $childrenContainerKey = null): Generator```

```php
use Smoren\TreeTools\TreeWalker;

Expand Down Expand Up @@ -115,6 +120,96 @@ var_dump($result);
// [1, 2, 3, 11, 12, 21, 121, 122]
```

### Tree Builder

#### Build

Builds a tree from given flat collection of items with relations.

```
TreeBuilder::build(
iterable $collection,
string $idField = 'id',
string $parentIdField = 'parent_id',
string $childrenContainerField = 'children',
string $itemContainerField = 'item'
): array
```

```php
use Smoren\TreeTools\TreeBuilder;

$input = [
['id' => 1, 'name' => 'Item 1', 'parent_id' => null],
['id' => 2, 'name' => 'Item 1.1', 'parent_id' => 1],
['id' => 3, 'name' => 'Item 1.2', 'parent_id' => 1],
['id' => 4, 'name' => 'Item 1.1.1', 'parent_id' => 2],
['id' => 5, 'name' => 'Item 2', 'parent_id' => null],
['id' => 6, 'name' => 'Item 3', 'parent_id' => null],
['id' => 7, 'name' => 'Item 3.1', 'parent_id' => 6],
['id' => 8, 'name' => 'Item 3.2', 'parent_id' => 6],
];

$tree = TreeBuilder::build($input);
print_r($tree);
/*
[
[
'id' => 1,
'name' => 'Item 1',
'parent_id' => null,
'children' => [
[
'id' => 2,
'name' => 'Item 1.1',
'parent_id' => 1,
'children' => [
[
'id' => 4,
'name' => 'Item 1.1.1',
'parent_id' => 2,
'children' => [],
]
],
],
[
'id' => 3,
'name' => 'Item 1.2',
'parent_id' => 1,
'children' => [],
],
],
],
[
'id' => 5,
'name' => 'Item 2',
'parent_id' => null,
'children' => [],
],
[
'id' => 6,
'name' => 'Item 3',
'parent_id' => null,
'children' => [
[
'id' => 7,
'name' => 'Item 3.1',
'parent_id' => 6,
'children' => [],
],
[
'id' => 8,
'name' => 'Item 3.2',
'parent_id' => 6,
'children' => [],
],
]
],
]
*/

```

## Unit testing
```
composer install
Expand Down
19 changes: 13 additions & 6 deletions src/TreeMaker.php → src/TreeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@
namespace Smoren\TreeTools;

use Smoren\TypeTools\MapAccess;
use ArrayAccess;
use stdClass;

class TreeMaker
class TreeBuilder
{
/**
* @param iterable<mixed> $list
* Builds a tree from given flat collection of items with relations.
*
* @param iterable<mixed> $collection
* @param string $idField
* @param string $parentIdField
* @param string $childrenContainerField
* @param string $itemContainerField
*
* @return array<mixed>
*/
public static function fromList(
iterable $list,
public static function build(
iterable $collection,
string $idField = 'id',
string $parentIdField = 'parent_id',
string $childrenContainerField = 'children',
Expand All @@ -29,7 +30,7 @@ public static function fromList(
$result = [];
$map = [];

foreach($list as $item) {
foreach($collection as $item) {
$map[MapAccess::get($item, $idField)] = static::wrapItem(
$item,
$childrenContainerField,
Expand All @@ -50,6 +51,8 @@ public static function fromList(
}

/**
* Returns value of parent relation.
*
* @param mixed $item
* @param string $parentIdField
* @param string $itemContainerField
Expand All @@ -69,6 +72,8 @@ protected static function getParentId($item, string $parentIdField, string $item
}

/**
* Returns children container of given item.
*
* @param mixed $item
* @param string $childrenContainerField
*
Expand All @@ -84,6 +89,8 @@ protected static function &getChildrenContainer(&$item, string $childrenContaine
}

/**
* Wraps collection item for tree representation.
*
* @param mixed $item
* @param string $childrenContainerField
* @param string $itemContainerField
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php

namespace Smoren\TreeTools\Tests\Unit\TreeMaker;
namespace Smoren\TreeTools\Tests\Unit\TreeBuilder;

use Smoren\TreeTools\TreeMaker;
use Smoren\TreeTools\TreeBuilder;

class ArrayTest extends \Codeception\Test\Unit
{
Expand All @@ -15,7 +15,7 @@ class ArrayTest extends \Codeception\Test\Unit
public function testWithoutParentIds(array $input, array $expected): void
{
// When
$result = TreeMaker::fromList($input);
$result = TreeBuilder::build($input);

// Then
$this->assertEquals($expected, $result);
Expand All @@ -33,59 +33,59 @@ public function dataProviderForWithoutParentIds(): array
],
[
[
['id' => 1, 'name' => 'Элемент 1'],
['id' => 2, 'name' => 'Элемент 1.1', 'parent_id' => 1],
['id' => 3, 'name' => 'Элемент 1.2', 'parent_id' => 1],
['id' => 4, 'name' => 'Элемент 1.1.1', 'parent_id' => 2],
['id' => 5, 'name' => 'Элемент 2'],
['id' => 6, 'name' => 'Элемент 3'],
['id' => 7, 'name' => 'Элемент 3.1', 'parent_id' => 6],
['id' => 8, 'name' => 'Элемент 3.2', 'parent_id' => 6],
['id' => 1, 'name' => 'Item 1'],
['id' => 2, 'name' => 'Item 1.1', 'parent_id' => 1],
['id' => 3, 'name' => 'Item 1.2', 'parent_id' => 1],
['id' => 4, 'name' => 'Item 1.1.1', 'parent_id' => 2],
['id' => 5, 'name' => 'Item 2'],
['id' => 6, 'name' => 'Item 3'],
['id' => 7, 'name' => 'Item 3.1', 'parent_id' => 6],
['id' => 8, 'name' => 'Item 3.2', 'parent_id' => 6],
],
[
[
'id' => 1,
'name' => 'Элемент 1',
'name' => 'Item 1',
'children' => [
[
'id' => 2,
'name' => 'Элемент 1.1',
'name' => 'Item 1.1',
'parent_id' => 1,
'children' => [
[
'id' => 4,
'name' => 'Элемент 1.1.1',
'name' => 'Item 1.1.1',
'parent_id' => 2,
'children' => [],
]
],
],
[
'id' => 3,
'name' => 'Элемент 1.2',
'name' => 'Item 1.2',
'parent_id' => 1,
'children' => [],
],
]
],
[
'id' => 5,
'name' => 'Элемент 2',
'name' => 'Item 2',
'children' => [],
],
[
'id' => 6,
'name' => 'Элемент 3',
'name' => 'Item 3',
'children' => [
[
'id' => 7,
'name' => 'Элемент 3.1',
'name' => 'Item 3.1',
'parent_id' => 6,
'children' => [],
],
[
'id' => 8,
'name' => 'Элемент 3.2',
'name' => 'Item 3.2',
'parent_id' => 6,
'children' => [],
],
Expand All @@ -105,7 +105,7 @@ public function dataProviderForWithoutParentIds(): array
public function testWithNullableParentIds(array $input, array $expected): void
{
// When
$result = TreeMaker::fromList($input);
$result = TreeBuilder::build($input);

// Then
$this->assertEquals($expected, $result);
Expand All @@ -123,62 +123,62 @@ public function dataProviderForWithNullableParentIds(): array
],
[
[
['id' => 1, 'name' => 'Элемент 1', 'parent_id' => null],
['id' => 2, 'name' => 'Элемент 1.1', 'parent_id' => 1],
['id' => 3, 'name' => 'Элемент 1.2', 'parent_id' => 1],
['id' => 4, 'name' => 'Элемент 1.1.1', 'parent_id' => 2],
['id' => 5, 'name' => 'Элемент 2', 'parent_id' => null],
['id' => 6, 'name' => 'Элемент 3', 'parent_id' => null],
['id' => 7, 'name' => 'Элемент 3.1', 'parent_id' => 6],
['id' => 8, 'name' => 'Элемент 3.2', 'parent_id' => 6],
['id' => 1, 'name' => 'Item 1', 'parent_id' => null],
['id' => 2, 'name' => 'Item 1.1', 'parent_id' => 1],
['id' => 3, 'name' => 'Item 1.2', 'parent_id' => 1],
['id' => 4, 'name' => 'Item 1.1.1', 'parent_id' => 2],
['id' => 5, 'name' => 'Item 2', 'parent_id' => null],
['id' => 6, 'name' => 'Item 3', 'parent_id' => null],
['id' => 7, 'name' => 'Item 3.1', 'parent_id' => 6],
['id' => 8, 'name' => 'Item 3.2', 'parent_id' => 6],
],
[
[
'id' => 1,
'name' => 'Элемент 1',
'name' => 'Item 1',
'parent_id' => null,
'children' => [
[
'id' => 2,
'name' => 'Элемент 1.1',
'name' => 'Item 1.1',
'parent_id' => 1,
'children' => [
[
'id' => 4,
'name' => 'Элемент 1.1.1',
'name' => 'Item 1.1.1',
'parent_id' => 2,
'children' => [],
]
],
],
[
'id' => 3,
'name' => 'Элемент 1.2',
'name' => 'Item 1.2',
'parent_id' => 1,
'children' => [],
],
],
],
[
'id' => 5,
'name' => 'Элемент 2',
'name' => 'Item 2',
'parent_id' => null,
'children' => [],
],
[
'id' => 6,
'name' => 'Элемент 3',
'name' => 'Item 3',
'parent_id' => null,
'children' => [
[
'id' => 7,
'name' => 'Элемент 3.1',
'name' => 'Item 3.1',
'parent_id' => 6,
'children' => [],
],
[
'id' => 8,
'name' => 'Элемент 3.2',
'name' => 'Item 3.2',
'parent_id' => 6,
'children' => [],
],
Expand Down
Loading

0 comments on commit 45da421

Please sign in to comment.