У меня есть две модели: UserPayout и UserTransaction, где UserTransaction является полиморфной и нужно знать, к какой модели она принадлежит. Всякий раз, когда пользователь создает выплату, транзакция должна быть автоматически сделана. Если что-то пошло не так в этом процессе, оба должны вернуться.
Мое фактическое решение выглядит следующим образом:
контроллер:
$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; }
Ну, это решение действительно работает, но что, если мне нужно обновить выплату? Это вызовет функцию сохранения и (конечно), что создаст новую транзакцию. Это абсолютно неправильно.
Итак, каково было бы решение применить его только к созданию выплаты?
Я думал о таких событиях, как создание и создание. В случае создания я не могу сказать модели транзакции, кому она принадлежит, потому что выплата еще не создана. С другой стороны, в случае создания я не могу сказать, что что-то пошло не так во время сохранения транзакции, чтобы я мог откатить выплату.
Итак, что было бы правильным решением здесь? Любая помощь приветствуется.
Итак, каково было бы решение применить его только к созданию выплаты?
Вы можете легко определить, создается или обновляется выплата, проверяя, установлен ли идентификатор в вашем методе save () :
if ($this->id) { //update transaction } else { //create transaction }
Во-вторых, если вы посмотрите, как Eloquent обрабатывает транзакции, вы увидите, что они не будут вложены. Только первый вызов beginTransaction () в стеке вызовов запускает транзакцию БД, и только последний вызов commit () совершает транзакцию, поэтому вам не нужно беспокоиться о вложенных транзакциях.
И, говоря о событиях, они обеспечивают хорошее разделение проблем, и использование сделает ваш код более гибким. Неправильно, что вы пишете:
В случае создания я не могу сказать модели транзакции, кому она принадлежит, потому что выплата еще не создана. С другой стороны, в случае создания я не могу сказать, что что-то пошло не так во время сохранения транзакции, чтобы я мог откатить выплату.
Обратный вызов, вызываемый при создании события, получает объект известного типа. У вас не будет ID, это правда. Но вы можете связать эту модель с другими моделями, и Eloquent правильно установит внешние ключи. Просто убедитесь, что вы напрямую используете методы отношения, такие как associate (), а не просто устанавливаете значение внешнего ключа, поскольку идентификаторы еще не установлены:
$transaction = new UserTransaction($data); $transaction->payout()->associate($payout); $transaction->save();
Вы также должны взглянуть на обертку DB :: transaction (), которую предоставляет Eloquent . Он обрабатывает начало / фиксацию / откат для вас, поэтому требуется меньше кода:
DB::transaction(function () { // do whatever logic needs to be executed in a transaction });
Подробнее о транзакциях в Laravel можно узнать здесь: http://laravel.com/docs/5.1/database#database-transactions