Skip to content

Commit

Permalink
Support cycle detection with UUIDs on PostgreSQL
Browse files Browse the repository at this point in the history
  • Loading branch information
staudenmeir committed May 7, 2023
1 parent 3928fa7 commit 82db6aa
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 35 deletions.
10 changes: 7 additions & 3 deletions src/Query/Grammars/PostgresGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,14 @@ public function compilePivotColumnNullValue(string $type): string
*/
public function compileCycleDetection(string $localKey, string $path): string
{
$localKey = $this->wrap($localKey);
$path = $this->wrap($path);
$wrappedLocalKey = $this->wrap($localKey);
$wrappedPath = $this->wrap($path);

return "$localKey = any($path)";
if ($this->model->isIntegerAttribute($localKey)) {
return "$wrappedLocalKey = any($wrappedPath)";
}

return "$wrappedLocalKey::varchar = any($wrappedPath)";
}

/**
Expand Down
44 changes: 36 additions & 8 deletions tests/Graph/AncestorsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,35 @@ public function testLazyLoading()
);
}

public function testLazyLoadingWithCycleDetection()
/**
* @dataProvider cycleDetectionClassProvider
*/
public function testLazyLoadingWithCycleDetection(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$ancestors = NodeWithCycleDetection::find(12)->ancestors;
$ancestors = $class::find(12)->ancestors;

$this->assertEquals([14, 13, 12], $ancestors->pluck('id')->all());
$this->assertEquals([-1, -2, -3], $ancestors->pluck('depth')->all());
}

public function testLazyLoadingWithCycleDetectionAndStart()
/**
* @dataProvider cycleDetectionAndStartClassProvider
*/
public function testLazyLoadingWithCycleDetectionAndStart(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$ancestors = NodeWithCycleDetectionAndStart::find(12)->ancestors;
$ancestors = $class::find(12)->ancestors;

$this->assertEquals([14, 13, 12, 14], $ancestors->pluck('id')->all());
$this->assertEquals([-1, -2, -3, -4], $ancestors->pluck('depth')->all());
Expand Down Expand Up @@ -125,23 +139,37 @@ public function testEagerLoading()
);
}

public function testEagerLoadingWithCycleDetection()
/**
* @dataProvider cycleDetectionClassProvider
*/
public function testEagerLoadingWithCycleDetection(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$nodes = NodeWithCycleDetection::with([
$nodes = $class::with([
'ancestors' => fn (Ancestors $query) => $query->orderByDesc('depth'),
])->findMany([12, 13, 14]);

$this->assertEquals([14, 13, 12], $nodes[0]->ancestors->pluck('id')->all());
$this->assertEquals([-1, -2, -3], $nodes[0]->ancestors->pluck('depth')->all());
}

public function testEagerLoadingWithCycleDetectionAndStart()
/**
* @dataProvider cycleDetectionAndStartClassProvider
*/
public function testEagerLoadingWithCycleDetectionAndStart(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$nodes = NodeWithCycleDetectionAndStart::with([
$nodes = $class::with([
'ancestors' => fn (Ancestors $query) => $query->orderByDesc('depth'),
])->findMany([12, 13, 14]);

Expand Down
44 changes: 36 additions & 8 deletions tests/Graph/DescendantsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,35 @@ public function testLazyLoading()
);
}

public function testLazyLoadingWithCycleDetection()
/**
* @dataProvider cycleDetectionClassProvider
*/
public function testLazyLoadingWithCycleDetection(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$descendants = NodeWithCycleDetection::find(12)->descendants;
$descendants = $class::find(12)->descendants;

$this->assertEquals([13, 14, 12], $descendants->pluck('id')->all());
$this->assertEquals([1, 2, 3], $descendants->pluck('depth')->all());
}

public function testLazyLoadingWithCycleDetectionAndStart()
/**
* @dataProvider cycleDetectionAndStartClassProvider
*/
public function testLazyLoadingWithCycleDetectionAndStart(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$descendants = NodeWithCycleDetectionAndStart::find(12)->descendants;
$descendants = $class::find(12)->descendants;

$this->assertEquals([13, 14, 12, 13], $descendants->pluck('id')->all());
$this->assertEquals([1, 2, 3, 4], $descendants->pluck('depth')->all());
Expand Down Expand Up @@ -133,23 +147,37 @@ public function testEagerLoading()
);
}

public function testEagerLoadingWithCycleDetection()
/**
* @dataProvider cycleDetectionClassProvider
*/
public function testEagerLoadingWithCycleDetection(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$nodes = NodeWithCycleDetection::with([
$nodes = $class::with([
'descendants' => fn (Descendants $query) => $query->orderBy('depth'),
])->findMany([12, 13, 14]);

$this->assertEquals([13, 14, 12], $nodes[0]->descendants->pluck('id')->all());
$this->assertEquals([1, 2, 3], $nodes[0]->descendants->pluck('depth')->all());
}

public function testEagerLoadingWithCycleDetectionAndStart()
/**
* @dataProvider cycleDetectionAndStartClassProvider
*/
public function testEagerLoadingWithCycleDetectionAndStart(string $class, array $exclusions)
{
if (in_array($this->database, $exclusions)) {
$this->markTestSkipped();
}

$this->seedCycle();

$nodes = NodeWithCycleDetectionAndStart::with([
$nodes = $class::with([
'descendants' => fn (Descendants $query) => $query->orderBy('depth')
])->findMany([12, 13, 14]);

Expand Down
2 changes: 2 additions & 0 deletions tests/Graph/Models/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class Node extends Model
}
use SoftDeletes;

protected $table = 'nodes';

public function getPivotTableName(): string
{
return 'edges';
Expand Down
2 changes: 0 additions & 2 deletions tests/Graph/Models/NodeWithCycleDetection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

class NodeWithCycleDetection extends Node
{
protected $table = 'nodes';

public function enableCycleDetection(): bool
{
return true;
Expand Down
21 changes: 21 additions & 0 deletions tests/Graph/Models/NodeWithUuid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Staudenmeir\LaravelAdjacencyList\Tests\Graph\Models;

class NodeWithUuid extends Node
{
public function getParentKeyName(): string
{
return 'parent_uuid';
}

public function getChildKeyName(): string
{
return 'child_uuid';
}

public function getLocalKeyName(): string
{
return 'uuid';
}
}
11 changes: 11 additions & 0 deletions tests/Graph/Models/NodeWithUuidAndCycleDetection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Staudenmeir\LaravelAdjacencyList\Tests\Graph\Models;

class NodeWithUuidAndCycleDetection extends NodeWithUuid
{
public function enableCycleDetection(): bool
{
return true;
}
}
11 changes: 11 additions & 0 deletions tests/Graph/Models/NodeWithUuidAndCycleDetectionAndStart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Staudenmeir\LaravelAdjacencyList\Tests\Graph\Models;

class NodeWithUuidAndCycleDetectionAndStart extends NodeWithUuidAndCycleDetection
{
public function includeCycleStart(): bool
{
return true;
}
}
Loading

0 comments on commit 82db6aa

Please sign in to comment.