Skip to content

Instantly share code, notes, and snippets.

@hugodotlau
Created September 27, 2012 13:40
Show Gist options
  • Save hugodotlau/3794059 to your computer and use it in GitHub Desktop.
Save hugodotlau/3794059 to your computer and use it in GitHub Desktop.
Using before and after for transactions
<?php
/**
* @property int $bit_id
*/
class Thing extends CActiveRecord {
}
/**
* @property int $id
* @property Thing[] $things
*/
class Bit extends CActiveRecord {
public function relations() {
return array(
'things' => array(self::HAS_MANY, 'Thing', 'bit_id'),
);
}
/**
* This is an example function that another model method or a controller might use.
*
* @param Thing[] $things
* @return bool true on success
*/
public function saveThings($things) {
$curTr = Yii::app()->db->getCurrentTransaction();
$transaction = $curTr === null || !$curTr->getActive()
? app()->db->beginTransaction()
: false;
try {
// Begin of example task...
if ($this->things)
foreach ($this->things as $thing)
$thing->delete();
if ($things)
foreach ($things as $thing) {
$thing->bit_id = $this->id;
if (!$thing->save())
throw new CDbException('Cannot save a thing');
}
// ...end of example task.
if ($transaction)
$transaction->commit();
} catch(Exception $e) {
if ($transaction) {
$transaction->rollback();
Yii::log(__CLASS__ . '::' . __FUNCTION__ . '() failed');
return false;
} else
throw $e;
}
return true;
}
}
/**
* @property int $id
* @property Thing[] $things
*/
class Bit extends CActiveRecord {
/**
* @var CDbTransaction
*/
protected $_transaction;
public function relations() {
return array(
'things' => array(self::HAS_MANY, 'Thing', 'bit_id'),
);
}
protected function beforeDelete() {
$this->beforeChange();
return parent::beforeDelete();
}
protected function beforeSave() {
$this->beforeChange();
return parent::beforeSave();
}
protected function afterDelete() {
$this->afterChange();
return parent::afterDelete();
}
protected function afterSave() {
$this->afterChange();
return parent::afterDelete();
}
/**
* Begins a transaction if the caller of the model's method didn't already do so.
*/
protected function beforeChange() {
$transaction = Yii::app()->db->getCurrentTransaction();
if (($transaction === null || !$transaction->getActive())
&& ($this->_transaction === null || !$this->_transaction->getActive())
)
$this->_transaction = Yii::app()->db->beginTransaction();
}
/**
* If this model has a $this->_transaction then try to committ it and roll it
* back if commit throws a CDbException.
*/
protected function afterChange() {
if ($this->_transaction !== null && $this->_transaction->active) {
// Does Yii know to *not* call either afterDelete() or afterSave()
// until the method has finished all its deletes, updates and saves?
try {
$this->_transaction->commit();
} catch (Exception $e) {
$this->_transaction->rollback();
// Handle the error with some application logic, e.g. log it and
// display an error message.
}
}
}
/**
* This is an example function that another model method or a controller might use.
*
* @param Thing[] $things
* @return bool true on success
*/
public function saveThings($things) {
// Handle the exceptions thrown by model manipulation here but
// leave the commit to afterChnage().
try {
// Begin of example task...
if ($this->things)
foreach ($this->things as $thing)
$thing->delete();
if ($things)
foreach ($things as $thing) {
$thing->bit_id = $this->id;
if (!$thing->save())
throw new CDbException('Cannot save a thing');
}
// ...end of example task.
} catch (Exception $e) {
if ($this->_transaction !== null && $this->_transaction->getActive())
$this->_transaction->rollback();
else
throw $e;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment