'Laravel - How to properly generate unique slugs from article titles?

I need to generate unique slugs based on titles of articles. For matching slugs I want to append a number to the end to make them unique. I made this function based on others work I found around:

static function slugify($title)
    {
        $slug = str_slug($title, '-');
        //THIS IS THE PROBLEMATIC LINE:
        $common = Article::whereRaw("slug RLIKE '^{$slug}(-[0-9]+)?$'")->orderBy('slug', 'desc')->get();
        $count = count($common);
        if( $count > 0 ){
                    $last = $common[0];
                    $broken = explode('-', $last->slug);
                    $num = $broken[count($broken)-1];
                    $num = intval($num) + 1;
                    return $slug.'-'.$num;
        } else
            return $slug.'-1';
    }

The problem: As you can see I try to generate new slugs by using Laravel's str_slug function which will convert a string to a slug form. I then try to query the existing slugs from the databse, order them descendingly (high > low) and take essentialy the highest one within the ordered set. The problem is that MySQL orders them of course as strings and so slug-title-9 would be actually considered higher than slug-title-10 because it's ordering them as characters and not based on the value of the number at the end. How can I make this work? Is there a way to order them based on the last number or am I going in the wrong direction?

Solutions I want to avoid:

I have seen other implementations where people query all the simmilar slugs at once, count them and then append count+1 to the end of the new slug. This is bad because if you delete some articles the overall count would lower and the new slug could conflict with an older one.

I have seen an implementation where a person would append 1 to end of the slug and check if it exists. If it exists they would instead append 2 and try to check if that exists and so one until they find for example slug-title-9 which would not exists. IMO this is bad because you are putting unnecessary strain on the database.

I need a decent solution because the project I am working on has a decend potential of encountering matching slugs often.



Solution 1:[1]

lastInsertId returns id's per-connection, meaning concurrent sql connections will not interfere with each other.

However, lastInsertId() is probably not the best approach for whatever you're doing. I see you're already using Eloquent models. When creating a new entry in table, those models return corresponding object, and then you can get id of exactly that object without any confusion.

$article = Article::create(['title' => 'First post', 'content' => '...']);
echo $article->id;

This will always output the correct article id and you do not have to worry about the implementation details. I cannot see a good reason to use lastInsertId in a modern web framework, except maybe for a very few edge cases.

As a general rule, try to avoid dealing directly with database in your application and use the abstractions that the framework provides - it will make your code much more maintainable and simpler to understand.

P.S. that might not be important to you, but lastInsertId() does not work with transactions.

Solution 2:[2]

You can use this method. This is one that I am using to get unique seo friendly slug https://stackoverflow.com/a/72137537/7147060

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tadas Paplauskas
Solution 2 Mohit Prajapati