Laravel多模型事件

10

我正在尝试将我的数据库与一个外部服务同步。

我在 Web 应用程序的几个地方使用 Algolia 搜索。

它已经被几个模型索引,但是如果数据库发生任何更改,例如当多个模型事件被触发时,我需要重新索引它。

我的第一种方法是在 AppServiceProviderboot 方法中执行所有操作。

public function boot()
{
    $events = ['created', 'updated', 'deleted', 'restored'];

    // reindex handlers for models relevant to Algolia search
    foreach ($events as $evt) {
        Order::registerModelEvent($evt, function () {
            Order::reindex();
        });
        Product::registerModelEvent($evt, function () {
            Product::reindex();
            Product::setSettings();
        });
    }
}

这是我采用标准模型功能避免多条件语句的方法,示例在文档中。

但是我认为使用Laravel事件监听器可能有更好的方法。

namespace App\Listeners;

class OrderEventListener
{
    // handlers

    public function subscribe($events)
    {
        $events->listen(
            // model events
        );
    }
}

虽然我不确定如何在listen方法中获取模型事件。

2个回答

8
我强烈建议为此情况添加自己的事件和处理程序。
在您的“Product”和“Order”类中,可以覆盖模型的“boot”方法:
class Product extends Model
{
    protected static function boot()
    {
        parent::boot();

        self::created(function($product) {
            event(new ProductCreatedEvent($product));
        });
    }
}

你需要创建自己的ProductCreatedEvent对象。现在,在EventServiceProvider中,你需要将其添加到监听器数组中;
protected $listeners = [
    'App\Events\ProductCreatedEvent' => [
        'App\Listeners\UpdateAlgoliaProductIndex',
    ]
];

一旦您设置完成,您实际上可以运行php artisan event:generate,这将为您创建事件对象和监听器。我会跳过事件对象,因为它非常简单,它只是接收创建的产品并将其发送到UpdateAlgoliaProductIndex监听器。

现在,在您的监听器中,您将拥有以下内容:

class UpdateAlgoliaProductIndex
{
    public function handle($event)
    {
        Product::reindex();
        Product::setSettings();
    }
}

我建议这种方法的原因是你可以使用ShouldQueue接口将侦听器排队,这意味着在等待应用程序重新索引Algolia时不会阻止请求,从而为用户提供更好的体验。
你可以在这里阅读有关事件对象和侦听器的更多信息。
另一种选择是使用模型观察器

我已经为通知做过这样的事情,例如产品更新时会触发ProductCreatedEvent。我对此感到满意,因为我只需要在引导方法中使用self::updated()来触发它,就像你展示的那样。 然而,像重新索引这样的操作应该在任何模型事件触发时执行,我正在寻找一种不需要列出每个模型事件及其后续事件侦听器、处理程序等的解决方案。更深入地研究后,创建一个作业并在引导时调度它是否不是更好的选择?然后它仍然可以实现ShouldQueue,但它将始终触发。 - Luke Vincent
1
如果你想要每个模型事件,你可能会更幸运地观察 saved 而不是 createdShouldQueue 可以消除需要定义额外 Job 对象的需求。如果你不想立即运行它,我建议只需在你的产品对象中添加一个 requires_search_indexing 字段。然后你可以定期使用 cron job 进行扫描,以便将更新分组。 - marcus.ramsden
这是一个很好的观点。如果你想要一些折中方案,那么你仍然主张使用所有的事件监听器。Laravel 5.2似乎已经从文档中删除了模型观察器,这让我想知道他们是否不再推荐这种方法了。 - Luke Vincent
我确实想知道模型观察器是否被放弃,而采用引发更具领域特定事件的方式,而不是直接钩入Eloquent事件。如果这是一个问题,它确实会使你与Eloquent解耦得更多。 - marcus.ramsden

0

经过一番搜索,我发现 Laravel 事件 subscribers 更符合我的需求,因此我已将其作为自己的答案提交。

我认为这比为每个事件设置监听器产生更简洁的代码。

namespace App\Listeners;

use App\Events\OrderCreatedEvent;
use App\Events\OrderUpdatedEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class OrderEventListener
{
    /**
     * Handle order created events.
     *
     * @param OrderCreatedEvent $event
     */
    public function onOrderCreation(OrderCreatedEvent $event)
    {
        // do something
    }

    /**
     * Handle order updated events.
     *
     * @param OrderUpdatedEvent $event
     */
    public function onOrderUpdate(OrderUpdatedEvent $event)
    {
        // do something
    }

    /**
     * Register the listeners for the subscriber.
     *
     * @param $events
     */
    public function subscribe($events)
    {
        $events->listen(
            'App\Events\OrderCreatedEvent',
            'App\Listeners\OrderEventListener@onOrderCreation'
        );

        $events->listen(
            'App\Events\OrderUpdatedEvent',
            'App\Listeners\OrderEventListener@onOrderUpdate'
        );
    }
}

我会将marcus.ramsden的答案标记为正确,但这对于遇到这个问题的人来说可能非常相关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接