资讯专栏INFORMATION COLUMN

Laravel最佳实践--事件驱动编程

Drummor / 2790人阅读

摘要:事件驱动编程是图形用户界面和其他应用程序例如应用程序中使用的主要范例,用于执行某些操作来响应用户输入。我们来看一下事件驱动编程带来的收益。现在让我们看看采用事件驱动编程方法如何实现上述相同的功能。

在这篇文章中我们将了解到什么是“事件驱动编程”以及在Laravel中如何开始构建一个事件驱动应用,同时我们还将看到如何通过事件驱动编程来对应用程序的逻辑进行解耦。

在开始之前,先说明一下这篇文章主要是阐述事件驱动这种编程思维和理念的,所以不会涉及到Laravel Events的方方面面。如果你需要更全面地了解Laravel Events和它的各种用法可以访问Laravel Events文档来了解详细信息。

何为事件驱动编程

在我们深入事件驱动应用之前,我们先看一下在维基百科里对事件驱动编程的定义:

事件驱动编程是一种编程模式,其中的程序流由诸如用户动作(鼠标点击,按键)、传感器输出或来自其他程序/线程的消息等事件来决定确定。事件驱动编程是图形用户界面和其他应用程序(例如JavaScript Web应用程序)中使用的主要范例,用于执行某些操作来响应用户输入。

事件驱动应用程序会响应用户的动作,然后执行对应的代码来响应用户的动作。

Laravel Events

通过上面的定义,事件是发生在应用程序中的动作。Javascript的事件是像鼠标点击、鼠标悬浮、按下键盘这样的用户动作。在Laravel中事件是发生在应用程序中的动作,像邮件通知、记录日志、用户注册、CRUD操作等。Laravel Events系统提供了简易的观察者模式实现,让开发者能够订阅和监听发生在应用中的动作。

应用中有些事件是由Laravel框架自动发起。比如说当使用Eloquent Model执行create、save、update或者delete操作时Laravel将分别发起createdsavedupdated、和deleted事件。如果需要的话我们可以监听这些事件从而执行相应的代码来完成自己的需求。除了Laravel框架自动发起的事件,我们还可以根据自己应用的需要让Laravel发起我们自己定义的事件。比如说你可以发起一个userRegistered事件,在事件处理程序中发送用户验证邮件好让新注册的用户能够验证自己的邮箱。

发起一个事件并不会让应用程序执行任何相应的操作,我们必须在事件处理程序中对被发起的事件进行相应地回应。Laravel Events由两部分组成Event HandlerEvent ListenerEvent Handler中包含了发起事件相关的信息。Event Listener监听事件对象并对事件进行回应,Event Listener是我们实现事件逻辑的地方。在Laravel中Event类文件被存放在app/Events目录,Listener类文件被存放在app/Listeners目录。

为何使用事件驱动编程

我们已经了解事件驱动应用和Laravel Events的概念了,你可能会好奇为什么要采用事件驱动这种方法来构建你的应用程序。我们来看一下事件驱动编程带来的收益。

首先,事件是一种解耦应用程序各个方面的好方法,因为单个事件可以有多个不依赖于彼此的监听器。通过解耦,不会因为你使用了不适合域逻辑的代码而污染了代码库。其次,由于应用程序是松散耦合的,你可以轻松扩展应用程序的功能,而不必打乱/重写应用程序或应用程序的某些其他功能。

应用示例

现在假设新用户注册了我们的应用程序后,应用程序会给用户发送一封欢迎邮件,同时会自动给用户订阅应用上的每周新闻简报。在不应用事件驱动方式的情况下代码往往是如下这样:

// without event-driven approach

public function register(Request $request)
{
    // validate input
    $this->validate($request->all(), [
      "name" => "required",
      "email" => "required|unique:users",
      "password" => "required|min:6|confirmed",
    ]);

    // create user and persist in database
    $user = $this->create($request->all());

    // send welcome email
    Mail::to($user)->send(new WelcomeToSiteName($user));

    // Sign user up for weekly newsletter
    Newsletter::subscribe($user->email, [
      "FNAME": $user->fname,
      "LNAME": $user->lname
    ], "SiteName Weekly");

    // login newly registered user
    $this->guard()->login($user);

    return redirect("/home");
}


你可以看到发送欢迎邮件和订阅新闻简报的逻辑紧密耦合到了register方法里, 根据关注点分离原则,register方法不应该关心发送欢迎邮件和订阅新闻简报的具体实现。你可能会觉得发送欢迎邮件和订阅新闻放到register方法里也没什么,但是如果在注册时除了发送邮件还要给用户发送短信呢?继续写在register方法里:

public function register(Request $request)
{
    // validate input

    // create user and persist in database

    // send welcome email
    Mail::to($user)->send(new WelcomeToSiteName($user));

    // send SMS
    Nexmo::message()->send([
      "to" => $user->phone_number,
      "from" => "SiteName",
      "text" => "Welcome and thanks for signup on SiteName."
    ]);

    // Sign user up for weekly newsletter
    Newsletter::subscribe($user->email, [
      "FNAME": $user->fname,
      "LNAME": $user->lname
    ], "SiteName Weekly");

    // login newly registered user

    return redirect("/home");
}

可以看到代码库开始变得臃肿。现在让我们看看采用事件驱动编程方法如何实现上述相同的功能。

// with event-driven approach

public function register(Request $request)
{
    // validate input
    $this->validate($request->all(), [
      "name" => "required",
      "email" => "required|unique:users",
      "password" => "required|min:6|confirmed",
    ]);

    // create user and persist in database
    $user = $this->create($request->all());

    // fire event once user has been created
    event(new UserRegistered($user));

    // login newly registered user
    $this->guard()->login($user);

    return redirect("/home");
}

一旦创建了用户,UserRegistered事件就会被触发。回想一下,我们之前提到,发起一个事件后应用并不会自己做任何事情,我们需要监听UserRegistered事件并执行必要的操作。让我们创建UserRegistered事件类和SendWelcomeMail以及SignupForWeeklyNewsletter监听器类:

php artisan make:event UserRegistered
php artisan make:listener SendWelcomeMail --event=UserRegistered
php artisan make:listener SignupForWeeklyNewsletter --event=UserRegistered

事件和监听器之间的对应关系需要注册到EventServiceProvider的$listen属性里:

protected $listen = [
    UserRegistered::class => [
        SendWelcomeMail::class,
        SignupForWeeklyNewsletter::class,
    ],
];

打开app/Events/UserRegistered.php文件更新它的构造方法:

public $user;

public function __construct(User $user)
{
  $this->user = $user;
}

声明$user为public,它将被传递给监听器,而监听器可以用它来执行必要的逻辑。接下来,事件监听器将在其handle方法中接收到事件实例。在handle方法中,我们可以执行响应事件的操作。

// app/Listeners/SendWelcomeMail.php
public function handle(UserRegistered $event)
{
  // send welcome email
  Mail::to($event->user)->send(new WelcomeToSiteName($event->user));
}


// app/Listeners/SignupForWeeklyNewsletter.php
public function handle(UserRegistered $event)
{
  // Sign user up for weekly newsletter
  Newsletter::subscribe($event->user->email, [
    "FNAME": $event->user->fname,
    "LNAME": $event->user->lname
  ], "SiteName Weekly");
}

可以看到通过事件驱动的方式我们让register方法的代码尽可能的少并且专注于用户注册这件事上,其它的逻辑由UserRegistered事件的监听器来负责,现在如果说我们想在用户注册后发送短信给新注册的用户,我们所要做的就是创建一个新的事件监听器来监听UserRegistered事件何时被触发

php artisan make:listener SendWelcomeSMS --event=UserRegistered

// app/Listeners/SendWelcomeSMS.php
public function handle(UserRegistered $event)
{
  // send SMS
  Nexmo::message()->send([
    "to" => $event->user->phone_number,
    "from" => "SiteName",
    "text" => "Welcome and thanks for signup on SiteName."
  ]);
}

注:记得要更新EventServiceProvider里的$listen属性

Conclusion

在这篇文章中,我们已经能够理解事件驱动的编程是什么,事件驱动的应用程序是什么以及Laravel事件是什么。我们还研究了事件驱动应用程序的优势。但是,像跟所有有积极影响的编程概念一样,它也有缺点。事件驱动型应用程序的主要缺点是让程序流变得复杂了,尤其一些刚接触开发的人可能很难真正理解应用程序的流程。以上面的实现为例,通过register方法我们并不能直观地看到程序在创建用户后会向新用户发送一封欢迎邮件,并将其注册到新闻通讯中。

所以在开发中应该根据场景创造性地使用它,利用它的优势为你的应用程序解耦,而不是过度使用它。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28430.html

相关文章

  • Laravel 深入核心系列教程

    摘要:前言年底了不太忙,最近一段时间也一直在研究,就想写篇关于比较深一点的教程系列啥的,于是就找到站长给开了写教程的渠道。优点的就是为艺术家创造的框架,它也是工程化的趋势。项目维护方便也是事实。如果有遇到问题可以直接在教程下面留言。 前言 年底了不太忙,最近一段时间也一直在研究laravel,就想写篇关于laravel比较深一点的教程系列啥的,于是就找到站长给开了写教程的渠道。由于第一次写,...

    wemall 评论0 收藏0
  • Laravel 动态添加 Artisan 命令的最佳实践

    摘要:初步尝试既然最常见的注册命令的方式是修改类中的,那么一般正常人都会从这边开始下手。又要自己取出实例,又要自己调用方法,调用方法之前还有自己先把实例化这么繁琐,肯定不是运行时添加命令的最佳实践,所以我决定继续寻找更优解。 本文首发于我的博客,原文链接:https://blessing.studio/best-... 虽然 Laravel 官方文档提供的添加 Artisan Command...

    ninefive 评论0 收藏0
  • React 可视化开发工具 shadow-widget 最佳实践(上)

    摘要:上例的功能块定义了如下节点树入口节点是面板,结合该节点的函数书写特点,我们接着介绍最佳实践如何处理功能块之内的编程。 本文介绍 React + Shadow Widget 应用于通用 GUI 开发的最佳实践,只聚焦于典型场景下最优开发方法。分上、下两篇讲解,上篇概述最佳实践,介绍功能块划分。 showImg(https://segmentfault.com/img/bVWu3d?w=6...

    techstay 评论0 收藏0
  • AI开发书籍分享

    摘要:编程书籍的整理和收集最近一直在学习深度学习和机器学习的东西,发现深入地去学习就需要不断的去提高自己算法和高数的能力然后也找了很多的书和文章,随着不断的学习,也整理了下自己的学习笔记准备分享出来给大家后续的文章和总结会继续分享,先分享一部分的 编程书籍的整理和收集 最近一直在学习deep learning深度学习和机器学习的东西,发现深入地去学习就需要不断的去提高自己算法和高数的能力然后...

    huayeluoliuhen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<