概要
SEO対策の一環として、RailsアプリケーションのURLに含まれるid
を固有名詞に置き換える実装を行いました。
例えば、以下のようなケースです。
# これを
https://spocale.com/fruit/1
# 以下の形式にしたい。
https://spocale.com/fruit/apple
フルーツに関する一覧ページがあり、そこから「りんご」を選択して詳細ページに飛ぶ場合を想定します。このとき、URLのid
部分をapple
という固有の文字列に変えたいとします。
ただし、既存のURLを突然変更すると、ブックマークなどで保存されている従来のURLが使えなくなる可能性があります。そこで、id付きの従来のURLも維持しつつ、新しい形式のURLも利用できるように実装しました。この記事では、その過程で直面した課題と解決方法についてまとめます。
対象読者
- Railsアプリケーションでid付きのURLを固有名詞で表示する方法を知りたい方
- 既存のURL形式を維持しつつ、新しいURL形式を導入したいと考えている方
実装内容
to_param
メソッドの定義
まず、モデル内でto_param
メソッドをオーバーライドし、URL生成時にid
以外の値を使うように定義します。今回は、Fruit
モデルにtype_string
というURLで使用する文字列を保存するカラムがあるとします。
# app/models/fruit.rb
class Fruit < ActiveRecord::Base
# to_paramをオーバーライドしてtype_stringを返すようにする
def to_param
type_string.present? ? type_string : id.to_s
end
end
ポイントは三項演算子で条件分岐をしている部分です。
type_string.present? ? type_string : id.to_s
このように書くことで、type_string
が存在する場合にはその値を、存在しない場合には従来のid
を使ったURLを生成することができます。これによって、新しい形式のURLと既存のid
形式のURLの両方に対応が可能となります。
コントローラーの修正
従来はパラメーターでid
が渡ってきていましたが、今回の変更により、URLの一部が文字列に変わるため、パラメーターの取得ロジックを修正する必要があります。このとき、文字列でパラメーターが渡ってくる場合に、それをid
に変換する処理をbefore_action
で定義することで、既存のコントローラーのロジックを大きく変更せずに対応できます。
class FruitsController < ApplicationController
# 追加
before_action :set_params_fruit_id, only: [:show]
# 既存のコード
def show
@fruit = Fruit.find_by(id: params[:fruit_id])
end
private
# 追加
def set_params_fruit_id
return true if params[:fruit_id].to_i.to_s == params[:fruit_id]
params[:fruit_id] = Sport.find_by(name: params[:fruit_id])&.id
end
end
従来通り、パラメーターでid
が渡ってきた場合はreturn
で処理を終了し、type_string
の文字列が渡ってきた場合には、それをid
に変換しています。
詰まった部分
ここまでの修正を行い、URLが変わるか試してみましたが、従来通りid
付きのURL
しか生成されず、そこで詰まってしまいました。特にエラーも表示されないため悩んでいましたが、URL生成部分に問題があることに気づきました。
<%= link_to "詳細", fruit_path(@fruit.id) %>
このように、link_to
やredirect_to
の中で手動でid
を指定してURLを生成している場合、to_param
は呼び出されず、to_param
が適用されるのは、Railsのパスヘルパー(例: fruit_path(@fruit)
)を使ってURLを生成する場面のようです。
そのため、以下のように_pathヘルパー
を適切に使うように修正しました。
<%= link_to "詳細", fruit_path(@fruit) %>
終わりに
今回は、Railsアプリケーションでid
を固有の文字列に置き換える実装の方法をまとめました。to_param
メソッドを活用することで、既存のid
形式のURLを維持しつつ、新しいURL形式にも対応できるような実装が可能です。この記事がお役に立てば幸いです。
参考