Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does CakePHP support transactions over multiple models?

I'm writing an application which supports multiple units of measurement. In the rare event a user wanted to change their system of measurement, I need to run a query which applies a multiplier to scale every unit column in the application to the correct system of measurement. To make sure all the data stays sane in the event something in this operation goes wrong, I need to run the queries inside a transaction.

Is it possible in Cake to perform a transaction which contains queries covering multiple models?

All I've found so far is DataSource::begin/commit/rollback(), but that only supports queries against single models.

like image 527
Brad Koch Avatar asked Aug 29 '11 20:08

Brad Koch


1 Answers

yes it does. I have this in my app model to make transactions easy.

https://github.com/infinitas/infinitas/blob/dev/Model/AppModel.php#L677

    /**
     * @brief wrapper for transactions
     *
     * Allow you to easily call transactions manually if you need to do saving
     * of lots of data, or just nested relations etc.
     *
     * @code
     *  // start a transaction
     *  $this->transaction();
     *
     *  // rollback if things are wrong (undo)
     *  $this->transaction(false);
     *
     *  // commit the sql if all is good
     *  $this->transaction(true);
     * @endcode
     *
     * @access public
     *
     * @param mixed $action what the command should do
     *
     * @return see the methods for tranasactions in cakephp dbo
     */
    public function transaction($action = null) {
        $this->__dataSource = $this->getDataSource();
        $return = false;
        if($action === null) {
            $return = $this->__dataSource->begin($this);
        } else if($action === true) {
            $return = $this->__dataSource->commit($this);
        } else if($action === false) {
            $return = $this->__dataSource->rollback($this);
        }
        return $return;
    }

then you can do something like this:

$saved = true;
$this->transaction();
$saved = $saved && $this->save($data);
$saved = $saved && $this->SomeOtherModel->save($data2);
$saved = $saved && $this->AnotherModel->save($data3);

if($saved){
$this->transaction(true);
    return $this->id;
}
$this->transaction(false);
return false;

you can also do more complex things like the following:

function save1(){

    $saved = true;
    $this->transaction();
    $saved = $saved && $this->save($data);
    $saved = $saved && $this->save2($data);


    if($saved){
        $this->transaction(true);
        return $this->id;
    }

    $this->transaction(false);
    return false;
}

cake does not support nested transactions, but you can sort of fake them

// this will use transactions if its called directly, but will allow a calling method to

// create and manage the transaction.

function save2($data){
    $saved = true;
    $transaction = $this->transaction(); // will only be true if not already started
    $saved = $saved && $this->save($data);

    if($transaction){ // if it was started here, finish it
        if($saved){
            $this->transaction(true);
            return true;
        }

        $this->transaction(false);
        return false;
    }

    return $saved; // return just the status so the other model will finish the transaction
}

just to be clear, you could be doing something like ClassRegistry::init('SomeRandomModel')->save2(). the transactions are not limited to the current model, or related models. its for any model.

like image 158
dogmatic69 Avatar answered Nov 15 '22 10:11

dogmatic69