Generate Slug Keyword from Title: Laravel + AJAX

If you work with project or blog where records require so-called slug (posts, pages etc.), it’s convenient to generate slug immediately after title has been typed in. This article will show you how to do it in Laravel, with AJAX and with a help of one Laravel package.

First, what we’re doing here. This is the example:

We have a form to add a page, and our goal is to have Slug field auto-generated, as we finish typing in the Title.

Not only that, slug should be unique for every post, even if title is the same.


Step 1. Blade Create Form

I will show you, how resources/views/admin/pages/create.blade.php looks like. To be exact, the part responsible for those two fields – title and slug:

<div class="form-group">
    <label for="title">Title*</label>
    <input type="text" id="title" name="title" class="form-control">
</div>
<div class="form-group">
    <label for="slug">Slug*</label>
    <input type="text" id="slug" name="slug" class="form-control">
</div>

Notice: this form code has been generated by our QuickAdminPanel, you should check it out.

So, the code is simple, nothing to comment here. Now, let’s add some JavaScript, that is fired when title value is changed.


Step 2. AJAX Call On Title Change

In the “main” Blade layout file, I have a special @yield(‘scripts’) code that allows to add any JavaScript to any other Blade template. So, that’s exactly what we will do – add this code to the bottom of our pages/create.blade.php:

@section('scripts')
<script>
  $('#title').change(function(e) {
    $.get('{{ route('pages.check_slug') }}', 
      { 'title': $(this).val() }, 
      function( data ) {
        $('#slug').val(data.slug);
      }
    );
  });
</script>
@endsection

Now, we need to create the logic behind pages.check_slug route.


Step 3. Route/Controller to Return Slug

First, in routes/web.php we add this line:

Route::get('pages/check_slug', 'PagesController@check_slug')
  ->name('pages.check_slug');

Next, we create app/Http/Controllers/PagesController method check_slug(). We’ll start from a simple version using Laravel’s str_slug() helper.

public function check_slug(Request $request)
{
    $slug = str_slug($request->title);
    return response()->json(['slug' => $slug]);
}

Simple, right? We make a slug and return it. That could be the end of our tutorial, but we forgot one important rule – slugs should be unique. How to check it?


Step 4. Making Slug Unique

Not only that, we need to not only check uniqueness, but also automatically add -1, -2, -3 and other numbers to the end of the slug, to generate a new unique one.

For that, I’ve decided to not reinvent the wheel and to use an existing package cviebrock/eloquent-sluggable. It’s not the only slug-related package on the market, but it has a very useful (in our case) feature of generating slug without saving it to the database.

We install the package with this command:

composer require cviebrock/eloquent-sluggable

Next, we need to prepare our Model app/Page.php to make it “sluggable”:

use Cviebrock\EloquentSluggable\Sluggable;

class Page extends Model
{
    use Sluggable;

    /**
     * Return the sluggable configuration array for this model.
     *
     * @return array
     */
    public function sluggable()
    {
        return [
            'slug' => [
                'source' => 'title'
            ]
        ];
    }
}

We’ve created a method that defines to generate slug field from title field of pages database table.

And now, final step, we can replace old “simple” method of generating slug in PagesController:

use Cviebrock\EloquentSluggable\Services\SlugService;

public function check_slug(Request $request)
{
    // Old version: without uniqueness
    $slug = str_slug($request->title);

    // New version: to generate unique slugs
    $slug = SlugService::createSlug(Page::class, 'slug', $request->title);

    return response()->json(['slug' => $slug]);
}

And, that’s it!

P.S. Oh, and don’t forget to still check uniqueness on back-end validation when doing store() for the page, add rule ‘slug’ => unique:pages in validation. You never know if someone else is using the system and creates the same page at the same time from another computer 🙂

Like our articles?
Check out our Laravel online courses!

8 COMMENTS

    • Yes, but Spatie package (at least in the docs) doesn’t have the feature to “check” the slug and show it, without saving it it to the database. That’s why I’ve chosen that other package.

      Sometimes it does make a huge sense to have manual slug field, for SEO purposes. So, in this article, it’s a mix of auto-generating slug and possibility to change it manually.

  1. One other thing to consider when setting up slugs is the question of redirects:
    * When slugs are changed, how will you ensure that users who visit the old URL are redirected to the correct URL? Presumably you don’t want to be manually setting up 301 redirects.

    There are two general options:

    // ***** Keep a DB Table of Redirects ***** //
    Keep a database table of slugs:
    * Fields something like id, post_id, slug, redirect_to_slug_id
    * If redirect_to_slug_id is not null, follow IDs until you find the live URL
    Wordpress uses a system like this.

    // ***** Put an ID Into the URL; Slug is Purely Cosmetic ***** //
    For this approach, include an ID in the URL, then have that be what is passed to the route the find the post. Then, generate the slugs *after* finding the post.

    This is similar to what StackExchange does. For example, both of these go to the same place. The route uses the post ID part of the URL and the slug is generated on the fly:
    https://meta.stackexchange.com/questions/155878/
    https://meta.stackexchange.com/questions/155878/some-fake-url
    https://meta.stackexchange.com/questions/155878/stackoverflow-url-format

LEAVE A REPLY

Please enter your comment!
Please enter your name here