Tìm hiểu về thư viện baum tree (p2)

Lời nói đầu :

Trong bài viết lần trước , chúng ta đã tìm hiểu về thuật toán Nested Sets và 1 phần cách sử dụng của thư viện etrepat/baum sử dụng thuật toán Nested Sets để quản lý dữ liệu. Ngày hôm nay mình xin chia sẻ nốt phần còn lại của nó.

Continue

1 , Lấy các nút gốc và các nút lá

Trong các truy vấn dữ liệu đôi khi chũng ta cần lấy toàn bộ các nút gốc và nút là của cây dữ liệu này. Với etrepat/baum điều này thật đơn giản.

// Lấy tất cả các nút gốc
Category::roots();

// lấy tất cả các nút lá
Category:allLeaves();

Và nếu bạn chỉ cần lấy nút gốc đầu tiên thì có thể sử dụng :

$firstRootNode = Category::root();

Và đương nhiên khi bạn sử dụng các hàm này cũng có thể kết hợp vs các điệu kiên where thích hợp để truy vấn dữ liệu sao cho hợp lý.

2, Các phương thức truy cập dữ liệu

Như các bạn đã thấy , cấu trúc dữ liệu của chúng ta là hết sức phức tạp, vì thế việc truy cập dữ liệu cũng là hết sức phức tạp khi bạn từ 1 nút là tìm đến nút gốc hoặc là từ 1 nút là tìm đến các nút lân cận cùng tầng.

Thật là may vì etrepat/baum có cung cấp 1 số phương pháp để hỗ trợ việc truy cập dữ liệu này. Có 2 cách để truy cập dữ liệu :

  • Cách đầu tiên là sử dụng query scope, với việc trả ra Illuminate\Database\Eloquent\Builder bạn có thể tiếp tục truy vấn tiếp ... Nên nhớ sử phương thức get hoặc first sau khi truy vấn xong :

    • ancestorsAndSelf() : Lấy tất cả các nút tổ tiên và bản thân nút hiện tại.
    • ancestors() : Lấy tất cả các nút tổ tiên
    • siblingsAndSelf(): Tìm kiếm tất cả các nút anh em cùng cấp với nút hiện tại (bao gồm cả nút hiện tại) ... Tức là các nút cùng 1 cha vơi nút hiện tại
    • siblings() : Tìm kiếm tất cả các nút anh em cùng cấp với nút hiện tại (bao gồm cả nút hiện tại)
    • leaves() : Tìm kiếm các nút là nút hậu duệ (con, cháu ,...) của nút hiện tại và các nút con đó không có con (tức là các nút không có cấp thấp hơn)
    • descendantsAndSelf() : Tìm kiếm các nút hậu duệ của nút hiện tại (con, cháu ,...) và bao gồm luôn cả nút hiện tại
    • descendants() : Tìm kiếm các nút hậu duệ của nút hiện tại (con, cháu ,...)
    • immediateDescendants() : Tìm kiếm các nút con của nút hiện tại.
  • Cách thứ 2 đó là sử dụng method.

    • getRoot() : Trả về nút gốc của nút hiện tại

    • getAncestorsAndSelf() : Trả về các nút tổ tiên và nút hiện tại

    • getAncestorsAndSelfWithoutRoot() : Trả về các nút tổ tiên và nút hiện tại tuy nhiên loại bỏ nút gốc

    • getAncestors() : Trả về tát cả các nút tổ tiên trừ nút hiện tại

    • getAncestorsWithoutRoot() :Trả về tát cả các nút tổ tiên trừ nút hiện tại và nút gốc

    • getSiblingsAndSelf() : Lấy tất cả các nút anh em cùng cấp (cùng 1 cha) và nút hiện tại

    • getSiblings() : Lấy tất cả các nút anh em cùng cấp (cùng 1 cha) và không bao gồm nút hiện tại

    • getLeaves() : Trả về tất cả các hậu duệ cuối cùng của nút hiện tại (tức là các nút hậu duệ không có mức thấp hơn nữa)

    • getDescendantsAndSelf() : Trả về tất cả các hậu duệ cuối cùng của nút hiện tại và chính nó

    • getDescendants() : Trả về tất cả các hậu duệ cuối cùng của nút hiện tại và không bao gồm chính nó

    • getImmediateDescendants() : Trả về con của nút hiện tại.

Ví dụ :

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

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

3, Giới hạn số lượng tầng truy vấn dữ liệu

Trong trượng hợp tầng dữ liệu của bạn quá phức tạp (quá nhiều tầng) và bạn chỉ mong muốn lấy dữ liệu ở giới hạn trong 1 số tầng nhất định. Thì có thế sử dụng limitDepth query scope mà etrepat/baum cung cấp :

Ví dụ bạn chỉ muốn query giới hạn trong 5 tầng thì có thể dùng như sau :

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

Một cách khác đó là bạn có thể sử dụng 2 method getDescendantsgetDescendantsAndSelfbằng cách cung cấp tham số giới hạn độ sâu query . Ví dụ :

// query vẫn làm việc khi không có limit 
// 1. Dùng mặc định
$node->getDescendants();
// 2. Chỉ lấy 1 số thuộc tính
$other->getDescendants(array('id', 'parent_id', 'name'));
...
// Sử dụng với limit
// 1. Lấy tối đa trong 5 tầng con của nút hiện tại
$node->getDescendants(5);
// 2. Lấy tối đa trong 5 tầng con của nút hiện tại và chỉ lấy 1 số thuộc tính
$other->getDescendants(5, array('id', 'parent_id', 'name'));

4, Điều chỉnh sự sắp xếp dữ liệu.

  • Mặc dịnh, dữ liệu trả ra của etrepat/baum sẽ được xắp xếp theo giá trị cộtlft để nhật quán.

  • Nếu bạn muốn thay đổi hành vi mặc định này, bạn cần chỉ định trong model của bạn tên của cột bạn muốn sử dụng để sắp xếp các kết quả như sau:

    protected $orderColumn = 'name';

5, Trả về cây dữ liệu.

Baum model extend từ class Eloquent \ Collection và mặc định sẽ trả ra các Collection . Tuy nhiên nếu bạn muốn truy vấn trả ra 1 cây dữ liệu phân cấp để có 1 cái nhìn tổng quát, thì có thể sử dụng phương thức toHierarchy . Ví dụ :

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

6, Model events: moving and moved.

Baum cung cấp cho chúng ta 2 event moving (trước khi di chuyển) và moved (sau khi di chuyển) . Tương tự như các event khác của model. Nếu event trả về false thì sự thay đổi trước và sau của nút đó sẽ bị dừng lại :

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

7, Scope

  • Có đôi khi bạn muốn 1 bảng dữ liệu có thể lưu nhiều loại cây khác nhau để tránh lãng phí DB. Điều này hoàn toàn có thể. Baum cung cấp một phương pháp đơn giản để cung cấp tập hợp Nested Set "scoping" hạn chế những gì chúng ta coi là một phần của cây tập hợp lồng nhau.

  • Để sử dụng chức năng tính toán phạm vi, bạn có thể ghi đè thuộc tính scoped trong model của bạn.

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

8 : Tree rebuilding

  • Baum hỗ trợ xây dựng lại toàn bộ cấu trúc cây (hoặc reindexing) thông qua phương thức static :: rebuild ().

  • Phương pháp này sẽ lập chỉ mục lại tất cả các giá trị các cột lft, rgtdepth của bạn, kiểm tra cây của bạn chỉ từ các nút cha mẹ. Điều đó có nghĩa là bạn chỉ cần một cột parent_id đầy đủ chính xác và Baum sẽ làm phần còn lại.

  • Điều này có thể khá hữu ích khi có 1 sai sót không mong muốn với các giá trị chỉ mục hoặc nó có thể trở nên tiện dụng khi move nút này sang một nhánh khác (đảm bảo parent_id luôn đúng).

      Category::rebuild()
    

Kêt bài

Ok, vậy là mình đã nói hết các chức năng của thư viện etrepat/baum . Cám ơn các bạn đã đọc hết bài viết của mình, hy vọng bài viết có ích cho các bạn.

Tài liệu tham khảo

https://github.com/etrepat/baum