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

Related tags

Database baum
Overview

Baum

Build Status

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 or use the latest 1.0.x tagged release.

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 onwards. You can add it to your composer.json file with:

"baum/baum": "~1.1"

Run composer install to install it.

As with most Laravel 5 packages you'll then need to register the Baum service provider. To do that, head over your config/app.php file and add the following line into the providers array:

'Baum\Providers\BaumServiceProvider',

Getting started

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

php artisan baum:install MODEL

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.
  • 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()

Valid trees (per the isValidNestedSet method) will not get rebuilt. To force the index rebuilding process simply call the rebuild method with true as the first parameter:

Category::rebuild(true);

Soft deletes

Baum comes with limited support for soft-delete operations. What I mean by limited is that the testing is still limited and the soft delete functionality is changing in the upcoming 4.2 version of the framework, so use this feature wisely.

For now, you may consider a safe restore() operation to be one of:

  • Restoring a leaf node
  • Restoring a whole sub-tree in which the parent is not soft-deleted

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 = ' ');

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.

Comments
  • Eager loading children

    Eager loading children

    How do I eager load all a model's descendants? I'm traversing the tree from a given node and every time I ask for a node's children it's sending out another database query. They're really adding up.

    I'm using toHierarchy in a different area of my code which already seems very awkward since I have to get a single Node model, then call the method, and now I've got a collection I need to get the first element of again. I can live with that, though I'd prefer to just start with the Node and be able to grab children without a new query actually being made, having already preloaded all children. But anyway, when I traverse through the Collection toHierarchy gave me I still get new database queries getting fired off.

    In that case I'm doing

    $menus = [];
    foreach (MenuItem::roots()->get() as $root) {
        $menus[$root->menu] = $root->getDescendantsAndSelf()->toHierarchy();
    }
    

    The view has

    @foreach ($menus as $menu => $hierarchy)
        <?php $root = $hierarchy->first(); ?>
        <div class="menu panel panel-primary" data-id="{{{ $root->getKey() }}}">
    ...
            @include('admin.menu-items.children', ['model' => $root])
    ...
        </div>
    @endforeach
    

    The children partial view has

    <ol class="nested-sortable">
        @foreach ($model->children as $node)
            @include('admin.menu-items.item', ['model' => $node, 'parentId' => $model->getKey()])
        @endforeach
    </ol>
    

    The item partial view prints an <li> in which the children partial is called again recursively on $model. There are more and more queries being fired off.

    opened by tremby 13
  • [ERROR] Using $this when not in object context

    [ERROR] Using $this when not in object context

    I'm sorry if i implemented wrong but i cant get around this error. This error is thrown on /vendor/baum/baum/src/Baum/Node.php on line 697.

    /**

    • Sets the depth attribute *

    • @return \Baum\Node */ public function setDepth() { $this->getConnection()->transaction(function() { -----> $this->reload();

      $level = $this->getLevel();

      $this->newQuery()->where($this->getKeyName(), '=', $this->getKey())->update(array($this->getDepthColumnName() => $level)); $this->setAttribute($this->getDepthColumnName(), $level); });

      return $this; }

    opened by justinhilles 11
  • L5 - BadMethodCallException in ServiceProvider.php line 111: Call to undefined method [package]

    L5 - BadMethodCallException in ServiceProvider.php line 111: Call to undefined method [package]

    I've added Baum to my composer file and run composer update which has worked fine. I've then added 'Baum\BaumServiceProvider', to my providers array.

    Now, as soon as I load any page in the application or run artisan I get the following:

    in ServiceProvider.php line 111
    at ServiceProvider->__call('package', array('baum/baum')) in BaumServiceProvider.php line 32
    at BaumServiceProvider->package('baum/baum') in BaumServiceProvider.php line 32
    at BaumServiceProvider->boot()
    at call_user_func_array(array(object(BaumServiceProvider), 'boot'), array()) in Container.php line 530
    at Container->call(array(object(BaumServiceProvider), 'boot')) in Application.php line 564
    at Application->bootProvider(object(BaumServiceProvider)) in Application.php line 546
    at Application->Illuminate\Foundation\{closure}(object(BaumServiceProvider), '18')
    at array_walk(array(object(EventServiceProvider), object(RoutingServiceProvider), object(AppServiceProvider), object(BusServiceProvider), object(EventServiceProvider), object(RouteServiceProvider), object(AuthServiceProvider), object(ControllerServiceProvider), object(CookieServiceProvider), object(DatabaseServiceProvider), object(EncryptionServiceProvider), object(FilesystemServiceProvider), object(FormRequestServiceProvider), object(FoundationServiceProvider), object(PaginationServiceProvider), object(SessionServiceProvider), object(ValidationServiceProvider), object(ViewServiceProvider), object(BaumServiceProvider), object(BusServiceProvider)), object(Closure)) in Application.php line 547
    at Application->boot() in BootProviders.php line 15
    at BootProviders->bootstrap(object(Application)) in Application.php line 141
    at Application->bootstrapWith(array('Illuminate\Foundation\Bootstrap\DetectEnvironment', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', 'Illuminate\Foundation\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', 'Illuminate\Foundation\Bootstrap\RegisterFacades', 'Illuminate\Foundation\Bootstrap\RegisterProviders', 'Illuminate\Foundation\Bootstrap\BootProviders')) in Kernel.php line 140
    at Kernel->bootstrap() in Kernel.php line 103
    at Kernel->sendRequestThroughRouter(object(Request)) in Kernel.php line 83
    at Kernel->handle(object(Request)) in index.php line 53
    
    opened by ajtrichards 10
  • Add Node::loadDescendants method

    Add Node::loadDescendants method

    For cases where I already have a Node object and now want to recurse over all descendants I find this method very useful. It loads all descendants and sets the children relation so no further database queries need to be made when looping through children and children's children.

    Thanks to @shaddowgh for the help.

    This fixes #45.

    opened by tremby 9
  • Laravel 5.0 support

    Laravel 5.0 support

    Any chance of updating the required part of the composer.json so it can reflect on the new version of Laravel? Worked fine when it was 4.3, but now the name was changed to 5.0.

    opened by slovenianGooner 8
  • isValid method conflict

    isValid method conflict

    When using Baum together with validators, such as the validator by Dwight Watson, both are using the method isValid which will cause a conflict. Is there a way to avoid this?

    opened by jonaholsson 7
  • Beginner questions

    Beginner questions

    Hello there,

    Sorry but i could answer my own questions by looking at the docs or the example. Firstly, when i copy paste syntax like this "$root = Category::create(['name' => 'Root category']);", then i get an error. (Laravel 4.1, PHP 5.3.10 on Ubuntu 12.04)

    Is this because i should really pass an array as so: "$root = Category::create(array('name' => 'Root category'));" ? Because when i do that, a new Root category does get added into my categories table.

    Then, imagine i added a bunch of Root categories. How do i loop over them? The below will not work.

    $categories = Category::roots();
    foreach($categories as $c){
    print $c->name;
    }
    

    Thanks!

    opened by overrider 7
  • how to import a csv file  and transfer it into a baum tree table

    how to import a csv file and transfer it into a baum tree table

    I am loading the csv file into a table named as "node". Then query that"node" table to create a new baum-tree table "section". Basically, "node" table includes columns of "id", "organization_id", "parent_id", "entity_id", "entity_name", "active", "exclude_rollup". I hope can first scan out root nodes in "node" table and create corresponding root nodes in "sections" table; then scan "node" table line by line and create corresponding "section" nodes. At the same time, I find out parent section nodes in "section" table and use makeChildOf() function to setup their relationships.

    here is the codes I used:

    public function create()
        {
            DB::table('sections')->truncate();
    
            $rnodes = DB::table('nodes')->where('parent_id', '=', 0)->get();
            foreach ($rnodes as $rnode){
                //var_dump($rnode);
                $root = Section::create(['entity_id' => $rnode->entity_id, 'entity_name' => $rnode->entity_name, 'active' => $rnode->active, 'exclude_rollup' => $rnode->exclude_rollup, 'organization_id' => $rnode->organization_id]);
                $root->makeroot();
            }
    
            $nodes = DB::table('nodes')->where('parent_id', '!=', 0)->get();
    
             foreach($nodes as $node){
                $section = Section::create(['entity_id' => $node->entity_id, 'entity_name' => $node->entity_name, 'active' => $node->active, 'exclude_rollup' => $node->exclude_rollup, 'organization_id' => $node->organization_id]);
                $parent_node = DB::table('nodes')->where('id', '=', $node->parent_id)->first();
                $parent_section = DB::table('sections')->where('entity_id', '=', $parent_node->entity_id)->first();
                $section->makeChildOf($parent_section);
             }
        }
    

    Everything is fine, except the last step, which is to run makeChildOf() function. It throws error:

    Object of class stdClass could not be converted to string
    

    Does I miss something?

    opened by msrlinliu 6
  • MoveNotPossibleException

    MoveNotPossibleException

    First, ty for your work! My first post here. I just downloaded and installed Baum, but it is not working for me.

    • Created a new table [id name user_id order created_at updated_at parent_id lft rgt depth]
    • Created a model Category class Category extends Baum\Node { }

    And when trying to create a new Category i'm always getting a "MoveNotPossibleException" - A new node cannot be moved.

    Seems that some dispatcher is running after the saving! Any ideas?

    opened by leichti 6
  • makeRoot throws error

    makeRoot throws error

    // Throws 'Could not resolve target node. This node cannot move any further to the right.'
    $post->makeRoot();
    
    // Throws 'A node cannot be moved to itself.'
    $post->parent_id = null;
    $post->save();
    

    Probably not how it should be.

    bug 
    opened by tomhooijenga 6
  • Why are lft/rgt/depth always null?

    Why are lft/rgt/depth always null?

    When I use create or other methods, the lft/rgt/depth of the newly created node is always null, but the parent_id and so on are very normal. Then if I use getDescendantsAndSelf and so on, I will get an exception because of the null columns. Only if I use rebuild() will all the nodes have not-null normal values. But then if I create() a new node the columns of it are still null. Looking forward to your reply!

    opened by fzyzcjy 5
  • Laravel 9.x Compatibility

    Laravel 9.x Compatibility

    This is an automated pull request from Shift to update your package code and dependencies to be compatible with Laravel 9.x.

    Before merging, you need to:

    • Checkout the l9-compatibility branch
    • Review all comments for additional changes
    • Thoroughly test your package

    If you do find an issue, please report it by commenting on this PR to help improve future automation.

    opened by laravel-shift 4
  • not supported php 8.0

    not supported php 8.0

    Problem 1 - illuminate/console[v7.0.0, ..., v7.28.4] require php ^7.2.5 -> your php version (8.0.10) does not satisfy that requirement. - illuminate/queue[v7.0.0, ..., v7.28.4] require php ^7.2.5 -> your php version (8.0.10) does not satisfy that requirement. - illuminate/auth[v7.0.0, ..., v7.28.4] require php ^7.2.5 -> your php version (8.0.10) does not satisfy that requirement. - illuminate/console[v8.0.0, ..., v8.11.2] require php ^7.3 -> your php version (8.0.10) does not satisfy that requirement. - illuminate/queue[v8.0.0, ..., v8.11.2] require php ^7.3 -> your php version (8.0.10) does not satisfy that requirement. - illuminate/auth[v8.0.0, ..., v8.11.2] require php ^7.3 -> your php version (8.0.10) does not satisfy that requirement. - Root composer.json requires kalnoy/nestedset ^6.0 -> satisfiable by kalnoy/nestedset[v6.0.0].

    opened by devHarandi 2
  • Change default key from `id` to another column

    Change default key from `id` to another column

    I have a table which has these columns:

    id, user_id, parent_id, sport_id

    I want to create children using user_id column instead of id. for example:

    id      user_id      parent_id       sport_id
    1	 20		  null		2
    2	 25		  20		2	# user#20 is user#25 parent.
    3	 28		  25		2	# user#25 is user#28 parent.
    4	 29		  20		2	# user#20 is user#29 parent.
    

    please tell me what should I do?

    opened by rasadeghnasab 0
  •  Unresolvable dependency resolving [Parameter #1 [ <required> $customStubPath

    Unresolvable dependency resolving [Parameter #1 [ $customStubPath

    on update

    In Container.php line 993:
                                                                                   
      Unresolvable dependency resolving [Parameter #1 [ <required> $customStubPath ]] in class Illuminate\Database\Migrations\MigrationCreator
    
    opened by KalimeroMK 2
Owner
Estanislau Trepat
Estanislau Trepat
Articulate - An alternative ORM for Laravel, making use of the data mapper pattern

Articulate Laravel: 8.* PHP: 8.* License: MIT Author: Ollie Read Author Homepage: https://ollie.codes Articulate is an alternative ORM for Laravel bas

Ollie Codes 4 Jan 4, 2022
Extensions for the Eloquent ORM

Sofa/Eloquence Easy and flexible extensions for the Eloquent ORM. Currently available extensions: Searchable query - crazy-simple fulltext search thro

Jarek Tkaczyk 1.1k Dec 20, 2022
Simple Enum cast for Eloquent ORM using myclabs/php-enum.

Enum cast for Eloquent Simple Enum cast for Eloquent ORM using myclabs/php-enum. Requirements PHP 7.3 or higher Laravel 8.0 or higher Installation You

Orkhan Ahmadov 5 Apr 21, 2022
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
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
Eloquent Repository implementation

Eloquent Repository Eloquent Repository using nilportugues/repository as foundation. Installation Use Composer to install the package: $ composer requ

Nil Portugués Calderó 17 Feb 12, 2022
Eloquent MongoDB Repository Implementation

Eloquent MongoDB Repository Eloquent MongoDB Repository using nilportugues/repository as foundation, using jenssegers/mongodb. Installation Use Compos

Nil Portugués Calderó 18 Feb 12, 2022
Doctrine Object Relational Mapper (ORM)

3.0.x 2.9.x 2.8.x Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence for PHP objects. It sits on top o

Doctrine 9.5k Jan 2, 2023
ORM layer that creates models, config and database on the fly

RedBeanPHP 5 RedBeanPHP is an easy to use ORM tool for PHP. Automatically creates tables and columns as you go No configuration, just fire and forget

Gabor de Mooij 2.2k Jan 9, 2023
Propel2 is an open-source high-performance Object-Relational Mapping (ORM) for modern PHP

Propel2 Propel2 is an open-source Object-Relational Mapping (ORM) for PHP. Requirements Propel uses the following Symfony Components: Config Console F

Propel 1.2k Dec 27, 2022
PHP DataMapper, ORM

Cycle ORM Cycle is PHP DataMapper, ORM and Data Modelling engine designed to safely work in classic and daemonized PHP applications (like RoadRunner).

Cycle ORM 1.1k Jan 8, 2023
Ouzo Framework - PHP MVC ORM

Ouzo is a PHP MVC framework with built-in ORM and util libraries. PHP 8.0 or later is required. We believe in clean code and simplicity. We value unit

Ouzo 69 Dec 27, 2022
Builds Cycle ORM schemas from OpenAPI 3 component schemas

Phanua OpenAPI 3 + Jane + Cycle ORM = ?? Phanua builds Cycle ORM schemas from OpenAPI 3 component schemas. Released under the MIT License. WARNING: Th

Matthew Turland 5 Dec 26, 2022
Extra RedBean ORM

RedBeanPHP 5 RedBeanPHP is an easy to use ORM tool for PHP. Automatically creates tables and columns as you go No configuration, just fire and forget

GingTeam Development 5 Nov 23, 2022
MongoDB ORM that includes support for references,embed and multilevel inheritance.

Introduction Features Requirements Installation Setup Database Basic Usage - CRUD Relationship - Reference Relationship - Embed Collection Inheritance

Michael Gan 202 Nov 17, 2022
Low code , Zero Configuration ORM that creates models, config, database and tables on the fly.

?? ARCA ORM ?? Low code , Zero Configuration ORM that creates models, config, database and tables on the fly. ?? ???? Made in India ???? Complete docu

Scrawler Labs 28 Dec 18, 2022
The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

Mark Armendariz 0 Jan 7, 2022
Orm is a simple database abstraction layer that supports postgresql.

Orm What is it Orm is a simple database abstraction layer that supports postgresql. Welcome to join us or star us for encouragement. Requires php 8.1

null 2 Sep 28, 2022
[READ-ONLY] A flexible, lightweight and powerful Object-Relational Mapper for PHP, implemented using the DataMapper pattern. This repo is a split of the main code that can be found in https://github.com/cakephp/cakephp

CakePHP ORM The CakePHP ORM provides a powerful and flexible way to work with relational databases. Using a datamapper pattern the ORM allows you to m

CakePHP 146 Sep 28, 2022