laravel-nestedset : 올바른 자세의 무제한 다단계 분류

laravel-nestedset : 올바른 자세의 무제한 다단계 분류

laravel-nestedset은 패키지 larvel4-5에서 플러그인의 관계형 데이터베이스 트리 탐색이다

내용 :

  • 중첩 된 세트 모델 소개
  • 설치 요구 사항
  • 설치
  • 시작
    • 파일 마이그레이션
    • 노드 삽입
    • 노드를 가져옵니다
    • 삭제 노드
    • 일관성 검사 및 수리
    • 범위

 

중첩 된 세트 모델 소개

중첩 된 세트 모델은 빠르고하고 아무리 많은 레이어와 같은 트리로 재귀 쿼리를 필요로하지 않습니다 주문 트리를 구현하기위한 현명한 방법이다, 당신은 노드의 모든 자손을 얻기 위해 단 하나 개의 쿼리를 사용할 수 있으며, 단점은 그것이 그 복잡한 SQL 문을 수행해야하지만, 이러한이 플러그인 내에서 처리하고, 삭제, 이동, 삽입! 위키 백과에 대한 자세한하세요! 중첩 된 세트 모델  과 중국어 번역! 중첩 된 세트 모델

설치 요구 사항

  • PHP> = 5.4
  • laravel> = 4.1
  • V4.3 지원 향후 버전 Laravel - 5.5
  • V4 버전 지원 Laravel-5.2,5.3,5.4
  • v3 릴리즈가 지원 Laravel - 5.1
  • V2 버전 지원 Laravel-4는 강력하게 잠재적 인 데이터 손상을 방지하기 위해 (MySQL의 InnoDB의 같은) 엔진의 기능을 지원 것들 데이터의 사용을 권장합니다.

설치

에서 composer.json다음 코드 파일을 추가 :

"kalnoy/nestedset": "^4.3",

실행 composer install 하여 설치.

또는 직접 명령 행 입력에서

composer require kalnoy/nestedset

버전 기록을 클릭하여 설치합니다 더 많은 버전을

시작

파일 마이그레이션

당신이 사용할 수있는 NestedSet클래스를 columns방법의 기본 이름으로 필드를 추가 :

...
use Kalnoy\Nestedset\NestedSet;

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

삭제 필드 :

...
use Kalnoy\Nestedset\NestedSet;

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

기본 필드는 _lft이름 : _rgt,, parent_id, 소스 코드는 다음과 같습니다 :

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

모델

당신은 모델 사용할 필요가 Kalnoy\Nestedset\NodeTrait중첩 된 세트를 구현하는 특성을

use Kalnoy\Nestedset\NodeTrait;

class Foo extends Model {
    use NodeTrait;
}

기존의 로컬 데이터 마이그레이션

다른 중첩 된 세트 모델 라이브러리에서 마이그레이션

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

다른 모델 라이브러리에서 마이그레이션 부모 - 자식 관계가

데이터베이스가 나무가 포함 된 경우  parent_id 필드 정보를, 당신은 당신의 청사진 문서에 다음과 같은 두 개의 열 필드를 추가해야합니다 :

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

모델을 설정 한 후에 만 채우기에 트리를 수정해야 _lft하고 _rgt필드 :

MyModel::fixTree();

관계

노드가 완벽하게 작동하고 미리로드되어, 다음과 같은 기능이 있습니다 :

  • 노드는 부모에 속하는
  • 노드는 많은 아이들이있다
  • 노드는 여러 조상을 가지고
  • 노드는 많은 자손을 가지고

우리는 분류 모델을 가지고 가정, $ 노드는 모델의 인스턴스 변수는 우리의 작업의 노드 (노드)입니다. 그것은 새로운 노드가 생성하거나 데이터베이스 노드에서 제거 될 수 있습니다

삽입 노드 (노드)

노드를 삽입하거나 이동할 때마다 몇 가지 데이터베이스 작업이 모두 좋습니다 트랜잭션을 실행합니다.

주의! V4.2.0 버전이 자동으로 열려있는 트랜잭션이 아니며, 구조 작업의 다른 노드는 모델에 저장 수동으로 수행 할 필요는 없지만, 일부는 저장 및 작동 상태로 복귀의 부울 결과를 수행하는 방법을 숨겨집니다.

만들기 노드 (노드)

간단한 노드를 만들 때, 그것은 나무의 끝에 추가됩니다.

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

또는

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

다음은 노드가 부모가없는 것을 의미 루트로 설정

루트에 기존의 노드

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

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

부모 노드 또는 지정된 프론트 엔드의 마지막에 자식 노드를 추가합니다

당신이 자식 노드를 추가 할 경우, 부모 노드 또는 마지막 아이의 첫 번째 자식 노드를 추가 할 수 있습니다. * 다음과 같은 예에서,  기존의 노드로$parent 

부모 노드에있어서의 끝에 추가 :

// #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);

부모 노드의 선단에 추가

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

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

노드는 앞에 삽입 또는 지정된 노드의 후면되고

당신은 다음 방법을 사용하여 $node지정된 노드 추가 $neighbor인접 노드를

$neighbor있어야합니다, $node당신은 새로운 노드를 만들 수 있습니다, 또는 경우, 기존 될 수 있습니다 $node이미 존재하는 노드가이 새로운 위치로 이동합니다 $neighbor필요한 경우, 이웃, 부모가 변경됩니다.

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

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

배열은 트리를 구성한다

그러나 사용하여 create정적 메서드 배열이 포함되어 있는지 여부, 그것을 확인 children키 것은있는 경우, 반복적으로 더 많은 노드를 작성합니다.

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

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

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

이제 $node->children생성 된 노드 세트가 포함되어 있습니다.

배열은 나무를 다시

당신은 쉽게 트리 구조의 수정을 많이 절약 할 매우 유용한 트리를 재구성 할 수 있습니다. Category::rebuildTree($data, $delete);

$data어레이 주제 노드

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

최고 가지고 name에 대한 foo지정된 한 노드, id노드가 존재하지 않는 경우 노드의 기존 대신이 던져처럼, 채워집니다 ModelNotFoundException 또한,이 노드뿐만 아니라 children배열을, 배열은 같은 방식으로 할 것 추가받는 foo내부 노드. bar노드는이 생성됩니다, 존재하지 않는, 기본 키가 없습니다. $delete 대표 데이터베이스를 삭제할지 여부 것은 이미 존재하지만 $data데이터에 존재하지 않는, 기본은 삭제되지 않습니다.

이후 재건 하위 트리 버전 4.3.8에는 서브 트리를 재 구축 할 수 있습니다

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

이 단지 $ 루트 하위 트리의 재건을 제한 할 것이다

검색 노드

우리는 변수 $ id를 사용해야하는 경우에는 기본 키 대상 노드의 ID를 나타냅니다

조상과 후손

선조는 빵의 전류 형태의 디스플레이를 위해 도움이 부스러기 부모 노드의 체인을 생성한다. 자손은 부모 노드의 모든 자식 노드입니다. 조상과 후손이 사전로드되어 있습니다.

// Accessing ancestors
$node->ancestors;

// Accessing descendants
$node->descendants;

사용자 정의 쿼리를 통해 조상과 후손을로드 :

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

대부분의 경우, 계층을 기준으로 정렬해야합니다

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

조상 세트는 사전로드 할 수 있습니다 :

$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

조상은 name모든 스플 라이스에서> 문자열, 배열로 꺼내.

형제

우리는 같은 부모는 형제 노드 상호이라고했다

$result = $node->getSiblings();

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

인접 형제 뒤에 가져 오기 :

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

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

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

인접 형제 앞에 가져 오기 :

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

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

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

관련 모델 테이블에 액세스

각 카테고리는 많은 제품을 가지고 있으며, hasMany의 관계가 설정되었음을 가정, 어떻게 간단하게는 전체를 위하여, 전체는 미래 세대에 제품의 $ 범주를 얻을?

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

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

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

노드는 깊이를 포함 (깊이)

당신은 그 수준의 액세스 노드를 알 필요가있는 경우 :

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

$depth = $result->depth;

사용할 수 루트 노드 (루트), 레이어 0 (레벨 0), 부 - 루트 노드는 제 1 층이 (레벨 1) 인 having특정 노드 계층을 구하는 제약

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

이 데이터베이스 엄격 모드에서 사용할 수 없습니다 있습니다

기본 정렬

모든 노드는 엄격한 조직 내에서 기본적으로 어떤 순서없는, 노드는 임의 쇼 당신은 알파벳 순서 및 정렬 다른 노드에서, 이것의 영향을 보여주고, 할 수있다.

그러나 경우에 따라서는 조상과 메뉴 순서를 얻기위한 유용 계층 구조를 표시하는 것이 필요하다.

사용 deaultOrder 나무의 종류를 사용합니다 : $result = Category::defaultOrder()->get();

또한 역순를 사용할 수 있습니다 : $result = Category::reversed()->get();

정렬 기본값을 변경하기까지 내부 아래로 부모 노드로 이동하자 :

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

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

부울 값의 노드의 동작 위치를 변경할 것인지로 되돌아갑니다

강제

많은 제약이 쿼리 빌더에서 사용할 수 있습니다 :

  • whereIsRoot ()는 루트 노드를 가져옵니다;
  • whereIsAfter ($ ID는) 특정 노드 (뿐만 아니라 형제 자매)의 id의 뒷면에있는 모든 노드를 가져옵니다.
  • whereIsBefore ($ ID는) 특정 노드 (뿐만 아니라 형제 자매)의 id의 앞에 모든 노드를 가져옵니다.

조상 제약

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

$node 한 예는 주요 키 또는 모델의 모델이 될 수있다

자손 제약

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

트리 구축

결과 집합의 노드를 취득 후, 우리는 예를 들어, 나무로 변환 할 수 있습니다 : $tree = Category::get()->toTree();

이는 각 노드에서 부모와 자녀의 관계를 추가 할 것입니다, 당신은 나무를 렌더링하는 재귀 알고리즘을 사용할 수 있습니다 :

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

그것은 다음과 유사한 출력과 같이 될 것입니다 :

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

평평한 나무의 건설

당신은 평면 트리를 구성 할 수 있습니다 : 하위 노드가 직접 부모 노드 뒤에 배치됩니다. 사용자 정의 정렬 노드를 얻을 할 때 루프에 노드를 재귀를 사용하지 유용합니다. $nodes = Category::get()->toFlatTree();

이것의 예는 다음과 같은 출력하기 전에 될 것입니다 :

Root
Child 1
Sub child 1
Child 2
Another root

하위 트리의 건설

때때로 당신은 전체 트리하지만 특정 하위 트리를로드 할 필요가 없습니다 :  $root = Category::descendantsAndSelf($rootId)->toTree()->first(); 간단한 쿼리 우리는 루트 및 어린이의 사용 사이의 관계를 얻을 수있는 하위 트리의 모든 자손을 얻을

당신이 $ 루트 노드 자체가 필요하지 않은 경우, 당신은 할 수 있습니다 : $tree = Category::descendantsOf($rootId)->toTree($rootId);

삭제 노드

노드 삭제 :

$node->delete();

** 참고! ** 모든 자손 노드는 참고 삭제됩니다! 노드가 동일한 모델에 제거해야합니다, 당신은 노드를 삭제하려면 다음 문을 사용할 수 없습니다 :

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

이 트리 구조 지원의 파괴 SoftDeletes특성, 그리고 모델 층에서

도우미 메서드

노드를 확인하는 것은 다른 노드에 자식 노드 $bool = $node->isDescendantOf($parent);

루트 여부 확인 $bool = $node->isRoot();

기타 시험

  • $ 노드 -> isChildOf ($ 기타);
  • $ 노드 -> isAncestorOf ($ 기타);
  • $ 노드 -> isSiblingOf ($ 기타);
  • $ 노드 -> isLeaf ()

일관성을 확인

당신은 나무가 반지를 파괴 여부를 확인할 수 있습니다 $bool = Category::isBroken();

오류 통계를 얻을 : $data = Category::countErrors();

이 키에 대한 포함하는 배열을 반환

  • 노드의 오류 번호의 LFT와 RGT 값 - oddness
  • LFT RGT 값 또는 노드를 반복 수 - 중복
  • wrong_parent는 - 왼쪽 RGT 값은 PARENT_ID 잘못된 번호를 PARENT_ID 원인 노드에 해당하지 않는
  • PARENT_ID 포함하는 노드를 존재하지 않는 부모 노드에 해당하는 번호 - missing_parent

수리 트리

버전 3.1 지원 향후 복원 트리에서, 정보의 상속 PARENT_ID 필드를 통해 각 노드는 적절한 값 LFT와 RGT를 설정합니다 Node::fixTree();

범위 (범위)

. 당신이 한 수많은 모델과 메뉴 아이템이 있다고 가정 사이에 일대 다 관계입니다. 메뉴 아이템은 menu_id 속성이 중첩 된 세트 모델을 실현. 물론 당신이 재산 기반이 이러한 기능을 달성하기 위해, 개별적으로 각각의 나무를 menu_id 처리하려면, 우리는 범위 속성에 속성을 menu_id 지정해야합니다.

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

이제 우리는 사용자 지정 쿼리를 구현해야, 우리는 재산의 범위를 제한 할 필요를 제공해야합니다.

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

그러나 자동 기반 모델 인스턴스 쿼리 노드 범위를 사용하면 선택한 노드를 삭제하는 범위 속성 세트를 제한합니다. 예를 들면 :

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

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

예 쿼리는 선택의 삭제를 얻을 수 있습니다 : $node->newScopedQuery();

키가 범위를 사용하여 수집 모델을 필요로하지 않을 때, 그 주

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

 

2018년 2월 25일에 편집 됨
Laravel
PHP 프레임 워크

추천

출처www.cnblogs.com/mouseleo/p/12032659.html