ActiveModel のメソッドを使って、
- URL に含まれるモデルの id を別の文字列にしたい
- 作成済みのリソースの数がばれないように、HTML 中に生の id を表示したくない
みたいなときに何とかする方法の紹介。
今回は id の代わりに、id を Base64 エンコードしたものを URL や HTML 中に含めるようにしてみます。
準備
まずは適当なリソースを scaffold で作成。
bin/rails g scaffold posts title:string body:text
リソースを create した後で /posts/1/edit に移動し、HTML のソースを見るとこんな感じ。
to_key
ここで Post の to_key
を上書きする。
to_key
はリソースを一意に特定するキーを含んだ Array を返す。そいつが dom_id
によって利用される。dom_id ってのによって、fomr_for などで埋め込まれる id が変えられている、らしい。。。
今回は to_key
で Base64 エンコードした id を含む配列を返す。ちなみに ActiveRecord のデフォルトでは、to_key
はリソースの id を含む Array を返す。
class Post < ActiveRecord::Base
def to_key
[Base64.encode64(id.to_s)]
end
end
こうすると to_key の返り値が以下のように変わる。
# これが
Post.first.to_key
=> [1]
# こうなる
Post.first.to_key
=> ["MQ==\n"]
form の id に埋め込まれる Post の id も変わった!
to_param
しかしパスに含まれる id はまだ生の id のまま。こちらは to_param
で変更することが可能。
to_param
はリソースのユニークな URL を発行するために使われる。post_path(@post)
とやると @post
に対して to_param
が呼び出されて、その結果が URL に含まれる。ちなみに ActiveRecord の場合、デフォルトでは id が返ってくる。
ということで以下のように変更。
class Post < ActiveRecord::Base
def to_key
[Base64.encode64(id.to_s)]
end
def to_param
Base64.encode64(id.to_s)
end
end
するとこんな感じになる。
# これが
app.edit_post_path(Post.first)
=> "/posts/1/edit"
# こうなる
app.edit_post_path(Post.first)
=> "/posts/MQ==%0A/edit"
URL と HTML のソースに含まれるパスに含まれる id もエンコードされた!
controller の更新
ただ、このままだと Update Post
ボタンをおした時にうまく動かない。
エラー画面を見れば分かる通り、Post.find(params[:id])
しても params[:id]
が 1 でなく MQ==
なので当たり前。
find に渡す id を先に decode してやるとうまく動く。
def set_post
@post = Post.find(Base64.decode64(params[:id]))
end
Post.find(params[:id])
を Post.find(Base64.decode64(params[:id]))
としてやることで動くようになる。
params[:id] の :id も変える
ただ、params[:id]
に入ってくるのが id をエンコードしたもの、というのが気持ち悪い。params[:encoded_id]
のように params のキーを変えたい。そのため、config/routes.rb
で resources の params オプションを使う。
Rails.application.routes.draw do
resources :posts, param :encoded_id
end
controller 側も忘れずに書き換え。
def set_post
@post = Post.find(Base64.decode64(params[:encoded_id]))
end
はい、params[:encoded_id]
に値が入るようになりました。(ちなみに初めての web console)
補足
実際には Base64 エンコードしただけだとあんまり意味ないかもしれませんが、暗号化するなり、ユーザやタグなどの id の代わりに名前を使って見るなりやると良いんじゃないでしょうか。
なお、to_key も to_param も ActiveModel のメソッドなので、ActiveRecord でなくても使えます。
dom_id なんかは、自分でリソースの id を HTML に埋め込むときにも使えそうですね。