diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
new file mode 100644
index 0000000..e9c4015
--- /dev/null
+++ b/.github/workflows/run-tests.yml
@@ -0,0 +1,43 @@
+name: Unit Tests
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - "*"
+ schedule:
+ - cron: '0 0 * * *'
+
+jobs:
+ php-tests:
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ env:
+ COMPOSER_NO_INTERACTION: 1
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php: [8.3, 8.2]
+
+ name: P${{ matrix.php }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: none
+ tools: composer:v2
+
+ - name: Install dependencies
+ run: |
+ composer install -o --quiet
+
+ - name: Execute Unit Tests
+ run: composer test
diff --git a/.gitignore b/.gitignore
index 8879189..6bef520 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
composer.phar
composer.lock
.DS_Store
+.phpunit.result.cache
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d7e4828..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-language: php
-
-php:
- - 5.5
- - 5.6
- - 7.0
-
-before_script:
- - curl -s http://getcomposer.org/installer | php
- - php composer.phar install --dev
-
-script: phpunit
\ No newline at end of file
diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
index e1d837b..dcb7bb0 100644
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,4 +1,14 @@
-### 4.2.8
+### 4.3.4
+* Support Laravel 5.8
+
+### 4.3.3
+* Support Laravel 5.7
+
+### 4.3.2
+* Support Laravel 5.6
+* Added `nestedSet` and `dropNestedSet` blueprint macros
+
+### 4.3.0
* Support Laravel 5.5
* Added `fixSubtree` and `rebuildSubtree` methods
* Increased performance of tree rebuilding
@@ -104,4 +114,4 @@
### 1.1.0
* `Collection::toDictionary` is now obsolete. Use `Collection::groupBy`.
-* Laravel 4.2 is required
\ No newline at end of file
+* Laravel 4.2 is required
diff --git a/README.markdown b/README.markdown
index 06ba4d7..f4d2d51 100644
--- a/README.markdown
+++ b/README.markdown
@@ -4,17 +4,18 @@
[](https://packagist.org/packages/kalnoy/nestedset)
[](https://packagist.org/packages/kalnoy/nestedset)
-This is a Laravel 4-5 package for working with trees in relational databases.
-
-* **Laravel 5.2, 5.3, 5.4, 5.5** is supported since v4
+This is a Laravel 4-11 package for working with trees in relational databases.
+
+* **Laravel 11.0** is supported since v7
+* **Laravel 10.0** is supported since v6.0.2
+* **Laravel 9.0** is supported since v6.0.1
+* **Laravel 8.0** is supported since v6.0.0
+* **Laravel 5.7, 5.8, 6.0, 7.0** is supported since v5
+* **Laravel 5.5, 5.6** is supported since v4.3
+* **Laravel 5.2, 5.3, 5.4** is supported since v4
* **Laravel 5.1** is supported in v3
* **Laravel 4** is supported in v2
-Although this project is completely free for use, I appreciate any support!
-
-- __[Donate via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5TJUM7FYU5VR2)__
-- My Visa: 4276 0700 1073 4244
-
__Contents:__
- [Theory](#what-are-nested-sets)
@@ -384,6 +385,9 @@ position.
Various constraints that can be applied to the query builder:
- __whereIsRoot()__ to get only root nodes;
+- __hasParent()__ to get non-root nodes;
+- __whereIsLeaf()__ to get only leaves;
+- __hasChildren()__ to get non-leave nodes;
- __whereIsAfter($id)__ to get every node (not just siblings) that are after a node
with specified id;
- __whereIsBefore($id)__ to get every node that is before a node with specified id.
@@ -571,17 +575,17 @@ protected function getScopeAttributes()
}
```
-But now in order to execute some custom query, you need to provide attributes
+But now, in order to execute some custom query, you need to provide attributes
that are used for scoping:
```php
MenuItem::scoped([ 'menu_id' => 5 ])->withDepth()->get(); // OK
MenuItem::descendantsOf($id)->get(); // WRONG: returns nodes from other scope
-MenuItem::scoped([ 'menu_id' => 5 ])->fixTree();
+MenuItem::scoped([ 'menu_id' => 5 ])->fixTree(); // OK
```
When requesting nodes using model instance, scopes applied automatically based
-on the attributes of that model. See examples:
+on the attributes of that model:
```php
$node = MenuItem::findOrFail($id);
@@ -595,12 +599,13 @@ To get scoped query builder using instance:
$node->newScopedQuery();
```
-Note, that scoping is not required when retrieving model by primary key
-(since the key is unique):
+#### Scoping and eager loading
+
+Always use scoped query when eager loading:
```php
-$node = MenuItem::findOrFail($id); // OK
-$node = MenuItem::scoped([ 'menu_id' => 5 ])->findOrFail(); // OK, but redundant
+MenuItem::scoped([ 'menu_id' => 5])->with('descendants')->findOrFail($id); // OK
+MenuItem::with('descendants')->findOrFail($id); // WRONG
```
Requirements
@@ -625,7 +630,21 @@ composer require kalnoy/nestedset
#### The schema
-You can use a method to add needed columns with default names:
+For Laravel 5.5 and above users:
+
+```php
+Schema::create('table', function (Blueprint $table) {
+ ...
+ $table->nestedSet();
+});
+
+// To drop columns
+Schema::table('table', function (Blueprint $table) {
+ $table->dropNestedSet();
+});
+```
+
+For prior Laravel versions:
```php
...
diff --git a/composer.json b/composer.json
index efa2656..9f8d6c1 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "kalnoy/nestedset",
- "description": "Nested Set Model for Laravel 4-5",
+ "description": "Nested Set Model for Laravel 5.7 and up",
"keywords": ["laravel", "nested sets", "nsm", "database", "hierarchy"],
"license": "MIT",
@@ -12,10 +12,10 @@
],
"require": {
- "php": ">=5.5.9",
- "illuminate/support": "5.2 - 5.5",
- "illuminate/database": "5.2 - 5.5",
- "illuminate/events": "5.2 - 5.5"
+ "php": "^8.2",
+ "illuminate/support": "^11.0",
+ "illuminate/database": "^11.0",
+ "illuminate/events": "^11.0"
},
"autoload": {
@@ -24,15 +24,33 @@
}
},
+ "autoload-dev": {
+ "psr-4": {
+ "Kalnoy\\Nestedset\\Tests\\": "tests/"
+ }
+ },
+
"require-dev": {
- "phpunit/phpunit": "4.8.*"
+ "phpunit/phpunit": "10.*"
},
"minimum-stability": "dev",
+ "prefer-stable": true,
"extra": {
"branch-alias": {
- "dev-master": "v4.2.x-dev"
+ "dev-master": "v5.0.x-dev"
+ },
+
+ "laravel": {
+ "providers": [
+ "Kalnoy\\Nestedset\\NestedSetServiceProvider"
+ ]
}
+ },
+ "scripts": {
+ "test": [
+ "@php ./vendor/bin/phpunit"
+ ]
}
}
diff --git a/phpunit.php b/phpunit.php
index a9fa67a..7f72aff 100644
--- a/phpunit.php
+++ b/phpunit.php
@@ -8,4 +8,5 @@
$capsule->bootEloquent();
$capsule->setAsGlobal();
-include __DIR__.'/tests/models/Category.php';
\ No newline at end of file
+include __DIR__.'/tests/models/Category.php';
+include __DIR__.'/tests/models/MenuItem.php';
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
index 80d7085..8021fb0 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,24 +1,21 @@
-
-
-
-
- ./tests/
-
-
-
-
-
- ./src
-
-
-
\ No newline at end of file
+
+
+
+
+ ./tests/
+
+
+
+
+ ./src
+
+
+
diff --git a/src/AncestorsRelation.php b/src/AncestorsRelation.php
index 089e382..b59fba2 100644
--- a/src/AncestorsRelation.php
+++ b/src/AncestorsRelation.php
@@ -15,7 +15,8 @@ public function addConstraints()
{
if ( ! static::$constraints) return;
- $this->query->whereAncestorOf($this->parent)->defaultOrder();
+ $this->query->whereAncestorOf($this->parent)
+ ->applyNestedSetScope();
}
/**
diff --git a/src/BaseRelation.php b/src/BaseRelation.php
index b09e031..7f82f29 100644
--- a/src/BaseRelation.php
+++ b/src/BaseRelation.php
@@ -111,28 +111,15 @@ public function initRelation(array $models, $relation)
return $models;
}
- /**
- * @param EloquentBuilder $query
- * @param EloquentBuilder $parent
- * @param array $columns
- *
- * @return mixed
- */
- public function getRelationQuery(
- EloquentBuilder $query, EloquentBuilder $parent,
- $columns = [ '*' ]
- ) {
- return $this->getRelationExistenceQuery($query, $parent, $columns);
- }
-
/**
* Get a relationship join table hash.
*
+ * @param bool $incrementJoinCount
* @return string
*/
- public function getRelationCountHash()
+ public function getRelationCountHash($incrementJoinCount = true)
{
- return 'nested_set_'.self::$selfJoinCount++;
+ return 'nested_set_'.($incrementJoinCount ? static::$selfJoinCount++ : static::$selfJoinCount);
}
/**
@@ -154,10 +141,6 @@ public function getResults()
*/
public function addEagerConstraints(array $models)
{
- $model = reset($models);
-
- $this->query = $model->newScopedQuery();
-
$this->query->whereNested(function (Builder $inner) use ($models) {
// We will use this query in order to apply constraints to the
// base query builder
@@ -207,4 +190,16 @@ protected function matchForModel(Model $model, EloquentCollection $results)
return $result;
}
-}
\ No newline at end of file
+
+ /**
+ * Get the plain foreign key.
+ *
+ * @return mixed
+ */
+ public function getForeignKeyName()
+ {
+ // Return a stub value for relation
+ // resolvers which need this function.
+ return NestedSet::PARENT_ID;
+ }
+}
diff --git a/src/DescendantsRelation.php b/src/DescendantsRelation.php
index 5240d3a..4c6457d 100644
--- a/src/DescendantsRelation.php
+++ b/src/DescendantsRelation.php
@@ -17,7 +17,8 @@ public function addConstraints()
{
if ( ! static::$constraints) return;
- $this->query->whereDescendantOf($this->parent);
+ $this->query->whereDescendantOf($this->parent)
+ ->applyNestedSetScope();
}
/**
diff --git a/src/NestedSetServiceProvider.php b/src/NestedSetServiceProvider.php
new file mode 100644
index 0000000..b4516f7
--- /dev/null
+++ b/src/NestedSetServiceProvider.php
@@ -0,0 +1,20 @@
+newScopedQuery(), $this);
+ return new DescendantsRelation($this->newQuery(), $this);
}
/**
@@ -337,7 +338,7 @@ public function prevNodes()
*/
public function ancestors()
{
- return new AncestorsRelation($this->newScopedQuery(), $this);
+ return new AncestorsRelation($this->newQuery(), $this);
}
/**
@@ -724,7 +725,7 @@ protected function getScopeAttributes()
/**
* @param array $attributes
*
- * @return self
+ * @return QueryBuilder
*/
public static function scoped(array $attributes)
{
@@ -752,7 +753,7 @@ public function newCollection(array $models = array())
*/
public static function create(array $attributes = [], self $parent = null)
{
- $children = array_pull($attributes, 'children');
+ $children = Arr::pull($attributes, 'children');
$instance = new static($attributes);
@@ -1004,7 +1005,8 @@ public function getPrevSibling(array $columns = [ '*' ])
public function isDescendantOf(self $other)
{
return $this->getLft() > $other->getLft() &&
- $this->getLft() < $other->getRgt();
+ $this->getLft() < $other->getRgt() &&
+ $this->isSameScope($other);
}
/**
@@ -1200,6 +1202,24 @@ protected function assertSameScope(self $node)
}
}
+ /**
+ * @param self $node
+ */
+ protected function isSameScope(self $node): bool
+ {
+ if ( ! $scoped = $this->getScopeAttributes()) {
+ return true;
+ }
+
+ foreach ($scoped as $attr) {
+ if ($this->getAttribute($attr) != $node->getAttribute($attr)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* @param array|null $except
*
diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php
index 56c702b..5be2e5b 100644
--- a/src/QueryBuilder.php
+++ b/src/QueryBuilder.php
@@ -86,9 +86,11 @@ public function whereIsRoot()
*/
public function whereAncestorOf($id, $andSelf = false, $boolean = 'and')
{
- $keyName = $this->model->getKeyName();
+ $keyName = $this->model->getTable() . '.' . $this->model->getKeyName();
+ $model = null;
if (NestedSet::isNode($id)) {
+ $model = $id;
$value = '?';
$this->query->addBinding($id->getRgt());
@@ -100,7 +102,7 @@ public function whereAncestorOf($id, $andSelf = false, $boolean = 'and')
->toBase()
->select("_.".$this->model->getRgtName())
->from($this->model->getTable().' as _')
- ->where($keyName, '=', $id)
+ ->where($this->model->getKeyName(), '=', $id)
->limit(1);
$this->query->mergeBindings($valueQuery);
@@ -108,17 +110,22 @@ public function whereAncestorOf($id, $andSelf = false, $boolean = 'and')
$value = '('.$valueQuery->toSql().')';
}
- $this->query->whereNested(function ($inner) use ($value, $andSelf, $id) {
+ $this->query->whereNested(function ($inner) use ($model, $value, $andSelf, $id, $keyName) {
list($lft, $rgt) = $this->wrappedColumns();
+ $wrappedTable = $this->query->getGrammar()->wrapTable($this->model->getTable());
- $inner->whereRaw("{$value} between {$lft} and {$rgt}");
+ $inner->whereRaw("{$value} between {$wrappedTable}.{$lft} and {$wrappedTable}.{$rgt}");
if ( ! $andSelf) {
- $inner->where($this->model->getKeyName(), '<>', $id);
+ $inner->where($keyName, '<>', $id);
+ }
+ if ($model !== null) {
+ // we apply scope only when Node was passed as $id.
+ // In other cases, according to docs, query should be scoped() before calling this method
+ $model->applyNestedSetScope($inner);
}
}, $boolean);
-
return $this;
}
@@ -177,12 +184,13 @@ public function ancestorsAndSelf($id, array $columns = [ '*' ])
* @param array $values
* @param string $boolean
* @param bool $not
+ * @param Query $query
*
* @return $this
*/
- public function whereNodeBetween($values, $boolean = 'and', $not = false)
+ public function whereNodeBetween($values, $boolean = 'and', $not = false, $query = null)
{
- $this->query->whereBetween($this->model->getLftName(), $values, $boolean, $not);
+ ($query ?? $this->query)->whereBetween($this->model->getTable() . '.' . $this->model->getLftName(), $values, $boolean, $not);
return $this;
}
@@ -216,19 +224,26 @@ public function orWhereNodeBetween($values)
public function whereDescendantOf($id, $boolean = 'and', $not = false,
$andSelf = false
) {
- if (NestedSet::isNode($id)) {
- $data = $id->getBounds();
- } else {
- $data = $this->model->newNestedSetQuery()
- ->getPlainNodeData($id, true);
- }
+ $this->query->whereNested(function (Query $inner) use ($id, $andSelf, $not) {
+ if (NestedSet::isNode($id)) {
+ $id->applyNestedSetScope($inner);
+ $data = $id->getBounds();
+ } else {
+ // we apply scope only when Node was passed as $id.
+ // In other cases, according to docs, query should be scoped() before calling this method
+ $data = $this->model->newNestedSetQuery()
+ ->getPlainNodeData($id, true);
+ }
- // Don't include the node
- if ( ! $andSelf) {
- ++$data[0];
- }
+ // Don't include the node
+ if (!$andSelf) {
+ ++$data[0];
+ }
- return $this->whereNodeBetween($data, $boolean, $not);
+ return $this->whereNodeBetween($data, 'and', $not, $inner);
+ }, $boolean);
+
+ return $this;
}
/**
@@ -715,6 +730,7 @@ protected function getOdnessQuery()
protected function getDuplicatesQuery()
{
$table = $this->wrappedTable();
+ $keyName = $this->wrappedKey();
$firstAlias = 'c1';
$secondAlias = 'c2';
@@ -727,7 +743,7 @@ protected function getDuplicatesQuery()
->newNestedSetQuery($firstAlias)
->toBase()
->from($this->query->raw("{$table} as {$waFirst}, {$table} {$waSecond}"))
- ->whereRaw("{$waFirst}.{$pk} < {$waSecond}.{$pk}")
+ ->whereRaw("{$waFirst}.{$keyName} < {$waSecond}.{$keyName}")
->whereNested(function (BaseQueryBuilder $inner) use ($waFirst, $waSecond) {
list($lft, $rgt) = $this->wrappedColumns();
diff --git a/tests/.gitkeep b/tests/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/NodeTest.php b/tests/NodeTest.php
index 3f7bad0..16d6d9e 100644
--- a/tests/NodeTest.php
+++ b/tests/NodeTest.php
@@ -2,10 +2,11 @@
use Illuminate\Database\Capsule\Manager as Capsule;
use Kalnoy\Nestedset\NestedSet;
+use Kalnoy\Nestedset\Tests\Models\Category;
-class NodeTest extends PHPUnit_Framework_TestCase
+class NodeTest extends PHPUnit\Framework\TestCase
{
- public static function setUpBeforeClass()
+ public static function setUpBeforeClass(): void
{
$schema = Capsule::schema();
@@ -23,7 +24,7 @@ public static function setUpBeforeClass()
Capsule::enableQueryLog();
}
- public function setUp()
+ public function setUp(): void
{
$data = include __DIR__.'/data/categories.php';
@@ -36,7 +37,7 @@ public function setUp()
date_default_timezone_set('America/Denver');
}
- public function tearDown()
+ public function tearDown(): void
{
Capsule::table('categories')->truncate();
}
@@ -104,12 +105,7 @@ public function assertNodeReceivesValidValues($node)
);
}
- /**
- * @param $name
- *
- * @return \Category
- */
- public function findCategory($name, $withTrashed = false)
+ public function findCategory(string $name, bool $withTrashed = false): Category
{
$q = new Category;
@@ -221,32 +217,29 @@ public function testCategoryMovesUp()
$this->assertNodeReceivesValidValues($node);
}
- /**
- * @expectedException Exception
- */
public function testFailsToInsertIntoChild()
{
+ $this->expectException(Exception::class);
+
$node = $this->findCategory('notebooks');
$target = $node->children()->first();
$node->afterNode($target)->save();
}
- /**
- * @expectedException Exception
- */
public function testFailsToAppendIntoItself()
{
+ $this->expectException(Exception::class);
+
$node = $this->findCategory('notebooks');
$node->appendToNode($node)->save();
}
- /**
- * @expectedException Exception
- */
public function testFailsToPrependIntoItself()
{
+ $this->expectException(Exception::class);
+
$node = $this->findCategory('notebooks');
$node->prependTo($node)->save();
@@ -338,11 +331,10 @@ public function testParentIdAttributeAccessorAppendsNode()
$this->assertTrue($node->isRoot());
}
- /**
- * @expectedException Exception
- */
public function testFailsToSaveNodeUntilNotInserted()
{
+ $this->expectException(Exception::class);
+
$node = new Category;
$node->save();
}
@@ -405,11 +397,10 @@ public function testSoftDeletedNodeisDeletedWhenParentIsDeleted()
$this->assertNull($this->findCategory('sony'));
}
- /**
- * @expectedException Exception
- */
public function testFailsToSaveNodeUntilParentIsSaved()
{
+ $this->expectException(Exception::class);
+
$node = new Category(array('title' => 'Node'));
$parent = new Category(array('title' => 'Parent'));
@@ -641,11 +632,10 @@ public function testDescendantsOfNonExistingNode()
$this->assertTrue($node->getDescendants()->isEmpty());
}
- /**
- * @expectedException \Illuminate\Database\Eloquent\ModelNotFoundException
- */
public function testWhereDescendantsOf()
{
+ $this->expectException(\Illuminate\Database\Eloquent\ModelNotFoundException::class);
+
Category::whereDescendantOf(124)->get();
}
@@ -827,8 +817,6 @@ public function testRebuildSubtree()
[ 'id' => '8' ],
]);
- echo PHP_EOL.$fixed.PHP_EOL;
-
$this->assertTrue($fixed > 0);
$this->assertTreeNotBroken();
@@ -854,11 +842,10 @@ public function testRebuildTreeWithDeletion()
$this->assertTrue($nodes->count() > 1);
}
- /**
- * @expectedException \Illuminate\Database\Eloquent\ModelNotFoundException
- */
public function testRebuildFailsWithInvalidPK()
{
+ $this->expectException(\Illuminate\Database\Eloquent\ModelNotFoundException::class);
+
Category::rebuildTree([ [ 'id' => 24 ] ]);
}
@@ -872,7 +859,9 @@ public function testFlatTree()
$this->assertEquals('galaxy', $tree[3]->name);
}
- public function testSeveralNodesModelWork()
+ // Commented, cause there is no assertion here and otherwise the test is marked as risky in PHPUnit 7.
+ // What's the purpose of this method? @todo: remove/update?
+ /*public function testSeveralNodesModelWork()
{
$category = new Category;
@@ -885,7 +874,7 @@ public function testSeveralNodesModelWork()
$duplicate->name = 'test';
$duplicate->saveAsRoot();
- }
+ }*/
public function testWhereIsLeaf()
{
diff --git a/tests/ScopedNodeTest.php b/tests/ScopedNodeTest.php
index 0584ca7..aed1fe1 100644
--- a/tests/ScopedNodeTest.php
+++ b/tests/ScopedNodeTest.php
@@ -2,10 +2,11 @@
use Illuminate\Database\Capsule\Manager as Capsule;
use Kalnoy\Nestedset\NestedSet;
+use Kalnoy\Nestedset\Tests\Models\MenuItem;
-class ScopedNodeTest extends PHPUnit_Framework_TestCase
+class ScopedNodeTest extends PHPUnit\Framework\TestCase
{
- public static function setUpBeforeClass()
+ public static function setUpBeforeClass(): void
{
$schema = Capsule::schema();
@@ -23,7 +24,7 @@ public static function setUpBeforeClass()
Capsule::enableQueryLog();
}
- public function setUp()
+ public function setUp(): void
{
$data = include __DIR__.'/data/menu_items.php';
@@ -36,7 +37,7 @@ public function setUp()
date_default_timezone_set('America/Denver');
}
- public function tearDown()
+ public function tearDown(): void
{
Capsule::table('menu_items')->truncate();
}
@@ -98,6 +99,13 @@ public function testDescendants()
$this->assertEquals(1, $result->count());
$this->assertEquals(5, $result->first()->getKey());
+
+ $node = MenuItem::scoped([ 'menu_id' => 1 ])->with('descendants')->find(2);
+
+ $result = $node->descendants;
+
+ $this->assertEquals(1, $result->count());
+ $this->assertEquals(5, $result->first()->getKey());
}
public function testAncestors()
@@ -109,7 +117,7 @@ public function testAncestors()
$this->assertEquals(1, $result->count());
$this->assertEquals(2, $result->first()->getKey());
- $node = MenuItem::with('ancestors')->find(5);
+ $node = MenuItem::scoped([ 'menu_id' => 1 ])->with('ancestors')->find(5);
$result = $node->ancestors;
@@ -152,11 +160,10 @@ public function testInsertion()
$this->assertOtherScopeNotAffected();
}
- /**
- * @expectedException \Illuminate\Database\Eloquent\ModelNotFoundException
- */
public function testInsertionToParentFromOtherScope()
{
+ $this->expectException(\Illuminate\Database\Eloquent\ModelNotFoundException::class);
+
$node = MenuItem::create([ 'menu_id' => 2, 'parent_id' => 5 ]);
}
@@ -186,31 +193,47 @@ protected function assertOtherScopeNotAffected()
$this->assertEquals(1, $node->getLft());
}
- public function testRebuildsTree()
+ // Commented, cause there is no assertion here and otherwise the test is marked as risky in PHPUnit 7.
+ // What's the purpose of this method? @todo: remove/update?
+ /*public function testRebuildsTree()
{
$data = [];
MenuItem::scoped([ 'menu_id' => 2 ])->rebuildTree($data);
- }
+ }*/
- /**
- * @expectedException LogicException
- */
public function testAppendingToAnotherScopeFails()
{
+ $this->expectException(LogicException::class);
+
$a = MenuItem::find(1);
$b = MenuItem::find(3);
$a->appendToNode($b)->save();
}
- /**
- * @expectedException LogicException
- */
public function testInsertingBeforeAnotherScopeFails()
{
+ $this->expectException(LogicException::class);
+
$a = MenuItem::find(1);
$b = MenuItem::find(3);
$a->insertAfterNode($b);
}
+
+ public function testEagerLoadingAncestorsWithScope()
+ {
+ $filteredNodes = MenuItem::where('title', 'menu item 3')->with(['ancestors'])->get();
+
+ $this->assertEquals(2, $filteredNodes->find(5)->ancestors[0]->id);
+ $this->assertEquals(4, $filteredNodes->find(6)->ancestors[0]->id);
+ }
+
+ public function testEagerLoadingDescendantsWithScope()
+ {
+ $filteredNodes = MenuItem::where('title', 'menu item 2')->with(['descendants'])->get();
+
+ $this->assertEquals(5, $filteredNodes->find(2)->descendants[0]->id);
+ $this->assertEquals(6, $filteredNodes->find(4)->descendants[0]->id);
+ }
}
\ No newline at end of file
diff --git a/tests/data/categories.php b/tests/data/categories.php
index 1f5b8ab..cd16d40 100644
--- a/tests/data/categories.php
+++ b/tests/data/categories.php
@@ -1,15 +1,15 @@
1, 'name' => 'store', '_lft' => 1, '_rgt' => 20, 'parent_id' => null),
- array('id' => 2, 'name' => 'notebooks', '_lft' => 2, '_rgt' => 7, 'parent_id' => 1),
- array('id' => 3, 'name' => 'apple', '_lft' => 3, '_rgt' => 4, 'parent_id' => 2),
- array('id' => 4, 'name' => 'lenovo', '_lft' => 5, '_rgt' => 6, 'parent_id' => 2),
- array('id' => 5, 'name' => 'mobile', '_lft' => 8, '_rgt' => 19, 'parent_id' => 1),
- array('id' => 6, 'name' => 'nokia', '_lft' => 9, '_rgt' => 10, 'parent_id' => 5),
- array('id' => 7, 'name' => 'samsung', '_lft' => 11, '_rgt' => 14, 'parent_id' => 5),
- array('id' => 8, 'name' => 'galaxy', '_lft' => 12, '_rgt' => 13, 'parent_id' => 7),
- array('id' => 9, 'name' => 'sony', '_lft' => 15, '_rgt' => 16, 'parent_id' => 5),
- array('id' => 10, 'name' => 'lenovo', '_lft' => 17, '_rgt' => 18, 'parent_id' => 5),
- array('id' => 11, 'name' => 'store_2', '_lft' => 21, '_rgt' => 22, 'parent_id' => null),
-);
\ No newline at end of file
+return [
+ ['id' => 1, 'name' => 'store', '_lft' => 1, '_rgt' => 20, 'parent_id' => null],
+ ['id' => 2, 'name' => 'notebooks', '_lft' => 2, '_rgt' => 7, 'parent_id' => 1],
+ ['id' => 3, 'name' => 'apple', '_lft' => 3, '_rgt' => 4, 'parent_id' => 2],
+ ['id' => 4, 'name' => 'lenovo', '_lft' => 5, '_rgt' => 6, 'parent_id' => 2],
+ ['id' => 5, 'name' => 'mobile', '_lft' => 8, '_rgt' => 19, 'parent_id' => 1],
+ ['id' => 6, 'name' => 'nokia', '_lft' => 9, '_rgt' => 10, 'parent_id' => 5],
+ ['id' => 7, 'name' => 'samsung', '_lft' => 11, '_rgt' => 14, 'parent_id' => 5],
+ ['id' => 8, 'name' => 'galaxy', '_lft' => 12, '_rgt' => 13, 'parent_id' => 7],
+ ['id' => 9, 'name' => 'sony', '_lft' => 15, '_rgt' => 16, 'parent_id' => 5],
+ ['id' => 10, 'name' => 'lenovo', '_lft' => 17, '_rgt' => 18, 'parent_id' => 5],
+ ['id' => 11, 'name' => 'store_2', '_lft' => 21, '_rgt' => 22, 'parent_id' => null],
+];
\ No newline at end of file
diff --git a/tests/data/menu_items.php b/tests/data/menu_items.php
index 5490f7d..fba6acb 100644
--- a/tests/data/menu_items.php
+++ b/tests/data/menu_items.php
@@ -1,8 +1,10 @@
- 1, 'menu_id' => 1, '_lft' => 1, '_rgt' => 2, 'parent_id' => null, 'title' => 'menu item 1' ],
- [ 'id' => 2, 'menu_id' => 1, '_lft' => 3, '_rgt' => 6, 'parent_id' => null, 'title' => 'menu item 2' ],
- [ 'id' => 5, 'menu_id' => 1, '_lft' => 4, '_rgt' => 5, 'parent_id' => 2, 'title' => 'menu item 3' ],
- [ 'id' => 3, 'menu_id' => 2, '_lft' => 1, '_rgt' => 2, 'parent_id' => null, 'title' => 'menu item 1' ],
- [ 'id' => 4, 'menu_id' => 2, '_lft' => 3, '_rgt' => 6, 'parent_id' => null, 'title' => 'menu item 2' ],
- [ 'id' => 6, 'menu_id' => 2, '_lft' => 4, '_rgt' => 5, 'parent_id' => 4, 'title' => 'menu item 3' ],
+ 1, 'menu_id' => 1, '_lft' => 1, '_rgt' => 2, 'parent_id' => null, 'title' => 'menu item 1' ],
+ ['id' => 2, 'menu_id' => 1, '_lft' => 3, '_rgt' => 6, 'parent_id' => null, 'title' => 'menu item 2' ],
+ ['id' => 5, 'menu_id' => 1, '_lft' => 4, '_rgt' => 5, 'parent_id' => 2, 'title' => 'menu item 3' ],
+ ['id' => 3, 'menu_id' => 2, '_lft' => 1, '_rgt' => 2, 'parent_id' => null, 'title' => 'menu item 1' ],
+ ['id' => 4, 'menu_id' => 2, '_lft' => 3, '_rgt' => 6, 'parent_id' => null, 'title' => 'menu item 2' ],
+ ['id' => 6, 'menu_id' => 2, '_lft' => 4, '_rgt' => 5, 'parent_id' => 4, 'title' => 'menu item 3' ],
];
\ No newline at end of file
diff --git a/tests/models/Category.php b/tests/models/Category.php
index bcce8e9..0c35daa 100644
--- a/tests/models/Category.php
+++ b/tests/models/Category.php
@@ -1,12 +1,17 @@