Laravel-admin 创建美观的树形无限极分类视图
版本说明
- laravel/framework: 5.5.*
- encore/laravel-admin: ^1.7
表和模型
- 表结构没啥好说的,其定义如下:
字段名称 | 字段类型 | 字段描述 |
---|---|---|
id | unsigned int primary key | 自增主键 |
pid | unsigned int | 父级分类ID |
cate_name | varchar(30) | 分类名称 |
sort | unsigned smallint | 排序字段 |
- CategoriesModel.php的定义:
<?php namespace App\Models; use Encore\Admin\Traits\AdminBuilder; use Encore\Admin\Traits\ModelTree; use Illuminate\Database\Eloquent\Model; class CategoriesModel extends Model { use ModelTree, AdminBuilder; protected $table = 'categories'; protected $fillable = ['pid', 'cate_name', 'sort']; protected $with = [ 'parent' ]; public function __construct(array $attributes = []) { parent::__construct($attributes); $this->setParentColumn('pid'); // 父ID $this->setOrderColumn('sort'); // 排序 $this->setTitleColumn('cate_name'); // 标题 } // 该分类下的品牌 public function brand() { return $this->hasMany(BrandModel::class, 'cate_id', $this->getKeyName()); } /** * 该分类的子分类 */ public function child() { return $this->hasMany(get_class($this), 'pid', $this->getKeyName()); } /** * 该分类的父分类 */ public function parent() { return $this->hasOne(get_class($this), $this->getKeyName(), 'pid'); } }
使用了两个trait: ModelTree
和AdminBuilder
方便构造树形结构数据。 需要注意的是在定义表结构时,需要存在parent_id, order, title字段,当然意义上跟其一致即可,名称可以随意定义,可以在构造方法中设置对应字段的名称。
控制器定义
通过
php artisan admin:make CategoriesController --model=App\\Models\\CategoriesModel
生成控制器。发现CategoriesController是默认继承AdminController类的,点进去看AdminController类的定义如下:
<?php namespace Encore\Admin\Controllers; use Encore\Admin\Layout\Content; use Illuminate\Routing\Controller; class AdminController extends Controller { use HasResourceActions; /** * Title for current resource. * * @var string */ protected $title = 'Title'; /** * Set description for following 4 action pages. * * @var array */ protected $description = [ // 'index' => 'Index', // 'show' => 'Show', // 'edit' => 'Edit', // 'create' => 'Create', ]; /** * Get content title. * * @return string */ protected function title() { return $this->title; } /** * Index interface. * * @param Content $content * * @return Content */ public function index(Content $content) { return $content ->title($this->title()) ->description($this->description['index'] ?? trans('admin.list')) ->body($this->grid()); } /** * Show interface. * * @param mixed $id * @param Content $content * * @return Content */ public function show($id, Content $content) { return $content ->title($this->title()) ->description($this->description['show'] ?? trans('admin.show')) ->body($this->detail($id)); } /** * Edit interface. * * @param mixed $id * @param Content $content * * @return Content */ public function edit($id, Content $content) { return $content ->title($this->title()) ->description($this->description['edit'] ?? trans('admin.edit')) ->body($this->form()->edit($id)); } /** * Create interface. * * @param Content $content * * @return Content */ public function create(Content $content) { return $content ->title($this->title()) ->description($this->description['create'] ?? trans('admin.create')) ->body($this->form()); } }
- AdminController控制器使用了一个trait:
HasResourceActions
,还有就是对index丶show丶edit丶create操作的封装。点击HasResourceActions,看其定义:
trait HasResourceActions { /** * Update the specified resource in storage. * * @param int $id * * @return \Illuminate\Http\Response */ public function update($id) { return $this->form()->update($id); } /** * Store a newly created resource in storage. * * @return mixed */ public function store() { return $this->form()->store(); } /** * Remove the specified resource from storage. * * @param int $id * * @return \Illuminate\Http\Response */ public function destroy($id) { return $this->form()->destroy($id); } }
发现Trait:
HasResourceActions
是对store丶destroy丶update的封装,类中只有定义了form方法即可实现三种操作。那么我们的分类控制器:
Categories
可以通过继承基本的Controller
和使用trait:HasResourceActions来实现树形图的操作,CategoriesController.php
代码如下:
<?php namespace App\Admin\Controllers; use App\Models\CategoriesModel; use Encore\Admin\Controllers\HasResourceActions; use Encore\Admin\Layout\{Column, Row, Content}; use Encore\Admin\{Tree,Form}; use Encore\Admin\Widgets\Box; use Illuminate\Http\RedirectResponse; /** * 分类管理 * @package App\Admin\Controllers */ class CategoriesController extends Content { use HasResourceActions; protected $title = '分类'; /** * 首页 * @param Content $content * @return Content */ public function index(Content $content) { return $content->title('分类') ->description('列表') ->row(function (Row $row){ // 显示分类树状图 $row->column(6, $this->treeView()->render()); $row->column(6, function (Column $column){ $form = new \Encore\Admin\Widgets\Form(); $form->action(admin_url('categories')); $form->select('pid', __('Parent Category'))->options(CategoriesModel::selectOptions()); $form->text('cate_name', __('Category Name'))->required(); $form->number('sort', __('Asc Sort'))->default(99)->help('越小越靠前'); $form->hidden('_token')->default(csrf_token()); $column->append((new Box(__('category.new'), $form))->style('success')); }); }); } /** * 树状视图 * @return Tree */ protected function treeView() { return CategoriesModel::tree(function (Tree $tree){ $tree->disableCreate(); // 关闭新增按钮 $tree->branch(function ($branch) { return "<strong>{$branch['cate_name']}</strong>"; // 标题添加strong标签 }); }); } /** * 详情页 * @param $id * @return RedirectResponse */ public function show($id) { return redirect()->route('categories', ['id' => $id]); } /** * 编辑 * @param $id * @param Content $content * @return Content */ public function edit($id, Content $content) { return $content->title(__('Categories')) ->description(__('edit')) ->row($this->form()->edit($id)); } /** * 表单 * @return Form */ public function form() { $form = new Form(new CategoriesModel()); $form->display('id', 'ID'); $form->select('pid', __('Parent Category'))->options(CategoriesModel::selectOptions()); $form->text('cate_name', __('Category Name'))->required(); $form->number('sort', __('Asc Sort'))->default(99)->help('越小越靠前'); return $form; } }
- 需要注意的是index方法中的Form和form方法中的form是不同的。
- CategoriesModel::selectOptions()方法可以快捷的列出可选项包括默认值0。
实现效果图
- 首页(左边是所有分类,右边是快捷创建分类。)
- 编辑页