We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

error using isolated transactions

I am creating two related models with transactions.

When I use normal transactions everything goes fine. When I use isolated transactions I am getting an error...

This code (normal) works ok:


public function test2Action() {

    $this->db->begin();

    $user = new Users();
    $user->setType(Users::COMPUTER);
    $user->create();

    $userName = new Names();
    $userName->setName("Andrej");

    $user->names = $userName;
    $user->update();

    $this->db->commit();
}

This code (isolated) does not work:


public function testAction() {

    $manager = $this->transactions;
    $transaction = $manager->get();

    $user = new Users();
    $user->setTransaction($transaction);
    $user->setType(Users::COMPUTER);
    $user->create();

    $userName = new Names();
    $userName->setTransaction($transaction);
    $userName->setName("Andrej");

    $user->names = $userName;

    var_dump($user->update()); //returns false

    var_dump($user->getMessages()); //errors here
}

Error I get:


array (size=1)
  0 => 
    object(Phalcon\Mvc\Model\Message)[89]
      protected '_type' => string 'InvalidUpdateAttempt' (length=20)
      protected '_message' => string 'Record cannot be updated because it does not exist' (length=50)
      protected '_field' => null
      protected '_model' => null
      protected '_code' => int 0

Is this a bug or am I doing sth wrong?

Another question, assuming both methods work, what is the difference "inside"? Security, consistency, performance?

TIA!



98.9k

Because since the transactional connection has not been commited the record does not exist when it's checked in the var_dump($user->update()) by the global connection, so you have to do:

$transaction = $manager->get();

$user = new Users();
$user->setTransaction($transaction);
$user->setType(Users::COMPUTER);

$userName = new Names();
$userName->setTransaction($transaction);
$userName->setName("Andrej");

$user->names = $userName;

var_dump($user->save()); 

$transaction->commit();


26.3k

Thanks for answer! But I think that problem still exists.

The reason why I am using both $user->create() and $user->update() in one transaction is that I need to put in Names auto increment ID from Users. And it works with normal transactions ($this->db->begin();) but it doesn't work with isolated transactions. Sorry that I didn't put it into script before but wanted to keep it simple.

Please check my comments:


public function test2Action() {

    $this->db->begin();

    $user = new Users();
    $user->setType(Users::COMPUTER);
    $user->create();

    $creator_id =  $user->getId(); // I am receiving here auto increment ID

    $userName = new Names();
    $userName->setName("Andrej");
    $userName->setCreatorId($creator_id); //I am setting here the ID of the user who created the record

    $user->names = $userName;
    $user->update();

    $this->db->commit();
}

And it works.

The code which doesn't work would be:


public function testAction() {

    $manager = $this->transactions;
    $transaction = $manager->get();

    $user = new Users();
    $user->setTransaction($transaction);
    $user->setType(Users::COMPUTER);
    $user->create();

    $creator_id =  $user->getId(); // I am receiving here auto increment ID

    $userName = new Names();
    $userName->setTransaction($transaction);
    $userName->setName("Andrej");
    $userName->setCreatorId($creator_id); //set creator

    $user->names = $userName;

    var_dump($user->update()); //returns false

    var_dump($user->getMessages()); //errors here
}

Going ahead question why I need this auto increment id? My Names model has two fields related to Users. The owner field and creator(author) field. The above is a sign up action so in this situation both creator and owner will be the same. It will be user who is registering himself. But when sb's account is created by an admin, then Name's owner and creator are different.

For now I am using normal transactions but my actions are more complex then written here. I have read in the docs that isolated transactions should be used in such cases because they guarantee less risk of inconsistensy.

TIA!



98.9k

Actually, you don't need a transaction, just doing it this way Phalcon will obtain the auto incremented id and will assign automatically to the child records:

$user = new Users();
$user->setType(Users::COMPUTER);

$userName = new Names();
$userName->setName("Andrej");

$user->names = $userName;

var_dump($user->save()); 


26.3k

Yes, Phalcon is assigning ID to one field (owner_id).

But I have two fields in child model: owner_id and creator_id. I want both of them to have the same auto increment ID taken from parent model. The first one is set by Phalcon and the second one I am seting manually.

It works with normal transation but doesn't with isolated.



26.3k

My child model is:


<?php

class Names extends ModelBase {

    protected $id;

    protected $name;

    protected $owner_id;

    protected $creator_id;

    public function initialize(){

        //relation to Users model as Owner of the name
        $this->belongsTo(
            'owner_id',
            'Users',
            'id',
            array('alias' => 'Owner')
        );

        // relation to Users model as Creator of the name
        $this->belongsTo(
            'creator_id',
            'Users',
            'id',
            array('alias' => 'Creator')
        );

    }
}

I have left only important part of code. So during sign up I want to assign to owner_id and creator_id the same auto increment ID from parent model Users. And I cannot with isolated transactions.



26.3k

My parent model Users:


class Users extends ModelBase {

    const HUMAN = 1; 
    const COMPUTER = 2; 

    protected $id;
    protected $type;

    public function initialize(){

        //relation to Names model as Owner of the name
        $this->hasMany(
            'id',
            'Names',
            'owner_id',
            array('alias' => 'Names')
        );

        //there is no relation to names as Creator of the name.

    }

}

I had the same issue and solved removing from the initialize method in the model class the references, in this case in User Model Class:

  $this->hasMany(
        'id',
        'Names',
        'owner_id',
        array('alias' => 'Names')
    );