Effective tree structures in Laravel 4-5

Overview

Build Status Total Downloads Latest Stable Version Latest Unstable Version License

This is a Laravel 4-8 package for working with trees in relational databases.

  • Laravel 5.7, 5.8, 6.0, 7.0, 8.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!

Contents:

What are nested sets?

Nested sets or Nested Set Model is a way to effectively store hierarchical data in a relational table. From wikipedia:

The nested set model is to number the nodes according to a tree traversal, which visits each node twice, assigning numbers in the order of visiting, and at both visits. This leaves two numbers for each node, which are stored as two attributes. Querying becomes inexpensive: hierarchy membership can be tested by comparing these numbers. Updating requires renumbering and is therefore expensive.

Applications

NSM shows good performance when tree is updated rarely. It is tuned to be fast for getting related nodes. It'is ideally suited for building multi-depth menu or categories for shop.

Documentation

Suppose that we have a model Category; a $node variable is an instance of that model and the node that we are manipulating. It can be a fresh model or one from database.

Relationships

Node has following relationships that are fully functional and can be eagerly loaded:

  • Node belongs to parent
  • Node has many children
  • Node has many ancestors
  • Node has many descendants

Inserting nodes

Moving and inserting nodes includes several database queries, so it is highly recommended to use transactions.

IMPORTANT! As of v4.2.0 transaction is not automatically started

Another important note is that structural manipulations are deferred until you hit save on model (some methods implicitly call save and return boolean result of the operation).

If model is successfully saved it doesn't mean that node was moved. If your application depends on whether the node has actually changed its position, use hasMoved method:

if ($node->save()) {
    $moved = $node->hasMoved();
}

Creating nodes

When you simply creating a node, it will be appended to the end of the tree:

Category::create($attributes); // Saved as root
$node = new Category($attributes);
$node->save(); // Saved as root

In this case the node is considered a root which means that it doesn't have a parent.

Making a root from existing node

// #1 Implicit save
$node->saveAsRoot();

// #2 Explicit save
$node->makeRoot()->save();

The node will be appended to the end of the tree.

Appending and prepending to the specified parent

If you want to make node a child of other node, you can make it last or first child.

In following examples, $parent is some existing node.

There are few ways to append a node:

// #1 Using deferred insert
$node->appendToNode($parent)->save();

// #2 Using parent node
$parent->appendNode($node);

// #3 Using parent's children relationship
$parent->children()->create($attributes);

// #5 Using node's parent relationship
$node->parent()->associate($parent)->save();

// #6 Using the parent attribute
$node->parent_id = $parent->id;
$node->save();

// #7 Using static method
Category::create($attributes, $parent);

And only a couple ways to prepend:

// #1
$node->prependToNode($parent)->save();

// #2
$parent->prependNode($node);

Inserting before or after specified node

You can make $node to be a neighbor of the $neighbor node using following methods:

$neighbor must exists, target node can be fresh. If target node exists, it will be moved to the new position and parent will be changed if it's required.

# Explicit save
$node->afterNode($neighbor)->save();
$node->beforeNode($neighbor)->save();

# Implicit save
$node->insertAfterNode($neighbor);
$node->insertBeforeNode($neighbor);

Building a tree from array

When using static method create on node, it checks whether attributes contains children key. If it does, it creates more nodes recursively.

$node = Category::create([
    'name' => 'Foo',

    'children' => [
        [
            'name' => 'Bar',

            'children' => [
                [ 'name' => 'Baz' ],
            ],
        ],
    ],
]);

$node->children now contains a list of created child nodes.

Rebuilding a tree from array

You can easily rebuild a tree. This is useful for mass-changing the structure of the tree.

Category::rebuildTree($data, $delete);

$data is an array of nodes:

$data = [
    [ 'id' => 1, 'name' => 'foo', 'children' => [ ... ] ],
    [ 'name' => 'bar' ],
];

There is an id specified for node with the name of foo which means that existing node will be filled and saved. If node is not exists ModelNotFoundException is thrown. Also, this node has children specified which is also an array of nodes; they will be processed in the same manner and saved as children of node foo.

Node bar has no primary key specified, so it will be created.

$delete shows whether to delete nodes that are already exists but not present in $data. By default, nodes aren't deleted.

Rebuilding a subtree

As of 4.2.8 you can rebuild a subtree:

Category::rebuildSubtree($root, $data);

This constraints tree rebuilding to descendants of $root node.

Retrieving nodes

In some cases we will use an $id variable which is an id of the target node.

Ancestors and descendants

Ancestors make a chain of parents to the node. Helpful for displaying breadcrumbs to the current category.

Descendants are all nodes in a sub tree, i.e. children of node, children of children, etc.

Both ancestors and descendants can be eagerly loaded.

// Accessing ancestors
$node->ancestors;

// Accessing descendants
$node->descendants;

It is possible to load ancestors and descendants using custom query:

$result = Category::ancestorsOf($id);
$result = Category::ancestorsAndSelf($id);
$result = Category::descendantsOf($id);
$result = Category::descendantsAndSelf($id);

In most cases, you need your ancestors to be ordered by the level:

$result = Category::defaultOrder()->ancestorsOf($id);

A collection of ancestors can be eagerly loaded:

$categories = Category::with('ancestors')->paginate(30);

// in view for breadcrumbs:
@foreach($categories as $i => $category)
    <small>{{ $category->ancestors->count() ? implode(' > ', $category->ancestors->pluck('name')->toArray()) : 'Top Level' }}</small><br>
    {{ $category->name }}
@endforeach

Siblings

Siblings are nodes that have same parent.

$result = $node->getSiblings();

$result = $node->siblings()->get();

To get only next siblings:

// Get a sibling that is immediately after the node
$result = $node->getNextSibling();

// Get all siblings that are after the node
$result = $node->getNextSiblings();

// Get all siblings using a query
$result = $node->nextSiblings()->get();

To get previous siblings:

// Get a sibling that is immediately before the node
$result = $node->getPrevSibling();

// Get all siblings that are before the node
$result = $node->getPrevSiblings();

// Get all siblings using a query
$result = $node->prevSiblings()->get();

Getting related models from other table

Imagine that each category has many goods. I.e. HasMany relationship is established. How can you get all goods of $category and every its descendant? Easy!

// Get ids of descendants
$categories = $category->descendants()->pluck('id');

// Include the id of category itself
$categories[] = $category->getKey();

// Get goods
$goods = Goods::whereIn('category_id', $categories)->get();

Including node depth

If you need to know at which level the node is:

$result = Category::withDepth()->find($id);

$depth = $result->depth;

Root node will be at level 0. Children of root nodes will have a level of 1, etc.

To get nodes of specified level, you can apply having constraint:

$result = Category::withDepth()->having('depth', '=', 1)->get();

IMPORTANT! This will not work in database strict mode

Default order

All nodes are strictly organized internally. By default, no order is applied, so nodes may appear in random order and this doesn't affect displaying a tree. You can order nodes by alphabet or other index.

But in some cases hierarchical order is essential. It is required for retrieving ancestors and can be used to order menu items.

To apply tree order defaultOrder method is used:

$result = Category::defaultOrder()->get();

You can get nodes in reversed order:

$result = Category::reversed()->get();

To shift node up or down inside parent to affect default order:

$bool = $node->down();
$bool = $node->up();

// Shift node by 3 siblings
$bool = $node->down(3);

The result of the operation is boolean value of whether the node has changed its position.

Constraints

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.

Descendants constraints:

$result = Category::whereDescendantOf($node)->get();
$result = Category::whereNotDescendantOf($node)->get();
$result = Category::orWhereDescendantOf($node)->get();
$result = Category::orWhereNotDescendantOf($node)->get();
$result = Category::whereDescendantAndSelf($id)->get();

// Include target node into result set
$result = Category::whereDescendantOrSelf($node)->get();

Ancestor constraints:

$result = Category::whereAncestorOf($node)->get();
$result = Category::whereAncestorOrSelf($id)->get();

$node can be either a primary key of the model or model instance.

Building a tree

After getting a set of nodes, you can convert it to tree. For example:

$tree = Category::get()->toTree();

This will fill parent and children relationships on every node in the set and you can render a tree using recursive algorithm:

$nodes = Category::get()->toTree();

$traverse = function ($categories, $prefix = '-') use (&$traverse) {
    foreach ($categories as $category) {
        echo PHP_EOL.$prefix.' '.$category->name;

        $traverse($category->children, $prefix.'-');
    }
};

$traverse($nodes);

This will output something like this:

- Root
-- Child 1
--- Sub child 1
-- Child 2
- Another root
Building flat tree

Also, you can build a flat tree: a list of nodes where child nodes are immediately after parent node. This is helpful when you get nodes with custom order (i.e. alphabetically) and don't want to use recursion to iterate over your nodes.

$nodes = Category::get()->toFlatTree();

Previous example will output:

Root
Child 1
Sub child 1
Child 2
Another root
Getting a subtree

Sometimes you don't need whole tree to be loaded and just some subtree of specific node. It is show in following example:

$root = Category::descendantsAndSelf($rootId)->toTree()->first();

In a single query we are getting a root of a subtree and all of its descendants that are accessible via children relation.

If you don't need $root node itself, do following instead:

$tree = Category::descendantsOf($rootId)->toTree($rootId);

Deleting nodes

To delete a node:

$node->delete();

IMPORTANT! Any descendant that node has will also be deleted!

IMPORTANT! Nodes are required to be deleted as models, don't try do delete them using a query like so:

Category::where('id', '=', $id)->delete();

This will break the tree!

SoftDeletes trait is supported, also on model level.

Helper methods

To check if node is a descendant of other node:

$bool = $node->isDescendantOf($parent);

To check whether the node is a root:

$bool = $node->isRoot();

Other checks:

  • $node->isChildOf($other);
  • $node->isAncestorOf($other);
  • $node->isSiblingOf($other);
  • $node->isLeaf()

Checking consistency

You can check whether a tree is broken (i.e. has some structural errors):

$bool = Category::isBroken();

It is possible to get error statistics:

$data = Category::countErrors();

It will return an array with following keys:

  • oddness -- the number of nodes that have wrong set of lft and rgt values
  • duplicates -- the number of nodes that have same lft or rgt values
  • wrong_parent -- the number of nodes that have invalid parent_id value that doesn't correspond to lft and rgt values
  • missing_parent -- the number of nodes that have parent_id pointing to node that doesn't exists

Fixing tree

Since v3.1 tree can now be fixed. Using inheritance info from parent_id column, proper _lft and _rgt values are set for every node.

Node::fixTree();

Scoping

Imagine you have Menu model and MenuItems. There is a one-to-many relationship set up between these models. MenuItem has menu_id attribute for joining models together. MenuItem incorporates nested sets. It is obvious that you would want to process each tree separately based on menu_id attribute. In order to do so, you need to specify this attribute as scope attribute:

protected function getScopeAttributes()
{
    return [ 'menu_id' ];
}

But now, in order to execute some custom query, you need to provide attributes that are used for scoping:

MenuItem::scoped([ 'menu_id' => 5 ])->withDepth()->get(); // OK
MenuItem::descendantsOf($id)->get(); // WRONG: returns nodes from other scope
MenuItem::scoped([ 'menu_id' => 5 ])->fixTree(); // OK

When requesting nodes using model instance, scopes applied automatically based on the attributes of that model:

$node = MenuItem::findOrFail($id);

$node->siblings()->withDepth()->get(); // OK

To get scoped query builder using instance:

$node->newScopedQuery();

Scoping and eager loading

Always use scoped query when eager loading:

MenuItem::scoped([ 'menu_id' => 5])->with('descendants')->findOrFail($id); // OK
MenuItem::with('descendants')->findOrFail($id); // WRONG

Requirements

  • PHP >= 5.4
  • Laravel >= 4.1

It is highly suggested to use database that supports transactions (like MySql's InnoDb) to secure a tree from possible corruption.

Installation

To install the package, in terminal:

composer require kalnoy/nestedset

Setting up from scratch

The schema

For Laravel 5.5 and above users:

Schema::create('table', function (Blueprint $table) {
    ...
    $table->nestedSet();
});

// To drop columns
Schema::table('table', function (Blueprint $table) {
    $table->dropNestedSet();
});

For prior Laravel versions:

...
use Kalnoy\Nestedset\NestedSet;

Schema::create('table', function (Blueprint $table) {
    ...
    NestedSet::columns($table);
});

To drop columns:

...
use Kalnoy\Nestedset\NestedSet;

Schema::table('table', function (Blueprint $table) {
    NestedSet::dropColumns($table);
});

The model

Your model should use Kalnoy\Nestedset\NodeTrait trait to enable nested sets:

use Kalnoy\Nestedset\NodeTrait;

class Foo extends Model {
    use NodeTrait;
}

Migrating existing data

Migrating from other nested set extension

If your previous extension used different set of columns, you just need to override following methods on your model class:

public function getLftName()
{
    return 'left';
}

public function getRgtName()
{
    return 'right';
}

public function getParentIdName()
{
    return 'parent';
}

// Specify parent id attribute mutator
public function setParentAttribute($value)
{
    $this->setParentIdAttribute($value);
}

Migrating from basic parentage info

If your tree contains parent_id info, you need to add two columns to your schema:

$table->unsignedInteger('_lft');
$table->unsignedInteger('_rgt');

After setting up your model you only need to fix the tree to fill _lft and _rgt columns:

MyModel::fixTree();

License

Copyright (c) 2017 Alexander Kalnoy

Permission 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/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE 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.

Comments
  • Creating Node model in observer fails

    Creating Node model in observer fails

    Hi

    Suppose the following setup where a node model is being created as a consequence of the creation of ModelA:

    class NodeModel extends Model
    {
        use NodeTrait;
    }
    
    class ModelA extends Model {
    
        protected static function boot()
        {
            parent::boot();
    
            static::created(function ($model) {
                NodeModel::create([]);
            });
        }
    }
    

    Unfortunately this doesn't work. On dd'ing the NodeModel::create line a model is effectively returned with $exists set to true, but it simply isn't persisted to the database. Upon removing the NodeTrait the setup does work and the NodeModel can be found in the database as expected.

    Can someone replicate this?

    opened by Propaganistas 32
  • Deleting and appending to a subtree

    Deleting and appending to a subtree

    There seems to be a problem with tree updating.

    Let's suppose I have a simple 2 level tree created using the model methods create() and append(), i.e. only a root node and a couple children. The tree is correct in the begining.

    Now, if I replace the whole 2nd level, i.e. everything under my root node, and append new nodes to the root node, the _lft and _rgt numbers for the first appended child outbound the parents'. Despite the children() method returning all the children nodes (because the parent_id property links them correctly), a call to descendantsOf(), which traverses the tree using the _lft and _rgt numbers, leave the first child out.

    The procedure I used to replace the 2nd level is by calling delete() on every child to be removed and then calling append() on the root node for each new child. It is always the first newly appended child which is left out because the _lft and _rgt numbers on the parent don't include this child's ones.

    opened by clapas 21
  • Undefined method Builder::moveNode()

    Undefined method Builder::moveNode()

    It's probably me missing something, but when I try to append a node to another one I receive the following error

    BadMethodCallException in Builder.php line 2345:
    Call to undefined method Illuminate\Database\Query\Builder::moveNode()
    

    My code is as follows

    $first = Category::first();
    $last = Category::orderBy('id', 'desc')->first();
    $last->appendToNode($first)->save();
    

    The Category model uses Kalnoy\Nestedset\NodeTrait.

    opened by ivandokov 20
  • Updating existing tree

    Updating existing tree

    Hi @lazychaser, I am currently using this package and so far I like it. But I got stuck in one part that probably is possible to do but I haven't found solution in documentation. :smile:

    Can I use "create" method to update or delete all nodes which are not present in scope?

    I need some method that'll do same job as seeding/mass assingment methods in etrepat/baum project.

    In my example, I am trying to make forum where user can reorder the display of forum and subforum categories.

    Thank you :smile:

    enhancement 
    opened by DamjanZ 18
  • Nothing happens..

    Nothing happens..

    Hi, here's how I am trying to get a category to be a child of another category:

    $category->appendTo($parentCategory)->save()
    

    both $category and $parentCategory are an instance of Node class. The category saves fine, but with the default values in the _lft, _rgt and parent_id.. same thing that would have happened if I used save() alone without appendTo. What am I missing?

    opened by AdamGold 16
  • Set is broken but fixTree return null

    Set is broken but fixTree return null

    I have a nested set which contains about 5200 nodes. The isBroken() it returns true but fixTree() always returns null. Is there any problem with a large set?

    {
        "name": "kalnoy/nestedset",
        "version": "v4.0.1"
    }
    
    opened by hiendv 14
  • adding a multi trees in 1 table feature?

    adding a multi trees in 1 table feature?

    I have a use case of like 2000 categories with endless sub categories but it never gonna be too big most will be 3-8 deep sub categories like must 100 items in each tree instead of creating 1 big tree with 2000 children's I want to create from each category a new nestedset in same table. column like nestedset_id that it will create new nestedset based on nestedset_id? so for example this nestedset: https://upload.wikimedia.org/wikipedia/commons/thumb/4/41/NestedSetModel.svg/701px-NestedSetModel.svg.png

    we can simplify it by saying that mens is new nestedset and womens is new nestedset so like the women set will be left=1 and right=12 and men's sets will be 1-8 so a change in the men nestedset will not change anything in the women sets and vise versa so we should get pretty good insert performance

    opened by boynet 14
  • Not compatible with Laravel 8.43

    Not compatible with Laravel 8.43

    In BaseRelation.php line 121:

    Declaration of Kalnoy\Nestedset\BaseRelation::getRelationQuery(Illuminate\D
    atabase\Eloquent\Builder $query, Illuminate\Database\Eloquent\Builder $pare
    nt, $columns = Array) should be compatible with Illuminate\Database\Eloquen
    t\Relations\Relation::getRelationQuery()

    opened by jwohlfert23 13
  • Model  / Single Table Inheritance -

    Model / Single Table Inheritance - "parent_id" sets/unsets (null) itself.

    Hi there,

    laravel/framework v5.1.29
    kalnoy/nestedset v3.1.2
    

    Firstly thank you for sharing such a great plugin and forgive me if this is being caused by something unrelated...

    $post->update(['parent_id' => 2]);
    dd($post);
    

    This produces the following output (as I'd expect):

        [
        "id" => 8
        "_lft" => 20
        "_rgt" => 21
        "parent_id" => 2
    ]
    

    If I update a second time with the same logic I get the following:

    [
        "id" => 8
        "_lft" => 21
        "_rgt" => 22
        "parent_id" => null
    ]
    

    It just seems to set and unset itself? I don't understand why it would behave like this as I'm always specifying the parent_id as 2?

    This behaviour also happens when using fill()->save();

    It doesn't happen however if I do:

    $post->parent_id = 2;
    $post->save();
    

    This only happens on my "Post" model but works fine on other models in my application.

    I am using https://github.com/Nanigans/single-table-inheritance so my Post model is using the SingleTableInheritanceTrait.

    It uses a global scope which applies a whereIn() to all model queries and and an observer which listens for the ::saving() event. Even if I comment this out I still get the same behaviour.

    //static::observe(new SingleTableInheritanceObserver());
    

    I understand I'm asking you to support a potential conflict with another plugin but even if you could push me in the right direction to debug it I'd be very grateful.

    opened by gritdigital-mibu 12
  • Seeding

    Seeding

    Can u plz explain how to seed a nested set model. I can't figure out how to fill in the fields. Even when I try building a tree like demonstrated. It gives me an error saying: Invalid argument supplied for foreach() inNode.php line 758. The seeding code is: Department::create( [ 'name' => 'A', 'head_user_id'=>2222, 'admin_user_id'=>2222, 'children' => [ [ 'name' => 'B', 'head_user_id'=>1111, 'admin_user_id'=>1111, ], ], ]);

    opened by mrabbasi 12
  • up() / down() affects nothing.

    up() / down() affects nothing.

    I'm using L5.2 on PHP7.

    There are 4 nodes(categories) on the root level.

    $category = Category::find($id); // finding 2nd node.
    $category->down();
    

    $category->down() returns true, but all rows on category table doesn't change at all.

    opened by domesticlion 11
  • Integrity constraint violation error when deleting a node.

    Integrity constraint violation error when deleting a node.

    Hi, I'm having a problem when I try to delete a node with descendants.

    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    use App\Models\Post\Category;
    
    class Post extends Model
    {
        use HasFactory;
    
        ...
    
    
        /**
         * The categories that belong to the post.
         */
        public function categories()
        {
            return $this->belongsToMany(Category::class);
        }
    
        ...
    
    }
    
    namespace App\Models\Post;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    use App\Models\Post;
    use Kalnoy\Nestedset\NodeTrait;
    
    class Category extends Model
    {
        use HasFactory, NodeTrait;
    
        protected $table = 'post_categories';
    
        ...
    
        /**
         * The posts that belong to the category.
         */
        public function posts()
        {
            return $this->belongsToMany(Post::class);
        }
    
    
        public function delete()
        {
            $this->posts()->detach(); 
            parent::delete();
        }
    
        ...
    
    }
    

    When I delete a node that hasn't any descendant everything it's ok.

    $category->delete();

    Now when I try to delete a node that has one or more descendants I get this error:

    SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`myproject`.`category_post`, CONSTRAINT `category_post_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `post_categories` (`id`))
    delete from `post_categories` where `post_categories`.`_lft` between 4 and 8
    

    It looks like the Category delete() method is not called when the nested-set trait deletes the descendants.
    How can I fix this issue, should I delete the descendant nodes manually ?

    opened by Duddy67 0
  • Bypassing nestedset functionality for PHPUnit tests

    Bypassing nestedset functionality for PHPUnit tests

    We have a large dataset based around an Eloquent model using the NodeTrait trait, with many thousands of records (both in production, and locally, for testing). Project current runs with Laravel 8.x and laravel-nestedset 6.x on PHP 7.4.

    As part of our PHPUnit tests setUp process, an instance of this model is created and persisted to the database. Each test uses Laravel's DatabaseTransactions system, whereby the transaction is rolled back each time.

    However, due to the way nestedset works, the entry created during setUp gets assigned a lft or rgt value which means another entry in the table (not actually related to the entry created for the test) gets deleted during the teardown process in which we delete the entry which has been created for the test. This is something we want to avoid happening.

    The entry created for the tests is totally 'autonomous' for the purposes of the tests, and doesn't need to 'exist' within the tree structure at all.

    So we're wondering - is there a way to completely 'bypass' the listeners that are set up in NodeTrait (in bootNodeTrait()), or some other way entirely, such that none of those processes are triggered when a particular entry is created or deleted - therefore the entry can temporarily exist in the table without any lft/rgt values set at all, and nothing effecting other entries happening when it's deleted. e.g. a completely 'stealth' entry.

    Something similar to how Laravel's WithoutModelEvents for seeders works, maybe?

    @lazychaser

    opened by jonnott 1
  • Search by descendants but get ancestors

    Search by descendants but get ancestors

    Hello community and thanks to creators for beautiful package! Suppose I have a tree:

    • parent1 -- child1 -- child2 --- grandson1 --- grandson2
    • parent2 and so on...

    I need search children and grandsons (and next depth levels) but tree should be like above (in natural order).

    After search:

    Category::with('ancestors')
        ->when($request->title, function ($query) use ($request) {
            $locale = app()->getLocale();
            $title = $request->title;
            $sql = "title::json->>'$locale' ilike '%$title%'";
            $query->whereRaw($sql);
        });
    

    I get something like:

    • grandson1 -- ancestors[ --- child1 ---- ancestors [ ----- parent1 ] ]

    But I need normal tree. I must do reverse by hand or there is a way?

    reversed() doesn't do the job, it is reverse in one depth but I need swap ancestors and descendants (the most depth level should be first) Thanx.

    opened by pls-kick-me 0
Releases(v4.1.0)
  • v4.1.0(Mar 2, 2016)

    New features

    Converted to trait

    Nested set features are now enabled using a trait and overrides as less methods as possible. For smooth transition, Node class is still available but is now deprecated and will be removed in the next significant release. Also, some methods were renamed (see changelog).

    Scoping

    You can now store multiple trees in the same table by filtering using model attributes. Read more.

    Rebuilding a tree

    In this release, tree rebuilding feature is added. You can completely rebuild a tree from an array. This can be used when you editing your tree on the client side and then sending full tree to the server. Algorithm updates nodes based on primary key, creates new ones and can delete nodes that are not on array. More info.

    Descendants as a relation

    Nodes now have descendants relation which can be eagerly loaded. This is helpful when you have a large tree and need to load few subtrees:

    $nodes = Category::with('descendants')->whereIn('id', $idList)->get();
    

    Other

    hasParent and hasChildren is now deprecated in favor of default laravel functionality has('parent') and has('children').

    Default order is no longer applied for siblings(), descendants(), prevNodes(), nextNodes().

    Other small features and bug fixes. See changelog.

    Source code(tar.gz)
    Source code(zip)
  • v4.1.0-beta(Feb 24, 2016)

    • #75: Converted to trait. Following methods were renamed:
    • appendTo to appendToNode
    • prependTo to prependToNode
    • insertBefore to insertBeforeNode
    • insertAfter to insertAfterNode
    • getNext to getNextNode
    • getPrev to getPrevNode
    • #82: Fixing tree now handles case when nodes pointing to non-existing parent
    • The number of missing parent is now returned when using countErrors
    • #79: implemented scoping feature
    • #81: moving node now makes model dirty before saving it
    • #45: descendants is now a relation that can be eagerly loaded
    • hasChildren and hasParent are now deprecated. Use has('children') has('parent') instead
    • Default order is no longer applied for siblings(), descendants(), prevNodes, nextNodes
    • #50: implemented tree rebuilding feature
    Source code(tar.gz)
    Source code(zip)
  • v2.0-beta2(Jul 15, 2014)

  • v2.0-beta(Jun 17, 2014)

Owner
Alexander Kalnoy
Alexander Kalnoy
Doctrine2 behavioral extensions, Translatable, Sluggable, Tree-NestedSet, Timestampable, Loggable, Sortable

Doctrine Behavioral Extensions This package contains extensions for Doctrine ORM and MongoDB ODM that offer new functionality or tools to use Doctrine

Doctrine Extensions 3.8k Jan 5, 2023
A simple PHP and MySQL based internet forum that displays the messages in classical threaded view (tree structure)

my little forum my little forum is a simple PHP and MySQL based internet forum that displays the messages in classical threaded view (tree structure).

Mark Hoschek 97 Dec 29, 2022
A Laravel package to output a specific sql to your favourite debugging tool. The supported log output is Laravel Telescope, Laravel Log, Ray, Clockwork, Laravel Debugbar and your browser.

Laravel showsql A Laravel package to output a specific sql to your favourite debugging tool, your browser or your log file. Use case You often want to

Dieter Coopman 196 Dec 28, 2022
Laravel Thermite is an extended PostgreSQL Laravel database driver to connect to a CockroachDB cluster.

Laravel Thermite Laravel Thermite is an extended PostgreSQL Laravel database driver to connect to a CockroachDB cluster. ?? Supporting If you are usin

Renoki Co. 9 Nov 15, 2022
This project is about import CSV to Laravel with Laravel Excel library.

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Abraham Víctor Zaragoza Rodríguez 1 Nov 20, 2021
[READ ONLY] Subtree split of the Illuminate Database component (see laravel/framework)

Illuminate Database The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style OR

The Laravel Components 2.5k Dec 27, 2022
Baum is an implementation of the Nested Set pattern for Laravel's Eloquent ORM.

Baum Baum is an implementation of the Nested Set pattern for Laravel 5's Eloquent ORM. For Laravel 4.2.x compatibility, check the 1.0.x branch branch

Estanislau Trepat 2.2k Jan 3, 2023
Driver to seamlessly integrate the Backup Manager into Laravel applications.

Laravel Driver for the Database Backup Manager This package pulls in the framework agnostic Backup Manager and provides seamless integration with Lara

Backup Manager 636 Dec 30, 2022
Adjacency List’ed Closure Table database design pattern implementation for the Laravel framework.

ClosureTable This is a database manipulation package for the Laravel 5.4+ framework. You may want to use it when you need to store and operate hierarc

Yan Ivanov 441 Dec 11, 2022
A drop-in library for certain database functionality in Laravel, that allows for extra features that may never make it into the main project.

Eloquence Eloquence is a package to extend Laravel's base Eloquent models and functionality. It provides a number of utilities and classes to work wit

Kirk Bushell 470 Dec 8, 2022
Laravel Inverse Seed Generator

Inverse seed generator (iSeed) is a Laravel package that provides a method to generate a new seed file based on data from the existing database table.

Orange Hill Development 2.5k Dec 29, 2022
Oracle DB driver for Laravel 4|5|6|7|8 via OCI8

Oracle DB driver for Laravel 4|5|6|7|8 via OCI8 Laravel-OCI8 Laravel-OCI8 is an Oracle Database Driver package for Laravel. Laravel-OCI8 is an extensi

Arjay Angeles 751 Jan 6, 2023
A package to backup your Laravel app

A modern backup solution for Laravel apps This Laravel package creates a backup of your application. The backup is a zip file that contains all files

Spatie 5.1k Jan 1, 2023
A drop-in Doctrine ORM 2 implementation for Laravel 5+ and Lumen

Laravel Doctrine ORM A drop-in Doctrine ORM 2 implementation for Laravel 5+ $scientist = new Scientist( 'Albert', 'Einstein' ); $scientist->a

Laravel Doctrine 777 Dec 17, 2022
A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)

Laravel MongoDB This package adds functionalities to the Eloquent model and Query builder for MongoDB, using the original Laravel API. This library ex

Jens Segers 6.3k Jan 5, 2023
Laravel Migrations Generator: Automatically generate your migrations from an existing database schema.

Laravel Migrations Generator Generate Laravel Migrations from an existing database, including indexes and foreign keys! Upgrading to Laravel 5.4 Pleas

Bernhard Breytenbach 3.3k Dec 30, 2022
[Package] Multi-tenant Database Schema Manager for Laravel

Multi-tenant Database Schema Manager for Laravel Tenanti allow you to manage multi-tenant data schema and migration manager for your Laravel applicati

Orchestra Platform 580 Dec 5, 2022
Laravel 5 - Repositories to abstract the database layer

Laravel 5 Repositories Laravel 5 Repositories is used to abstract the data layer, making our application more flexible to maintain. See versions: 1.0.

Anderson Andrade 4k Jan 6, 2023
A Redis based, fully automated and scalable database cache layer for Laravel

Lada Cache A Redis based, fully automated and scalable database cache layer for Laravel Contributors wanted! Have a look at the open issues and send m

Matt 501 Dec 30, 2022