How to properly use Sluggable Behavior

Hello everyone,

I am trying to create nice SEO friendly URLs. My company required from me to use yii2 framework and, among others, gave me task that application should have nice SEO friendly URLs with article names displayed in URL, so Google will like it. I wanted to use Sluggable Behaviour, but I got stuck right after I tried what I saw in documentation.

I’ve put this code in my model, and nothing happens:


public function behaviors()

{

    return [

        [

            'class' => SluggableBehavior::className(),

            'attribute' => 'title',

            // 'slugAttribute' => 'slug',

        ],

    ];

}

Then I figure it out that I need to create rules that will use slug, and that I need to pass slug to my view action. I got some advices how to do it, but everything failed.

If I create this rule:


article/<slug>' => 'article/view',

First, I am forced to get articles by title and not by ID, and I need to do it by id. Second, even if I change my code to get articles by slug, my article/index page do not work anymore. Or in some cases, I do not get SEO friendly title in URL ( First-article ), I get "First+article". To make long story short, I made some solution that seem to work, but I am not using Sluggable Behavior at all, or I think I am not. I will show you what I did, and hopefully some one will be able to tell me if my solution is bad, and again hopefully tell be how can I make this work with Sluggable Behavior.

  1. URLManager rule:

'urlManager' => [

    'class' => 'yii\web\UrlManager',

    'enablePrettyUrl' => true,

    'showScriptName' => false,

    'rules' => [

        'article/<id:\d+>/<slug:[-a-zA-Z]+>' => 'article/view',

    ],

],

  1. In my index view, I have link that will redirect user to the "view" view, where he can see article:

Url::to(['article/view', 'id' => $model->id, 'slug' => str_replace(' ', '-', $model->title)]) ?>

  1. My actionView in ArticleController:

public function actionView($id, $slug = null)

{

    return $this->render('view', [

        'model' => $this->findModel($id),

    ]);

}

If user clicks on the link on index page, he will get to this URL:


example.com/article/1/First-article

This is it, I am not using Sluggable Behavior at all.

Am I doing this wrong ? And if I do, how can I do this with Sluggable Behavior. Thanks in advance

I can write anything in URL this way such as example.com/article/1/this-website-is-umm-not-very-secure and it will successfully open article #1.

1 Like

So If I manage to find out how to use Sluggable Behavior, I need to compare both slug and id with the one in database ?

Either that or use slug alone.

What I did was implement a findModelBySlug function and change a few actions to take a slug instead of an id.

However, when deleting or updating I kept the id (for reasons I can’t remember).

You need to adjust your url-rules to take slugs instead, then.

It should be straight forward.

The behavior does help to generate proper slugs but that is probably all it does. The rest is up to you. :)

I do not understand why it is a problem that you can access article with id of 1 by writing anything as slug. Whatever you write as a slug, you will still get article with the id of 1. I get articles by id not by slug, I just output title in URL for SEO purposes. I do not see how this is a security issue ?

It’s not a security issue. It just can harm website reputation if people will start linking your website with example.com/article/1/something-really-nasty-there.

It is a big problem because search engines, especially Google, will punish you for not having unique URLs.

Yeah, that too.

Thank you, I wasn’t thinking about that. But what about if titles ( slugs ) are unique ?

The last time I checked, all of the sluggable behavior extensions for Yii did at least one thing: make sure that slugs were unique. That’s the whole reason why you should be using a behavior like that. :)

The problem with the findBySlug() approach is that it makes difficult to handle changes in the slug which are sometimes necessary. You have to store the whole history of slug modifications in database and check it on every request.

I usually use the following mechanism instead:

[list=1][]Find model by id (prepended or appended to the slug in the URL). Ids won’t change ever (hopefully).[]Compare the slug in the URL with the one stored in the database. If not the same, send a 301 with the correct slug in the URL.[*]Othervise serve the page.[/list]The whole process can be implemented as a reusable action filter and allows slug modifications but prevents duplicate content/inappropriate URL problems.

I use URLs in the form of

example.com/123-the-article-title

The whole thing is produced by URLManager rule and In controller I just search the article ID in this case 123

Not sure its good way and am no SEO expert so it might be bad way as well :)