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

Laravel 附加多对多关系,同时仍绑定到创建的事件,laravel,laravel-5,laravel-5.7,Laravel,Laravel 5,Laravel 5.7,所以我已经多次遇到这个问题,现在我决定要找到一个更好的解决方案 例如,我有两种型号,订单和产品。存在多对多关系,因此一个订单可以有多个产品,一个产品当然可以有多个订单。表结构如下所示- 命令 身份证 更多领域 产品 身份证 更多领域 产品订单 订单号 产品标识 因此,当创建订单时,我运行以下命令- $order = Order::create($request->validated()) $order->products()->attach([1,2,3,4...]

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

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

命令

  • 身份证
  • 更多领域
产品

  • 身份证
  • 更多领域
产品订单

  • 订单号
  • 产品标识
因此,当创建订单时,我运行以下命令-

$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...]);
      });
    }
}
或使用

注册这个

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

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

我认为您的问题的解决方案可以是提供的事务事件

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

您的场景似乎与我类似,因为您不想立即执行事件侦听器,因为缺少的数据仅在实际创建模型后附加。因此,我建议在事务中包装订单创建和所有其他操作订单的任务。结合使用上述包,您可以触发模型创建的事件,因为实际的事件侦听器只有在事务提交后才会被调用。所有这些的代码基本上可以归结为您已经描述的代码:

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

如果您正在使用观察者收听事件,并且当前事件列表不是您想要的。在创建订单并添加所有产品后,您能否不抛出一个新事件?比如在这里@RobBiermann-当然可以,但出于测试目的等,我希望有一个标准事件。比如说,在测试中,我使用数据库种子设定创建了一个订单,但在生产中,我在订单上有一个名为
place
的方法,我需要在两个位置触发该事件扫描您在问题描述中发布两个用例?这有助于找到解决方案:)我认为可以归结为在函数中触发事件,然后在播种器和模型(位置)函数中使用该函数。但现在这只是猜测:)@RobBiermann我添加了几个用例。如前所述,我可以在
Order::place()
中触发一个事件,但感觉不对劲——也许是我太爱写代码了?!不幸的是,实际上产品ID是动态的。感谢您的输入,您可以在observer中使用user request(),例如,这正是我想要的!因此,通过使用此包,并在事务中包装订单创建和产品附加,我可以确保我的
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;
    }
}