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

Posted

技术标签:

【中文标题】在 Laravel 5.2 中捕获保存和删除的最可靠方法是啥?【英文标题】:What's the most reliable way to catch saves and deletes in Laravel 5.2?在 Laravel 5.2 中捕获保存和删除的最可靠方法是什么? 【发布时间】:2015-07-13 03:51:35 【问题描述】:

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

我知道三种不同的方式:

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

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

例如,在 Django 中,删除只会在您一个接一个地删除模型时触发,而不是在批量删除中。


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

【问题讨论】:

Same as in Django with Eloquent: “当通过 Eloquent 执行批量删除语句时,删除和删除模型事件不会为已删除模型触发。这是因为模型在执行删除语句。” @jakubwrona 那么这三种方法都不支持批量删除吗?好的。还有什么我应该注意的吗?效率有区别吗?它们中的任何一个是否比其他的更健壮/更不容易破裂?还是只是偏好问题?为什么有这么多方法来做同样的事情? 【参考方案1】:

这只是我对你之前提到的几种方法的看法。

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

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

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

您可以注册事件和监听器来保存和删除记录。 Laravel 在 Model ( Illuminate\Database\Eloquent\Model ) 上有 fireModelEvent 方法,它会触发特定的 Laravel 事件。如果您注册了 Event,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 然后将 Event 和 Listener 添加到 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 方法作为 Event 的 Listener,以便您可以在保存或删除后执行特定操作。 class YourModel extends Model public function eventSaved() // You can add your code to catch save here

【讨论】:

【参考方案2】:

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

让我一一为您描述:

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

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

2) 在启动方法中添加创建/更新/删除回调

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

3) 在引导方法中绑定观察者

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

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

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

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

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

假设现在您需要在 created 事件上向用户发送电子邮件,并且您需要在客户 CRM 中创建用户的输入日志用户。那么您将不得不以相同的方法为两者编写代码。可能,您可能没有遵循单一责任原则。在这种情况下我们应该怎么做?见方法4。

4) Other method suggested by @joko

我在方法 4 结束时建议的场景。您可以在创建客户 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 映射的一个点。

【讨论】:

【参考方案3】:

您可以为模型的每次创建/更新创建事件处理程序,例如添加以缓存刚刚保存到数据库或要保存到数据库的模型数据,无需选择查询调用即可轻松检索, 在删除调用时,对缓存处理程序事件上的给定键使用忘记来删除缓存以及从数据库中删除。

【讨论】:

【参考方案4】:

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

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

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

【讨论】:

【参考方案5】:

您可以创建抽象 Model 类来扩展 Illuminate\Database\Eloquent\Model 类,并且您的所有模型都将扩展该类。通过这样的实现,您可以对模型进行更多控制。例如

<?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 类的所有方法执行此操作,也可以添加与应用程序中所有模型相关的其他方法

【讨论】:

是的,但这会适用于所有模型——我只对一个特定模型感兴趣。 @mpen 如果您只想在一个模型上实现此功能,您可以覆盖保存功能,就像我在您的特定模型中的示例一样。 确实如此。那是我问题中的选项#1。我更想知道这是否比其他两种方法有任何优势。 @mpen 我使用这个选项是因为我的代码更简洁,与特定模型相关的每个逻辑都很容易找到,而且我总是遵循相同的模式,所以我不必记住为什么我的模型就是这样,因为我知道在哪里可以找到它。 @mpen 我也总是在我的回答中使用该实现,因为它为我的应用程序中不可预测的行为提供了灵活性

以上是关于在 Laravel 5.2 中捕获保存和删除的最可靠方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 laravel 5.2 中从 url 中删除 public

Laravel 5.2 删除功能给了我 NotFoundHttpException

Summernote(数据未保存在数据库中)(laravel 5.2)

删除功能 Laravel 5.2

Laravel 5.2 删除外键

Laravel 5.2 验证错误