laravel-nestedset

repository·v7·Indexed 25 days ago

https://github.com/lazychaser/laravel-nestedset

A Laravel package for managing hierarchical data using the Nested Set Model. It provides tools for efficient querying of ancestors, descendants, and siblings, as well as methods for inserting, moving, and rebuilding tree structures. Features include database schema macros (nestedSet, dropNestedSet), a NodeTrait for models, tree integrity checks (isBroken, fixTree), and support for multi-tree environments via scoping.

Tokens
3K
Snippets
6
Records
28
Agent score
86%

What's inside laravel-nestedset

  1. Upgrade to 2.0

    v7

    When upgrading to version 2.0, be aware of these breaking changes:

    • Automatic Saving: Calling $parent->append($node) and $parent->prepend($node) now automatically saves the $node. These methods return a boolean indicating whether the node was successfully saved.
    • Ancestors Retrieval: The ancestorsOf method now returns only the ancestors, excluding the target node itself.
    • Ordering: Default order is no longer applied automatically. To ensure nodes are returned in tree-order, you must explicitly call defaultOrder() on the query.
    • Root Node Management: A root node is no longer required. Consequently, the NestedSet::createRoot method has been removed.
    • Schema Changes: NestedSet::columns no longer automatically creates a foreign key for the parent_id column.
  2. Use Scoping for multi-tree environments

    v7

    If your table contains multiple independent trees (e.g., a menu_id column), you must use scoping to ensure queries only affect the intended tree.

    1. Define Scopes: In your model, implement getScopeAttributes() to return the attribute(s) used for scoping.
    2. Query with Scopes: Use scoped([...]) when performing queries.
    3. Eager Loading: Always use scoped queries when eager loading relationships to avoid loading nodes from other trees.

    Note: When requesting nodes via a model instance, scopes are applied automatically.

  3. Insert and move nodes in the tree

    v7

    Structural manipulations are deferred until you call save() on the model. It is highly recommended to wrap these operations in a database transaction.

    To check if a node actually changed its position after saving, use the hasMoved() method.

    Creating Nodes

    • As Root: Category::create($attributes); or $node->save(); (if new).
    • From existing node to Root: $node->saveAsRoot(); or $node->makeRoot()->save();.

    Appending/Prepending to a Parent

    • Append: $node->appendToNode($parent)->save();, $parent->appendNode($node);, or $parent->children()->create($attributes);.
    • Prepend: $node->prependToNode($parent)->save(); or $parent->prependNode($node);.

    Inserting relative to a neighbor

    • Explicit save: $node->afterNode($neighbor)->save(); or $node->beforeNode($neighbor)->save();.
    • Implicit save: $node->insertAfterNode($neighbor); or $node->insertBeforeNode($neighbor);.
    if ($node->save()) {
        $moved = $node->hasMoved();
    }
  4. Configure the database schema for Nested Sets

    v7

    You must add the necessary columns to your table to support the Nested Set Model.

    For Laravel 5.5 and above, use the nestedSet() blueprint method. For older versions, use the NestedSet::columns($table) method.

    To remove these columns, use dropNestedSet() (Laravel 5.5+) or NestedSet::dropColumns($table) (older versions).

    // Laravel 5.5+
    Schema::create('table', function (Blueprint $table) {
        $table->nestedSet();
    });
    
    // Prior Laravel versions
    use Kalnoy\Nestedset\NestedSet;
    Schema::create('table', function (Blueprint $table) {
        NestedSet::columns($table);
    });
  5. Delete nodes safely

    v7

    To delete a node, use the model's delete() method.

    WARNING: Deleting a node will also automatically delete all of its descendants.

    CRITICAL: Do not use a direct database query like Category::where('id', '=', $id)->delete(); as this will break the tree structure. Always delete via the model instance.

  6. Upgrade from 4.0 to 4.1

    v7

    When upgrading from version 4.0 to 4.1, note the following changes:

    • Trait Migration: The Nested Set functionality has moved to the Kalnoy\Nestedset\NodeTrait. However, the legacy Kalnoy\Nestedset\Node class remains available for backward compatibility.
    • Method Renaming: Some methods on the trait have been renamed. While these renamed methods are still available on the legacy Node class, you should check the changelog for specific renames to ensure compatibility with the trait.
    • Ordering Changes: The default order is no longer automatically applied for the following methods: siblings(), descendants(), prevNodes, and nextNodes.
  7. Check and fix tree consistency

    v7

    If you suspect the tree structure is corrupted, use the following methods:

    • isBroken(): Returns true if the tree has structural errors.
    • countErrors(): Returns an array of error statistics:
      • oddness: Nodes with incorrect lft and rgt values.
      • duplicates: Nodes with duplicate lft or rgt values.
      • wrong_parent: Nodes with invalid parent_id values.
      • missing_parent: Nodes with parent_id pointing to non-existent nodes.
    • fixTree(): Automatically repairs the tree using parent_id information to set correct _lft and _rgt values.
  8. Build or rebuild a tree from an array

    v7

    Recursive Creation

    When using Category::create(), if the attributes array contains a children key, the package will recursively create those nodes.

    $node = Category::create([
        'name' => 'Foo',
        'children' => [
            ['name' => 'Bar', 'children' => [['name' => 'Baz']]],
        ],
    ]);

    Mass Rebuilding

    Use rebuildTree to mass-change the structure.

    • $data: Array of nodes. If an id is provided, the existing node is updated. If no id is provided, a new node is created.
    • $delete: Boolean. If true, nodes existing in the database but missing from $data will be deleted.

    Rebuilding a Subtree

    To rebuild only the descendants of a specific node, use rebuildSubtree($root, $data).

  9. Retrieve ancestors, descendants, and siblings

    v7

    The package provides several ways to traverse the tree:

    Ancestors and Descendants

    • Relationships: $node->ancestors and $node->descendants (can be eagerly loaded).
    • Query Builder:
      • Category::ancestorsOf($id)
      • Category::ancestorsAndSelf($id)
      • Category::descendantsOf($id)
      • Category::descendantsAndSelf($id)
    • Ordering: Use Category::defaultOrder()->ancestorsOf($id) to ensure ancestors are ordered by level.

    Siblings

    • All siblings: $node->getSiblings() or $node->siblings()->get().
    • Next siblings: $node->getNextSibling() (single) or $node->getNextSiblings() (all) or $node->nextSiblings()->get() (query).
    • Previous siblings: $node->getPrevSibling() (single) or $node->getPrevSiblings() (all) or $node->prevSiblings()->get() (query).
  10. Convert node sets to tree structures

    v7

    After retrieving a collection of nodes, you can convert them into a hierarchical tree or a flat list.

    • toTree(): Fills parent and children relationships on every node in the set.
    • toFlatTree(): Returns a list where child nodes are immediately after their parent nodes (useful for custom ordering like alphabetical).