Let's say you want to generate a public-facing slug for an item, based on its name. The name isn't guaranteed unique in the database, but you want the slug to be unique since it will be used in a URL. How would you handle that?
You could just append a random four-digit number and hope for the best. I mean, what are the odds that two items would a) have the same name and b) that the same random four-digit number was generated. It might not ever collide, and for some projects, maybe that's a fair trade-off, but it's not that hard to handle it properly.
$attempts = 0;
$originalSlug = $slug = Str::slug($item->name);
do {
$attempts++;
if (Item::withTrashed()->whereSlug($slug)->doesntExist()) {
$item->slug = $slug;
} else {
$slug = $originalSlug . '-' . random_int(1000, 9999);
}
} while ($item->slug === null && $attempts < 100);
I don't reach for the do/while
loop very frequently, but this is a perfect use case. First, we're generating the slug based solely on the item name. If nothing in the database matches, not even soft-deleted records, then we use it. But if there's a collision, now we append a random four digits and check again. If it still matches, as unlikely as that is, we try again. In fact, we let it try up to 100 times. You could argue that number seems higher than necessary, so adjust as needed to your taste. But you should definitely have some limit, otherwise you run the risk of an infinite loop if there's a bug in your logic.
On the other hand, if you have a scenario where the length or content of a random, yet unique, value is not important, you could simplify this by using a ULID. There is still technically the possibility of a collision, but you'd need to be generating trillions of trillions of records per millisecond to even have a one-in-a-million chance of a collision. Even I'm not that paranoid.
Here to help,
Joel
P.S. Wish you could ship new features more quickly? We can help.