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

ⅰ亾dé卋堺 提交于 2019-12-08 11:09:14

问题


I have two models: UserPayout and UserTransaction where UserTransaction is polymorph and needs to know which model it belongs to. Whenever a user creates a payout, a transaction should automatically be made. If something went wrong in this process, both should get rolled back.

My actual solution is as follows:

Controller:

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

UserPayout:

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

UserTransaction:

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

Well, this solution would actually work but what if I need to update a payout? It would trigger the save function and (of course) it would create a new transaction. This is absolutely wrong.

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

I thought about events like creating and created. 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.

So what would be the right solution here? Any help is appreciated.


回答1:


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

You can easily determine if a payout is created or updated by checking if the id is set in your save() method:

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

Secondly, if you look at how Eloquent handles transactions, you'll see that they won't nest. Only the first call to beginTransaction() in the call stack starts a DB transaction and only the last call to commit() commits the transaction, so you don't need to worry about nested transactions.

And speaking of events, they provide a nice separation of concerns and using would make your code more flexible. It's not correct what you write:

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.

The callback that is called on creating event gets an object of known type. You won't have ID, that's true. But you can still associate this model with other models and Eloquent will set the foreign keys correctly. Just make sure you use relation methods like associate() directly and not just set a value of a foreign key, as IDs have not been set yet:

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

You should also have a look at the DB::transaction() wrapper that Eloquent provides. It handles begin/commit/rollback for you so less code is required:

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

You can read more about transactions in Laravel here: http://laravel.com/docs/5.1/database#database-transactions



来源:https://stackoverflow.com/questions/32038172/laravel-4-db-transaction-on-save-while-automatically-creating-other-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!