D
D
Denis2018-03-11 21:53:51
Yii
Denis, 2018-03-11 21:53:51

How to save 2 models in a Yii2 transaction?

There is a form/model. The function saves 2 models, User and Token.
Saving is happening. But!!! If the Token model fails to save, no records are added to the database, but on the next successful record in the User model, the id of the new user will be larger by the previous number of incorrect saves.
Let's say the last user with id=10 had two bad save attempts, and then a successful save, id=13.
Is this normal or is there a bug in my code?

<?php
namespace frontend\modules\account\models\forms;

use frontend\modules\account\models\Token;
use Yii;
use common\models\User;
use yii\base\Model;

class RegistrationForm extends Model
{
    public $nickname;
    public $email;
    public $password;

    /**
     * Validation rules
     * @inheritdoc
     * @return array
     */
    public function rules()
    {
        return [
            // nickname rules
            'nicknameTrim'     => ['nickname', 'trim'],

            // email rules
            'emailTrim'     => ['email', 'trim'],

            // password rules
            'passwordTrim' => ['password', 'trim'],
        ];
    }

    /**
     * Field name
     * @inheritdoc
     * @return array
     */
    public function attributeLabels()
    {
        return [
            'nickname' => 'Никейм',
            'email' => 'E-mail',
            'password' => 'Пароль',
        ];
    }

    /**
     * Form name
     * @inheritdoc
     * @return string
     */
    public function formName()
    {
        return 'registration-form';
    }
    
    /**
     * Registrations function
     * @return bool
     * @throws \Exception
     * @throws \Throwable
     */
    public function registration()
    {
        if ( !$this->validate() ) {
            return false;
        }

        $transaction = Yii::$app->db->beginTransaction();

        try {
            $user = new User();
            $user->nickname = $this->nickname;
            $user->email = $this->email;
            $user->generatePassword($this->password);
            $user->save();

            $token = new Token();
            $token->user_id = $user->getPrimaryKey();
            $token->save();

            $transaction->commit();

        } catch (\Exception $e) {
            $transaction->rollBack();
        }
        
        return true;
    }
}

Answer the question

In order to leave comments, you need to log in

1 answer(s)
D
Decadal, 2018-03-11
@valetu

This is normal because the insertion uses a number from the autoincrement sequence.
For example, a transaction includes two queries: a light one and a very heavy one.
The first inserts a record and gets its id, for example 13, which is used in the second query.
At that moment, when the second query began to be executed, an insert could occur in the table into which the record was added by the first query.
If this competing insert used the "idle" id 13, for which the record "as if" does not yet exist, then a collision would occur when the heavy query is executed: it already used id 13, but in fact this id belongs to another record. Plus, it is not clear where to commit the first record from the transaction.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question