Help in OptimisticLock - Version

Hi.

I read the documentation about the Optimistic Lock

I have tried to implement it, but it not work unfortunately, or I misunderstand the documentation.

So in the database the column is called version.




'version BIGINT DEFAULT 0 '



The original optimistic lock method in the active record is overridden, it returns with the column’s name.




public function optimisticLock() {

    return 'version';

}



It is in the view file too.




<?= $form->field($model, 'version')->hiddenInput() ?>



But when I update the model (the first save was sucessful) I get the following SQL error always:

SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: ""

Because the SQL is wrong, unfortunatelly the value of version is an empty string (in the hidden input too…)!




The SQL being executed was: UPDATE "advertiser" SET "country_id"=NULL, "county_id"=NULL, "city_id"=NULL, "type"=1, "gender"=1, "updated_at"=1470513315, "version"=1 WHERE ("id"=1) AND ("version"='')



What should I do?

Before save should I somehow transform the empty string to 0 value?

Did you add the field version to rules? You need specify it like required.

If I add it, then I will get the "version is required" error message on the form.

But I do not think I should previously somehow fill up the version field via Javascript, or is it?

The field is required because all model need from this data to locking system works.

On create action, you load the default value to new versioned model (0 like described on your schema) or you set it manually.




  public function actionCreate()

    {

        $model = new MyModel();

        $model->loadDefaultValues();

// or

        $model->version = 0;

// others codes

    }



On update action, i have the following snippet. Maybe it can help you.




public function actionUpdate($id)

    {

        $model = $this->findModel($id);


        try

        {

            if ($model->load(Yii::$app->request->post()) && $model->save())

            {

                return $this->redirect(['view', 'id' => $model->id]);

            }


            return $this->render('update', [

                'model' => $model,

            ]);

        }

        catch (StaleObjectException $e)

        {

            // Find the model with current data

            $staleModel = $this->findModel($id);


            // Remove version field from POST data

            unset( $_POST[$model->formName()][$model->optimisticLock()] );


            // Force reload $_POST without version field

            Yii::$app->request->setBodyParams(null);


            // Get the modified data by user and validate to apply any filters rules and behaviors

            $staleModel->load(Yii::$app->request->post()) and $model->validate();


            // Get last data (update version and others fields if necessary)

            $model->refresh();


            // This flash i use to show the stale model to user on view file

            Yii::$app->session->setFlash('staleObject');


            return $this->render('update', [

                'model' => $model,

                'staleModel' => $staleModel,

            ]);

        }

    }



On your view, you can call ActiveRecord::getDirtyAttributes() to show the changed data. My view update.php, before render form.




    <?php if ( Yii::$app->session->getFlash('staleObject') ) : ?>

        <p class="alert alert-warning"><?= Yii::t('app', 'This {modelClass} was updated by another user.', ['modelClass' => $modelClass ]) ?></p>

        <h3><?= Yii::t( 'app', 'Your Modifications' ); ?></h3>

        <?= DetailView::widget([

            'model' => $staleModel,

            'attributes' => array_keys( $staleModel->getDirtyAttributes( $staleModel->trackeableAttributes ) ),

        ]) ?>

    <?php endif; ?>



And model, you need a rule to get the field on Model::load() call (i prefer required but you can use safe too).




    public function rules()

    {

        return [

            [['version'], 'required'],

        ];

    }