附加多对多关系,同时仍然绑定到创建的事件

Attaching many to many relations while still binding to created event

所以我已经 运行 进入这个问题几次,现在我决定要找到一个更好的解决方案。

为了举例,我有两个模型,OrderProduct。存在多对多关系,因此一个订单可以有多个产品,一个产品当然可以有多个订单。 Table 结构如下所示 -

订单

产品

product_orders

因此,当创建订单时,我 运行 以下 -

$order = Order::create($request->validated())
$order->products()->attach([1,2,3,4...]);

因此这将创建一个订单并将相关产品附加到它。

但是,我想使用观察者来确定订单何时创建并发出并在后台执行相关任务(发送订单确认电子邮件等)。问题是,当时订单创建的观察者已触发,产品尚未附加。

有没有什么办法可以做到以上几点,同时建立所有多对多关系并创建订单,这样我就可以在订单创建的观察者中访问链接的产品?

用例 1

AJAX 调用命中 PUT /api/order,后者又调用 Order::place() 方法。创建订单后,系统会向下订单的客户发送一封电子邮件。现在我可以在这个方法中放置一个事件调度,进而触发电子邮件发送,但这感觉有点老套。

public static function place (SubmitOrderRequest $request)
{
    $order = Order::create($request->validated());

    $order->products()->attach($request->input('products'));

    return $order;
}

用例 2

我正在测试功能以确保在创建订单时发送电子邮件。现在,这个测试通过了(并且电子邮件发送工作),但是在执行的这个时候它无法输出链接的产品。

/**
 * @test
 **/
public function an_email_is_sent_on_order_creation()
{
    Mail::fake();

    factory(Order::class)->create();

    Mail::assertSent(OrderCreatedMailable::class);
}


Thanks,
Chris.

如果产品 ID 是静态的,您可以在模型中重新定义 boot 方法

class Order extends Eloquent {

   protected static function boot() {
      parent::boot();

      static::saving(function ($user) {
        $this->products()->attach([1,2,3,4...]);
      });
    }
}

或使用observers

class OrderObserver 
{
    public function created($model)
    {
          //
    }
}

并注册这个

class EventServiceProvider extends ServiceProvider
{
    public function boot(DispatcherContract $events)
    {
        parent::boot($events);

        Order::observe(new OrderObserver());
    }
}

我认为您的问题的解决方案可能是 this package from fntneves 提供的交易事件。

就我个人而言,出于另一个原因,我偶然发现了交易事件的想法。我遇到的问题是我的业务逻辑需要在创建特定实体后执行一些排队的作业。因为我的实体是在一个事务中批量创建的,所以有可能触发了一个事件(并且相应的事件监听器被排队),尽管事务很快就因为错误而回滚了。结果是总是失败的排队侦听器。

您的场景似乎与我相似,因为您不想立即执行事件侦听器,因为缺少仅在实际创建模型后附加的数据。出于这个原因,我建议将您的订单创建和所有其他操作订单的任务包装在一个事务中。结合上述包的用法,您可以触发 model created 事件,因为实际的事件侦听器只会在 after 事务被调用坚定的。所有这些的代码基本上归结为您已经描述的内容:

DB::transaction(function() {
    $order = Order::create($request->validated());
    $order->products()->attach($request->input('products'));
});

在您的模型中,您只需定义一个 OrderCreated 事件或使用另一个答案中建议的观察者:

class Order
{
    protected $dispatchesEvents = [
        'created' => OrderCreated::class,
    ];
}

class OrderCreated implements TransactionalEvent
{
     public $order;

    /**
     * Create a new event instance.
     *
     * @param  \App\Order  $order
     * @return void
     */
    public function __construct(Order $order)
    {
        $this->order = $order;
    }
}