showやeditなど、member系のアクションに、数値のIDなどではなく、TwitterIDのような一意の文字列でアクセスする方法。
例えば、
http://your.app.com/users/400
でなく
http://your.app.com/users/bob-smith
でアクセスできるようにする。
単純にURLのかっこ良さが上がる気がするし、文字列でのアクセスのみ受け入れるようにしたら、サイトの持っている情報のスクレイピング対策にもなる。
例として、適用するモデル名をUser
、urlに使用するカラム名をcanonical_name
とする。ここではid
とcanonical_name
のどちらでもアクセスできるようにする例を書く。
モデル側のコード
class User < ActiveRecord::Base
validates :canonical_name,
uniqueness: { case_sensitive: false },
format: { with: /^[A-Za-z][\w-]*$/ },
length: { minimum: 3, maximum: 25 }
def to_param
canonical_name ? canonical_name : id.to_s
end
def self.find_by_canonical_name_or_id(arg)
find_by_canonical_name(arg) || find(arg)
end
end
to_param
というActiveRecordのメソッドが肝。これで通常の表示がid
からcanonical_name
に切り替わる。
validationは大体こんな感じにすると良いと思う。ここでは大文字小文字が違うだけのcanonical_name
は許さないようにしているが、そもそも大文字を禁止するのもありだと思う。
なお、ここではcanonical_name
が設定されていないUser
もいることを前提としている。
コントローラー側のコード
class UsersController < ApplicationController
before_filter :load_user, only: [:show, :edit, :update, :destroy]
## if you use cancan
# load_and_authorize_resource :user, find_by: 'find_by_canonical_name_or_id'
private
def load_user
@user = User.find_by_canonical_name_or_id(params[:id])
end
end
数字でなくてもidの情報はparams[:id]
に入って渡される。
canonical_name
でしかアクセスできないようにしたい時はload_user
でUser.find_by_canonical_name
を使えば良い。
別案
探すときに、find_by_canonical_name_or_id
というのを使うとto_paramを変更しているクラスだけ、find
でModelを探せなくなってしまう。これだと困る場合は、
def self.find(arg)
find_by_canonical_name(arg) || super
end
とfindを上書きしてしまえばいいかもしれない。
追記
https://gist.github.com/agnellvj/1209733 にもっといろんな方法が載っているのを発見。興味ある人は見てみるべし。