Duplicate inserts of records in a foreach

Hi, I am currently trying to do a data transfer. Now when I try to mirgrate the data in my foreach, multiple entries are created in the database.

$transaction = Yii::$app->db->beginTransaction();
try {
    foreach($oldUsers as $oldUser){
        $newUser = new User();
        //some attributes
        $newUser->save();
        foreach($oldUser->userData as $oldUserdata) {
            $newUserData = new UserData();
            $newUserData->user_id = $newUser->id;
            $newUserData->save();            
            foreach($oldUserData->otherDatas as $otherOldData) {
                $newOtherData = new OtherData();
                $newOtherData->user_data_id = $newUserData->id;
                $newOtherData->save();
            }
        }
    }
    $transaction->commit();
} catch(Exception $exception){
    $transaction->rollBack();
}

As in the example tried to describe what I do. If I now execute this code several entries of UserData and OtherData are created.

What am I doing wrong?

Hi @simpleforyou, welcome to the forum.

Is the following understanding of mine correct?

An OldUser has many OldUserData;
An OldUserData has many OtherOldData;
A NewUser has many NewData;

If it is correct, I believe your code might create and save multiple NewUsers per one OldUser. And so, the multiple NewData might be created.

Hi, sorry I had an error in my code

OK, that makes sense. I don’t find anything wrong with your code now.
Well, then I’m wondering what’s wrong, too.

I found a solution for my currently problem. But I think there must be exist a solution.

For my solution I just add a if in the first line of each foreach:

$transaction = Yii::$app->db->beginTransaction();
try {
    foreach($oldUsers as $oldUser){
        $newUser = new User();
        //some attributes
        $newUser->save();
        foreach($oldUser->userData as $oldUserdata) {
            if(UserData::find()->where(['user_id' => $newUser->id])->one() == null){
                $newUserData = new UserData();
                $newUserData->user_id = $newUser->id;
                $newUserData->save();            
                foreach($oldUserData->otherDatas as $otherOldData) {
                    if(OtherData::find()->where(['user_data_id' => $newUserData->id])->one() == null){
                        $newOtherData = new OtherData();
                        $newOtherData->user_data_id = $newUserData->id;
                        $newOtherData->save();
                    }
                }
            }
        }
    }
    $transaction->commit();
} catch(Exception $exception){
    $transaction->rollBack();
}

It’s not really beautifyfull, but its works for me :confused:

Thanks for your time

Does the new User model share the same UserData and OtherData models with the old User model?

I mean, is the following correct?

  • Old users and their data:
    • an OldUser has many UserData
    • a UserData has many OtherData
  • New users and their data:
    • a NewUser has many UserData
    • a UserData has many OtherData

If this is the case, then a newly created NewUser may already have some UserData without intentionally creating new UserDatas. They are originally created for an OldUser and have its id as their user_id, but when a NewUser has the same id, they will be treated as the child records of it.

So I think the following will work if the NewUser has the same UserData and OtherData with the OldUser:

$oldUsers = OldUser::findAll();
foreach ($oldUsers as $oldUser) {
    $newUser = new User();
    $newUser->id = $oldUser->id;
    // some attributes
    $newUser->save();
}

There’s no need to loop through the UserDatas and OtherDatas.

Hi, I’m really sorry :smiley: it was complete my fault.
I had in the model classes of UserData and OtherData an afterSave function… I comment my code out and now it works fine XD. Sorry :smiley:

1 Like