Laravel中的巢狀集是今天的主題的巢狀集模型是根據樹遍歷對節點進行編號,遍歷每個節點兩次,按訪問順序分配編號,並在兩次訪問時分配。這為每個節點留下了兩個數字編號,它們儲存為兩個屬性。查詢變得更便宜:可以通過比較這些數字來測試層次結構成員資格。 更新則需要重新編號。
什麼是 Nested Set
巢狀集或巢狀集模型(NSM)是有效地將分層資料儲存在關係表中的方法。
用例
當樹很少更新時,NSM顯示出良好的效能。 它被建立為快速獲取相關節點。
它非常適合為商店構建多個深度選單或類別。
關係
巢狀集是一個填充了不同節點的樹結構。 所以他們之間的關係如下。
- 節點屬於父節點
- 節點有許多子節點
- 節點有很多祖先
- 節點有很多後代
Nested Set 在Laravel中的例子
我們將使用以下命令安裝Framework來開始此Laravel 5.7巢狀集教程。
Step 1: 安裝Laravel
我正在使用Valet
。 所以,我需要輸入以下命令來建立一個專案。
laravel new nestedset
當然如果你沒有用Homestead
或者Valet
, 你也可以用下面的命令。
composer create-project laravel/laravel nestedset --prefer-dist
安裝好後進入專案目錄。
cd nestedset
在.env
檔案中配置你的資料庫。
Step 2: 安裝 laravel-nestedset composer 包。
又可以在這裡找到官方的Github倉庫。
使用下面的composer
命令來安裝。
composer require kalnoy/nestedset
裝好了後,我們來配置下它。
首先,我們正在努力建立一個電子商務商店。所以這是一個完美的場景,我們可以使用巢狀集,因為我們有第一個類別,然後是子類別和子子類別等等。 所以我們試圖製作一個列表,其中,根是主要類別,然後它的所有子節點都是子類別。
Step 3: 建立一個模型和資料庫遷移。
在你的終端中,使用如下命令:
php artisan make:model Shop -m
上面的命令就是同時生產模型和遷移檔案的。
現在,在遷移檔案中,我們需要新增一些由巢狀集庫提供的額外列。
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
// 以下僅在laravel版本小於5.5時用
// use Kalnoy\Nestedset\NestedSet;
class CreateShopsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('shops', function (Blueprint $table) {
$table->increments('id');
$table->string('category_name');
// NestedSet::columns($table); //低於laravel 5.5的老版本用這個命令
$table->nestedSet();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('shops');
}
//以下的命令只單獨刪除Nested Set的欄位,不會刪除表,注意!
// public function down()
// {
// Schema::table('shops', function (Blueprint $table){
// $table->dropNestedSet();
// });
// }
}
因此,這個商店表包含category_name和Nested Set列。
現在我們使用下面的命令建立表。
php artisan migrate
我們可以看到在我們的商店表中有三列,與巢狀集有關。
_lft
_rgt
parent_id
Step 3: 填充資料庫。
對於此示例,我不是在建立動態資料,而是構建靜態資料,然後將其提供給資料庫。
首先,使用以下命令建立Seeder檔案。
php artisan make:seeder ShopTableSeeder
開啟App\Shop.php
檔案,寫入以下程式碼,啟用Nested Set。(低於laravel5.5的版本不需要設定)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Kalnoy\Nestedset\NodeTrait;
class Shop extends Model
{
use NodeTrait;
}
好了,現在在ShopTableSeeder.php
檔案中編寫以下程式碼。
<?php
use Illuminate\Database\Seeder;
class ShopTableSeeder extends Seeder
{
public function run()
{
$shops = [
[
'category_name' => '圖書',
'children' => [
[
'category_name' => '漫畫書',
'children' => [
['category_name' => '曼威漫畫書'],
['category_name' => '言情漫畫書'],
['category_name' => '動作漫畫書'],
],
],
[
'category_name' => '教科書',
'children' => [
['category_name' => '商業'],
['category_name' => '金融'],
['category_name' => '電腦科學'],
],
],
],
],
[
'category_name' => '電器',
'children' => [
[
'category_name' => '電視',
'children' => [
['category_name' => '顯示器'],
['category_name' => '藍光'],
],
],
[
'category_name' => '手機',
'children' => [
['category_name' => '華為'],
['category_name' => 'iPhone'],
['category_name' => '小米'],
],
],
],
],
];
\App\Shop::create($shops);
}
}
我們將靜態陣列插入資料庫。 在實時專案中,我們需要動態地完成它,但是對於這個例子,我只是使用靜態陣列。
現在我們找到DatabaseTableSeeder.php
檔案並且新增以下程式碼。
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(ShopTableSeeder::class);
}
}
去終端執行填充命令如下:
php artisan db:seed
Step 4: 建立路由和控制器
好了,現在我們進入終端建立一個ShopController.php
檔案
php artisan make:controller ShopController
將以下程式碼寫入其中:
<?php
namespace App\Http\Controllers;
use App\Shop;
use Illuminate\Http\Request;
class ShopController extends Controller
{
public function index()
{
$shops = Shop::get()->toTree();
return view('shop', compact('shops'));
}
}
那麼,它將做的是,我們以樹格式獲取所有資料,我們可以在其上呼叫children方法,並且我們可以在檢視中顯示確切的分層資料。
我們還沒有建立shop的view檔案,不過在這之前我們先建立下路由檔案。找到web.php
檔案,修改程式碼如下:
// web.php
Route::get('/', 'ShopController@index');
Step 5: 在檢視中顯示分層資料
現在,我們已經獲得了資料,我們只需要將資料傳遞到View中並根據父子關係顯示資料。
在views資料夾中,建立一個名為layout
的資料夾,在該資料夾中建立一個名為app.blade.php
的檔案。
<!DOCTYPE html>
<html lang='{{ app()->getLocale() }}'>
@include('layouts.partials._head')
<body>
<div id='app'>
<main class='py-4'>
@yield('content')
</main>
</div>
</body>
</html>
我們還沒有建立partials
資料夾, 現在在layout
資料夾下面建立partials
資料夾。
完成後再在partials
資料夾裡面,建立一個_head.blade.php
檔案。
<!-- _head.blade.php -->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
最後,建立shop.blade.php
檢視,程式碼如下:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="card">
<div class="row">
<div class="card-body">
@foreach($shops as $shop)
<div class="col-md-12">
<h3>{{ $shop->category_name }}</h3>
<hr />
<div class="row">
@foreach($shop->children as $cats)
<div class="col-md-4">
<h4>{{ $cats->category_name }}</h4>
<hr />
@foreach($cats->children as $cat)
<h5>{{$cat->category_name}}</h5>
@endforeach
</div>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</div>
</div>
@endsection
現在,儲存檔案,進入瀏覽器,點選這個連結,http://nestedset.test
你將看到如下圖片顯示的:
因此,這也就意味著,你獲取到了所有基於父分類的資料,怎麼樣,是不是很爽!根本不用寫額外的程式碼,直接用包就解決了。
在我們上述的例子中,我們的主要分類有兩個:
- 圖書
- 電器
圖書的子類又有: - 漫畫書
- 教科書
電器的子類又有: - 電視
- 手機
如果有更多子類別,我們可以進一步進入遞迴。
我們可以使用此層次結構為購物類別構建多級選單。
所以這就是巢狀集模型的基本示例。
最後
關於Laravel Nested Set的基礎示例就結束了,如果你想了解更多,多去讀讀官方文件吧!
本教程的原始碼上傳到了github,大家有興趣可以看一下,後續我會繼續更新一些實戰儲存動態資料的例子!