laravel-nestedset: unlimited multi-level classification of the correct posture

laravel-nestedset: unlimited multi-level classification of the correct posture

laravel-nestedset is a relational database tree traversal of the plug-in package larvel4-5

table of Contents:

  • Nested Sets Model Introduction
  • Installation Requirements
  • installation
  • start using
    • File migration
    • Insert Node
    • Gets node
    • Delete Node
    • Consistency check and repair
    • Scope

 

Nested Sets Model Introduction

Nested Set Model is a clever method for implementing ordered tree, which is fast and does not require recursive queries, such as a tree no matter how many layers, you can use only one query to get all the offspring of a node, the disadvantage is that it insert, move, delete, you need to perform complex sql statement, but these were dealt with within this plugin! See more on Wikipedia! Nested set model  and its Chinese translation! Nested Set Model

Installation Requirements

  • PHP>=5.4
  • laravel>=4.1
  • v4.3 support future versions Laravel-5.5
  • v4 version supports Laravel-5.2,5.3,5.4
  • v3 release supports Laravel-5.1
  • v2 release supports Laravel-4 strongly recommend the use of data to support things function of engine (like the MySql innoDb) to prevent potential data corruption.

installation

In composer.jsonadding the following code file:

"kalnoy/nestedset": "^4.3",

Run composer install to install it.

Or directly from the command line input

composer require kalnoy/nestedset

To install the version history Click more versions

start using

File migration

You can use the NestedSetclass columnsto add a field with a default name of the method:

...
use Kalnoy\Nestedset\NestedSet;

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

Delete field:

...
use Kalnoy\Nestedset\NestedSet;

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

The default field _lftnamed: _rgt, , parent_id, source code as follows:

public static function columns(Blueprint $table)
    {
        $table->unsignedInteger(self::LFT)->default(0);
        $table->unsignedInteger(self::RGT)->default(0);
        $table->unsignedInteger(self::PARENT_ID)->nullable();

        $table->index(static::getDefaultColumns());
    }

model

You need to use the model Kalnoy\Nestedset\NodeTraittrait to implement nested sets

use Kalnoy\Nestedset\NodeTrait;

class Foo extends Model {
    use NodeTrait;
}

Migrate other existing local data

Migration from other nested set model library

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 another model library has a parent-child relationship

If your database contains tree  parent_id field information, you need to add the following two columns field to your blueprint document:

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

After setting up your model you only need to fix your tree to populate _lftand _rgtfield:

MyModel::fixTree();

relationship

Node has the following functions, they are fully functional and are pre-loaded:

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

Suppose we have a Category model; $ node is an instance variable of the model is the node (node) of our operations. It may be a new node is created or removed from the database node

Insertion node (node)

Each time you insert or move a node to be executed are several database operations, all strongly recommended transaction.

note! For v4.2.0 version is not automatically open transaction, and the other node of structured operations need be performed manually save on the model, but some will be hidden method to perform Boolean result of the save and return to operation.

Creating nodes (node)

When you create a simple node, it will be added to the end of the tree.

Category::create($attributes); // 自动save为一个根节点(root)

or

$node = new Category($attributes);
$node->save(); // save为一个根节点(root)

Here node is set to root, meaning that it has no parent

An existing node to root

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

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

Add a child node to the parent node or the end of the specified front end

If you want to add a child node, you can add the first child node of the parent node or the last child. * In the following examples,  as existing nodes$parent 

Added to the end of the parent node comprising:

// #1 使用延迟插入
$node->appendToNode($parent)->save();

// #2 使用父节点
$parent->appendNode($node);

// #3 借助父节点的children关系
$parent->children()->create($attributes);

// #5 借助子节点的parent关系
$node->parent()->associate($parent)->save();

// #6 借助父节点属性
$node->parent_id = $parent->id;
$node->save();

// #7 使用静态方法
Category::create($attributes, $parent);

Added to a front end of the parent node

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

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

Node is inserted into the front or rear of the specified node

You can use the following methods to $nodeadd the specified node $neighboradjacent node

$neighborMust be present, $nodeyou may be newly created node, or may be existing, if $nodea node that already exists, it will move to a new location and $neighborthe adjacent, if necessary, its parent will change.

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

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

The array is constructed tree

But using createa static method, it checks whether the array contains the childrenkey, if any, will recursively create more nodes.

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

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

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

Now $node->childrencontains a set of nodes that have been created.

The array rebuild a tree

You can easily reconstruct a tree, which is very useful to save a lot of modifications of the tree structure. Category::rebuildTree($data, $delete);

$dataAn array representative node

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

Has a top namefor the foonode, which has a specified id, existing behalf of the node will be filled, if the node does not exist, like a throw ModelNotFoundException addition, this node as well as childrenan array, the array will be in the same manner adding to the foointernal node. barNode has no primary key, just do not exist, it will be created. $delete Representatives whether to delete the database already exists but $datadoes not exist in the data, default is not deleted.

For subsequent reconstruction sub-tree version 4.3.8 you can rebuild the subtree

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

This will limit the reconstruction of only $ root subtree

Retrieval node

In some cases we need to use the variable $ id represents the primary key target node id

Ancestors and descendants

Ancestors create a chain of the parent node, which for the display of the current types of bread crumbs helpful. Descendants are all child nodes of a parent node. Ancestors and Descendants are preloaded.

// Accessing ancestors
$node->ancestors;

// Accessing descendants
$node->descendants;

Load ancestors and descendants through customized queries:

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

In most cases, you need to sort by hierarchy:

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

Ancestors set can be pre-loaded:

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

// 视图模板中面包屑:
@foreach($categories as $i => $category)
    <small> $category->ancestors->count() ? implode(' > ', $category->ancestors->pluck('name')->toArray()) : 'Top Level' </small><br>
    $category->name
@endforeach

The ancestors nameall taken out into an array, with> string from the splice.

Sibling

We have the same parent is referred to as sibling node mutual

$result = $node->getSiblings();

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

Get behind the adjacent sibling:

// 获取相邻的下一个兄弟节点
$result = $node->getNextSibling();

// 获取后面的所有兄弟节点
$result = $node->getNextSiblings();

// 使用查询获得所有兄弟节点
$result = $node->nextSiblings()->get();

Get in front of the adjacent sibling:

// 获取相邻的前一个兄弟节点
$result = $node->getPrevSibling();

// 获取前面的所有兄弟节点
$result = $node->getPrevSiblings();

// 使用查询获得所有兄弟节点
$result = $node->prevSiblings()->get();

Access to relevant model table

Assume that each category has many goods, and hasMany relationship has been established, how simple it all and get $ category of goods in all future generations?

// 获取后代的id
$categories = $category->descendants()->pluck('id');

// 包含Category本身的id
$categories[] = $category->getKey();

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

Node contains depth (depth)

If you need to know the access node of that level:

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

$depth = $result->depth;

The root node (root) is the layer 0 (level 0), the sub-root node is a first layer (level 1), so you can use havingconstraints to obtain a particular node hierarchy

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

Note that this is not available in the database strict mode

The default sort

All nodes are within the strict organization, no order by default, the node is random show, to show the impact of this, you can in alphabetical order and the other nodes sorting.

But in some cases it is necessary to show a hierarchy, it is useful for obtaining ancestors and menu order.

Use deaultOrder use the sort of tree: $result = Category::defaultOrder()->get();

You can also use the reverse order: $result = Category::reversed()->get();

Let the parent node moves up and down inside to change the default sort:

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

// 向下移动3个兄弟节点
$bool = $node->down(3);

The operation returns to whether to change the operating position of the node of the Boolean value

constraint

Many constraints can be used on these query builder:

  • whereIsRoot () Gets the root node only;
  • whereIsAfter ($ id) Gets all nodes in the back of the id of a particular node (not just siblings).
  • whereIsBefore ($ id) Gets all nodes in the front of the id of a particular node (not just siblings).

Ancestor constraints

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

$node Examples which may be a primary key or the model of the model

Offspring 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();

//结果集合中包含目标node自身
$result = Category::whereDescendantOrSelf($node)->get();

Building a Tree

After acquiring a node of the result set, we can convert it to a tree, for example: $tree = Category::get()->toTree();

This will add the parent and children relationship on each node, and you can use a recursive algorithm to render the tree:

$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);

It will be like the following output similar to:

- Root
-- Child 1
--- Sub child 1
-- Child 2
- Another root

Construction of a flat tree

You can construct a flat tree: the sub-node is placed directly behind the parent node. When you get custom sorting nodes and do not want to use recursion to loop your nodes is useful. $nodes = Category::get()->toFlatTree();

Examples of this will be before the following output:

Root
Child 1
Sub child 1
Child 2
Another root

Construction of a subtree

Sometimes you do not need to load the entire tree, but only certain sub-tree:  $root = Category::descendantsAndSelf($rootId)->toTree()->first(); a simple query that we can get the relationship between the root and the use of children subtree get it all descendants

If you do not need $ root node itself, you can: $tree = Category::descendantsOf($rootId)->toTree($rootId);

Delete Node

Delete a node:

$node->delete();

**note! ** All descendant nodes will be deleted Note! Nodes need to be removed to the same model, you can not use the following statement to delete a node:

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

This will destroy the tree structure supports SoftDeletestrait, and in the model layer

helper method

Check the node is a child node to other nodes $bool = $node->isDescendantOf($parent);

Check whether the root $bool = $node->isRoot();

Other tests

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

Check the consistency

You can check whether the tree is broken ring $bool = Category::isBroken();

Get the error statistics: $data = Category::countErrors();

It returns an array containing about key

  • Lft and rgt value of the error number of nodes - oddness
  • Lft rgt value or number of repeating node - duplicates
  • wrong_parent - left and rgt values ​​parent_id parent_id invalid number does not correspond to nodes causing
  • Number corresponding to the parent node does not exist parent_id containing nodes - missing_parent

Repair tree

From v3.1 support future restoration tree, through inheritance parent_id field of information, each node to set the appropriate value lft and rgt Node::fixTree();

Scope (scope)

Suppose you have a Memu model and MenuItems. Is a one-to-many relationship between them. MenuItems have menu_id property and realize nested sets model. Obviously you want to deal with property-based menu_id each tree individually, in order to achieve such a function, we need to specify the scope menu_id property to property.

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

Now we have to implement a custom query, we need to provide the need to limit the scope of the property.

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

But using model instance query node, scope based automatically limit the scope attribute set to delete the selected node. E.g:

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

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

Example query to get the deletion of selected: $node->newScopedQuery();

Note that, when the keys do not require acquisition model by using the scope

$node = MenuItem::findOrFail($id); // OK
$node = MenuItem::scoped([ 'menu_id' => 5 ])->findOrFail(); // OK, 但是多余 

 

Edited on 2018-02-25
Laravel
PHP Framework

Guess you like

Origin www.cnblogs.com/mouseleo/p/12032659.html