GithubHelp home page GithubHelp logo

gazsp / baum Goto Github PK

View Code? Open in Web Editor NEW

This project forked from unikent/baum

129.0 15.0 39.0 818 KB

Baum is an implementation of the Nested Set pattern for Laravel 5's Eloquent ORM.

License: MIT License

PHP 99.96% Shell 0.04%

baum's Introduction

Baum v2

Build Status Coverage Status StyleCI

Nested Set implementation for Laravel

Baum is an implementation of the Nested Set pattern for the Laravel Eloquent ORM.


this

If you find a bug, please file an issue and submit a pull request with a failing unit test.


Installation

Baum v2 works with Laravel 5.8 (and PHP 7.2) and above. You can add it to your project with:

composer require gazsp/baum

Documentation

About Nested Sets

A nested set is a smart way to implement an ordered tree that allows for fast, non-recursive queries. For example, you can fetch all descendants of a node in a single query, no matter how deep the tree. The drawback is that insertions/moves/deletes require complex SQL, but that is handled behind the curtains by this package!

Nested sets are appropriate for ordered trees (e.g. menus, commercial categories) and big trees that must be queried efficiently (e.g. threaded posts).

See the wikipedia entry for nested sets for more info. Also, this is a good introductory tutorial: http://www.evanpetersen.com/item/nested-sets.html

The theory behind, a TL;DR version

An easy way to visualize how a nested set works is to think of a parent entity surrounding all of its children, and its parent surrounding it, etc. So this tree:

root
  |_ Child 1
    |_ Child 1.1
    |_ Child 1.2
  |_ Child 2
    |_ Child 2.1
    |_ Child 2.2

Could be visualized like this:

 ___________________________________________________________________
|  Root                                                             |
|    ____________________________    ____________________________   |
|   |  Child 1                  |   |  Child 2                  |   |
|   |   __________   _________  |   |   __________   _________  |   |
|   |  |  C 1.1  |  |  C 1.2 |  |   |  |  C 2.1  |  |  C 2.2 |  |   |
1   2  3_________4  5________6  7   8  9_________10 11_______12 13  14
|   |___________________________|   |___________________________|   |
|___________________________________________________________________|

The numbers represent the left and right boundaries. The table then might look like this:

id | parent_id | lft  | rgt  | depth | data
 1 |           |    1 |   14 |     0 | root
 2 |         1 |    2 |    7 |     1 | Child 1
 3 |         2 |    3 |    4 |     2 | Child 1.1
 4 |         2 |    5 |    6 |     2 | Child 1.2
 5 |         1 |    8 |   13 |     1 | Child 2
 6 |         5 |    9 |   10 |     2 | Child 2.1
 7 |         5 |   11 |   12 |     2 | Child 2.2

To get all children of a parent node, you

SELECT * WHERE lft IS BETWEEN parent.lft AND parent.rgt

To get the number of children, it's

(right - left - 1)/2

To get a node and all its ancestors going back to the root, you

SELECT * WHERE node.lft IS BETWEEN lft AND rgt

As you can see, queries that would be recursive and prohibitively slow on ordinary trees are suddenly quite fast. Nifty, isn't it?

Installation

Baum works with Laravel 5.8 (and PHP 7.2) and above. You can add it to your project with:

composer require gazsp/baum:2.*

Getting started

After the package is correctly installed the easiest way to get started is to run the provided generator:

php artisan make:baum {model_name}

Replace model by the class name you plan to use for your Nested Set model.

The generator will install a migration and a model file into your application configured to work with the Nested Set behaviour provided by Baum. You SHOULD take a look at those files, as each of them describes how they can be customized.

Next, you would probably run artisan migrate to apply the migration.

Model configuration

In order to work with Baum, you must ensure that your model class extends Baum\Node.

This is the easiest it can get:

class Category extends Baum\Node {

}

This is a slightly more complex example where we have the column names customized:

class Dictionary extends Baum\Node {

  protected $table = 'dictionary';

  // 'parent_id' column name
  protected $parentColumn = 'parent_id';

  // 'lft' column name
  protected $leftColumn = 'lidx';

  // 'rgt' column name
  protected $rightColumn = 'ridx';

  // 'depth' column name
  protected $depthColumn = 'nesting';

  // guard attributes from mass-assignment
  protected $guarded = array('id', 'parent_id', 'lidx', 'ridx', 'nesting');

}

Remember that, obviously, the column names must match those in the database table.

Migration configuration

You must ensure that the database table that supports your Baum models has the following columns:

  • parent_id: a reference to the parent (int)
  • lft: left index bound (int)
  • rgt: right index bound (int)
  • depth: depth or nesting level (int)

Here is a sample migration file:

class Category extends Migration {

  public function up() {
    Schema::create('categories', function(Blueprint $table) {
      $table->increments('id');

      $table->integer('parent_id')->nullable();
      $table->integer('lft')->nullable();
      $table->integer('rgt')->nullable();
      $table->integer('depth')->nullable();

      $table->string('name', 255);

      $table->timestamps();
    });
  }

  public function down() {
    Schema::drop('categories');
  }

}

You may freely modify the column names, provided you change them both in the migration and the model.

Usage

After you've configured your model and run the migration, you are now ready to use Baum with your model. Below are some examples.

Creating a root node

By default, all nodes are created as roots:

$root = Category::create(['name' => 'Root category']);

Alternatively, you may find yourself in the need of converting an existing node into a root node:

$node->makeRoot();

You may also nullify it's parent_id column to accomplish the same behaviour:

// This works the same as makeRoot()
$node->parent_id = null;
$node->save();

Inserting nodes

// Directly with a relation
$child1 = $root->children()->create(['name' => 'Child 1']);

// with the `makeChildOf` method
$child2 = Category::create(['name' => 'Child 2']);
$child2->makeChildOf($root);

Deleting nodes

$child1->delete();

Descendants of deleted nodes will also be deleted and all the lft and rgt bound will be recalculated. Pleases note that, for now, deleting and deleted model events for the descendants will not be fired.

Getting the nesting level of a node

The getLevel() method will return current nesting level, or depth, of a node.

$node->getLevel() // 0 when root

Moving nodes around

Baum provides several methods for moving nodes around:

  • moveLeft(): Find the left sibling and move to the left of it.
  • moveRight(): Find the right sibling and move to the right of it.
  • moveToLeftOf($otherNode): Move to the node to the left of ...
  • moveToRightOf($otherNode): Move to the node to the right of ...
  • makeNextSiblingOf($otherNode): Alias for moveToRightOf.
  • makeSiblingOf($otherNode): Alias for makeNextSiblingOf.
  • makePreviousSiblingOf($otherNode): Alias for moveToLeftOf.
  • makeChildOf($otherNode): Make the node a child of ...
  • makeFirstChildOf($otherNode): Make the node the first child of ...
  • makeLastChildOf($otherNode): Alias for makeChildOf.
  • makeRoot(): Make current node a root node.

For example:

$root = Creatures::create(['name' => 'The Root of All Evil']);

$dragons = Creatures::create(['name' => 'Here Be Dragons']);
$dragons->makeChildOf($root);

$monsters = new Creatures(['name' => 'Horrible Monsters']);
$monsters->save();

$monsters->makeSiblingOf($dragons);

$demons = Creatures::where('name', '=', 'demons')->first();
$demons->moveToLeftOf($dragons);

Asking questions to your nodes

You can ask some questions to your Baum nodes:

  • isRoot(): Returns true if this is a root node.
  • isLeaf(): Returns true if this is a leaf node (end of a branch).
  • isChild(): Returns true if this is a child node.
  • isChildOf($other): Returns true if this node is a child of the other.
  • isDescendantOf($other): Returns true if node is a descendant of the other.
  • isSelfOrDescendantOf($other): Returns true if node is self or a descendant.
  • isAncestorOf($other): Returns true if node is an ancestor of the other.
  • isSelfOrAncestorOf($other): Returns true if node is self or an ancestor.
  • equals($node): current node instance equals the other.
  • insideSubtree($node): Checks whether the given node is inside the subtree defined by the left and right indices.
  • inSameScope($node): Returns true if the given node is in the same scope as the current one. That is, if every column in the scoped property has the same value in both nodes.

Using the nodes from the previous example:

$demons->isRoot(); // => false

$demons->isDescendantOf($root) // => true

Relations

Baum provides two self-referential Eloquent relations for your nodes: parent and children.

$parent = $node->parent()->get();

$children = $node->children()->get();

Root and Leaf scopes

Baum provides some very basic query scopes for accessing the root and leaf nodes:

// Query scope which targets all root nodes
Category::roots()

// All leaf nodes (nodes at the end of a branch)
Category:allLeaves()

You may also be interested in only the first root:

$firstRootNode = Category::root();

Accessing the ancestry/descendancy chain

There are several methods which Baum offers to access the ancestry/descendancy chain of a node in the Nested Set tree. The main thing to keep in mind is that they are provided in two ways:

First as query scopes, returning an Illuminate\Database\Eloquent\Builder instance to continue to query further. To get actual results from these, remember to call get() or first().

  • ancestorsAndSelf(): Targets all the ancestor chain nodes including the current one.
  • ancestors(): Query the ancestor chain nodes excluding the current one.
  • siblingsAndSelf(): Instance scope which targets all children of the parent, including self.
  • siblings(): Instance scope targeting all children of the parent, except self.
  • leaves(): Instance scope targeting all of its nested children which do not have children.
  • descendantsAndSelf(): Scope targeting itself and all of its nested children.
  • descendants(): Set of all children & nested children.
  • immediateDescendants(): Set of all children nodes (non-recursive).

Second, as methods which return actual Baum\Node instances (inside a Collection object where appropiate):

  • getRoot(): Returns the root node starting at the current node.
  • getAncestorsAndSelf(): Retrieve all of the ancestor chain including the current node.
  • getAncestorsAndSelfWithoutRoot(): All ancestors (including the current node) except the root node.
  • getAncestors(): Get all of the ancestor chain from the database excluding the current node.
  • getAncestorsWithoutRoot(): All ancestors except the current node and the root node.
  • getSiblingsAndSelf(): Get all children of the parent, including self.
  • getSiblings(): Return all children of the parent, except self.
  • getLeaves(): Return all of its nested children which do not have children.
  • getDescendantsAndSelf(): Retrieve all nested children and self.
  • getDescendants(): Retrieve all of its children & nested children.
  • getImmediateDescendants(): Retrieve all of its children nodes (non-recursive).

Here's a simple example for iterating a node's descendants (provided a name attribute is available):

$node = Category::where('name', '=', 'Books')->first();

foreach($node->getDescendantsAndSelf() as $descendant) {
  echo "{$descendant->name}";
}

Limiting the levels of children returned

In some situations where the hierarchy depth is huge it might be desirable to limit the number of levels of children returned (depth). You can do this in Baum by using the limitDepth query scope.

The following snippet will get the current node's descendants up to a maximum of 5 depth levels below it:

$node->descendants()->limitDepth(5)->get();

Similarly, you can limit the descendancy levels with both the getDescendants and getDescendantsAndSelf methods by supplying the desired depth limit as the first argument:

// This will work without depth limiting
// 1. As usual
$node->getDescendants();
// 2. Selecting only some attributes
$other->getDescendants(array('id', 'parent_id', 'name'));
...
// With depth limiting
// 1. A maximum of 5 levels of children will be returned
$node->getDescendants(5);
// 2. A max. of 5 levels of children will be returned selecting only some attrs
$other->getDescendants(5, array('id', 'parent_id', 'name'));

Custom sorting column

By default in Baum all results are returned sorted by the lft index column value for consistency.

If you wish to change this default behaviour you need to specify in your model the name of the column you wish to use to sort your results like this:

protected $orderColumn = 'name';

Dumping the hierarchy tree

Baum extends the default Eloquent\Collection class and provides the toHierarchy method to it which returns a nested collection representing the queried tree.

Retrieving a complete tree hierarchy into a regular Collection object with its children properly nested is as simple as:

$tree = Category::where('name', '=', 'Books')->first()->getDescendantsAndSelf()->toHierarchy();

Model events: moving and moved

Baum models fire the following events: moving and moved every time a node is moved around the Nested Set tree. This allows you to hook into those points in the node movement process. As with normal Eloquent model events, if false is returned from the moving event, the movement operation will be cancelled.

The recommended way to hook into those events is by using the model's boot method:

class Category extends Baum\Node {

  public static function boot() {
    parent::boot();

    static::moving(function($node) {
      // Before moving the node this function will be called.
    });

    static::moved(function($node) {
      // After the move operation is processed this function will be
      // called.
    });
  }

}

Scope support

Baum provides a simple method to provide Nested Set "scoping" which restricts what we consider part of a nested set tree. This should allow for multiple nested set trees in the same database table.

To make use of the scoping funcionality you may override the scoped model attribute in your subclass. This attribute should contain an array of the column names (database fields) which shall be used to restrict Nested Set queries:

class Category extends Baum\Node {
  ...
  protected $scoped = array('company_id');
  ...
}

In the previous example, company_id effectively restricts (or "scopes") a Nested Set tree. So, for each value of that field we may be able to construct a full different tree.

$root1 = Category::create(['name' => 'R1', 'company_id' => 1]);
$root2 = Category::create(['name' => 'R2', 'company_id' => 2]);

$child1 = Category::create(['name' => 'C1', 'company_id' => 1]);
$child2 = Category::create(['name' => 'C2', 'company_id' => 2]);

$child1->makeChildOf($root1);
$child2->makeChildOf($root2);

$root1->children()->get(); // <- returns $child1
$root2->children()->get(); // <- returns $child2

All methods which ask or traverse the Nested Set tree will use the scoped attribute (if provided).

Please note that, for now, moving nodes between scopes is not supported.

Validation

The ::isValidNestedSet() static method allows you to check if your underlying tree structure is correct. It mainly checks for these 3 things:

  • Check that the bound indexes lft, rgt are not null, rgt values greater than lft and within the bounds of the parent node (if set).
  • That there are no duplicates for the lft and rgt column values.
  • As the first check does not actually check root nodes, see if each root has the lft and rgt indexes within the bounds of its children.

All of the checks are scope aware and will check each scope separately if needed.

Example usage, given a Category node class:

Category::isValidNestedSet()
=> true

Tree rebuilding

Baum supports for complete tree-structure rebuilding (or reindexing) via the ::rebuild() static method.

This method will re-index all your lft, rgt and depth column values, inspecting your tree only from the parent <-> children relation standpoint. Which means that you only need a correctly filled parent_id column and Baum will try its best to recompute the rest.

This can prove quite useful when something has gone horribly wrong with the index values or it may come quite handy when converting from another implementation (which would probably have a parent_id column).

This operation is also scope aware and will rebuild all of the scopes separately if they are defined.

Simple example usage, given a Category node class:

Category::rebuild()

No checks are made to see if the tree is already valid, meaning a call to rebuild will always rebuild the tree, whether it is valid or not. If you don't want this behaviour, don't call rebuild if isValidNestedSet returns true.

Soft deletes

Using soft deletes / restore() is not recommeded and may cause problems if a tree has been modified after a soft delete operation.

Seeding/Mass-assignment

Because Nested Set structures usually involve a number of method calls to build a hierarchy structure (which result in several database queries), Baum provides two convenient methods which will map the supplied array of node attributes and create a hierarchy tree from them:

  • buildTree($nodeList): (static method) Maps the supplied array of node attributes into the database.
  • makeTree($nodeList): (instance method) Maps the supplied array of node attributes into the database using the current node instance as the parent for the provided subtree.

Both methods will create new nodes when the primary key is not supplied, update or create if it is, and delete all nodes which are not present in the affecting scope. Understand that the affecting scope for the buildTree static method is the whole nested set tree and for the makeTree instance method are all of the current node's descendants.

For example, imagine we wanted to map the following category hierarchy into our database:

  • TV & Home Theater
  • Tablets & E-Readers
  • Computers
    • Laptops
      • PC Laptops
      • Macbooks (Air/Pro)
    • Desktops
    • Monitors
  • Cell Phones

This could be easily accomplished with the following code:

$categories = [
  ['id' => 1, 'name' => 'TV & Home Theather'],
  ['id' => 2, 'name' => 'Tablets & E-Readers'],
  ['id' => 3, 'name' => 'Computers', 'children' => [
    ['id' => 4, 'name' => 'Laptops', 'children' => [
      ['id' => 5, 'name' => 'PC Laptops'],
      ['id' => 6, 'name' => 'Macbooks (Air/Pro)']
    ]],
    ['id' => 7, 'name' => 'Desktops'],
    ['id' => 8, 'name' => 'Monitors']
  ]],
  ['id' => 9, 'name' => 'Cell Phones']
];

Category::buildTree($categories) // => true

After that, we may just update the hierarchy as needed:

$categories = [
  ['id' => 1, 'name' => 'TV & Home Theather'],
  ['id' => 2, 'name' => 'Tablets & E-Readers'],
  ['id' => 3, 'name' => 'Computers', 'children' => [
    ['id' => 4, 'name' => 'Laptops', 'children' => [
      ['id' => 5, 'name' => 'PC Laptops'],
      ['id' => 6, 'name' => 'Macbooks (Air/Pro)']
    ]],
    ['id' => 7, 'name' => 'Desktops', 'children' => [
      // These will be created
      ['name' => 'Towers Only'],
      ['name' => 'Desktop Packages'],
      ['name' => 'All-in-One Computers'],
      ['name' => 'Gaming Desktops']
    ]]
    // This one, as it's not present, will be deleted
    // ['id' => 8, 'name' => 'Monitors'],
  ]],
  ['id' => 9, 'name' => 'Cell Phones']
];

Category::buildTree($categories); // => true

The makeTree instance method works in a similar fashion. The only difference is that it will only perform operations on the descendants of the calling node instance.

So now imagine we already have the following hierarchy in the database:

  • Electronics
  • Health Fitness & Beaty
  • Small Appliances
  • Major Appliances

If we execute the following code:

$children = [
  ['name' => 'TV & Home Theather'],
  ['name' => 'Tablets & E-Readers'],
  ['name' => 'Computers', 'children' => [
    ['name' => 'Laptops', 'children' => [
      ['name' => 'PC Laptops'],
      ['name' => 'Macbooks (Air/Pro)']
    ]],
    ['name' => 'Desktops'],
    ['name' => 'Monitors']
  ]],
  ['name' => 'Cell Phones']
];

$electronics = Category::where('name', '=', 'Electronics')->first();
$electronics->makeTree($children); // => true

Would result in:

  • Electronics
    • TV & Home Theater
    • Tablets & E-Readers
    • Computers
      • Laptops
        • PC Laptops
        • Macbooks (Air/Pro)
      • Desktops
      • Monitors
    • Cell Phones
  • Health Fitness & Beaty
  • Small Appliances
  • Major Appliances

Updating and deleting nodes from the subtree works the same way.

Misc/Utility functions

Node extraction query scopes

Baum provides some query scopes which may be used to extract (remove) selected nodes from the current results set.

  • withoutNode(node): Extracts the specified node from the current results set.
  • withoutSelf(): Extracts itself from the current results set.
  • withoutRoot(): Extracts the current root node from the results set.
$node = Category::where('name', '=', 'Some category I do not want to see.')->first();

$root = Category::where('name', '=', 'Old boooks')->first();
var_dump($root->descendantsAndSelf()->withoutNode($node)->get());
... // <- This result set will not contain $node

Get a nested list of column values

The ::getNestedList() static method returns a key-value pair array indicating a node's depth. Useful for silling select elements, etc.

It expects the column name to return, and optionally: the column to use for array keys (will use id if none supplied) and/or a separator:

public static function getNestedList($column, $key = null, $seperator = ' ', $symbol = '');

An example use case:

$nestedList = Category::getNestedList('name');
// $nestedList will contain an array like the following:
// array(
//   1 => 'Root 1',
//   2 => ' Child 1',
//   3 => ' Child 2',
//   4 => '  Child 2.1',
//   5 => ' Child 3',
//   6 => 'Root 2'
// );

Further information

You may find additional information, usage examples and/or frequently asked questions about Baum in the wiki.

Feel free to browse the wiki after finishing this README:

https://github.com/etrepat/baum/wiki

Contributing

Thinking of contributing? Maybe you've found some nasty bug? That's great news!

  1. Fork & clone the project: git clone [email protected]:your-username/baum.git.
  2. Run the tests and make sure that they pass with your setup: phpunit.
  3. Create your bugfix/feature branch and code away your changes. Add tests for your changes.
  4. Make sure all the tests still pass: phpunit.
  5. Push to your fork and submit new a pull request.

Please see the CONTRIBUTING.md file for extended guidelines and/or recommendations.

License

Baum is licensed under the terms of the MIT License (See LICENSE file for details).


Coded by Estanislau Trepat (etrepat). I'm also @etrepat on twitter.

baum's People

Contributors

beingtomgreen avatar bmitch avatar booomerang avatar ceejayoz avatar ctf0 avatar danwithams7d avatar daxborges avatar dirkpostma avatar dzcpy avatar etrepat avatar gazsp avatar gerp avatar gjmh-unikent avatar grahamcampbell avatar ignaciogc avatar it-can avatar james2037 avatar jamesguthrie avatar joshuadoshua avatar mpociot avatar pawel-damasiewicz avatar poing avatar robmeijer avatar superbuba avatar surt avatar tasarsu avatar tomkita avatar vinicius73 avatar weareredrobot avatar ziadoz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

baum's Issues

laravel 7 no migration file creation

Hello ,๐Ÿ˜„
Just for information ,the php command artisan:
make:baum {model}
create model file but it does not create a migration file automatically under laravel 7

how to get hierarchy with relations?

Thanks for continuing the repo!

I can't seem to get the complete hierarchy tree with the relations intact.

    $tree = Group::where('id', '=', $id)
    ->with(['users')
    ->first()
    ->getDescendantsAndSelf()
    ->toHierarchy();

The tree is written out but the users aren't there.

Wrong rgt-value when creating child node on PHP 7.2

Wrong rgt-value when creating child node on PHP 7.2

I have this test:

  /** @test **/
    public function store_category()
    {
        // Arrange
        $root = factory(\App\Category::class)->create( ['name' => 'Root', 'depth' => 0]);
    
        // Act
        $response = $this->actingAsAdmin('api')
        ->json('POST', $this->endpoint, [
            'name' => 'New Category',
            'parent_id' => $root->id,
        ]);
    
        // Assert
        $response->assertStatus(200);
        $response->assertJson([
            'tree' => [
                'root' => true,
                'name' => 'Root',
                'children' => [
                    [
                    'name'  => 'New Category',
                    'lft'   => 2,
                    'rgt'   => 3
                    ]
                ]
            ]
        ]);

        $this->assertDatabaseHas('categories', [
            'name' =>  'New Category',
            'slug' =>  'new-category',
            'lft'  =>  2,
            'rgt'  =>  3
        ]);
        $this->assertDatabaseHas('categories', [
            'name' =>  'Root',
            'lft'  =>  1,
            'rgt'  =>  4
        ]);
    }

This works on PHP 7.0 and PHP 7.1 but fails with PHP 7.2

To get the test to pass on PHP 7.2 the last assertion (the one checking the root in the database) has to be changed to below

        $this->assertDatabaseHas('categories', [
            'name' =>  'Root',
            'lft'  =>  1,
            'rgt'  =>  3
        ]);

The actual store function in my app

    public function store(CreateCategoryRequest $request)
    {
        $parent = $request->input('parent_id');
        $category = new Category;
        $category->fill($request->all());
        $category->save();
        $category->makeChildOf($parent);

        return $this->categoryTree($request, $category);
    }

Performance optimisation with Composite Index

UPDATE/INFORMATION

After the creation of a compound index
I saw my performance greatly implied for the cost of a slightly slower insertion due to the reconstruction of the index but considering the size of the nested data I am a big winner in terms of performance: Index

$table->index(['id', 'lft', 'rgt']);

Looking for maintainers

Hi all.

I've moved on to other things, and am not actively using or developing Baum at the moment. Is anybody interested in becoming the main package maintainer? Preferably someone who is actively using the package in a Laravel 5.8 app...

CI builds are broken, and the next version needs releasing to Packagist once everything has been cleaned up with unit tests / CI verified as working.

Cheers,
Gaz.

Issue with custom primary keys

When using a custom primary key in a model

protected $primaryKey = 'reference';

and the following migration

        $table->string('reference', 20)->primary();
        $table->string('parent_id', 20)->nullable();
        $table->integer('lft')->nullable();
        $table->integer('rgt')->nullable();
        $table->integer('depth')->nullable();

The following:

    $things = [
          ['reference' => 'One'],
          [
               'reference' => 'Two',
               'children' => [
                    ['reference' => 'TwoDotOne'],
                    ['reference' => 'TwoDotTwo']
                 ]
          ],
          ['reference' => 'Three']
     ];
     App\Thing::buildTree($things);

results in a "No query results for model" Error, triggered by the $self->reload() in Node@setDepth.

I guess this is because we're reloading before the new model is persisted?

Laravel 5.5 Compatibility Issues?

I see that full support for Laravel 5.5 is in progress, but in the meantime, I'm using Baum 1.1 and it seems to be functioning just fine. I'm curious as to why 1.1 is only specifying 5.2 - 5.4 as supported. Are there known issues in 5.5?

Zero value parent_id instead of null values

Currently, parent_ids must be null to work with this package. In my case, root level nodes have a value of 0, to denote a root level node.

I've taken a look at the code, and it'd be a matter of changing a few queries to add an additional where clause to account for zero values. Is this something that others may need?

Load nested set by default in Laravel Relation definition

Thanks so much for taking this project over!!

Is there a way to eager load a nested relationship using Laravel's relationship definitions instead of calling ->toHierarchy()?

i.e.

class Post extends Model
{
    public function categories()
    {
        return $this->hasManyRootsWithDescendants(Category::class);
    }
}

to return a nested set of categories

If this is a feature request I'd be glad to take a look Or is there a better way to load a nested set by default?

Too much data causes updates to fail

When the data is too much (millions of data), it can report insufficient memory, can add data to judge, when the data amount is large enough, do not use one-time update

Invalid Tree

Is there a way to reset the tree?

  • i removed all LFT, RGT values then run the rebuild(), but still invalid.
  • i checked all parent_id exists
  • no parent_id is null

anymore to check, why tree stil invalid?

thanks

laravel 7

Hello,
I come from python/django and we have the excellent mptt https://github.com/django-mptt/django-mptt for tree management. I'm currently working on a laravel project where I have to integrate the equivalent, I have a lot of data to save on the tree I found the library: https://github.com/lazychaser/laravel-nestedset, but this one is horrible in terms of performance, it's not very user friendly (don't integrate the depth in the database, even after 50 requests ...., especially as it could solve some of the problems). After several researches the Baum library seems to me the closest to what I need in terms of quality and performance. Now I could see that it is not available for laravel 7, will you adapt it for laravel 7 ?
I could see on this post etrepat#332 that @mkwsra has been asking for a traction for a while couldn't there be a work between you ?
Thank you in advance

PHP 7.1 error

This is the error I get on Laravel 5.5 and PHP 7.1:
error

L5.6 baum:install issues

ver dev-feature/2.0
When run "php artisan baum:install" command .
will get the error
Class 'Baum\Console\BaumCommand' not found .
( Providers/BaumServiceProvider.php:46 )

Is this package development still in progress??

Updating node updated_at causes unwanted side-effects

When I want to touch the timestamp of a nested Model it looses it's depth.

I have a nested Page model that has a relationship with a Content model.
When I update the Content model I want to touch the updated_at timestamp of the Page model.
This all works fine with protected $touches = ['page']; in the Content model, but as a bonus I get a set of extra queries that also set the depth off the Page model to zero.

-
UPDATE `contents` set `fields` = '{\"leader\":[{\"file\":\"2\",\"description\":\"\"}],\"about_titel\":\"\",\"about_tekst_1\":\"\",\"about_tekst_2\":\"\",\"contact_titel\":\"\",\"contact_tekst\":\"\"}', `updated_at` = '2016-09-29 23:52:45' WHERE `id` = '129'
UPDATE `pages` set `updated_at` = '2016-09-29 23:52:45' WHERE `pages`.`id` = '118'
SELECT * FROM `pages` WHERE `pages`.`id` = '118' LIMIT 1
SELECT * FROM `pages` WHERE `pages`.`id` = '118' LIMIT 1
SELECT max(`rgt`) as aggregate FROM `pages`
SELECT * FROM `pages` WHERE `pages`.`id` = '118' LIMIT 1
UPDATE `pages` set `depth` = '0', `updated_at` = '2016-09-29 23:52:45' WHERE `id` = '118' ORDER BY `pages`.`lft` ASC
1.060 m

Is there some kind of event that triggers these last 5 queries?

How to get all children with where clause

In my project, i need to retrieve all of its children & nested children of a parent. But i want to get children with clause: where is_published= 1 (is activated) because there are some inactive children is_published= 0
But i did not found any solutions. I'm really confused now :(
@gazsp please help me, thanks

Returning tree with ancestors collection for each node, like breadcrumbs

Hello.

First of all, thank you for continuing the work on this package. Having implement a nested set library from scratch in the past, it's great to rely on a better thought out and tested package.

I'm struggling here trying to return the ancestors along with each node on a nested set. For example, each result for the descendantsAndSelf() method would have a sub collection with its full path to the root node represented by the ancestors() method. This would be extremely useful to return the breadcrumbs for each node, for example. Something that could be chained like descendantsAndSelf()->withAncestors().

Doing this with a loop after returning the main query is super simple, but I stumble upon a n+1 problem. I need to solve this in a way that can rely on eager loading and be processed with the main query.

Any pointers or suggestions?

Thank you in advance,

Support for changing connection.

I'm currently having an issue where I change the connection in the model I have that extends the Node class and call the rebuild() method.

When rebuilding it seems to always use the default connection even after I've set the connection on my model. Something like:

$this->myModelThatExtendsNode->setConnection('foobar');
$this->myModelThatExtendsNode->rebuild();

Since my default database connection is not where the table for the model is I get an error like SQLSTATE[42S02]: Base table or view not found: 1146 Table 'default.table_for_my_model_that_extends_node'

I think the reason for this is inside the rebuild() method a new static is created in the constructor for SetBuilder and that new static has the database connection set to the default DB.

I can try to put together a fix but wanted to see if anyone else has run into this and has any other suggestions/

Get descendants of multiple nodes

I'm struggling everywhere to accomplish this. I need to have all the descendants of multiple nodes as: Category::whereIn('id', [<ids>])->getDescendantsAndSelf()

Overwrite reOrderBy to accept RAND()?

I've tried every way to get random siblings using reOrderBy and the inRandomOrder in the laravel framework. Is it possible to query in random because i don't want to random a massive collection.

new item adding issue

if try to add a new tree to database using buildTree() method and the tree has left and right but not id , it will change new item's and its parent's left and right by itself.
i think this job should be done only when there are no left and right attributes, because there are jquery plugins such as nestedSortable that do this job perfectly at client side ,so performing it at server side for second time will disarrange menu order

example:
comments are values after saving in database using buildTree method

 $menu_cc = [
        [ 'id' => 1, 'parent_id' => null, 'left' => 1, 'right' => 4//8, 'name' => 'r1' ],
         [ 'id' => 2, 'parent_id' => 1, 'left' => 4 , 'right' => 5, 'name' => 'm2'] 
        [ 'id' => null, 'parent_id' => 1, 'left' => 2//6 , 'right' => 3//7, 'name' => 'm3'] 
      ];

[ReflectionException] Class path.database does not exist

Hi! Thanks for forking this and fixing things. I'm getting a weird issue when trying to install this package with Lumen:

php artisan baum:install MODEL

this error is thrown:

[ReflectionException] Class path.database does not exist

Setup everything correctly AFAIK, (composer require, added service provider, etc). What could cause this?

dynamically change the column values

Hi I have a special case where I need the left, right, parent, depth columns to be mapped to other columns in the same table dynamically to get two different tree structures. I tried setting the column properties to custom values as the first step of my experiment but it seems even if I changed the column names in the extended model it still referring the old columns and its not working. What I need actually trying to achieve is to get two types of nested set based on two specific columns say parent_id and sponsor_id etc. Hope you guys have some solutions.
Thanks

remove inheritance, use Trait

I am interested in using a version of Baum based on Traits. Baum looks well-featured, but we cannot use it by extending from it, because that would break our architecture.

At first glance, I don't really see a blocking issue to introduce Traits. In essence, all Baum\Node code should be moved into a separate Trait. You can then have Baum\Node use that trait, to provide backwards compatibility. If testing with instanceof is an issue, a Node-interface can be created to test on that interface.

In short, some refactoring is necessary to achieve the following pattern:

namespace Baum;

interface NodeInterface {
    // method signatures from Baum\Node
}

trait NodeTrait {
    // implementation from Baum\Node
}

// only necessary for BC
abstract class Node implements NodeInterface {
    use NodeTrait;
}

I'm not really in a position to assess the amount of work involved, but it seems like a straightforward refactoring. And I do not see any drawbacks, as this will be BC and makes the library more flexible in use.

If you're open to my proposed change, I can do the modifications and create a PR.

Looks like i have a corrupted tree

I'm using the old library. I'm thinking it would be better to post my issue here since this repo is being maintained. There is a problem on the parent node because lft column is bigger than rgt column

ss2

Also this one. The parent_id is 215 but lft and rgt is not within the parent's lft and rgt

ss3

I hope someone could help me how to fix this. Also this parent node(215) is the last node in the tree. There are no problems in other nodes. Only this one which is the last one.

Limit children

How to limit number children's by level, for example one parent only have two children and if have new children save in new level?

Time to create a new repo?

A forked repo isn't searchable on github, and since it's (almost) 2 years since this fork was created it might be a good idea to create a new repo.

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.