引言
说一个场景需求,假如有一个user模型,用户的上传图片存在另外一张表photo内。当删除该用户时,想要同时删除关联的photo表的相关记录。应该用什么办法呢?
本文就来说说 Laravel ORM 操作中的事件钩子。
学习时间
如果想要实现上一节所说的需求,代码写起来可能是这样的。
代码语言:javascript复制$user->delete();
当该事件发生时,我们接着执行关联的删除。
代码语言:javascript复制$this->photo()->delete();
如果程序中每个地方逻辑上进行了删除操作,岂不是都要手动这么重复写,这些重复代码真的是无用功。
那么Laravel中是怎么写的呢,如何自动在触发了 user 的删除时间,自动进行 photo 的删除操作?我们可以借助于 Eloquent ORM 提供的 deleting 事件,做删除动作。
代码如下:
只用在 Model 模型类中继承并实现 boot 方法,然后调用模型的 deleting 事件,使用回调声明执行的动作即可。
这样在删除动作上只用维护一处代码,程序内所有的 delete 事件都会自动触发该动作,复用率大大地提升了。
再进一步
实现同样的需求,往往有很多种方式。你还可以换用一种事件钩子方式,就是 Laravel 提供的 观察者(Observers)方式。
首先,在 AppServiceProvider 内注册某模型的观察者:
代码语言:javascript复制public function boot()
{
User::observe(UserObserver::class);
}
然后添加观察者类 UserObserver:
代码语言:javascript复制class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
这样就用观察者盯着 User 模型的动作,如果发现,立即执行 UserObserver 内对应的动作。
这样把程序功能单独摘开,调试的时候非常方便。而且对于后期有修改,或者复杂的动作,处理起来就会游刃有余,不会把代码逻辑写的一堆又一堆,在事件方法内,你只需集中处理一次就够了!
这才是有弹性的代码!鲁棒性非常好的代码!
不推荐的方法
还有一种方法,我们本不打算推荐,但是考虑到有的初学者容易犯此类错误,就拿出来最为参照。
程序功能应该单一。比如,尽量避免把数据关联操作放到数据库执行。MySQL提供了外键约束,并且可以定义触发器用于批次动作处理。
那么使用 Laravel migrations 时,创建photo表的外键关联事件:
代码语言:javascript复制$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
这样做,就是把动作交给了MySQL处理。
非常不利于程序调试,和数据全程追溯!不推荐使用!
写在最后
本文通过3种方式,实现了Laravel中关联删除表记录的功能。推荐写法是第一种,直接在模型内声明事件钩子,处理起来较为方便;
第二种会造成虽然整洁,但是如果注册的观察者过多,不易于模型与观察者之间的关联调试;
第三种方式,完全不推荐,我们不应该把数据有效性和完整性的操作交给MySQL。
Happy coding :-)