我們需要先執行數據庫遷移
然後會在數據庫創建出對應的表
然後我們需要創建出對應的模型跟數據填充文件 我們可以看一分類表的數據
平時的分類基本都市這樣的
水果(0)
|--外國水果(1)
| |--栗子(2)
|
|--國產水果(1)
|--栗子(2)
注意如果我們使用上面那種數據表的結構的話會存在這問題。如果數據量很多的時候就會出現。
1. 場景一:從上級開始查找它所有的子級,這個時候就需要使用遞歸的算法去查詢所有的子級內容,同時會產生很多的SQL查詢,從而影響性能。
2. 場景二:從最小的子級開始查找所有的上級,這個時候同樣的也是遞歸的方式查找,同樣也有很多的查詢SQL
3. 場景三:這個情景可能會在多級分銷中出現,就是判斷哪個內容是否存在上下級的關係
應對與這個三個問題的解決可以通過新增一個字段possess來記錄一個內容中的所有上級id,可以通過任意分割符號進行分 比如在本次項目中可以使用 ‘-’
php artisan make:migration add_path_to_goods_categories_table --table=goods_categories
加了這個字段之後對於上面的三個問題的解決
1. 從上往下查找的時候字只需要取出那個字段的內容 ,然後where ‘possess’ like ‘-1-%’ 即可
2.從下往上查找的時候只需要獲取那個字段,然後同樣的字符串處理就好了
3.判斷是否存在關係,先獲取level最大的那個信息中的possess的值。然後再獲取了一個possess同時在這個值上跟上這條數據的id-。 然後判斷第一個值的內容是否以第二個值的內容開頭如果是就存在關係,否則相反
修改模型
定義了一個關聯關係,在category定義一個與上級以及子級的模型關聯。然後添加三個獲取器;1.獲取所有的上級id,2.根據獲取的id獲取子級的所有上級,並且根據level排序。 至於之後一個只是獲取所有的上級名稱
源碼:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* 商品分類
*/
class GoodsCategory extends Model
{
protected $fillable = ['name', 'category_image'];
public function parent()
{
//反向關聯
return $this->belongsTo(GoodsCategory::class);
}
public function children() {
//一對多
return $this->hasMany(GoodsCategory::class, 'parent_id');
}
//定義一個訪問器,獲取所有祖先類目的ID值
public function getPossessIdsAttribute()
{
//array_filter 將數組中的空值移除
return array_filter(explode('-', trim($this->possess, '-')));
}
//定義一個訪問器,獲取祖先類目並按層級排序
public function getAncestorsAttribute()
{
return GoodsCategory::query()
->whereIn('id', $this->possess_ids)
//按層級排序
->orderBy('level')->get();
}
//定義一個訪問器,獲取以 - 爲分隔的所有祖先類目的名稱以及當前類目的名稱
public function getFullNameAttribute()
{
return $this->ancestors //獲取所有祖先類
->pluck('name') //獲取祖先類目的name 字段爲一個數組
->push($this->name)//獲取當前類目的 name 字段加到數組的末尾
->implode(' - '); //用 - 符合將數組的值組成一個字符串
}
// public function getLevelAttribute($value) {
// $data = [
// '0' => '根目錄',
// '1' => '二級',
// '2' => '三級',
// ];
// return (is_null($value)) ? $data : $data[$value];
// }
/**
* 測試方法
* @return [type] [description]
*/
public function test() {
$category = GoodsCategory::where('id', 10)->first();
$data = $category->ancestors->toArray();
return $data;
}
}
然後添加一個觀察者針對於數據新增的時候操作
php artisan make:observer GoodsCategoryObserver --model=GoodsCategory
在裏面主要是判斷,是否存在上級的;如果存在上級就操作一下;設計一下possess與level,這裏的方法一定要注意,不瞭解的去看文檔,否則你會進入無限的報錯中
然後註冊觀察者
php artisan make:provider ModelObserverServiceProvider
我們可以看一下文檔怎麼進行註冊
然後把ModelObserverProvider註冊到服務裏邊
最後就是數據填充
創建GoodsCategoryTableSeeder數據填充文件
php artisan make:seeder GoodsCategoryTableSeeder
源碼:
<?php
use App\Models\GoodsCategory;
use Illuminate\Database\Seeder;
class GoodsCategoryTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$categories = [
[
'name' => '手機配件',
'sort' => '0',
'children' => [
[
'name' => '手機殼',
'sort' => '0',
'children' => [
[
'name' => '華爲V10手機',
'sort' => '0',
],
[
'name' => '小米',
'sort' => '1',
],
],
],
[
'name' => '數據線',
'sort' => '4',
'children' => [
[
'name' => '蘋果數據線',
'sort' => '0',
],
[
'name' => '安卓數據線',
'sort' => '1',
],
],
],
[
'name' => '耳機',
'sort' => '0',
'children' => [
[
'name' => '有線耳機',
'sort' => '1',
],
[
'name' => '藍牙耳機',
'sort' => '0',
],
],
],
],
],
[
'name' => '六星果園',
'sort' => '0',
'children' => [
[
'name' => '國產水果',
'sort' => '0',
'children' => [
[
'name' => '蘋果',
'sort' => '0',
],
[
'name' => '梨',
'sort' => '1',
],
],
],
]
]
];
foreach ($categories as $data) {
$this->createCategory($data);
}
}
public function createCategory($data, $parent = null)
{
// 創建一個分類
$category = new GoodsCategory([
'name' => $data['name'],
'sort' => $data['sort'],
]);
// 如果有父級參數,代表有父類目
if (!is_null($parent)) {
// 將模型實例與給定的父實例關聯。
$category->parent()->associate($parent);
}
// 保存到數據庫
$category->save();
// 如果有children字段並且 children字段是一個數組
if (isset($data['children']) && is_array($data['children'])) {
foreach ($data['children'] as $child) {
$this->createCategory($child, $category);
}
}
}
}
然後執行這個填充數據
php artisan db:seed --class=GoodsCategoryTableSeeder
到這裏我們先不着急,我們引入一個單元測試類
控制器:
<?php
namespace App\Http\Controllers\Test;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TestController extends Controller
{
//
public function create() {
return view('test.create');
}
public function store(Request $request) {
$namespace = $request->input('namespace');
$className = $request->input('className');
$action = $request->input('action');
$param = $request->input('param');
$class = ($className == "") ? $namespace : $namespace.'\\'.$className;
$class = str_replace('/' , '\\', $class);
$object = new $class();
$param = ($param == "")? [] : explode("|",$param);
$data = call_user_func_array([$object, $action], $param);
return (is_array($data)) ? json_encode($data) : dd($data);
}
}
視圖:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>不區分大小寫(可以自行完善)</h1>
<br>
<form class="" action="{{route('test.store')}}" method="post">
@csrf()
命名空間:<input type="text" value='' style="width:300px" name='namespace' placeholder="如:app\index\controller 或app\index\controller\Index">可以寫全,然後下面類名不用些<br>
類名:<input type="text" name='className' placeholder="如:index ">命名空間全可以不用寫<br>
測試方法名:<input type="text" name='action' placeholder="index"><br>
傳遞參數以 | 分割:<input type="text" placeholder="如: 1|2|3" name='param'><br>
<input type="submit" name="" value="測試"/>
</form>
</body>
然後在 GoodsCategory裏邊添加一個測試方法
然後咱們訪問一下單元測試方法
這篇文章主要就是爲了處理商品分類的,那麼就先寫在到,下一篇文章 會結合後臺來顯示數據。在接下來會解析這裏邊的難點