Skip to content

Commit

Permalink
Add toTree() method
Browse files Browse the repository at this point in the history
  • Loading branch information
staudenmeir committed Aug 15, 2021
1 parent 8e680c9 commit 6edd164
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 0 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Supports Laravel 5.5.29+.
- [Depth](#depth)
- [Path](#path)
- [Custom Paths](#custom-paths)
- [Nested Results](#nested-results)

### Getting Started

Expand Down Expand Up @@ -328,6 +329,50 @@ echo $descendantsAndSelf[1]->slug_path; // user-1/user-2
echo $descendantsAndSelf[2]->slug_path; // user-1/user-2/user-3
```

# Nested Results

Use the `toTree()` method on the result collection to generate a nested tree:

```php
$users = User::tree()->get();

$tree = $users->toTree();
```

This recursively sets `children` and `parent` relationships:

```json
[
{
"id": 1,
"children": [
{
"id": 2,
"children": [
{
"id": 4,
"children": [],
"parent": {
"id": 2
}
}
],
"parent": {
"id": 1
}
},
{
"id": 3,
"children": [],
"parent": {
"id": 1
}
}
]
}
]
```

## Contributing

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) and [CODE OF CONDUCT](.github/CODE_OF_CONDUCT.md) for details.
38 changes: 38 additions & 0 deletions src/Eloquent/Collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Staudenmeir\LaravelAdjacencyList\Eloquent;

use Illuminate\Database\Eloquent\Collection as Base;

class Collection extends Base
{
public function toTree($childrenRelation = 'children', $parentRelation = 'parent')
{
if ($this->isEmpty()) {
return $this;
}

$parentKeyName = $this->first()->getParentKeyName();
$localKeyName = $this->first()->getLocalKeyName();
$depthName = $this->first()->getDepthName();

$depths = $this->pluck($depthName);

$tree = new static(
$this->where($depthName, $depths->min())->values()
);

$itemsByParentKey = $this->groupBy($parentKeyName);
$itemsByLocalKey = $this->keyBy($localKeyName);

foreach ($this->items as $item) {
$item->setRelation($childrenRelation, $itemsByParentKey[$item->$localKeyName] ?? new static());

if (isset($item->$parentKeyName, $itemsByLocalKey[$item->$parentKeyName])) {
$item->setRelation($parentRelation, $itemsByLocalKey[$item->$parentKeyName]);
}
}

return $tree;
}
}
11 changes: 11 additions & 0 deletions src/Eloquent/HasRecursiveRelationships.php
Original file line number Diff line number Diff line change
Expand Up @@ -416,4 +416,15 @@ public function newEloquentBuilder($query)
{
return new \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder($query);
}

/**
* Create a new Eloquent Collection instance.
*
* @param array $models
* @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection
*/
public function newCollection(array $models = [])
{
return new Collection($models);
}
}
47 changes: 47 additions & 0 deletions tests/CollectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Tests;

use Tests\Models\User;

class CollectionTest extends TestCase
{
public function testToTree()
{
$users = User::tree()->orderBy('id')->get();

$tree = $users->toTree();

$this->assertEquals([1, 11], $tree->pluck('id')->all());
$this->assertFalse($tree[0]->relationLoaded('parent'));
$this->assertEquals([2, 3, 4], $tree[0]->children->pluck('id')->all());
$this->assertTrue($tree[0]->children[0]->relationLoaded('parent'));
$this->assertEquals(1, $tree[0]->children[0]->parent->id);
$this->assertEquals([5], $tree[0]->children[0]->children->pluck('id')->all());
$this->assertEquals([8], $tree[0]->children[0]->children[0]->children->pluck('id')->all());
$this->assertEquals([12], $tree[1]->children->pluck('id')->all());
}

public function testToTreeWithDescendants()
{
$users = User::find(1)->descendants()->orderBy('id')->get();

$tree = $users->toTree();

$this->assertEquals([2, 3, 4], $tree->pluck('id')->all());
$this->assertFalse($tree[0]->relationLoaded('parent'));
$this->assertEquals([5], $tree[0]->children->pluck('id')->all());
$this->assertTrue($tree[0]->children[0]->relationLoaded('parent'));
$this->assertEquals(2, $tree[0]->children[0]->parent->id);
$this->assertEquals([8], $tree[0]->children[0]->children->pluck('id')->all());
}

public function testToTreeWithEmptyCollection()
{
$users = User::tree(1)->where('id', 0)->get();

$tree = $users->toTree();

$this->assertEmpty($tree);
}
}

0 comments on commit 6edd164

Please sign in to comment.