在 Laravel 5.2 中捕获保存和删除的最可靠方法是什么?

What's the most reliable way to catch saves and deletes in Laravel 5.2?

当我的一个模型被保存 (created/updated) 或删除时,我需要 运行 一些代码。最好的方法是什么?

我知道三种不同的方式:

  1. Override the save and delete methods on the model
  2. Add creating/updating/deleting callbacks in the boot method
  3. Bind an observer in the boot method

我没有看到这些比较和对比,所以我不知道有什么区别。我担心事件在某些情况下不会触发。

例如,在 Django 中,仅当您逐一删除模型时才会触发删除操作,而不是批量删除。


明确地说,我正在寻找比较和对比这些(或其他)方法的答案 -- 而不是简单地建议更多的方法来做同样的事情。

您可以为模型的每个 create/update 创建事件处理程序,例如添加以缓存刚刚保存到数据库或将要保存到数据库的模型数据,无需 [=13] 更容易检索=]查询电话, 在 delete 调用时,对缓存处理程序事件中的给定键使用 forget 来删除缓存以及从数据库中删除。

前面提到的几种方法,只是我个人的看法。

  1. 覆盖模型上的保存和删除方法(如果您覆盖它,那么下一次更新 Laravel 更改方法的可见性您的代码将不再工作。它会抛出异常或 PHP 错误.你必须修改它才能再次工作)
  2. 在启动方法中添加 creating/updating/deleting 回调(存在于 Laravel 4 你应该在 Laravel 5 中再次检查它可能使用 Event 和 Listener 的不同实现)
  3. 在 boot 方法中绑定一个观察者(存在于 Laravel 4 你应该在 Laravel 5 中再次检查它可能使用 Event 和 Listener 的不同实现)

我认为你应该使用 Laravel 提供的事件和监听器。它可能仍然适用于下一个 Laravel 更新。我假设 Event 和 Listener 是 Laravel 中的微小变化区域,并且可能只是改变了不同的方法实现。

Laravel 应该有开发计划指定 Laravel 的哪一部分将开发为 主要变化区域 (大修改)或 minor change area(小改动)。如果您尝试更改或覆盖主要更改区域,它将无法在下一个 Laravel 更新中使用。

您可以注册事件和监听器来保存和删除记录。 Laravel 在 Model ( Illuminate\Database\Eloquent\Model ) 上有 fireModelEvent 方法 触发特定的 Laravel 事件。如果你已经注册了事件,Dispatcher ( Illuminate\Events\Dispatcher ) 将执行 Listener of Event.

关于 Laravel 事件的文档:

https://laravel.com/docs/5.3/events

https://laravel.com/docs/5.2/events

我假设你有 YourModel 作为模型,然后在下面执行以下操作。

  • 注册事件和侦听器。打开 app\Providers\EventServiceProvider.php 然后将事件和侦听器添加到 YourModel 的 EventServiceProvider.listen 属性或按照 Laravel 文档使用其他方式创建事件。

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        ...
        'eloquent.saved: App\YourModel' => [
            'App\YourModel@eventSaved',
        ],
    ];
}

  • 在 App\YourModel 上添加 eventSaved 方法作为事件的 Listener 以便您可以在保存或删除后执行特定操作。

class YourModel extends Model
{
    public function eventSaved(){
        // You can add your code to catch save here
    }
}

当您需要确切地知道它们是如何完成的时候,我偏向于手动做事。我最近使用这个 Laravel 样板来启动一个项目,我喜欢他们在更新模型时在存储库中手动触发事件的方式:

https://github.com/rappasoft/laravel-5-boilerplate/blob/master/app/Repositories/Backend/Access/User/EloquentUserRepository.php

由于模型应始终通过存储库进行更新,因此您始终需要手动决定如何处理事件。当删除多个模型时,您可以触发自己的事件,并采取相应行动。不过,您的所有选项都可以使用,您只需要找到最适合您需求的选项即可。

您可以创建扩展 Illuminate\Database\Eloquent\Model class 的抽象 Model class,并且您的所有模型都将扩展此 class。通过这样的实现,您可以更好地控制模型。例如

<?php

namespace App\Base\Database;


use Illuminate\Database\Eloquent\Model as BaseModel;

abstract class Model extends BaseModel
{
    public function save(array $options = [])
    {
        //your code here
        return parent::save($options); 
    }
}

您可以对 Model class 的所有方法执行此操作,还可以添加与应用程序中的所有模型相关的其他方法

@joko提到的三种方法和第4种方法。可能还有更多,但让我们关注 4 种方法。

让我一一为您描述:

1) 覆盖模型上的保存和删除方法

在此方法中,您使用的是 OOPD 方法覆盖。您正在重写 Laravel 的内部 save 方法并通过在它之上定义您自己的 save 方法来添加您的附加代码。应该避免这种情况,因为 Laravel 不断发展,如果进行重大更改,事情可能会开始失败,例如假设将来 laravel 将 save 方法替换为任何其他方法来保存记录.然后,您将不得不再次创建另一个方法来覆盖该新方法。此外,在此处编写代码可能会增加您的模型 class 文件。您的模特可能会继续处理他不应该处理的事情(例如:发送电子邮件)。 应该避免这种方法。

2) 在boot方法中添加creating/updating/deleting回调

这里是在模型的 Boot 方法上定义代码。仅当您需要处理的事件很少 code/things 时才应使用此方法。这种方法的缺点是它使代码更加复杂和混乱,因为您可能像函数式编程一样将所有逻辑写在一个文件中。假设您必须在创建之前和创建之后做一些事情。你 boot 方法会成长。

3)在boot方法中绑定观察者

这个方法很不错。您创建了一个观察者 class 来处理 Laravel 事件中应该发生的事情。它使代码更清晰,更易于维护。

例子:假设你要在creatingsavingsaveddeleting这些方法中编写代码。在这种情况下,方法 1) 和方法 2) 不是好的做法,因为在

方法 1: 我们必须创建这 4 个方法并覆盖它们,并在 Laravel 的未来版本中支持它们。在这种情况下,模型中的代码也会因为覆盖此方法而增长

方法 2: 在这种情况下,您的 boot 方法也会增长,因此您的模型文件将变成代码垃圾。

在方法 1 和 2 中还请记住,您的模型不负责执行您要编写的许多内容。就像在创建用户时发送电子邮件一样。您最终可能会在 created 方法中编写这些代码。

假设现在您需要在 created 事件中向用户发送电子邮件,并且需要在客户 CRM 中创建用户的登录日志用户。那么你将不得不用相同的方法为两者编写代码。可能,您在那里可能不遵循单一责任原则。遇到这种情况怎么办?参见方法4。

4)

我在方法4的最后建议的场景。您可以向用户发送电子邮件,并在创建时将其登录到 Customer CRM 中。然后您的方法将做两件事(发送电子邮件和登录 CRM)。它可能不遵循单一责任原则。如果更好,我们可以将它们解耦。然后就是这个方法了。

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'eloquent.saved: App\User' => 'App\Listeners\SendWelcomeEmailToUser'
        'eloquent.saved: App\User' => 'App\Listeners\LogUserInCRM'
    ];
}

创建两个listener classes:

class SendWelcomeEmailToUser
{
    public function handle(User $user){
        // Write code to send email
    }
}

class LogUserInCRM
{
    public function handle(User $user){
        // Write code to log
    }
}

通过这个你可以分离出代码并使它们更清晰。

我通常更喜欢这种方法,它的模式是干净的。它还让您更好地了解事件发生时实际发生的情况。它成为 Event 到 Listener 映射的一个点。