yiisoft / yii2

Yii 2: The Fast, Secure and Professional PHP Framework
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
14.23k stars 6.92k forks source link

Active Record Optimistic locking does not work with multiple models #16889

Open Webkadabra opened 5 years ago

Webkadabra commented 5 years ago

What steps will reproduce the problem?

Controller code:

if (Model::loadMultiple($models, Yii::$app->request->post()) && Model::validateMultiple($models)) {
            $count = 0;
            foreach ($models as $index => $modelIN) {
                if ($modelIN->save()) {
                    $count++;
                }
            }
        }

Controller gets POST input:

  Array
  (
  [_csrf] => plOgdUA529aMw6mz-uan96qT-1Ty2NS2KbTasCtqK-LVHPpHK363--aB7di_it-23uuDLsP1neBuwIyCSC942g==
  [ShopItem] => Array
  (
  [lock_version] => 1
  [product_type] => group
  [frontend_name] => Main product name
  [sku] => 3904-G
  [available_yn] => 1
  ...
  [55] => Array
  (
  [id] => 55
  [lock_version] => 3
  [frontend_name] => Included product name
  [sku] => 9838
  [in_stock_yn] => 1
  [stock_available] => 1
  [price] => 98.00
  [old_price] => 106.00
  )
   
  )
  )
-- --
   

What is the expected result?

All items should be updated correctly given they are not outdated

What do you get instead?

yii\db\StaleObjectException: The object being updated is outdated. in

Copy Stacktrace Search Stackoverflow Search Google Exception Stale Object Exception – yii\db\StaleObjectException The object being updated is outdated.

Additional info

Q A
Yii version 2.0.15.1
PHP version 7.2.10
Operating system Win 7 (x64)
strtob commented 5 years ago

I have the same problem when I use yii2-relation-trait (https://github.com/mootensai/yii2-relation-trait/issues/60)

:-( any help?

Webkadabra commented 5 years ago

@strtob Laravel... JK. Honestly, I had no luck fixing this

strtob commented 5 years ago

Thank you for your efforts, that's really a shame. This is a basic functionality (save realted records after a first validation error) and without fixing this in near future, I'll need to rewrite my project with laravel or some other php framework... :-(

Webkadabra commented 5 years ago

@strtob I think in one of my projects I "fixed" this doing the following: just disable optimistic lock behavior before you save model, then do $this->updateCounters(...) ti update lock version for each model after save.

This is an issue for loading multiple models. Working with a single model is working fine

isan26 commented 5 years ago

@strtob changing to Laravel Will bring you more problema,i'm using it in my job and i don't recomendet..read and seek for a solution instead of just surrender

Webkadabra commented 5 years ago

Guys, I'm dabbling in comedy so Laravel was a joke. I don't understand why would anyone jump from Yii2 to any other PHP framework.

Webkadabra commented 5 years ago

We need to summon the great @samdark

strtob commented 5 years ago

...thanks to share your experience with laravel. In general I'm happy with yii2, but when the app will throw the error furthermore, a normal use can't handle this :-( I will try the $this->updateCounters(...) and hope this will fix the problem

isan26 commented 5 years ago

I hope you can solve your problem, your comment about the comedy made me laught because it felt relieve about another developer not using laravel. I never had the issue you are having but i had others and always managed to solve then, honestly everything i had worked brings challenges, from PHP to C# to Android there is always stuff like this, for me the most important is to have a well implemented framework that's why i'm a Yii fanatic..yes i admit this...i'm a fanatic...

i'm happy you wont use Laravel ;)

El lun., 8 abr. 2019 a las 10:15, strtob (notifications@github.com) escribió:

...thanks to share your experience with laravel. In general I'm happy with yii2, but when the app will throw the error furthermore, a normal use can't handle this :-( I will try the $this->updateCounters(...) and hope this will fix the problem

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/yiisoft/yii2/issues/16889#issuecomment-480874935, or mute the thread https://github.com/notifications/unsubscribe-auth/AcijSNQayckY7dxe8c6wM4C5W1DEhOWcks5ve10IgaJpZM4YePGY .

Webkadabra commented 5 years ago

Yii2 till I die! Really, I'm working on a big project of my own right now, and if it won't be successful then I'm gonna kill myself

isan26 commented 5 years ago

what are you up to if i may ask?, i have 2 proyects going on right now with Yii2, and i'm advancing without much trouble...what is this that is so important to you..i'm just curious cuz it looks like a very big deal to you

El lun., 8 abr. 2019 a las 13:39, WEBKADABRA (notifications@github.com) escribió:

Yii2 till I die! Really, I'm working on a big project of my own right now, and if it won't be successful then I'm gonna kill myself

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/yiisoft/yii2/issues/16889#issuecomment-480953964, or mute the thread https://github.com/notifications/unsubscribe-auth/AcijSGFcGdXO0kPoKcU3b4fp90xacLFTks5ve4zygaJpZM4YePGY .

Webkadabra commented 5 years ago

@Isan26 I'm super broke and spending my time developing a SaaS solution for Ukrainian market. It is very counter-intuitive to do anything for Ukrainian market

isan26 commented 5 years ago

why is that?...ukrainians are from planet earth too..

El lun., 8 abr. 2019 a las 14:58, WEBKADABRA (notifications@github.com) escribió:

@Isan26 https://github.com/Isan26 I'm super broke and spending my time developing a SaaS solution for Ukrainian market. It is very counter-intuitive to do anything for Ukrainian market

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/yiisoft/yii2/issues/16889#issuecomment-480983606, or mute the thread https://github.com/notifications/unsubscribe-auth/AcijSMfCwde7S2js8EUj26OWO_YhlIzcks5ve59vgaJpZM4YePGY .

Webkadabra commented 5 years ago

You'd think.... :laughing:

isan26 commented 5 years ago

if i can help you let me know...maybe a fresh eyes can give you ideas and i can use your opinion about something i'm building

El lun., 8 abr. 2019 a las 15:48, WEBKADABRA (notifications@github.com) escribió:

You'd think.... 😆

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/yiisoft/yii2/issues/16889#issuecomment-481001895, or mute the thread https://github.com/notifications/unsubscribe-auth/AcijSDTzNEgFElDmme4LIBDd3qpmsTJGks5ve6sqgaJpZM4YePGY .

Webkadabra commented 5 years ago

@Isan26 if you have some job that pays doing whatever yii2, I'd love to join.

samdark commented 5 years ago

@Webkadabra tried debugging it? Any idea why it happens? Does actual table schema/model code matters? Could it be reproduced with data-less model with only a few fields and Gii-generated code? What's DB engine?

strtob commented 2 years ago

this is a solution (shorten) I use in my controller, maybe this will help someone.

In first case of conflict, the user will get a message that the next update will overwrite the newest request:

public function actionUpdate($id) {

        /* PREPARE DATA */
        $model = $this->findModel($id);    

        /* Load and validate Model */
        if ($model->loadAll(Yii::$app->request->post()) && $model->validate()) {

            /* Try to save model */
            if ($model->saveAll([])
            ) {

                return $this->redirect(['update', 'id' => $model->id]);
            }
        } else {

            // check if lock error
            if (($model->getErrors('db_lock')) != null) {
                \Yii::$app->getSession()->setFlash('warning',
                        yii::t('app', 'Your data are behind of data from {username} - your next UPDATE will overwrite with your data or you will be overwritten!', [
                            'username' => \app\models\User::findOne($model->updated_by)->getNameFormal(),
                            'version' => $model->db_lock,
                ]));

                $model->db_lock = $model->db_lock + 1;
                // relation has entries? --> then also increment - next update wil overrule!
                foreach ($model->relatedRecords as $name => $records) {

                    if (($model->$name)) {
                        foreach($model->$name as $relatedModel)
                        {                            
                            if(isset($relatedModel->db_lock))                      
                            $relatedModel->db_lock = $relatedModel->db_lock + 1;
                        }                        
                    }
                }                
            }
        }

            return $this->render('update', [
                        'model' => $model,                    
            ]);
        }
    }