Посты / Laravel 5.5 и slug. Что такое слаги и зачем они нужны.

21.09.2017 13:15
Всем привет. Я обратил внимание на то, что не все знают, что такое слаги (на английском slug), зачем они нужны, и как их использовать в Laravel. Так как на сегодня последняя версия Ларавел - это Laravel 5.5, то он нем и буду сегодня говорить.

И так, что такое слаг и зачем он нужен. Как нам подсказывает вики - это семантический URL. От сюда можно сделать вывод, что это ссылка "красивого" вида, которая помогает и пользователям и поисковым роботам понять, что находится по этой ссылке. Например, на моем блоге я использую слаги.

Предположим, есть у нас статья, которая называется Привет Мир. Так вот, без слага будет построен путь, что-то наподобие /post/145, а со слагом: /post/privet-mir Согласитесь, что второй вариант выглядит красивее и изящнее. Да и пользователь уже из адресного пути может примерно понять о чем идет речь.

Когда мы разобрались, что такое слаги, давайте разберемся, как их использовать в Ларавел. Как я говорил выше, я использую Ларавел 5.5. Но по сути, то что мы сейчас будем рассматривать, можно смело использовать и в Ларавел 5.3 и в Ларавел 5.4.

Для работы со слагами в Laravel нам понадобится простой и удобный пакет cviebrock/eloquent-sluggable. Устанавливаем его для Ларавел 5.5:

composer require cviebrock/eloquent-sluggable:^4.3

Если вы захотите изменить какие-либо дефолтные настройки, то создайте файл конфигурации:

php artisan vendor:publish --provider="Cviebrock\EloquentSluggable\ServiceProvider"

После этой команды будет создан новый конфиг: config/sluggable.php в котором можно поменять некоторые значения. Думаю, что разберетесь с этим.

Теперь выбираете файл модели, к которой хотите создать слаг. Пусть, к примеру, эта модель называется Post. В этой модели нужно указать пространство имен и создать метод для генерации слага из какого-либо поля таблицы:

use Cviebrock\EloquentSluggable\Sluggable;

class Post extends Model
{
    use Sluggable;

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


Как видно выше, то слаг будет создан из поля title (заголовок). Но это еще не все. Ведь нам нужно этот слаг где-то хранить. А для этого нужно создать новое поле в таблице posts (мы ведь помним, что название таблицы - это множественное от названия модели).

Рассмотрим вариант, когда таблица у вас уже существует и необходимо создать новую миграцию, для добавления одного поля:

php artisan make:migration AddCollumnSlugPosts

После чего будет создана новая миграция и вы увидите в командной строке, что-то наподобие этого:

Created Migration: 2017_09_21_123338_AddCollumnSlugPosts

Внутри созданного файла создаем новое поле:


use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddColumnSlugPosts extends Migration
{
/**
* Add column slug to posts table.
*
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->string('slug')->nullable()->index();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('slug');
});
}
}


И запускаем миграцию:

php artisan migrate

Теперь к вашей таблице posts добавится новое поле slug. Поздравляю! Часть работы выполнена. Да, кстати. Почему поле мы назвали именно slug, а не как-то иначе. Ответ становится понятен из метода модели Post:

public function sluggable()
    {
        return [
            'slug' => [
                'source' => 'title'
            ]
        ];
    }

Ключ массива slug указывает на поле в таблице posts, куда автоматически будет записан слаг, созданный из поля-источника этой же таблицы. В нашем конкретном случае - это поле title. Можно создавать слаг из любого строкового поля. Вам нет нужны создавать слаг вручную. Он будет создаваться сам, автоматически. Надеюсь, это понятно.

Идем дальше. Теперь нас интересует, как для слага создать свой роут (route). Это очень просто - так же само, как и для числового индекса {id}:

// Paths for posts.
Route::get('post', 'PostController@index')->name('post.index');
Route::post('post', 'PostController@store')->name('post.store');
Route::get('post/create', 'PostController@create')->name('post.create');
Route::get('post/{slug}', 'PostController@show')->name('post.show');
Route::patch('post/{slug}', 'PostController@update')->name('post.update');
Route::delete('post/{slug}', 'PostController@destroy')->name('post.destroy');
Route::get('post/{slug}/edit', 'PostController@edit')->name('post.edit');

А в контроллере PostController, например, в методе show, пишем следующее:

/**
* Display the specified resource.
*
* @param string $slug
*
* @return \Illuminate\Http\RedirectResponse
*/
public function show($slug)
{
// If user come from ols link where was id then make 301 redirect.
if (is_numeric($slug)) {
// Get post for slug.
$post = Post::findOrFail($slug);

return Redirect::to(route('post.show', $post->slug), 301);
}

// Get post for slug.
$post = Post::whereSlug($slug)->firstOrFail();

return view('posts.show', [
'post' => $post
]);
}


Вначале мы проверяем, не является ли слаг числом. Зачем мы это делаем?! Часто слаги внедряют в программу уже после того, как был другой механизм построения пути. Например, через числовые индексы. Тогда может получится ситуация, что пользователь, зайдя на сайт по старой ссылке, увидит 404 ошибку, что такой страницы не существует. Ведь мы теперь работаем через слаги. Поэтому, нам необходимо сделать 301 редирект со старой страницы, на новую.

Для этого мы и проверяем полученный аргумент на числовое значение, пытаемся по этому значению получить коллекцию (collection), и если такой пост действительно существует (если нет, то выводим 404 ошибку), то делаем 301 редирект на страницу со слагом.

Если же вы создаете сайт с нуля и сразу используете слаги, то вам такой проверки и редирект делать не нужно.

Построения ссылки в шаблоне тоже очень простое:

href="{!! route('post.show', $post->slug) !!}"
class="btn btn-info">ReadMore

В принципе, это и все!


Как видите, сложности нет вообще никакой, а использование слагов очень просто и удобно для пользователей и поисковиков.


1

Комментарии (12):

alik писал(а):
Здравствуйте Кирилл! Со слагами все получилось.Спасибо. Слаги генерируются, но почему то на латинице хотя в поле источнике идет кириллица. Например в источнике - 'Продтовары' -а в поле slug- prodtovary(при этом в источнике с Большой буквы -'П' , а генерируется в слагах с маленькой- 'p'). Если есть возможность подскажите как сделать чтобы слаги формировались один к одному, хотя бы по графике)
Здравствуйте. Все правильно. Согласно правилам SEO слаги должны быть только на латинице и только в нижнем регистре. Кириллица очень плохо для СЕО, а заглавные буквы в урлах вообще плохая идея. И согласно лучшим рекомендациям, пакет все приводит к транслитерации и нижнему регистру.

Если же вы решили все таки делать слаги на кириллице, то тогда это можно делать и без пакета. Для этого нужно вырезать все служебные символы из заголовка и заменить пробелы знаком дефиса.
Здравствуйте Кирилл! Со слагами все получилось.Спасибо. Слаги генерируются, но почему то на латинице хотя в поле источнике идет кириллица. Например в источнике - 'Продтовары' -а  в поле slug- prodtovary(при этом в источнике с Большой буквы -'П' , а генерируется в слагах с маленькой- 'p'). Если есть возможность подскажите как сделать чтобы слаги формировались один к одному, хотя бы по графике)
alik писал(а):
Или задам вопрос по другому: - слово 'source' в данном конкретном случае не является спец словом определенным внутри самого фреймворка для слагов?
Это слово является служебным и должно использоваться, как указано в документации. Так как при работе пакета со слагами он будет получать данные из ассоциативного массива, где ожидает по ключу source увидеть поле из которого должен сформироваться слаг.
  Спасибо Кирилл! Я только не понял  получается вместо слово  ' source '  можно вписать любое другое слово например 'isto4nik' и код будет работать? Или задам вопрос по другому: - слово  'source'  в данном конкретном случае не является  спец словом определенным внутри самого фреймворка для слагов?
alik писал(а):
Здравтвуйте У Вас есть блок - public function sluggable() { return [ 'slug' => [ 'source' => 'title' ] ]; помогите разобраться что это за 'source' и как он согласуется с остальным кодом, Заранее спасибо. и если ответите ответ те пожалуйста на самом сайте. Заранее спасибо
Поскольку для работы со слагами мы используем готовый пакет https://github.com/cviebrock/eloquent-sluggable То и создание слага происходит согласно документации этого пакета (раздел

Updating your Eloquent Models).

Суть такова, что мы подключаем в классе модели трэит Sluggable:

use Sluggable;

в котором описан абстрактный метод sluggable() который должен вернуть массив. Поскольку метод абстрактный:

/**
* Return the sluggable configuration array for this model.
*
* @return array
*/
abstract public function sluggable(): array;

то мы не можем его заюзать напрямую, а обязаны переопределить (реализовать). Таким образом достигается автоматическая генерация слага и запись его в базу (благодаря логике пакета).

Теперь непосредственно о самом массиве. Запись вида:

[
  'slug' =>
[
'source' => 'title' ] ]

говорит нам, что мы создаем в нашей таблице модели запись для поля slug, которое должно быть сформировано из источника source, которым в нашем примере является поле title

При сохранении модели с полем title="Привет мир" будет сформирован слаг (название поля в таблице/модели slug) из этого поля ('source' => 'title'): privet-mir

Надеюсь, я прояснил для вас этот момент. Если не поняли, то спрашивайте. Попробую объяснить по другому.

 Здравтвуйте 
У Вас есть блок -

public function sluggable()

    {

        return [

            'slug' => [

                'source' => 'title'

            ]

        ];

помогите разобраться что это за  'source' и как он согласуется с остальным кодом, Заранее спасибо. и  если ответите ответ те пожалуйста на самом сайте. Заранее спасибо

George писал(а):
Устанавливаю через composer, пишет то что на картинке, пробовал менять цифры вместо 4.3 написал 5.4, вот тогда и выдала консоль что надо php 5.4
В ошибке написано, что пакет eloquent-sluggable версии 4.3 не совместим с вашей версией php 5.6
А в требованиях к пакету стоит php от 7.0

Устанавливаю через composer, пишет то что на картинке, пробовал менять цифры вместо 4.3 написал 5.4, вот тогда и выдала консоль что надо php 5.4

George писал(а):
Обидно, что не хочет работать с версией php выше 5.4. Есть решения для 5.6 ?
Вообще-то - это решение для php от 7.0 и выше.
Обидно, что не хочет работать с версией php выше 5.4. Есть решения для 5.6 ?
helldar wrote:
А не проще при создании записи сразу создавать слаг: `'slug' => str_slug($title)` ? К тому же, фича работает не только в версиях Ларки 5.3 и старше ;) Хоть они и устарели, отрицать этот факт не стоит.
Привет. Конечно проще, но не совсем правильно, так как может произойти так, что будут два разных поста, но с одинаковыми заголовками. Тогда придется делать проверку на уникальность записи и новую запись делать отличной от предыдущей. А так, пакет сам на себя берет эту заботу. Плюс еще ряд нюансов, которые берет на себя пакет. Ну и в настройках пакета можно разные правила указывать. В итоге получается, что использовать готовое решение проще, чем самому возится с этим.
А не проще при создании записи сразу создавать слаг: `'slug' => str_slug($title)` ?
К тому же, фича работает не только в версиях Ларки 5.3 и старше ;) Хоть они и устарели, отрицать этот факт не стоит.