保持多对多关系,同时仍然绑定到已创建的事件

时间:2019-04-17 09:18:44

标签: laravel laravel-5 laravel-5.7

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

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

订单

  • id
  • 更多字段...

产品

  • id
  • 更多字段...

product_orders

  • order_id
  • product_id

因此,在创建订单时,我将运行以下命令-

$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.

2 个答案:

答案 0 :(得分:1)

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

我个人出于另一个原因偶然发现了事务性事件的概念。我遇到的问题是,在创建特定实体后,我的业务逻辑需要执行一些排队的作业。因为我的实体是在事务中成批创建的,所以有可能触发了一个事件(并且相应的事件侦听器已排队),尽管不久之后由于错误而回滚了事务。结果是排队的侦听器始终失败。

您的情况似乎与我相当,因为您不想立即执行事件侦听器,因为丢失了数据,这些数据仅在实际创建模型后才附加。因此,我建议将您的订单创建和所有其他在事务中操纵订单的任务包装在一起。结合上述包的用法,您就可以触发模型创建事件,因为实际的事件侦听器仅在提交事务后被称为。所有这些的代码基本上都取决于您已经描述的内容:

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;
    }
}

答案 1 :(得分:0)

如果产品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());
    }
}