Laravel 4 - 在自动创建其他模型时保存数据库事务

Laravel 4 - db transaction on save while automatically creating other model

我有两个模型:UserPayout 和 UserTransaction,其中 UserTransaction 是多态的,需要知道它属于哪个模型。 每当用户创建支付时,交易应该自动进行。如果在此过程中出现问题,两者都应该回滚。

我的实际解决方案如下:

控制器:

$user_payout = new UserPayout($input);
$user->payouts()->save($user_payout);

用户支付:

public function save(array $options = Array())
{
    DB::beginTransaction();

    try{
        parent::save($options);

        $transaction = new UserTransaction(
            array(
                'user_id' => $this->user_id,
                'template_id' => $this->template->id,
                'value' => -$this->amount
            )
        );

        $this->transactions()->save($transaction);
    }
    catch(\Exception $e)
    {
        DB::rollback();
        throw $e;
    }

    DB::commit();

    return $this;
}

用户交易:

public function save(array $options = Array())
{
    DB::beginTransaction();

    try{
        $user = User::find($this->user_id);
        $user->balance = $user->balance + $this->value;

        if(!$user->save()) throw new Exception('User could not be saved. Check for validation rules.');

        parent::save($options);
    }
    catch(\Exception $e)
    {
        DB::rollback();
        throw $e;
    }

    DB::commit();

    return $this;
}

好吧,这个解决方案实际上可行,但如果我需要更新付款怎么办?它会触发保存功能并且(当然)它会创建一个新的交易。这是绝对错误的。

那么仅将其应用于创建支出的解决方案是什么?

我想到了创建和创建之类的事件。在创建的情况下,我无法告诉交易模型它属于谁,因为尚未创建支出。另一方面,在创建的情况下,我无法判断在保存交易时是否出了问题,以便我可以回滚支出。

那么正确的解决方案是什么?任何帮助表示赞赏。

So what would be the solution to only apply it on a creation of a payout?

您可以通过检查您的 save() 方法中是否设置了 id 来轻松确定是否创建或更新了支出:

if ($this->id) {
  //update transaction
} else {
  //create transaction
}

其次,如果您查看 Eloquent 处理事务的方式,您会发现它们不会嵌套。只有调用堆栈中对 beginTransaction() 的第一次调用会启动数据库事务,只有最后一次调用 commit() 才会提交事务,因此您无需担心嵌套事务。

说到事件,它们很好地分离了关注点,使用它们会使您的代码更加灵活。你写的不对:

In case of creating I can't tell the transaction model to whom it belongs because the payout isn't created yet. On the other hand in case of created I can't tell if something went wrong while saving a transaction so that I could rollback the payout.

创建 事件时调用的回调获取已知类型的对象。你不会有身份证,那是真的。但是您仍然可以将此模型与其他模型相关联,并且 Eloquent 将正确设置外键。只需确保直接使用 associate() 之类的关系方法,而不仅仅是设置外键的值,因为尚未设置 ID:

$transaction = new UserTransaction($data);
$transaction->payout()->associate($payout);
$transaction->save();

您还应该看看 DB::transaction() 包装器 Eloquent 提供的。它为您处理 begin/commit/rollback,因此需要更少的代码:

DB::transaction(function () {
  // do whatever logic needs to be executed in a transaction
});

您可以在 Laravel 中阅读有关交易的更多信息:http://laravel.com/docs/5.1/database#database-transactions