Railsで作ったサイトURLの一部を任意の文字列に設定する方法を紹介します。

RailsでWordPressみたいなことをやろうとすると気になるのがURLの文字列です。
WordPressでは標準でURLの一部を好きな文字列に設定できます。この変更可能な文字列をスラッグといいます。
主なメリットは記事の内容がURLだけなんとなくわかることです。管理もしやすいし、SEOにも影響があるようです。

Railsが生成するURLは標準だとidが使われます。
/posts/1
みたいな感じです。これを
/posts/custom-slug/
にしたいと言うのがこの記事の主旨です。

環境・背景など

Rails6で検証した内容になっていますが、おそらく5でも同じようにできると思います。
ブログの記事内容とタイトルカラムを持つPostモデルのスラッグを変更します。

routes.rbファイルの編集

普通にresourcesで書くと以下のような感じになります。

  resources :posts  
#普通に定義するとこんなURLになる=>/posts/:id

これをこうします。

    resources :posts,  param: :slug,  path: '/posts/'
  #こんなURLになる=>/posts/:slug

ターミナルでrails routesして確認すると変わっているのがわかると思います。
これでroutesの設定は完了です。

モデルにslugカラムを追加

以下のようなmigrationファイルを作成して、Postモデルにslugカラムを追加します

class AddslugtoPosts < ActiveRecord::Migration[6.0]
  def change
    add_column :posts, :slug, :string, nil:false
  end
end

忘れずにrails db:migrateしてください。

controllerの設定

params[:id]で取得していた部分をparams[:slug]で取得できるように変更します。

def show
#@post = Post.find(params[:id]) デフォルトはこんな感じで取得してるはず
  @post = Post.find_by(slug: params[:slug]) #こうするとslugがparams[:slug]のものを取得できる
end

※update、edit、destroyなども同様に変更する必要があります。
なので一つにまとめてこう書くのがおすすめです。

  #PostsController
  before_action :set_post, only: [:edit, :update, :destroy,:show]
  def show
  end
  #途中省略
  private
  def set_post
    @post = Post.find_by(slug:params[:slug])
  end

こうすることでedit, update, destroy,showで毎回@postを宣言する必要がなくなります。

Viewの設定

link_toの書き方を少し変える必要があります。
以下のように変えるとaタグのhrefにidではなくslugが入るようになります。

  #index.html.erbなど
  #今までの書き方
  <% @posts.each do |post| %>
    <%= link_to "続きを読む",post_path(post.id) %>
  <% end %>

 #変更後
  <% @posts.each do |post| %>
    <%= link_to "続きを読む",post_path(slug:post.slug) %>
  <% end %>

これでリクエスト時にparams[:slug]に値が渡ります。

おわりに

あとは記事作成フォームにslugを入力する欄を作成し、好きな文字を入れてPostするだけです。

#フォーム内に以下のような感じでslugのフィールドを追加
 <%= f.text_field :slug %>

Railsに慣れてなくてどこが何をやっているのか把握できていない場合は、従来の[:id]で動くものを作ってから本記事の通りにリプレイスしていくのがおすすめです。