2
\$\begingroup\$

Im using Laravel 5.4 and I'm using a slug to look-up users in database.

Im adding first_name and last_name to the slug, and if there is a user with the same name, appending a number (increment it if there is more than one with same name) to the end ex John.doe.3.

This is what I have in RegisterController.php

protected function create(array $data)
    {
        // Set the may be name
        $maybe_slug = strtolower($data['first_name'] . '.' . $data['last_name']);

        // Check if there is another one in the DB with the same name
        $user = User::where('slug', 'like', $maybe_slug . '%')->orderBy('created_at', 'desc')->first();

        if($user){
            $split_user_name = explode('.', $user->slug);
            $number = isset($split_user_name[2]) ? $split_user_name[2] : null;
            if(isset($number)){
                $slug = $maybe_slug . '.' . ($number + 1);
            }elseif($user->slug == $maybe_slug){
                $slug = $maybe_slug . '.2';
            }
        }else{
                $slug = $maybe_slug;
        }
        return User::create([
            'first_name' => $data['first_name'],
            'last_name' => $data['last_name'],
            'slug' => $slug,
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

This is how RegisterController looks like un-edited (GitHub)

Can I do this in a better way?

\$\endgroup\$
3
\$\begingroup\$
  • Searching for the slug duplicate in the database can be done in a more fast and straightforward way, by using = operator instead of like, and not using orderBy;
  • To be on the safe side, it's worth to make searches in a loop, incrementing the $next on each step;

So, it might look like this:

protected function create(array $data)
{
    $slug = $maybe_slug = strtolower($data['first_name'] . '.' . $data['last_name']);
    $next = 2;

    while (User::where('slug', '=', $slug)->first()) {
        $slug = "{$maybe_slug}.{$next}";
        $next++;
    }

    return User::create([
                'first_name' => $data['first_name'],
                'last_name' => $data['last_name'],
                'slug' => $slug,
                'email' => $data['email'],
                'password' => bcrypt($data['password']),
    ]);
}
\$\endgroup\$
0
2
\$\begingroup\$

I wouldn't name those BETTER ways, but rather SAFER ways to implement it:

  • $max=0, then grab all User::where('slug', 'like', $maybe_slug . '%') entries in a while loop and set $max if lower than $number. In the end, use no suffix or ++$max This will save you in case an user with slug John.Doe.2 is added before another user named Mr.Doe, John with slug John.Mr.Doe
  • as an alternate solution needed in case you have MANY duplicates restricting you from fetching and loading all dup users. You will need to change the User by adding and saving into database a slug_number integer field and use that in an algorithm similar to the one you provided above, with orderBy('slug_number', 'desc')
\$\endgroup\$
0
\$\begingroup\$

Assuming that you have a unique index on the slug field, I would just go directly to performing an insert, but looking for insert failure on duplicate index value. I would then apply increment and try to re-insert.

Why have two queries in your happy (no duplicate) path when you only need one? My guess is that the number of cases where you need to make 3+ queries to successfully insert a record into the database will be extremely low vs. having to perform 2 queries every time you want to do an insert.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.