@Toc
所用的知识点
1.迁移文件的创建
2.数据填充
3.通过观察者监听模型事件
4.注册观察者
5.模型
素材需要
数据库
| 字段 | value | 
|---|---|
| id | 分类id | 
| name | 分类名 | 
| parent_id | 父级id | 
| image | 分类图标 | 
| level | 当前分类的等级 | 
| sort | 排序 | 
| possess | 该分类的所有父类 | 
GoodsCategory模型

在category定义一个与上级以及子级的模型关联。然后添加三个获取器;1.获取所有的上级id,2.根据获取的id获取子级的所有上级,并且根据level排序。 至于之后一个只是获取所有的上级名称
<?phpnamespace 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;}}
创建GoodsCategoryTableSeeder.php数据填充文件
命令:php artisan make:seeder GoodsCategoryTableSeeder
<?phpuse 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);}}}}
创建GoodsCategoryObserver.php观察者
命令:php artisan make:observer GoodsCategoryObserver —model=GoodsCategory
<?phpnamespace App\Observers;use Log;use App\Models\GoodsCategory;class GoodsCategoryObserver{public function creating(GoodsCategory $goodsCategory) {//如果创建的是一个根类目if (is_null($goodsCategory->parent_id)) {//讲层级设置为0$goodsCategory->level = 0;//将path 设为 -$goodsCategory->possess = '-';}else {//将层级设为父类目层级 + 1$goodsCategory->level = $goodsCategory->parent->level +1;// 将path 设为父级目的的PATH 追加父级的id 并最后 跟上一个 - 分隔符$goodsCategory->possess = $goodsCategory->parent->possess.$goodsCategory->parent_id.'-';}}/*** Handle the goods category "created" event.** @param \App\GoodsCategory $goodsCategory* @return void*/public function created(GoodsCategory $goodsCategory){}/*** Handle the goods category "updated" event.** @param \App\GoodsCategory $goodsCategory* @return void*/public function updated(GoodsCategory $goodsCategory){//}/*** Handle the goods category "deleted" event.** @param \App\GoodsCategory $goodsCategory* @return void*/public function deleted(GoodsCategory $goodsCategory){//}/*** Handle the goods category "restored" event.** @param \App\GoodsCategory $goodsCategory* @return void*/public function restored(GoodsCategory $goodsCategory){//}/*** Handle the goods category "force deleted" event.** @param \App\GoodsCategory $goodsCategory* @return void*/public function forceDeleted(GoodsCategory $goodsCategory){//}}
创建数据库迁移文件
php artisan make:mmigration  create_goods_category_table —create=goods_category
<?phpuse Illuminate\Support\Facades\Schema;use Illuminate\Database\Schema\Blueprint;use Illuminate\Database\Migrations\Migration;class CreateGoodsCategoriesTable extends Migration{/*** Run the migrations.** @return void*/public function up(){Schema::create('goods_categories', function (Blueprint $table) {$table->increments('id')->comment('商品类别主键id');$table->string('name')->comment('类别名称');$table->integer('parent_id')->default(0)->comment('父级类别id');$table->string('image')->nullable()->comment('分类图片');$table->integer('level')->default(0)->comment('分类等级');$table->integer('sort')->default(0)->comment('分类排序');$table->timestamps();});}/*** Reverse the migrations.** @return void*/public function down(){Schema::dropIfExists('goods_categories');}}
注册观察者
命令:php artisan make:provider ModelObserverServiceProvider
<?phpnamespace App\Providers;use App\Observers\GoodsCategoryObserver;use App\Models\GoodsCategory;use Illuminate\Support\ServiceProvider;class ModelObserverProvider extends ServiceProvider{/*** Register services.** @return void*/public function register(){//}/*** Bootstrap services.** @return void*/public function boot(){GoodsCategory::observe(GoodsCategoryObserver::class);}}
然后在app里边注册进去即可
重现数据填充与访问器冲突
执行数据填充命令
php artisan db:seed —class=GoodsCategoryTableSeeder
然后打开数据库
日志检测问题
我们会发现我们自定义的数据都没有进去,只进去了第一行,这是为什么呢!
我们可以看到有个一报错信息是 A non-numeric value encountered,并且报到了观察者的19行,我们在这里写个日志看看原因
在把数据填充的命令执行一次
php artisan db:seed  —class =GoodsCategoryOberserver
同样也给出了这里的报错信息,那么咱们看看这level字段到底怎么回事
检测level字段的问题
可以看到在模型里边定义了一个访问器来重新定义了level字段,那么问题就在这里了,这个时候我们在注释了以后去执行填充命令
执行
查看数据库
这个时候就是可以的了
解决访问器与数据填充时的冲突
虽然我们在上边解决了这个问题,但是我们定义的这个访问器是在后台显示数据的时候使用的。其实数据填充出现的问题,也就是我们在后台进行数据添加时会报出的问题,所以这个问题不是一个注释解决的
1.在模型里边定义一个虚拟字段
2.修改之前的访问器
3.然后在执行填充命令
4.查看数据库
5.打开后台查看数据
解决设置的访问器不能正常使用
我们在添加了虚拟属性后, 我们的数据填充是好了,但是我们在后台获取数据时并没有作用
这个时候还需要一步操作,那就是把字段也给换成自定义的字段
这个时候在来查看,就没有问题了
