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?
3 Answers 3
- Searching for the slug duplicate in the database can be done in a more fast and straightforward way, by using
=
operator instead oflike
, and not usingorderBy
; - 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']),
]);
}
I wouldn't name those BETTER ways, but rather SAFER ways to implement it:
$max=0
, then grab allUser::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, withorderBy('slug_number', 'desc')
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.