| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000 |
- <?php
- use Illuminate\Database\Capsule\Manager as Capsule;
- use Kalnoy\Nestedset\NestedSet;
- class NodeTest extends PHPUnit\Framework\TestCase
- {
- public static function setUpBeforeClass(): void
- {
- $schema = Capsule::schema();
- $schema->dropIfExists('categories');
- Capsule::disableQueryLog();
- $schema->create('categories', function (\Illuminate\Database\Schema\Blueprint $table) {
- $table->increments('id');
- $table->string('name');
- $table->softDeletes();
- NestedSet::columns($table);
- });
- Capsule::enableQueryLog();
- }
- public function setUp(): void
- {
- $data = include __DIR__.'/data/categories.php';
- Capsule::table('categories')->insert($data);
- Capsule::flushQueryLog();
- Category::resetActionsPerformed();
- date_default_timezone_set('America/Denver');
- }
- public function tearDown(): void
- {
- Capsule::table('categories')->truncate();
- }
- // public static function tearDownAfterClass()
- // {
- // $log = Capsule::getQueryLog();
- // foreach ($log as $item) {
- // echo $item['query']." with ".implode(', ', $item['bindings'])."\n";
- // }
- // }
- public function assertTreeNotBroken($table = 'categories')
- {
- $checks = array();
- $connection = Capsule::connection();
- $table = $connection->getQueryGrammar()->wrapTable($table);
- // Check if lft and rgt values are ok
- $checks[] = "from $table where _lft >= _rgt or (_rgt - _lft) % 2 = 0";
- // Check if lft and rgt values are unique
- $checks[] = "from $table c1, $table c2 where c1.id <> c2.id and ".
- "(c1._lft=c2._lft or c1._rgt=c2._rgt or c1._lft=c2._rgt or c1._rgt=c2._lft)";
- // Check if parent_id is set correctly
- $checks[] = "from $table c, $table p, $table m where c.parent_id=p.id and m.id <> p.id and m.id <> c.id and ".
- "(c._lft not between p._lft and p._rgt or c._lft between m._lft and m._rgt and m._lft between p._lft and p._rgt)";
- foreach ($checks as $i => $check) {
- $checks[$i] = 'select 1 as error '.$check;
- }
- $sql = 'select max(error) as errors from ('.implode(' union ', $checks).') _';
- $actual = $connection->selectOne($sql);
- $this->assertEquals(null, $actual->errors, "The tree structure of $table is broken!");
- $actual = (array)Capsule::connection()->selectOne($sql);
- $this->assertEquals(array('errors' => null), $actual, "The tree structure of $table is broken!");
- }
- public function dumpTree($items = null)
- {
- if ( ! $items) $items = Category::withTrashed()->defaultOrder()->get();
- foreach ($items as $item) {
- echo PHP_EOL.($item->trashed() ? '-' : '+').' '.$item->name." ".$item->getKey().' '.$item->getLft()." ".$item->getRgt().' '.$item->getParentId();
- }
- }
- public function assertNodeReceivesValidValues($node)
- {
- $lft = $node->getLft();
- $rgt = $node->getRgt();
- $nodeInDb = $this->findCategory($node->name);
- $this->assertEquals(
- [ $nodeInDb->getLft(), $nodeInDb->getRgt() ],
- [ $lft, $rgt ],
- 'Node is not synced with database after save.'
- );
- }
- /**
- * @param $name
- *
- * @return \Category
- */
- public function findCategory($name, $withTrashed = false)
- {
- $q = new Category;
- $q = $withTrashed ? $q->withTrashed() : $q->newQuery();
- return $q->whereName($name)->first();
- }
- public function testTreeNotBroken()
- {
- $this->assertTreeNotBroken();
- $this->assertFalse(Category::isBroken());
- }
- public function nodeValues($node)
- {
- return array($node->_lft, $node->_rgt, $node->parent_id);
- }
- public function testGetsNodeData()
- {
- $data = Category::getNodeData(3);
- $this->assertEquals([ '_lft' => 3, '_rgt' => 4 ], $data);
- }
- public function testGetsPlainNodeData()
- {
- $data = Category::getPlainNodeData(3);
- $this->assertEquals([ 3, 4 ], $data);
- }
- public function testReceivesValidValuesWhenAppendedTo()
- {
- $node = new Category([ 'name' => 'test' ]);
- $root = Category::root();
- $accepted = array($root->_rgt, $root->_rgt + 1, $root->id);
- $root->appendNode($node);
- $this->assertTrue($node->hasMoved());
- $this->assertEquals($accepted, $this->nodeValues($node));
- $this->assertTreeNotBroken();
- $this->assertFalse($node->isDirty());
- $this->assertTrue($node->isDescendantOf($root));
- }
- public function testReceivesValidValuesWhenPrependedTo()
- {
- $root = Category::root();
- $node = new Category([ 'name' => 'test' ]);
- $root->prependNode($node);
- $this->assertTrue($node->hasMoved());
- $this->assertEquals(array($root->_lft + 1, $root->_lft + 2, $root->id), $this->nodeValues($node));
- $this->assertTreeNotBroken();
- $this->assertTrue($node->isDescendantOf($root));
- $this->assertTrue($root->isAncestorOf($node));
- $this->assertTrue($node->isChildOf($root));
- }
- public function testReceivesValidValuesWhenInsertedAfter()
- {
- $target = $this->findCategory('apple');
- $node = new Category([ 'name' => 'test' ]);
- $node->afterNode($target)->save();
- $this->assertTrue($node->hasMoved());
- $this->assertEquals(array($target->_rgt + 1, $target->_rgt + 2, $target->parent->id), $this->nodeValues($node));
- $this->assertTreeNotBroken();
- $this->assertFalse($node->isDirty());
- $this->assertTrue($node->isSiblingOf($target));
- }
- public function testReceivesValidValuesWhenInsertedBefore()
- {
- $target = $this->findCategory('apple');
- $node = new Category([ 'name' => 'test' ]);
- $node->beforeNode($target)->save();
- $this->assertTrue($node->hasMoved());
- $this->assertEquals(array($target->_lft, $target->_lft + 1, $target->parent->id), $this->nodeValues($node));
- $this->assertTreeNotBroken();
- }
- public function testCategoryMovesDown()
- {
- $node = $this->findCategory('apple');
- $target = $this->findCategory('mobile');
- $target->appendNode($node);
- $this->assertTrue($node->hasMoved());
- $this->assertNodeReceivesValidValues($node);
- $this->assertTreeNotBroken();
- }
- public function testCategoryMovesUp()
- {
- $node = $this->findCategory('samsung');
- $target = $this->findCategory('notebooks');
- $target->appendNode($node);
- $this->assertTrue($node->hasMoved());
- $this->assertTreeNotBroken();
- $this->assertNodeReceivesValidValues($node);
- }
- public function testFailsToInsertIntoChild()
- {
- $this->expectException(Exception::class);
- $node = $this->findCategory('notebooks');
- $target = $node->children()->first();
- $node->afterNode($target)->save();
- }
- public function testFailsToAppendIntoItself()
- {
- $this->expectException(Exception::class);
- $node = $this->findCategory('notebooks');
- $node->appendToNode($node)->save();
- }
- public function testFailsToPrependIntoItself()
- {
- $this->expectException(Exception::class);
- $node = $this->findCategory('notebooks');
- $node->prependTo($node)->save();
- }
- public function testWithoutRootWorks()
- {
- $result = Category::withoutRoot()->pluck('name');
- $this->assertNotEquals('store', $result);
- }
- public function testAncestorsReturnsAncestorsWithoutNodeItself()
- {
- $node = $this->findCategory('apple');
- $path = all($node->ancestors()->pluck('name'));
- $this->assertEquals(array('store', 'notebooks'), $path);
- }
- public function testGetsAncestorsByStatic()
- {
- $path = all(Category::ancestorsOf(3)->pluck('name'));
- $this->assertEquals(array('store', 'notebooks'), $path);
- }
- public function testGetsAncestorsDirect()
- {
- $path = all(Category::find(8)->getAncestors()->pluck('id'));
- $this->assertEquals(array(1, 5, 7), $path);
- }
- public function testDescendants()
- {
- $node = $this->findCategory('mobile');
- $descendants = all($node->descendants()->pluck('name'));
- $expected = array('nokia', 'samsung', 'galaxy', 'sony', 'lenovo');
- $this->assertEquals($expected, $descendants);
- $descendants = all($node->getDescendants()->pluck('name'));
- $this->assertEquals(count($descendants), $node->getDescendantCount());
- $this->assertEquals($expected, $descendants);
- $descendants = all(Category::descendantsAndSelf(7)->pluck('name'));
- $expected = [ 'samsung', 'galaxy' ];
- $this->assertEquals($expected, $descendants);
- }
- public function testWithDepthWorks()
- {
- $nodes = all(Category::withDepth()->limit(4)->pluck('depth'));
- $this->assertEquals(array(0, 1, 2, 2), $nodes);
- }
- public function testWithDepthWithCustomKeyWorks()
- {
- $node = Category::whereIsRoot()->withDepth('level')->first();
- $this->assertTrue(isset($node['level']));
- }
- public function testWithDepthWorksAlongWithDefaultKeys()
- {
- $node = Category::withDepth()->first();
- $this->assertTrue(isset($node->name));
- }
- public function testParentIdAttributeAccessorAppendsNode()
- {
- $node = new Category(array('name' => 'lg', 'parent_id' => 5));
- $node->save();
- $this->assertEquals(5, $node->parent_id);
- $this->assertEquals(5, $node->getParentId());
- $node->parent_id = null;
- $node->save();
- $node->refreshNode();
- $this->assertEquals(null, $node->parent_id);
- $this->assertTrue($node->isRoot());
- }
- public function testFailsToSaveNodeUntilNotInserted()
- {
- $this->expectException(Exception::class);
- $node = new Category;
- $node->save();
- }
- public function testNodeIsDeletedWithDescendants()
- {
- $node = $this->findCategory('mobile');
- $node->forceDelete();
- $this->assertTreeNotBroken();
- $nodes = Category::whereIn('id', array(5, 6, 7, 8, 9))->count();
- $this->assertEquals(0, $nodes);
- $root = Category::root();
- $this->assertEquals(8, $root->getRgt());
- }
- public function testNodeIsSoftDeleted()
- {
- $root = Category::root();
- $samsung = $this->findCategory('samsung');
- $samsung->delete();
- $this->assertTreeNotBroken();
- $this->assertNull($this->findCategory('galaxy'));
- sleep(1);
- $node = $this->findCategory('mobile');
- $node->delete();
- $nodes = Category::whereIn('id', array(5, 6, 7, 8, 9))->count();
- $this->assertEquals(0, $nodes);
- $originalRgt = $root->getRgt();
- $root->refreshNode();
- $this->assertEquals($originalRgt, $root->getRgt());
- $node = $this->findCategory('mobile', true);
- $node->restore();
- $this->assertNull($this->findCategory('samsung'));
- $this->assertNotNull($this->findCategory('nokia'));
- }
- public function testSoftDeletedNodeisDeletedWhenParentIsDeleted()
- {
- $this->findCategory('samsung')->delete();
- $this->findCategory('mobile')->forceDelete();
- $this->assertTreeNotBroken();
- $this->assertNull($this->findCategory('samsung', true));
- $this->assertNull($this->findCategory('sony'));
- }
- public function testFailsToSaveNodeUntilParentIsSaved()
- {
- $this->expectException(Exception::class);
- $node = new Category(array('title' => 'Node'));
- $parent = new Category(array('title' => 'Parent'));
- $node->appendTo($parent)->save();
- }
- public function testSiblings()
- {
- $node = $this->findCategory('samsung');
- $siblings = all($node->siblings()->pluck('id'));
- $next = all($node->nextSiblings()->pluck('id'));
- $prev = all($node->prevSiblings()->pluck('id'));
- $this->assertEquals(array(6, 9, 10), $siblings);
- $this->assertEquals(array(9, 10), $next);
- $this->assertEquals(array(6), $prev);
- $siblings = all($node->getSiblings()->pluck('id'));
- $next = all($node->getNextSiblings()->pluck('id'));
- $prev = all($node->getPrevSiblings()->pluck('id'));
- $this->assertEquals(array(6, 9, 10), $siblings);
- $this->assertEquals(array(9, 10), $next);
- $this->assertEquals(array(6), $prev);
- $next = $node->getNextSibling();
- $prev = $node->getPrevSibling();
- $this->assertEquals(9, $next->id);
- $this->assertEquals(6, $prev->id);
- }
- public function testFetchesReversed()
- {
- $node = $this->findCategory('sony');
- $siblings = $node->prevSiblings()->reversed()->value('id');
- $this->assertEquals(7, $siblings);
- }
- public function testToTreeBuildsWithDefaultOrder()
- {
- $tree = Category::whereBetween('_lft', array(8, 17))->defaultOrder()->get()->toTree();
- $this->assertEquals(1, count($tree));
- $root = $tree->first();
- $this->assertEquals('mobile', $root->name);
- $this->assertEquals(4, count($root->children));
- }
- public function testToTreeBuildsWithCustomOrder()
- {
- $tree = Category::whereBetween('_lft', array(8, 17))
- ->orderBy('title')
- ->get()
- ->toTree();
- $this->assertEquals(1, count($tree));
- $root = $tree->first();
- $this->assertEquals('mobile', $root->name);
- $this->assertEquals(4, count($root->children));
- $this->assertEquals($root, $root->children->first()->parent);
- }
- public function testToTreeWithSpecifiedRoot()
- {
- $node = $this->findCategory('mobile');
- $nodes = Category::whereBetween('_lft', array(8, 17))->get();
- $tree1 = \Kalnoy\Nestedset\Collection::make($nodes)->toTree(5);
- $tree2 = \Kalnoy\Nestedset\Collection::make($nodes)->toTree($node);
- $this->assertEquals(4, $tree1->count());
- $this->assertEquals(4, $tree2->count());
- }
- public function testToTreeBuildsWithDefaultOrderAndMultipleRootNodes()
- {
- $tree = Category::withoutRoot()->get()->toTree();
- $this->assertEquals(2, count($tree));
- }
- public function testToTreeBuildsWithRootItemIdProvided()
- {
- $tree = Category::whereBetween('_lft', array(8, 17))->get()->toTree(5);
- $this->assertEquals(4, count($tree));
- $root = $tree[1];
- $this->assertEquals('samsung', $root->name);
- $this->assertEquals(1, count($root->children));
- }
- public function testRetrievesNextNode()
- {
- $node = $this->findCategory('apple');
- $next = $node->nextNodes()->first();
- $this->assertEquals('lenovo', $next->name);
- }
- public function testRetrievesPrevNode()
- {
- $node = $this->findCategory('apple');
- $next = $node->getPrevNode();
- $this->assertEquals('notebooks', $next->name);
- }
- public function testMultipleAppendageWorks()
- {
- $parent = $this->findCategory('mobile');
- $child = new Category([ 'name' => 'test' ]);
- $parent->appendNode($child);
- $child->appendNode(new Category([ 'name' => 'sub' ]));
- $parent->appendNode(new Category([ 'name' => 'test2' ]));
- $this->assertTreeNotBroken();
- }
- public function testDefaultCategoryIsSavedAsRoot()
- {
- $node = new Category([ 'name' => 'test' ]);
- $node->save();
- $this->assertEquals(23, $node->_lft);
- $this->assertTreeNotBroken();
- $this->assertTrue($node->isRoot());
- }
- public function testExistingCategorySavedAsRoot()
- {
- $node = $this->findCategory('apple');
- $node->saveAsRoot();
- $this->assertTreeNotBroken();
- $this->assertTrue($node->isRoot());
- }
- public function testNodeMovesDownSeveralPositions()
- {
- $node = $this->findCategory('nokia');
- $this->assertTrue($node->down(2));
- $this->assertEquals($node->_lft, 15);
- }
- public function testNodeMovesUpSeveralPositions()
- {
- $node = $this->findCategory('sony');
- $this->assertTrue($node->up(2));
- $this->assertEquals($node->_lft, 9);
- }
- public function testCountsTreeErrors()
- {
- $errors = Category::countErrors();
- $this->assertEquals([ 'oddness' => 0,
- 'duplicates' => 0,
- 'wrong_parent' => 0,
- 'missing_parent' => 0 ], $errors);
- Category::where('id', '=', 5)->update([ '_lft' => 14 ]);
- Category::where('id', '=', 8)->update([ 'parent_id' => 2 ]);
- Category::where('id', '=', 11)->update([ '_lft' => 20 ]);
- Category::where('id', '=', 4)->update([ 'parent_id' => 24 ]);
- $errors = Category::countErrors();
- $this->assertEquals(1, $errors['oddness']);
- $this->assertEquals(2, $errors['duplicates']);
- $this->assertEquals(1, $errors['missing_parent']);
- }
- public function testCreatesNode()
- {
- $node = Category::create([ 'name' => 'test' ]);
- $this->assertEquals(23, $node->getLft());
- }
- public function testCreatesViaRelationship()
- {
- $node = $this->findCategory('apple');
- $child = $node->children()->create([ 'name' => 'test' ]);
- $this->assertTreeNotBroken();
- }
- public function testCreatesTree()
- {
- $node = Category::create(
- [
- 'name' => 'test',
- 'children' =>
- [
- [ 'name' => 'test2' ],
- [ 'name' => 'test3' ],
- ],
- ]);
- $this->assertTreeNotBroken();
- $this->assertTrue(isset($node->children));
- $node = $this->findCategory('test');
- $this->assertCount(2, $node->children);
- $this->assertEquals('test2', $node->children[0]->name);
- }
- public function testDescendantsOfNonExistingNode()
- {
- $node = new Category;
- $this->assertTrue($node->getDescendants()->isEmpty());
- }
- public function testWhereDescendantsOf()
- {
- $this->expectException(\Illuminate\Database\Eloquent\ModelNotFoundException::class);
- Category::whereDescendantOf(124)->get();
- }
- public function testAncestorsByNode()
- {
- $category = $this->findCategory('apple');
- $ancestors = all(Category::whereAncestorOf($category)->pluck('id'));
- $this->assertEquals([ 1, 2 ], $ancestors);
- }
- public function testDescendantsByNode()
- {
- $category = $this->findCategory('notebooks');
- $res = all(Category::whereDescendantOf($category)->pluck('id'));
- $this->assertEquals([ 3, 4 ], $res);
- }
- public function testMultipleDeletionsDoNotBrakeTree()
- {
- $category = $this->findCategory('mobile');
- foreach ($category->children()->take(2)->get() as $child)
- {
- $child->forceDelete();
- }
- $this->assertTreeNotBroken();
- }
- public function testTreeIsFixed()
- {
- Category::where('id', '=', 5)->update([ '_lft' => 14 ]);
- Category::where('id', '=', 8)->update([ 'parent_id' => 2 ]);
- Category::where('id', '=', 11)->update([ '_lft' => 20 ]);
- Category::where('id', '=', 2)->update([ 'parent_id' => 24 ]);
- $fixed = Category::fixTree();
- $this->assertTrue($fixed > 0);
- $this->assertTreeNotBroken();
- $node = Category::find(8);
- $this->assertEquals(2, $node->getParentId());
- $node = Category::find(2);
- $this->assertEquals(null, $node->getParentId());
- }
- public function testSubtreeIsFixed()
- {
- Category::where('id', '=', 8)->update([ '_lft' => 11 ]);
- $fixed = Category::fixSubtree(Category::find(5));
- $this->assertEquals($fixed, 1);
- $this->assertTreeNotBroken();
- $this->assertEquals(Category::find(8)->getLft(), 12);
- }
- public function testParentIdDirtiness()
- {
- $node = $this->findCategory('apple');
- $node->parent_id = 5;
- $this->assertTrue($node->isDirty('parent_id'));
- $node = $this->findCategory('apple');
- $node->parent_id = null;
- $this->assertTrue($node->isDirty('parent_id'));
- }
- public function testIsDirtyMovement()
- {
- $node = $this->findCategory('apple');
- $otherNode = $this->findCategory('samsung');
- $this->assertFalse($node->isDirty());
- $node->afterNode($otherNode);
- $this->assertTrue($node->isDirty());
- $node = $this->findCategory('apple');
- $otherNode = $this->findCategory('samsung');
- $this->assertFalse($node->isDirty());
- $node->appendToNode($otherNode);
- $this->assertTrue($node->isDirty());
- }
- public function testRootNodesMoving()
- {
- $node = $this->findCategory('store');
- $node->down();
- $this->assertEquals(3, $node->getLft());
- }
- public function testDescendantsRelation()
- {
- $node = $this->findCategory('notebooks');
- $result = $node->descendants;
- $this->assertEquals(2, $result->count());
- $this->assertEquals('apple', $result->first()->name);
- }
- public function testDescendantsEagerlyLoaded()
- {
- $nodes = Category::whereIn('id', [ 2, 5 ])->get();
- $nodes->load('descendants');
- $this->assertEquals(2, $nodes->count());
- $this->assertTrue($nodes->first()->relationLoaded('descendants'));
- }
- public function testDescendantsRelationQuery()
- {
- $nodes = Category::has('descendants')->whereIn('id', [ 2, 3 ])->get();
- $this->assertEquals(1, $nodes->count());
- $this->assertEquals(2, $nodes->first()->getKey());
- $nodes = Category::has('descendants', '>', 2)->get();
- $this->assertEquals(2, $nodes->count());
- $this->assertEquals(1, $nodes[0]->getKey());
- $this->assertEquals(5, $nodes[1]->getKey());
- }
- public function testParentRelationQuery()
- {
- $nodes = Category::has('parent')->whereIn('id', [ 1, 2 ]);
- $this->assertEquals(1, $nodes->count());
- $this->assertEquals(2, $nodes->first()->getKey());
- }
- public function testRebuildTree()
- {
- $fixed = Category::rebuildTree([
- [
- 'id' => 1,
- 'children' => [
- [ 'id' => 10 ],
- [ 'id' => 3, 'name' => 'apple v2', 'children' => [ [ 'name' => 'new node' ] ] ],
- [ 'id' => 2 ],
- ]
- ]
- ]);
- $this->assertTrue($fixed > 0);
- $this->assertTreeNotBroken();
- $node = Category::find(3);
- $this->assertEquals(1, $node->getParentId());
- $this->assertEquals('apple v2', $node->name);
- $this->assertEquals(4, $node->getLft());
- $node = $this->findCategory('new node');
- $this->assertNotNull($node);
- $this->assertEquals(3, $node->getParentId());
- }
- public function testRebuildSubtree()
- {
- $fixed = Category::rebuildSubtree(Category::find(7), [
- [ 'name' => 'new node' ],
- [ 'id' => '8' ],
- ]);
- $this->assertTrue($fixed > 0);
- $this->assertTreeNotBroken();
- $node = $this->findCategory('new node');
- $this->assertNotNull($node);
- $this->assertEquals($node->getLft(), 12);
- }
- public function testRebuildTreeWithDeletion()
- {
- Category::rebuildTree([ [ 'name' => 'all deleted' ] ], true);
- $this->assertTreeNotBroken();
- $nodes = Category::get();
- $this->assertEquals(1, $nodes->count());
- $this->assertEquals('all deleted', $nodes->first()->name);
- $nodes = Category::withTrashed()->get();
- $this->assertTrue($nodes->count() > 1);
- }
- public function testRebuildFailsWithInvalidPK()
- {
- $this->expectException(\Illuminate\Database\Eloquent\ModelNotFoundException::class);
- Category::rebuildTree([ [ 'id' => 24 ] ]);
- }
- public function testFlatTree()
- {
- $node = $this->findCategory('mobile');
- $tree = $node->descendants()->orderBy('name')->get()->toFlatTree();
- $this->assertCount(5, $tree);
- $this->assertEquals('samsung', $tree[2]->name);
- $this->assertEquals('galaxy', $tree[3]->name);
- }
- // 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;
- $category->name = 'test';
- $category->saveAsRoot();
- $duplicate = new DuplicateCategory;
- $duplicate->name = 'test';
- $duplicate->saveAsRoot();
- }*/
- public function testWhereIsLeaf()
- {
- $categories = Category::leaves();
- $this->assertEquals(7, $categories->count());
- $this->assertEquals('apple', $categories->first()->name);
- $this->assertTrue($categories->first()->isLeaf());
- $category = Category::whereIsRoot()->first();
- $this->assertFalse($category->isLeaf());
- }
- public function testEagerLoadAncestors()
- {
- $queryLogCount = count(Capsule::connection()->getQueryLog());
- $categories = Category::with('ancestors')->orderBy('name')->get();
- $this->assertEquals($queryLogCount + 2, count(Capsule::connection()->getQueryLog()));
- $expectedShape = [
- 'apple (3)}' => 'store (1) > notebooks (2)',
- 'galaxy (8)}' => 'store (1) > mobile (5) > samsung (7)',
- 'lenovo (4)}' => 'store (1) > notebooks (2)',
- 'lenovo (10)}' => 'store (1) > mobile (5)',
- 'mobile (5)}' => 'store (1)',
- 'nokia (6)}' => 'store (1) > mobile (5)',
- 'notebooks (2)}' => 'store (1)',
- 'samsung (7)}' => 'store (1) > mobile (5)',
- 'sony (9)}' => 'store (1) > mobile (5)',
- 'store (1)}' => '',
- 'store_2 (11)}' => ''
- ];
- $output = [];
- foreach ($categories as $category) {
- $output["{$category->name} ({$category->id})}"] = $category->ancestors->count()
- ? implode(' > ', $category->ancestors->map(function ($cat) { return "{$cat->name} ({$cat->id})"; })->toArray())
- : '';
- }
- $this->assertEquals($expectedShape, $output);
- }
- public function testLazyLoadAncestors()
- {
- $queryLogCount = count(Capsule::connection()->getQueryLog());
- $categories = Category::orderBy('name')->get();
- $this->assertEquals($queryLogCount + 1, count(Capsule::connection()->getQueryLog()));
- $expectedShape = [
- 'apple (3)}' => 'store (1) > notebooks (2)',
- 'galaxy (8)}' => 'store (1) > mobile (5) > samsung (7)',
- 'lenovo (4)}' => 'store (1) > notebooks (2)',
- 'lenovo (10)}' => 'store (1) > mobile (5)',
- 'mobile (5)}' => 'store (1)',
- 'nokia (6)}' => 'store (1) > mobile (5)',
- 'notebooks (2)}' => 'store (1)',
- 'samsung (7)}' => 'store (1) > mobile (5)',
- 'sony (9)}' => 'store (1) > mobile (5)',
- 'store (1)}' => '',
- 'store_2 (11)}' => ''
- ];
- $output = [];
- foreach ($categories as $category) {
- $output["{$category->name} ({$category->id})}"] = $category->ancestors->count()
- ? implode(' > ', $category->ancestors->map(function ($cat) { return "{$cat->name} ({$cat->id})"; })->toArray())
- : '';
- }
- // assert that there is number of original query + 1 + number of rows to fulfill the relation
- $this->assertEquals($queryLogCount + 12, count(Capsule::connection()->getQueryLog()));
- $this->assertEquals($expectedShape, $output);
- }
- public function testWhereHasCountQueryForAncestors()
- {
- $categories = all(Category::has('ancestors', '>', 2)->pluck('name'));
- $this->assertEquals([ 'galaxy' ], $categories);
- $categories = all(Category::whereHas('ancestors', function ($query) {
- $query->where('id', 5);
- })->pluck('name'));
- $this->assertEquals([ 'nokia', 'samsung', 'galaxy', 'sony', 'lenovo' ], $categories);
- }
- public function testReplication()
- {
- $category = $this->findCategory('nokia');
- $category = $category->replicate();
- $category->save();
- $category->refreshNode();
- $this->assertNull($category->getParentId());
- $category = $this->findCategory('nokia');
- $category = $category->replicate();
- $category->parent_id = 1;
- $category->save();
- $category->refreshNode();
- $this->assertEquals(1, $category->getParentId());
- }
- }
- function all($items)
- {
- return is_array($items) ? $items : $items->all();
- }
|