Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveRecord refresh() is failed when using transactions and default attrib is by Expression #8390

Closed
mootensai opened this issue May 13, 2015 · 8 comments

Comments

@mootensai
Copy link

I try to implement UUID on insert, so i use

class MyModel extends ActiveRecord{
    public function rules(){
        return ['id', 'default', 'value' => new Expression("REPLACE(UUID(),'-','')")]
    }
}

but when i do this

$db = $model->getDb();
$db->transaction(function ($db){
    $trans = $db->beginTransaction();
    if($model->save()){
        var_dump($model->refresh()); // output : bool(false)
        print_r($model_attributes);
//    output :
//    [id] => yii\db\Expression Object
//        (
//            [expression] => REPLACE(UUID(),'-','')
//            [params] => Array
//                (
//                )
//        )
        $child->parent_key = $model->id // i can't use this
    }
}

NB :
even if i use

$db->transaction(function($db){
    //my command
});

it's not working.

i still have to use

$db->transaction(function($db){
    $trans = $db->beginTransaction();
    //my command
    ($model->save()) ? $trans->commit() : $trans->rollback();
});

It's not the same as the documentation here :
http://www.yiiframework.com/doc-2.0/guide-db-dao.html#performing-transactions

@cebe
Copy link
Member

cebe commented May 13, 2015

  1. validation rules are to be used to validate user input. With the above code, the user could send arbitrary input as the ID...
  2. refresh works by using the primary key. If you generate the primary key in the DB, active record does not know how to load the data because it does not know the key.

Is it possible to get the value from last_insert_id?

@nineinchnick
Copy link
Contributor

@mootensai what database do you use? It should work for PostgreSQL and Oracle. It won't work for any other database, this is just not possible as the database evaluates the expression on INSERT but there's no way to return the value of the PK. You'd have to run that expression yourself and use a value in your filter.

@mootensai
Copy link
Author

@cebe

  1. I don't create input field for ID
  2. I try your work around and the result is zero. Here's what i try
$model->save();
print_r(mysql_insert_id());//output : 0
print_r($model->getDb()->lastInsertID); //output : 0

@nineinchnick I use MySQL. but when i try $model->save(), it insert to my table with generated UUID.
If I change the rules to this

class MyModel extends ActiveRecord{
    public function rules(){
        return ['id', 'default', 'value' => $this->generateUUID()] // PHP generated UUID
    }
}

$model->refresh() is working.
print_r($model->id) is outputting generated UUID.
So I think the problem is not on the transactions, but on the Expression on 'default' rules().

Can't I use the DB generated UUID? Is there any other work around?

@nineinchnick
Copy link
Contributor

@mootensai that's the way MySQL works. If you issue an INSERT INTO (pk_col) VALUES (expression) statement, the expression gets evaluated but you won't be able to retrieve the value. Since it's your primary key, you can't identify the new row created in the database. You have to know it before executing that statement. That's why you have to calculate it in PHP. It's the only way to associate your model with the new db record.

MySQL only allows you to associate last inserted record if using an autoincrementing column and the lastInsertId value, assuming you insert just one row.

Other databases, like PostgreSQL and Oracle, can return values from the insert statement, even if they're expressions. This is supported by Yii since the latest version, 2.0.4.

I think this issue should be closed.

@nineinchnick
Copy link
Contributor

@mootensai a workaround is to generate the UUID like this:

class MyModel extends ActiveRecord{
    public function rules(){
        return ['id', 'default', 'value' => $this->getDb()->createCommand("REPLACE(UUID(),'-','')")->queryScalar()];
    }
}

@cebe cebe closed this as completed May 14, 2015
@mootensai
Copy link
Author

@nineinchnick : your code works!! Thanks a lot!

@cebe
Copy link
Member

cebe commented May 15, 2015

still it does not belong into rules() because someone can send a different ID in the POST request.
Better put it into beforeSave()

@mootensai
Copy link
Author

@cebe Ok, Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants