Tìm hiểu về thư viện baum tree (p2)
Bài đăng này đã không được cập nhật trong 6 năm
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ứcget
hoặcfirst
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 getDescendants
và getDescendantsAndSelf
bằ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
,rgt
vàdepth
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
All rights reserved