Does CakePHP support transactions over multiple models?
Asked Answered
D

1

7

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.

Disagree answered 29/8, 2011 at 20:34 Comment(6)
Possible duplicate of #3013847Mameluke
Ugh, you're right. Same fundamental question, and apparently there's no solution judging by the lack of an appropriate answer.Disagree
If what you want is to save several related models you can just use the saveAll() methodMameluke
Hmm.. I am fortunate enough to have them all linked together under the model for the user's account, but I want to do the update on the database server (UPDATE SET field = field * 10) rather than pulling the data, doing the multiplication application side, and then pushing the result back. Which is forcing me to use the Model::query() method, so saveAll is out. I wonder if I'd be able to start and stop the transaction on the parent model though and achieve the desired result.. I'll check.Disagree
Good news- if it all falls underneath the same parent model, the query sequence works out properly. That's all fixed now; just need to sort out this questions' duplicate status.Disagree
Good to hear, well you could just delete the question :PMameluke
L
8

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.

Leveloff answered 30/8, 2011 at 11:19 Comment(4)
Hmm, you're right. I had thought the DboSource::begin() function was doing some model specific actions since Dbo it requires the model as a parameter, but apparently it doesn't actually use it for anything. github.com/cakephp/cakephp/blob/1.3/cake/libs/model/datasources/….Disagree
pretty much all methods in dbo and behaviors take a model as the first param. I would think the method dispatching the calls does this and thus even when its not needed you get it.Leveloff
Cake actually does support nested transactions in 2.X. See book.cakephp.org/2.0/en/models/…Favored
This is actually pretty great - shouldn't that be a standard functionality? It makes transactions in cake a lot easier.Saltine

© 2022 - 2024 — McMap. All rights reserved.