[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-8472":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":32,"readmeContent":33,"aiSummary":34,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":35,"discoverSource":36},8472,"laravel-nestedset","lazychaser\u002Flaravel-nestedset","lazychaser","Effective tree structures in Laravel 4-8","",null,"PHP",3802,481,94,195,0,2,10,1,30.05,false,"v7",true,[25,26,27,28,29,30,31],"hierarchical-data","laravel","menus","nested-set","php","tree-structure","trees","2026-06-12 02:01:54","[![Build Status](https:\u002F\u002Ftravis-ci.org\u002Flazychaser\u002Flaravel-nestedset.svg?branch=master)](https:\u002F\u002Ftravis-ci.org\u002Flazychaser\u002Flaravel-nestedset)\n[![Total Downloads](https:\u002F\u002Fposer.pugx.org\u002Fkalnoy\u002Fnestedset\u002Fdownloads.svg)](https:\u002F\u002Fpackagist.org\u002Fpackages\u002Fkalnoy\u002Fnestedset)\n[![Latest Stable Version](https:\u002F\u002Fposer.pugx.org\u002Fkalnoy\u002Fnestedset\u002Fv\u002Fstable.svg)](https:\u002F\u002Fpackagist.org\u002Fpackages\u002Fkalnoy\u002Fnestedset)\n[![Latest Unstable Version](https:\u002F\u002Fposer.pugx.org\u002Fkalnoy\u002Fnestedset\u002Fv\u002Funstable.svg)](https:\u002F\u002Fpackagist.org\u002Fpackages\u002Fkalnoy\u002Fnestedset)\n[![License](https:\u002F\u002Fposer.pugx.org\u002Fkalnoy\u002Fnestedset\u002Flicense.svg)](https:\u002F\u002Fpackagist.org\u002Fpackages\u002Fkalnoy\u002Fnestedset)\n\nThis is a Laravel package for working with trees in relational databases.\n\n*   **Laravel 13** is supported since v7.0.0\n*   **Laravel 12** is supported since v6.0.7\n*   **Laravel 11.0** is supported since v6.0.4\n*   **Laravel 10.0** is supported since v6.0.2\n*   **Laravel 9.0** is supported since v6.0.1\n*   **Laravel 8.0** is supported since v6.0.0\n*   **Laravel 5.7, 5.8, 6.0, 7.0** is supported since v5\n*   **Laravel 5.5, 5.6** is supported since v4.3\n*   **Laravel 5.2, 5.3, 5.4** is supported since v4\n*   **Laravel 5.1** is supported in v3\n*   **Laravel 4** is supported in v2\n\n__Contents:__\n\n- [Theory](#what-are-nested-sets)\n- [Documentation](#documentation)\n    -   [Inserting nodes](#inserting-nodes)\n    -   [Retrieving nodes](#retrieving-nodes)\n    -   [Deleting nodes](#deleting-nodes)\n    -   [Consistency checking & fixing](#checking-consistency)\n    -   [Scoping](#scoping)\n- [Requirements](#requirements)\n- [Installation](#installation)\n\nWhat are nested sets?\n---------------------\n\nNested sets or [Nested Set Model](http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FNested_set_model) is\na way to effectively store hierarchical data in a relational table. From wikipedia:\n\n> The nested set model is to number the nodes according to a tree traversal,\n> which visits each node twice, assigning numbers in the order of visiting, and\n> at both visits. This leaves two numbers for each node, which are stored as two\n> attributes. Querying becomes inexpensive: hierarchy membership can be tested by\n> comparing these numbers. Updating requires renumbering and is therefore expensive.\n\n### Applications\n\nNSM shows good performance when tree is updated rarely. It is tuned to be fast for\ngetting related nodes. It'is ideally suited for building multi-depth menu or\ncategories for shop.\n\nDocumentation\n-------------\n\nSuppose that we have a model `Category`; a `$node` variable is an instance of that model\nand the node that we are manipulating. It can be a fresh model or one from database.\n\n### Relationships\n\nNode has following relationships that are fully functional and can be eagerly loaded:\n\n-   Node belongs to `parent`\n-   Node has many `children`\n-   Node has many `ancestors`\n-   Node has many `descendants`\n\n### Inserting nodes\n\nMoving and inserting nodes includes several database queries, so it is\nhighly recommended to use transactions.\n\n__IMPORTANT!__ As of v4.2.0 transaction is not automatically started\n\nAnother important note is that __structural manipulations are deferred__ until you\nhit `save` on model (some methods implicitly call `save` and return boolean result\nof the operation).\n\nIf model is successfully saved it doesn't mean that node was moved. If your application\ndepends on whether the node has actually changed its position, use `hasMoved` method:\n\n```php\nif ($node->save()) {\n    $moved = $node->hasMoved();\n}\n```\n\n#### Creating nodes\n\nWhen you simply creating a node, it will be appended to the end of the tree:\n\n```php\nCategory::create($attributes); \u002F\u002F Saved as root\n```\n\n```php\n$node = new Category($attributes);\n$node->save(); \u002F\u002F Saved as root\n```\n\nIn this case the node is considered a _root_ which means that it doesn't have a parent.\n\n#### Making a root from existing node\n\n```php\n\u002F\u002F #1 Implicit save\n$node->saveAsRoot();\n\n\u002F\u002F #2 Explicit save\n$node->makeRoot()->save();\n```\n\nThe node will be appended to the end of the tree.\n\n#### Appending and prepending to the specified parent\n\nIf you want to make node a child of other node, you can make it last or first child.\n\n*In following examples, `$parent` is some existing node.*\n\nThere are few ways to append a node:\n\n```php\n\u002F\u002F #1 Using deferred insert\n$node->appendToNode($parent)->save();\n\n\u002F\u002F #2 Using parent node\n$parent->appendNode($node);\n\n\u002F\u002F #3 Using parent's children relationship\n$parent->children()->create($attributes);\n\n\u002F\u002F #5 Using node's parent relationship\n$node->parent()->associate($parent)->save();\n\n\u002F\u002F #6 Using the parent attribute\n$node->parent_id = $parent->id;\n$node->save();\n\n\u002F\u002F #7 Using static method\nCategory::create($attributes, $parent);\n```\n\nAnd only a couple ways to prepend:\n\n```php\n\u002F\u002F #1\n$node->prependToNode($parent)->save();\n\n\u002F\u002F #2\n$parent->prependNode($node);\n```\n\n#### Inserting before or after specified node\n\nYou can make `$node` to be a neighbor of the `$neighbor` node using following methods:\n\n*`$neighbor` must exists, target node can be fresh. If target node exists,\nit will be moved to the new position and parent will be changed if it's required.*\n\n```php\n# Explicit save\n$node->afterNode($neighbor)->save();\n$node->beforeNode($neighbor)->save();\n\n# Implicit save\n$node->insertAfterNode($neighbor);\n$node->insertBeforeNode($neighbor);\n```\n\n#### Building a tree from array\n\nWhen using static method `create` on node, it checks whether attributes contains\n`children` key. If it does, it creates more nodes recursively.\n\n```php\n$node = Category::create([\n    'name' => 'Foo',\n\n    'children' => [\n        [\n            'name' => 'Bar',\n\n            'children' => [\n                [ 'name' => 'Baz' ],\n            ],\n        ],\n    ],\n]);\n```\n\n`$node->children` now contains a list of created child nodes.\n\n#### Rebuilding a tree from array\n\nYou can easily rebuild a tree. This is useful for mass-changing the structure of\nthe tree.\n\n```php\nCategory::rebuildTree($data, $delete);\n```\n\n`$data` is an array of nodes:\n\n```php\n$data = [\n    [ 'id' => 1, 'name' => 'foo', 'children' => [ ... ] ],\n    [ 'name' => 'bar' ],\n];\n```\n\nThere is an id specified for node with the name of `foo` which means that existing\nnode will be filled and saved. If node is not exists `ModelNotFoundException` is\nthrown. Also, this node has `children` specified which is also an array of nodes;\nthey will be processed in the same manner and saved as children of node `foo`.\n\nNode `bar` has no primary key specified, so it will be created.\n\n`$delete` shows whether to delete nodes that are already exists but not present\nin `$data`. By default, nodes aren't deleted.\n\n##### Rebuilding a subtree\n\nAs of 4.2.8 you can rebuild a subtree:\n\n```php\nCategory::rebuildSubtree($root, $data);\n```\n\nThis constraints tree rebuilding to descendants of `$root` node.\n\n### Retrieving nodes\n\n*In some cases we will use an `$id` variable which is an id of the target node.*\n\n#### Ancestors and descendants\n\nAncestors make a chain of parents to the node. Helpful for displaying breadcrumbs\nto the current category.\n\nDescendants are all nodes in a sub tree, i.e. children of node, children of\nchildren, etc.\n\nBoth ancestors and descendants can be eagerly loaded.\n\n```php\n\u002F\u002F Accessing ancestors\n$node->ancestors;\n\n\u002F\u002F Accessing descendants\n$node->descendants;\n```\n\nIt is possible to load ancestors and descendants using custom query:\n\n```php\n$result = Category::ancestorsOf($id);\n$result = Category::ancestorsAndSelf($id);\n$result = Category::descendantsOf($id);\n$result = Category::descendantsAndSelf($id);\n```\n\nIn most cases, you need your ancestors to be ordered by the level:\n\n```php\n$result = Category::defaultOrder()->ancestorsOf($id);\n```\n\nA collection of ancestors can be eagerly loaded:\n\n```php\n$categories = Category::with('ancestors')->paginate(30);\n\n\u002F\u002F in view for breadcrumbs:\n@foreach($categories as $i => $category)\n    \u003Csmall>{{ $category->ancestors->count() ? implode(' > ', $category->ancestors->pluck('name')->toArray()) : 'Top Level' }}\u003C\u002Fsmall>\u003Cbr>\n    {{ $category->name }}\n@endforeach\n```\n\n#### Siblings\n\nSiblings are nodes that have same parent.\n\n```php\n$result = $node->getSiblings();\n\n$result = $node->siblings()->get();\n```\n\nTo get only next siblings:\n\n```php\n\u002F\u002F Get a sibling that is immediately after the node\n$result = $node->getNextSibling();\n\n\u002F\u002F Get all siblings that are after the node\n$result = $node->getNextSiblings();\n\n\u002F\u002F Get all siblings using a query\n$result = $node->nextSiblings()->get();\n```\n\nTo get previous siblings:\n\n```php\n\u002F\u002F Get a sibling that is immediately before the node\n$result = $node->getPrevSibling();\n\n\u002F\u002F Get all siblings that are before the node\n$result = $node->getPrevSiblings();\n\n\u002F\u002F Get all siblings using a query\n$result = $node->prevSiblings()->get();\n```\n\n#### Getting related models from other table\n\nImagine that each category `has many` goods. I.e. `HasMany` relationship is established.\nHow can you get all goods of `$category` and every its descendant? Easy!\n\n```php\n\u002F\u002F Get ids of descendants\n$categories = $category->descendants()->pluck('id');\n\n\u002F\u002F Include the id of category itself\n$categories[] = $category->getKey();\n\n\u002F\u002F Get goods\n$goods = Goods::whereIn('category_id', $categories)->get();\n```\n\n#### Including node depth\n\nIf you need to know at which level the node is:\n\n```php\n$result = Category::withDepth()->find($id);\n\n$depth = $result->depth;\n```\n\nRoot node will be at level 0. Children of root nodes will have a level of 1, etc.\n\nTo get nodes of specified level, you can apply `having` constraint:\n\n```php\n$result = Category::withDepth()->having('depth', '=', 1)->get();\n```\n\n__IMPORTANT!__ This will not work in database strict mode\n\n#### Default order\n\nAll nodes are strictly organized internally. By default, no order is\napplied, so nodes may appear in random order and this doesn't affect\ndisplaying a tree. You can order nodes by alphabet or other index.\n\nBut in some cases hierarchical order is essential. It is required for\nretrieving ancestors and can be used to order menu items.\n\nTo apply tree order `defaultOrder` method is used:\n\n```php\n$result = Category::defaultOrder()->get();\n```\n\nYou can get nodes in reversed order:\n\n```php\n$result = Category::reversed()->get();\n```\n\nTo shift node up or down inside parent to affect default order:\n\n```php\n$bool = $node->down();\n$bool = $node->up();\n\n\u002F\u002F Shift node by 3 siblings\n$bool = $node->down(3);\n```\n\nThe result of the operation is boolean value of whether the node has changed its\nposition.\n\n#### Constraints\n\nVarious constraints that can be applied to the query builder:\n\n-   __whereIsRoot()__ to get only root nodes;\n-   __hasParent()__ to get non-root nodes;\n-   __whereIsLeaf()__ to get only leaves;\n-   __hasChildren()__ to get non-leave nodes;\n-   __whereIsAfter($id)__ to get every node (not just siblings) that are after a node\n    with specified id;\n-   __whereIsBefore($id)__ to get every node that is before a node with specified id.\n\nDescendants constraints:\n\n```php\n$result = Category::whereDescendantOf($node)->get();\n$result = Category::whereNotDescendantOf($node)->get();\n$result = Category::orWhereDescendantOf($node)->get();\n$result = Category::orWhereNotDescendantOf($node)->get();\n$result = Category::whereDescendantAndSelf($id)->get();\n\n\u002F\u002F Include target node into result set\n$result = Category::whereDescendantOrSelf($node)->get();\n```\n\nAncestor constraints:\n\n```php\n$result = Category::whereAncestorOf($node)->get();\n$result = Category::whereAncestorOrSelf($id)->get();\n```\n\n`$node` can be either a primary key of the model or model instance.\n\n#### Building a tree\n\nAfter getting a set of nodes, you can convert it to tree. For example:\n\n```php\n$tree = Category::get()->toTree();\n```\n\nThis will fill `parent` and `children` relationships on every node in the set and\nyou can render a tree using recursive algorithm:\n\n```php\n$nodes = Category::get()->toTree();\n\n$traverse = function ($categories, $prefix = '-') use (&$traverse) {\n    foreach ($categories as $category) {\n        echo PHP_EOL.$prefix.' '.$category->name;\n\n        $traverse($category->children, $prefix.'-');\n    }\n};\n\n$traverse($nodes);\n```\n\nThis will output something like this:\n\n```\n- Root\n-- Child 1\n--- Sub child 1\n-- Child 2\n- Another root\n```\n\n##### Building flat tree\n\nAlso, you can build a flat tree: a list of nodes where child nodes are immediately\nafter parent node. This is helpful when you get nodes with custom order\n(i.e. alphabetically) and don't want to use recursion to iterate over your nodes.\n\n```php\n$nodes = Category::get()->toFlatTree();\n```\n\nPrevious example will output:\n\n```\nRoot\nChild 1\nSub child 1\nChild 2\nAnother root\n```\n\n##### Getting a subtree\n\nSometimes you don't need whole tree to be loaded and just some subtree of specific node.\nIt is show in following example:\n\n```php\n$root = Category::descendantsAndSelf($rootId)->toTree()->first();\n```\n\nIn a single query we are getting a root of a subtree and all of its\ndescendants that are accessible via `children` relation.\n\nIf you don't need `$root` node itself, do following instead:\n\n```php\n$tree = Category::descendantsOf($rootId)->toTree($rootId);\n```\n\n### Deleting nodes\n\nTo delete a node:\n\n```php\n$node->delete();\n```\n\n**IMPORTANT!** Any descendant that node has will also be deleted!\n\n**IMPORTANT!** Nodes are required to be deleted as models, **don't** try do delete them using a query like so:\n\n```php\nCategory::where('id', '=', $id)->delete();\n```\n\nThis will break the tree!\n\n`SoftDeletes` trait is supported, also on model level.\n\n### Helper methods\n\nTo check if node is a descendant of other node:\n\n```php\n$bool = $node->isDescendantOf($parent);\n```\n\nTo check whether the node is a root:\n\n```php\n$bool = $node->isRoot();\n```\n\nOther checks:\n\n*   `$node->isChildOf($other);`\n*   `$node->isAncestorOf($other);`\n*   `$node->isSiblingOf($other);`\n*   `$node->isLeaf()`\n\n### Checking consistency\n\nYou can check whether a tree is broken (i.e. has some structural errors):\n\n```php\n$bool = Category::isBroken();\n```\n\nIt is possible to get error statistics:\n\n```php\n$data = Category::countErrors();\n```\n\nIt will return an array with following keys:\n\n-   `oddness` -- the number of nodes that have wrong set of `lft` and `rgt` values\n-   `duplicates` -- the number of nodes that have same `lft` or `rgt` values\n-   `wrong_parent` -- the number of nodes that have invalid `parent_id` value that\n    doesn't correspond to `lft` and `rgt` values\n-   `missing_parent` -- the number of nodes that have `parent_id` pointing to\n    node that doesn't exists\n\n#### Fixing tree\n\nSince v3.1 tree can now be fixed. Using inheritance info from `parent_id` column,\nproper `_lft` and `_rgt` values are set for every node.\n\n```php\nNode::fixTree();\n```\n\n### Scoping\n\nImagine you have `Menu` model and `MenuItems`. There is a one-to-many relationship\nset up between these models. `MenuItem` has `menu_id` attribute for joining models\ntogether. `MenuItem` incorporates nested sets. It is obvious that you would want to\nprocess each tree separately based on `menu_id` attribute. In order to do so, you\nneed to specify this attribute as scope attribute:\n\n```php\nprotected function getScopeAttributes()\n{\n    return [ 'menu_id' ];\n}\n```\n\nBut now, in order to execute some custom query, you need to provide attributes\nthat are used for scoping:\n\n```php\nMenuItem::scoped([ 'menu_id' => 5 ])->withDepth()->get(); \u002F\u002F OK\nMenuItem::descendantsOf($id)->get(); \u002F\u002F WRONG: returns nodes from other scope\nMenuItem::scoped([ 'menu_id' => 5 ])->fixTree(); \u002F\u002F OK\n```\n\nWhen requesting nodes using model instance, scopes applied automatically based\non the attributes of that model:\n\n```php\n$node = MenuItem::findOrFail($id);\n\n$node->siblings()->withDepth()->get(); \u002F\u002F OK\n```\n\nTo get scoped query builder using instance:\n\n```php\n$node->newScopedQuery();\n```\n\n#### Scoping and eager loading\n\nAlways use scoped query when eager loading:\n\n```php\nMenuItem::scoped([ 'menu_id' => 5])->with('descendants')->findOrFail($id); \u002F\u002F OK\nMenuItem::with('descendants')->findOrFail($id); \u002F\u002F WRONG\n```\n\nRequirements\n------------\n\n- PHP >= 5.4\n- Laravel >= 4.1\n\nIt is highly suggested to use database that supports transactions (like MySql's InnoDb)\nto secure a tree from possible corruption.\n\nInstallation\n------------\n\nTo install the package, in terminal:\n\n```\ncomposer require kalnoy\u002Fnestedset\n```\n\n### Setting up from scratch\n\n#### The schema\n\nFor Laravel 5.5 and above users:\n\n```php\nSchema::create('table', function (Blueprint $table) {\n    ...\n    $table->nestedSet();\n});\n\n\u002F\u002F To drop columns\nSchema::table('table', function (Blueprint $table) {\n    $table->dropNestedSet();\n});\n```\n\nFor prior Laravel versions:\n\n```php\n...\nuse Kalnoy\\Nestedset\\NestedSet;\n\nSchema::create('table', function (Blueprint $table) {\n    ...\n    NestedSet::columns($table);\n});\n```\n\nTo drop columns:\n\n```php\n...\nuse Kalnoy\\Nestedset\\NestedSet;\n\nSchema::table('table', function (Blueprint $table) {\n    NestedSet::dropColumns($table);\n});\n```\n\n#### The model\n\nYour model should use `Kalnoy\\Nestedset\\NodeTrait` trait to enable nested sets:\n\n```php\nuse Kalnoy\\Nestedset\\NodeTrait;\n\nclass Foo extends Model {\n    use NodeTrait;\n}\n```\n\n### Migrating existing data\n\n#### Migrating from other nested set extension\n\nIf your previous extension used different set of columns, you just need to override\nfollowing methods on your model class:\n\n```php\npublic function getLftName()\n{\n    return 'left';\n}\n\npublic function getRgtName()\n{\n    return 'right';\n}\n\npublic function getParentIdName()\n{\n    return 'parent';\n}\n\n\u002F\u002F Specify parent id attribute mutator\npublic function setParentAttribute($value)\n{\n    $this->setParentIdAttribute($value);\n}\n```\n\n#### Migrating from basic parentage info\n\nIf your tree contains `parent_id` info, you need to add two columns to your schema:\n\n```php\n$table->unsignedInteger('_lft');\n$table->unsignedInteger('_rgt');\n```\n\nAfter [setting up your model](#the-model) you only need to fix the tree to fill\n`_lft` and `_rgt` columns:\n\n```php\nMyModel::fixTree();\n```\n\nLicense\n=======\n\nCopyright (c) 2017 Alexander Kalnoy\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and\u002For sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","lazychaser\u002Flaravel-nestedset 是一个用于在 Laravel 项目中处理关系数据库中树结构的包。它基于嵌套集模型，支持高效地存储和查询层次数据，特别适用于更新频率较低但需要频繁读取的场景，如构建多级菜单或商品分类。该包为 Laravel 4 到 Laravel 8+ 提供了广泛的支持，并且提供了包括插入、检索、删除节点以及一致性检查等在内的全面功能。通过定义节点之间的父子关系及祖先后代关系，用户可以轻松操作复杂的树状数据结构。","2026-06-11 03:18:12","top_language"]