Skip to content

Commit

Permalink
#259: Chaperone Tree Relations (#260)
Browse files Browse the repository at this point in the history
* #259: Added method to load in relations.

* #259: Fixed typo

* #259: Added missing include

* #259: Added attributes to be used with test

* #259: Updated to match schema

* #259: Added initial test

* #259: Updated attribute names

* #259: Updated to use display path

* #259: Added missing include

* #259: Sort results to ensure consistency across database platforms

* #259: More sorting to ensure consistency across database platforms

* Update src/Eloquent/Collection.php

Co-authored-by: Sander Muller <github@scode.nl>

* Update src/Eloquent/Collection.php

Co-authored-by: Sander Muller <github@scode.nl>

* Update tests/Tree/Models/User.php

Co-authored-by: Sander Muller <github@scode.nl>

* Update tests/Tree/Models/User.php

Co-authored-by: Sander Muller <github@scode.nl>

* Update src/Eloquent/Collection.php

Co-authored-by: Sander Muller <github@scode.nl>

* Update tests/Tree/CollectionTest.php

Co-authored-by: Sander Muller <github@scode.nl>

* Fix loading of missing models

* Refactoring

* Refactoring

---------

Co-authored-by: Jonas Staudenmeir <mail@jonas-staudenmeir.de>
Co-authored-by: Sander Muller <github@scode.nl>
  • Loading branch information
3 people authored Oct 6, 2024
1 parent 6924f5d commit 6970d6c
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/Eloquent/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,61 @@ public function toTree($childrenRelation = 'children')

return $tree;
}

/**
* Load parent/ancestor relations already present in the tree.
*
* @return static
*/
public function loadTreePathRelations(): static
{
if ($this->isEmpty()) {
return $this;
}

/** @var TModel $instance */
$instance = $this->first();

$keyName = $instance->getKeyName();
$pathName = $instance->getPathName();
$pathSeparator = $instance->getPathSeparator();

/** @var static<TKey, TModel> $lookup */
$lookup = $this->keyBy($keyName);

/** @var \Illuminate\Support\Collection<int, string> $paths */
$paths = $this->pluck($pathName);

$keys = $paths
->flatMap(
fn (string $path): array => explode($pathSeparator, $path)
)->unique()
->values();

$missing = $keys->diff(
$lookup->modelKeys()
);

$lookup = $lookup->union(
$instance->newQuery()->findMany($missing)->keyBy($keyName)
);

foreach ($this as $model) {
$pathSegments = array_reverse(
explode($pathSeparator, $model->$pathName)
);

$ancestorsAndSelf = array_reduce(
$pathSegments,
fn ($collection, string $key) => $collection->push($lookup[$key]),
$instance->newCollection(),
);

$model->setRelation('ancestors', $ancestorsAndSelf->slice(1));
$model->setRelation('ancestorsAndSelf', $ancestorsAndSelf);
$model->setRelation('parent', count($pathSegments) > 1 ? $lookup[$pathSegments[1]] : null);
}

return $this;
}
}
48 changes: 48 additions & 0 deletions tests/Tree/CollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Staudenmeir\LaravelAdjacencyList\Tests\Tree;

use Illuminate\Support\Facades\DB;
use Staudenmeir\LaravelAdjacencyList\Tests\Tree\Models\User;

class CollectionTest extends TestCase
Expand Down Expand Up @@ -38,4 +39,51 @@ public function testToTreeWithEmptyCollection(): void

$this->assertEmpty($tree);
}

public function testLoadTreePathRelations(): void
{
DB::enableQueryLog();

$tree = User::tree()->get()->loadTreePathRelations();

$this->assertCount(1, DB::getQueryLog());

foreach ($tree as $user) {
$this->assertTrue($user->relationLoaded('ancestors'));
$this->assertTrue($user->relationLoaded('ancestorsAndSelf'));
$this->assertTrue($user->relationLoaded('parent'));

$this->assertEquals($user->ancestors()->orderByDesc('depth')->pluck('id')->all(), $user->ancestors->pluck('id')->all());
$this->assertEquals($user->ancestorsAndSelf()->orderByDesc('depth')->pluck('id')->all(), $user->ancestorsAndSelf->pluck('id')->all());
$this->assertEquals($user->parent()->first()?->id, $user->parent?->id);
}
}

public function testLoadTreePathRelationsWithMissingModels(): void
{
DB::enableQueryLog();

$tree = User::tree()->where('id', '>', 5)->get()->loadTreePathRelations();

$this->assertCount(2, DB::getQueryLog());

foreach ($tree as $user) {
$this->assertTrue($user->relationLoaded('ancestors'));
$this->assertTrue($user->relationLoaded('ancestorsAndSelf'));
$this->assertTrue($user->relationLoaded('parent'));

$this->assertEquals($user->ancestors()->orderByDesc('depth')->pluck('id')->all(), $user->ancestors->pluck('id')->all());
$this->assertEquals($user->ancestorsAndSelf()->orderByDesc('depth')->pluck('id')->all(), $user->ancestorsAndSelf->pluck('id')->all());
$this->assertEquals($user->parent()->first()?->id, $user->parent?->id);
}
}

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

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

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

0 comments on commit 6970d6c

Please sign in to comment.