From 7d7f7bb9a3d824785b36efb57d8ad9f5cc74e2c3 Mon Sep 17 00:00:00 2001 From: leandrogehlen Date: Wed, 15 Aug 2018 20:59:56 -0300 Subject: [PATCH 1/6] Auto start transaction --- src/SaveRelationsTrait.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/SaveRelationsTrait.php b/src/SaveRelationsTrait.php index 8aff2b7..269f368 100644 --- a/src/SaveRelationsTrait.php +++ b/src/SaveRelationsTrait.php @@ -5,6 +5,9 @@ trait SaveRelationsTrait { + /** + * Populates the relations with input data. + */ public function load($data, $formName = null) { $loaded = parent::load($data, $formName); @@ -13,4 +16,17 @@ public function load($data, $formName = null) } return $loaded; } + + /** + * Auto start transaction if model has relations + */ + public function isTransactional($operation) + { + if ($this->hasProperty('relations')) { + return count($this->relations) > 0; + } + + return parent::isTransactional($operation); + } + } \ No newline at end of file From 6d476a390f331897338b092b67367f8036eb6d50 Mon Sep 17 00:00:00 2001 From: leandrogehlen Date: Fri, 24 Aug 2018 08:41:48 -0300 Subject: [PATCH 2/6] Added `autoStartTransaction` property --- src/SaveRelationsBehavior.php | 1 + src/SaveRelationsTrait.php | 6 +-- tests/SaveRelationsBehaviorTest.php | 60 +++++++++++++++++++++++++---- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/SaveRelationsBehavior.php b/src/SaveRelationsBehavior.php index 9e2b3bd..10e0419 100644 --- a/src/SaveRelationsBehavior.php +++ b/src/SaveRelationsBehavior.php @@ -28,6 +28,7 @@ class SaveRelationsBehavior extends Behavior public $relations = []; public $useFormName = true; + public $autoStartTransaction = false; private $_relations = []; private $_oldRelationValue = []; // Store initial relations value diff --git a/src/SaveRelationsTrait.php b/src/SaveRelationsTrait.php index 269f368..42c71a8 100644 --- a/src/SaveRelationsTrait.php +++ b/src/SaveRelationsTrait.php @@ -18,12 +18,12 @@ public function load($data, $formName = null) } /** - * Auto start transaction if model has relations + * Starts transaction if [autoStartTransaction] has been defined */ public function isTransactional($operation) { - if ($this->hasProperty('relations')) { - return count($this->relations) > 0; + if ($this->hasProperty('autoStartTransaction')) { + return $this->autoStartTransaction; } return parent::isTransactional($operation); diff --git a/tests/SaveRelationsBehaviorTest.php b/tests/SaveRelationsBehaviorTest.php index d830d9c..6d07be2 100644 --- a/tests/SaveRelationsBehaviorTest.php +++ b/tests/SaveRelationsBehaviorTest.php @@ -14,7 +14,9 @@ use tests\models\User; use tests\models\UserProfile; use Yii; +use yii\base\Behavior; use yii\base\Model; +use yii\db\BaseActiveRecord; use yii\db\Migration; use yii\helpers\VarDumper; @@ -846,13 +848,8 @@ public function testSaveProjectWithCompanyWithUserShouldSucceed() public function testLoadRelationNameAsDataKeyShouldSucceed() { $company = new Company([ - 'name' => 'NewSoft', - ]); - - $company->attachBehavior('saveRelations', [ - 'class' => SaveRelationsBehavior::className(), - 'relations' => ['users'], - 'useFormName' => false + 'useFormName' => false, + 'name' => 'NewSoft' ]); $data = [ @@ -869,4 +866,53 @@ public function testLoadRelationNameAsDataKeyShouldSucceed() $this->assertEquals('user1', $company->users[0]->username); $this->assertEquals('user2', $company->users[1]->username); } + + public function testAutoStartTransaction() + { + $transactional = false; + $user = User::findOne(1); + + $user->attachBehavior('transactional', [ + 'class' => BeforeUpdateBehavior::className(), + 'callback' => function($model) use (&$transactional) { + $transactional = $model->getDb()->getTransaction() !== null; + } + ]); + + $user->username = 'Linus Torvalds'; + $user->save(); + $this->assertFalse($transactional); + + $user->autoStartTransaction = true; + $user->username = 'Steve Jobs'; + $user->save(); + $this->assertTrue($transactional); + } +} + +class BeforeUpdateBehavior extends Behavior +{ + /** + * @var callable + */ + public $callback; + + /** + * @inheritdoc + */ + public function events() + { + return [ + BaseActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate', + ]; + } + + /** + * Runs the callback property + */ + public function beforeUpdate() + { + $result = call_user_func($this->callback, $this->owner); + } + } From c4fc84d64baef32c4e65ef8a58b326a37e069c7b Mon Sep 17 00:00:00 2001 From: leandrogehlen Date: Fri, 24 Aug 2018 11:32:26 -0300 Subject: [PATCH 3/6] Fixes test break --- tests/SaveRelationsBehaviorTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/SaveRelationsBehaviorTest.php b/tests/SaveRelationsBehaviorTest.php index 6d07be2..f0cc612 100644 --- a/tests/SaveRelationsBehaviorTest.php +++ b/tests/SaveRelationsBehaviorTest.php @@ -19,6 +19,7 @@ use yii\db\BaseActiveRecord; use yii\db\Migration; use yii\helpers\VarDumper; +use yii\db\ActiveRecord; class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase { @@ -869,13 +870,13 @@ public function testLoadRelationNameAsDataKeyShouldSucceed() public function testAutoStartTransaction() { - $transactional = false; + $transactional; $user = User::findOne(1); $user->attachBehavior('transactional', [ - 'class' => BeforeUpdateBehavior::className(), + 'class' => CallbackBehavior::className(), 'callback' => function($model) use (&$transactional) { - $transactional = $model->getDb()->getTransaction() !== null; + $transactional = $model->isTransactional(ActiveRecord::OP_UPDATE); } ]); @@ -883,14 +884,13 @@ public function testAutoStartTransaction() $user->save(); $this->assertFalse($transactional); - $user->autoStartTransaction = true; - $user->username = 'Steve Jobs'; + $user->username = 'Eric Schmidt'; $user->save(); - $this->assertTrue($transactional); + $this->assertFalse($transactional); } } -class BeforeUpdateBehavior extends Behavior +class CallbackBehavior extends Behavior { /** * @var callable @@ -903,7 +903,7 @@ class BeforeUpdateBehavior extends Behavior public function events() { return [ - BaseActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate', + BaseActiveRecord::EVENT_BEFORE_UPDATE=> 'beforeUpdate', ]; } From cc0b1f129166ab47d916fb654833083883a08c0d Mon Sep 17 00:00:00 2001 From: leandrogehlen Date: Fri, 24 Aug 2018 11:38:26 -0300 Subject: [PATCH 4/6] Fixes test --- tests/SaveRelationsBehaviorTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/SaveRelationsBehaviorTest.php b/tests/SaveRelationsBehaviorTest.php index f0cc612..19d7efc 100644 --- a/tests/SaveRelationsBehaviorTest.php +++ b/tests/SaveRelationsBehaviorTest.php @@ -884,9 +884,10 @@ public function testAutoStartTransaction() $user->save(); $this->assertFalse($transactional); + $user->autoStartTransaction = true; $user->username = 'Eric Schmidt'; $user->save(); - $this->assertFalse($transactional); + $this->assertTrue($transactional); } } From 8b7aedb55b4e1e1191355bfb9bc3ee1e5b1b08a1 Mon Sep 17 00:00:00 2001 From: leandrogehlen Date: Fri, 24 Aug 2018 11:42:19 -0300 Subject: [PATCH 5/6] Cleanup --- tests/SaveRelationsBehaviorTest.php | 44 ++--------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/tests/SaveRelationsBehaviorTest.php b/tests/SaveRelationsBehaviorTest.php index 19d7efc..e35a608 100644 --- a/tests/SaveRelationsBehaviorTest.php +++ b/tests/SaveRelationsBehaviorTest.php @@ -14,9 +14,7 @@ use tests\models\User; use tests\models\UserProfile; use Yii; -use yii\base\Behavior; use yii\base\Model; -use yii\db\BaseActiveRecord; use yii\db\Migration; use yii\helpers\VarDumper; use yii\db\ActiveRecord; @@ -873,47 +871,11 @@ public function testAutoStartTransaction() $transactional; $user = User::findOne(1); - $user->attachBehavior('transactional', [ - 'class' => CallbackBehavior::className(), - 'callback' => function($model) use (&$transactional) { - $transactional = $model->isTransactional(ActiveRecord::OP_UPDATE); - } - ]); - - $user->username = 'Linus Torvalds'; - $user->save(); + $transactional = $user->isTransactional(ActiveRecord::OP_UPDATE); $this->assertFalse($transactional); $user->autoStartTransaction = true; - $user->username = 'Eric Schmidt'; - $user->save(); + $transactional = $user->isTransactional(ActiveRecord::OP_UPDATE); $this->assertTrue($transactional); } -} - -class CallbackBehavior extends Behavior -{ - /** - * @var callable - */ - public $callback; - - /** - * @inheritdoc - */ - public function events() - { - return [ - BaseActiveRecord::EVENT_BEFORE_UPDATE=> 'beforeUpdate', - ]; - } - - /** - * Runs the callback property - */ - public function beforeUpdate() - { - $result = call_user_func($this->callback, $this->owner); - } - -} +} \ No newline at end of file From 9ff39adbd98a0c7b70a9bfa828a98de5bbc963ac Mon Sep 17 00:00:00 2001 From: leandrogehlen Date: Sun, 26 Aug 2018 18:49:55 -0300 Subject: [PATCH 6/6] Removed transactional control --- src/SaveRelationsBehavior.php | 43 ----------------------------------- 1 file changed, 43 deletions(-) diff --git a/src/SaveRelationsBehavior.php b/src/SaveRelationsBehavior.php index 10e0419..a836f43 100644 --- a/src/SaveRelationsBehavior.php +++ b/src/SaveRelationsBehavior.php @@ -324,7 +324,6 @@ public function beforeValidate(ModelEvent $event) */ protected function saveRelatedRecords(BaseActiveRecord $model, ModelEvent $event) { - $this->startTransactionForModel($model); try { foreach ($this->_relations as $relationName) { if (array_key_exists($relationName, $this->_oldRelationValue)) { // Relation was not set, do nothing... @@ -344,7 +343,6 @@ protected function saveRelatedRecords(BaseActiveRecord $model, ModelEvent $event } } catch (Exception $e) { Yii::warning(get_class($e) . ' was thrown while saving related records during beforeValidate event: ' . $e->getMessage(), __METHOD__); - $this->_rollback(); $model->addError($model->formName(), $e->getMessage()); $event->isValid = false; // Stop saving, something went wrong return false; @@ -352,30 +350,6 @@ protected function saveRelatedRecords(BaseActiveRecord $model, ModelEvent $event return true; } - /** - * @param BaseActiveRecord $model - */ - protected function startTransactionForModel(BaseActiveRecord $model) - { - if ($this->isModelTransactional($model) && is_null($model->getDb()->transaction)) { - $this->_transaction = $model->getDb()->beginTransaction(); - } - } - - /** - * @param BaseActiveRecord $model - * @return bool - */ - protected function isModelTransactional(BaseActiveRecord $model) - { - if (method_exists($model, 'isTransactional')) { - return ($model->isNewRecord && $model->isTransactional($model::OP_INSERT)) - || (!$model->isNewRecord && $model->isTransactional($model::OP_UPDATE)) - || $model->isTransactional($model::OP_ALL); - } - return false; - } - /** * @param BaseActiveRecord $model * @param ModelEvent $event @@ -455,18 +429,6 @@ private function _prepareHasManyRelation(BaseActiveRecord $model, $relationName) } } - /** - * Rollback transaction if any - * @throws DbException - */ - private function _rollback() - { - if (($this->_transaction instanceof Transaction) && $this->_transaction->isActive) { - $this->_transaction->rollBack(); // If anything goes wrong, transaction will be rolled back - Yii::info('Rolling back', __METHOD__); - } - } - /** * Set relation foreign keys that point to owner primary key * @param $relationName @@ -522,7 +484,6 @@ public function afterSave() } } catch (Exception $e) { Yii::warning(get_class($e) . ' was thrown while saving related records during afterSave event: ' . $e->getMessage(), __METHOD__); - $this->_rollback(); /*** * Sadly mandatory because the error occurred during afterSave event * and we don't want the user/developper not to be aware of the issue. @@ -531,9 +492,6 @@ public function afterSave() } $owner->refresh(); $this->_relationsSaveStarted = false; - if (($this->_transaction instanceof Transaction) && $this->_transaction->isActive) { - $this->_transaction->commit(); - } } } @@ -718,7 +676,6 @@ public function afterDelete() } } catch (Exception $e) { Yii::warning(get_class($e) . ' was thrown while deleting related records during afterDelete event: ' . $e->getMessage(), __METHOD__); - $this->_rollback(); throw $e; } }