Laravel 模型事件入门
Laravel 模型事件允许你监听模型生命周期内的多个关键点,甚至可以在阻止一个模型的保存或者删除。 Laravel 模型事件文档 概述了如何使用钩子将对应事件与相关的事件类型关联起来,但是本文的主旨是事件与监听器的构建与设置,并额外补充一些细节的说明。
事件概述
Eloquent 有很多事件可以让你使用钩子将它们关联起来,并且增加自定义的功能到你的模型中。该模型起始时有以下事件:
- retrieved
- creating
- created
- updating
- updated
- saving
- saved
- deleting
- deleted
- restoring
- restored
从文档这里我们可以了解它们都是如何实现的,你还可以进入 Model
的基类去看看它们到底是如何实现的:
当现有模型被数据库检索时,
retrieved
事件将会触发。当一个新的模型被第一次保存时,creating
和created
事件将会触发。如果对一个已经存在于数据库的模型调用save
方法,updating
/updated
事件将会触发。无论怎样,在这两种情况下,saving
/saved
事件都会触发。
文档中对模型事件进行了很好的概述,同时解释了怎样使用钩子去关联事件,但是如果你是初学者,或者并不是熟悉怎样使用钩子将事件监听器与这些自定义模型事件相关联,请进一步阅读本文。
注册 事件
为了在你的模型中关联一个事件,你需要做的第一件事是使用 $dispatchesEvents
属性去注册事件对象,这最终将通过 HasEvents::fireCustomModelEvent()
方法触发, 该方法将通过 fireModelEvent()
方法被调用。 fireCustomModelEvent()
方法原始的时候大致是下面这样:
/**
* 为给定的事件触发一个自定义模型。
*
* @param string $event
* @param string $method
* @return mixed|null
*/
protected function fireCustomModelEvent($event, $method)
{
if (! isset($this->dispatchesEvents[$event])) {
return;
}
$result = static::$dispatcher->$method(new $this->dispatchesEvents[$event]($this));
if (! is_null($result)) {
return $result;
}
}
一些事件,比如 delete
, 将进行检测判断是否这个事件会返回 false
然后退出操作。比如,你可以使用这个钩子去做一些检测,也可以防止一个用户被创建或删除。
使用 App\User
模型举例, 这里展示了如何配置你的模型事件:
protected $dispatchesEvents = [
'saving' => \App\Events\UserSaving::class,
];
你可以使用 artisan make:event
命令来为你创建这个事件, 但基本上这将是你最后得到结果 :
<?php
namespace App\Events;
use App\User;
use Illuminate\Queue\SerializesModels;
class UserSaving
{
use SerializesModels;
public $user;
/**
* 创建一个新的事件实例
*
* @param \App\User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
}
我们的事件提供了一个公有的 $user
属性以便你能够在 saving
事件期间访问 User
模型实例。
为了让它工作起来下一步需要做的是为这个事件建立一个实际的监听器。我们设置好模型的触发时机,当 User
模型触发 saving
事件,监听器就会被调。
创建一个事件监听器
现在,我们定义 User
模型并注册一个事件监听器来监听 saving
事件的触发。虽然,我能通过模型观察器快速实现,但是,我想引导你为单个事件触发配置事件监听器。
事件监听器就像 Laravel 其它事件监听一样,handle()
方法将接收 App\Events\UserSaving
事件类的一个实例。
你可以手动创建它,也可以使用 php artisan make:listener
命令。 不管怎么样,你都将创建一个像下面这样子监听类:
<?php
namespace App\Listeners;
use App\Events\UserSaving as UserSavingEvent;
class UserSaving
{
/**
* 处理事件。
*
* @param \App\Events\UserSavingEvent $event
* @return mixed
*/
public function handle(UserSavingEvent $event)
{
app('log')->info($event->user);
}
}
我只是添加了一个日志记录调用,以便于检查传递给监听器的模型。为此,我们还需要在 EventServiceProvider::$listen
属性中注册监听器:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* 应用的事件监听器。
*
* @var array
*/
protected $listen = [
\App\Events\UserSaving::class => [
\App\Listeners\UserSaving::class,
],
];
// ...
}
现在,当模型调用 saving
事件时,我们注册的事件监听器也会被触发并执行。
尝试事件监听
我们可以通过 tinker
会话快速生成事件监听代码:
php artisan tinker
>>> factory(\App\User::class)->create();
=> App\User {#794
name: "Aiden Cremin",
email: "josie05@example.com",
updated_at: "2018-03-15 03:57:18",
created_at: "2018-03-15 03:57:18",
id: 2,
}
如果你已正确注册了事件和监听器,则应该在 laravel.log
文件中可以看到该模型的 JSON 表达形式:
[2018-03-15 03:57:18] local.INFO: {"name":"Aiden Cremin","email":"josie05@example.com"}
要注意的一点,此时模型并没有 created_at
或 updated_at
属性。如果在模型上再次调用 save()
,日志上将会有一个带有时间戳的新记录,因为 saving
事件会在新创建的记录或现在有记录上触发:
>>> $u = factory(\App\User::class)->create();
=> App\User {#741
name: "Eloisa Hirthe",
email: "gottlieb.itzel@example.com",
updated_at: "2018-03-15 03:59:37",
created_at: "2018-03-15 03:59:37",
id: 3,
}
>>> $u->save();
=> true
>>>
停止一个保存操作
某些模型事件是允许你进行阻止操作的。举个荒谬的例子,假设我们不允许任何一个用户的模型保存其属性 $user->name
的内容为 Paul
:
/**
* 处理事件。
*
* @param \App\Events\UserSaving $event
* @return mixed
*/
public function handle(UserSaving $event)
{
if (stripos($event->user->name, 'paul') !== false) {
return false;
}
}
在 Eloquent 的 Model::save()
方法中,会根据事件监听的返回结果判断是否进行停止保存操作:
public function save(array $options = [])
{
$query = $this->newQueryWithoutScopes();
// 如果 "saving" 事件返回 false ,我们将退出保存并返回
// false,表示保存失败。这为服务监听者提供了一个机会,
// 当验证失败或者出现其它任何情况,都可以取消保存操作。
if ($this->fireModelEvent('saving') === false) {
return false;
}
这个 save()
是个很好的例子,它告诉了你如何在模型生命周期中自定义事件,以及被动执行日志数据记录或者任务调度。
使用观察者
如果你正在监听多个事件,那么你可能会发现使用观察者类来按类型分组存放事件会更加方便。这里是一个例子 Eloquent 观察者 :
<?php
namespace App\Observers;
use App\User;
class UserObserver
{
/**
* 监听 User 创建事件。
*
* @param \App\User $user
* @return void
*/
public function created(User $user)
{
//
}
/**
* 监听 User 删除事件。
*
* @param \App\User $user
* @return void
*/
public function deleting(User $user)
{
//
}
}
你可以在服务提供者 AppServiceProvider
中的 boot()
方法里注册观察者。
/**
* 运行所有应用服务。
*
* @return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
了解更多
我建议你阅读 Laravel 事件文档 去了解有关事件和监听器在整个框架中如何工作。 Eloquent 事件文档 对于可用事件以及如何使用观察者是一个非常好的参考。最后,我建议浏览 Illuminate\Database\Eloquent\Model
类来查找 fireModelEvent()
方法调用的用法去了解事件如何与模型和 HasEvents trait 将这些事件联系在一起。
现代化 PHP 知识日新月异,尤其是 PHP7 出来以后,欢迎加入 PHP / Laravel 知识社区 一起成长。
Laravel 模型事件入门的更多相关文章
- Laravel模型事件的实现原理详解
模型事件在 Laravel 的世界中,你对 Eloquent 大多数操作都会或多或少的触发一些模型事件,下面这篇文章主要给大家介绍了关于Laravel模型事件的实现原理,文中通过示例代码介绍的非常详细 ...
- laravel 模型事件 updated 触发条件
1. 只有 $sku->{attribute} != $sku->getOriginal({attribute}) 不一致的时候才会触发 getDirty() 不为空的时候才触发, 而且不 ...
- 【laravel】Eloquent 模型事件和监听方式
所有支持的模型事件 在 Eloquent 模型类上进行查询.插入.更新.删除操作时,会触发相应的模型事件,不管你有没有监听它们.这些事件包括: retrieved 获取到模型实例后触发 creatin ...
- Laravel 5 系列入门教程(一)【最适合中国人的 Laravel 教程】
Laravel 5 系列入门教程(一)[最适合中国人的 Laravel 教程] 分享⋅ johnlui⋅ 于 2年前 ⋅ 最后回复由 skys215于 11个月前 ⋅ 17543 阅读 原文发表在 ...
- Laravel 4 系列入门教程(一)
默认条件 本文默认你已经有配置完善的PHP+MySQL运行环境,懂得PHP网站运行的基础知识.跟随本教程走完一遍,你将会得到一个基础的包含登录的简单blog系统,并将学会如何使用一些强大的Larave ...
- 第一百一十九节,JavaScript事件入门
JavaScript事件入门 学习要点: 1.事件介绍 2.内联模型 3.脚本模型 4.事件处理函数 JavaScript事件是由访问Web页面的用户引起的一系列操作,例如:用户点击.当用户执行某些操 ...
- [.NET] C# 知识回顾 - 事件入门
C# 知识回顾 - 事件入门 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6057301.html 序 之前通过<C# 知识回顾 - 委托 de ...
- c#委托事件入门--第二讲:事件入门
上文 c#委托事件入门--第一讲:委托入门 中和大家介绍了委托,学习委托必不可少的就要说下事件.以下思明仍然从事件是什么.为什么用事件.怎么实现事件和总结介绍一下事件 1.事件是什么:. 1.1 NE ...
- Scala进阶之路-并发编程模型Akka入门篇
Scala进阶之路-并发编程模型Akka入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Akka Actor介绍 1>.Akka介绍 写并发程序很难.程序员不得不处 ...
随机推荐
- python︱模块加载(pip安装)以及pycharm安装与报错解决方式
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 准备放下R开始学python,真是痛苦,因为找 ...
- Win7/8出现An error occurred on the server when processing the URL解决办法
使用的是win8系统搭建的本地服务器,win7使用的方法是相同的.如果你的系统是精简版的Win7/8,那么安装IIS7也有可能出现这问题.下面SJY带领大家来解决这个错误. 解决方法 打开控制面板→管 ...
- PHP学习笔记--Php预定义超全局变量$_GET
Php中的许多预定义变量都是超全局的,无需使用global声明 超全局变量 不需要声明就可以直接使用 提供超全局变量(数组)为了让程序员更快捷的编程 $GLOBALS— 引用全局作用域中可用的全部变量 ...
- Oracle 存储过程中的 =>
oracle实参与形参有两种对应方式1.一种是位置方式,和面向对象语言参数传递类似;2.另外一种是=> 作为形参对应,因为位置对应方法有缺限,比如一个函数有3个参数,但第2个是可以不传(有默认值 ...
- 用DriverStudio开发USB驱动程序
很多写Windows Device Driver的开发人员基本上都是使用Windows DDK进行开发的.但是,现在也有不少人都开始借助一些辅助工具.笔者去年开始接触到DriverStudio,发现它 ...
- VC++信息安全编程(13)Windows2000/xp/vista/7磁盘扇区读写技术
有些时候,我们读取磁盘文件,会被hook.我们读到的可能并非实际的文件. 我们直接读取磁盘扇区获取数据. 实现磁盘数据的读写,不依赖WindowsAPI. [cpp] view plaincopy v ...
- 芝麻HTTP:Python爬虫入门之URLError异常处理
1.URLError 首先解释下URLError可能产生的原因: 网络无连接,即本机无法上网 连接不到特定的服务器 服务器不存在 在代码中,我们需要用try-except语句来包围并捕获相应的异常.下 ...
- Linux安装jdk1.8和配置环境变量
每次感觉配这个都很简单,但每次都要查一下,毕竟配错一点 后面都比较麻烦,记录一下,方便以后查看. linux 下安装jdk和windows下的安装是一样的,之前在windows安装的时候是先下载压缩包 ...
- GitHub图形界面使用笔记
GitHub图形界面使用笔记 学会了最简单的在GitHub上上传项目和展示项目,怕自己会忘记所以还是先记录下了来. GitHub 是一个共享虚拟主机服务,用于存放使用Git版本控制的软件代码和内容 ...
- 【HDU1754】I hate it!
题面 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然, ...