Laravel avoid duplication between existing data and new inputs

Viewed 91

I have an edit form where it shows users, sub-users and I need to make my update method the way that it can update existed sub-users as well as add new ones.

Logic

  1. User can have a maximum of 5 sub-users
  2. Let's assume my user already have 2 sub-users s/he can add up to 3 more
  3. I want to be able to update those 2 that are existed already
  4. I want to be able to add those 3 new sub-users

Code

Here is what I have currently. Code is commented for better understanding

public function subUsersUpdate(Request $request, $id) {
    $will = Will::where('id', $id)->where('user_id', Auth::id())->first();
    // main user
    $user = User::where('id', $will->user_id)->first();

    // making custom emails for new users like (admin1@example.com)
    // admin is email name of main user, originally (admin@gmail.com)
    // 1~5 are added to names (separate those 5 sub-users)
    // example.com is website domain
    $userMail = substr($user->email, 0, strpos($user->email, '@'));
    $websiteName = env('DOMAIN_NAME');

    // here is what I get from edit form including 2 existed sub-users and 3 new sub-users
    foreach($request->input('subs') as $index => $sub) {
        // trying to separate existed sub-users from new ones
        $existedUser = User::where('name', $sub)->where('user_id', $user->id)->first();

        if($existedUser) {
            // if already existed just update the name
            $existedUser->update(['name' => $sub]);
        } else {
            // if new add them as new sub-user
            $num = $index+1;
            $password = str_random(15);
            User::create([
                'name' => $sub,
                'email' => $userMail . $num ."@" . $websiteName,
                'password' => Hash::make($password),
                'user_id' => $user->id,
            ]);
        }
    }
    return redirect()->back();
}

Error

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'admin4@example.com' for key 'users_email_unique'

It appears that my code works in order to separate old and new sub-users but the problem is it makes the same email for new-users as old ones based on $num provided in my foreach. I need somehow avoid that duplication.

Any suggestion?

Update

What I did

  1. I've added hidden input in my form which has sub-user email as value (if user existed) and if user is newly added it is null.
  2. Then I merged those emails array and names array into one
  3. Finally I've validate if the email field is null or not in order to update or create new sub-user.

Issue

Issue is name of all sub-users will be same as last input (sub-user 5), regardless of user being newly created or updated all 5 get same name!

    public function subUsersUpdate(Request $request, $id) {
        $will = Will::where('id', $id)->where('user_id', Auth::id())->first();
        $user = User::where('id', $will->user_id)->first();
        $userMail = substr($user->email, 0, strpos($user->email, '@'));
        $websiteName = env('DOMAIN_NAME');
    
    
        //Changed part
        $mails = $request->input('mails');
        $names = $request->input('subs');
    
        // created array of data
        $finalInputs = [];
        foreach($mails as $index => $mmm) {
            foreach($names as $index2 => $nnn) {
                $finalInputs[$index] = ['email' => $mmm, 'name' => $nnn];
            }
        }
    
        foreach($finalInputs as $index => $sub) {
            if(!empty($sub['email'])) {
                $existedUser = User::where('email', $sub['email'])->where('user_id', $user->id)->first();
                $existedUser->update(['name' => $sub['name']]);
                // $userMail2 = substr($sub['email'], 0, strpos($sub['email'], '@'));
            } else {
                if($sub['name'] != null) {
                    $num = $index+1;
                    $password = str_random(15);
                    User::create([
                        'name' => $sub['name'],
                        'email' => $userMail . $num ."@" . $websiteName,
                        'password' => Hash::make($password),
                        'user_id' => $user->id,
                    ]);
                }
            }
        }
        // end of changed parts
    }
1 Answers

Using updateOrCreate()

updateOrCreate() can handle conditional updating or creating of records for you.
This could be what you are asking, hope you get the idea:

foreach($finalInputs as $index => $sub) {
    
    $pwHash = Hash::make( str_random(15) );
    $num = $index+1;
    
    $search = [
        'user_id' => $user->id,
        'email' => $userMail . $num ."@" . $websiteName,
    ];
    
    $update = [
        'name' => $sub['name'],
    ];
    
    // Only add 'password' for new users
    if (empty($sub['email']))) {
      $update['password'] = $pwHash;
    }
    
    User::updateOrCreate(
        $search,
        $update
    );
}

This will update or create users with the given email address and user-id and create if they don't exist. I don't know how your form works in detail, but I think you can adjust to your exact use-case.

Related