Posted at

【Rails】URLでidの代わりにランダムな文字列を表示させる


Introduction

Railsのscaffoldを使うと、デフォルトで/users/:id/のようにidを使ったURLが生成されます。

しかしながら、システムでシーケンシャルに払い出していて主キーとして使っているidをURLというユーザーが見えるところで使うのって少し抵抗ありません?

ということでURLにはidではなくランダムな文字列(Qiitaの記事はそうなってますね)が表示されるようにする方法をメモります。


Environment


  • Rails 5.2.2

  • Ruby 2.6.3


1. public_uid gemをインストール

便利そうなgemを作っている方がいらっしゃいました。データをDBに登録するときに、ランダムな文字列を生成してくれるgemです。デフォルトだとHexString(16進数)で生成されます。

GitHub - equivalent/public_uid: Automatically generates random unique public id for record.

今回はこちらを使ってみます。


Gemfile

gem 'public_uid'


$ bundle install

今回は、Userモデルをつくろうとしている前提で物語を進めていきます。


2. public_uidカラムを追加

対象のモデルにpublic_uidカラムを追加していきます。


2-1. Userモデルをこれから作る場合

$ rails g scaffold User name:string public_uid:string

でscaffoldが作成されます。この中にマイグレーションファイルも含まれますが、public_uidは検索対象のカラムになるので検索速度をあげるためにindexをはりました。


/db/migrate/YYYYMMDDhhmmss_create_users.rb

class CreateUsers < ActiveRecord::Migration[5.2]

def change
create_table :users do |t|
t.string :name, null: false
t.string :public_uid, null: false

t.timestamps null: false
end
add_index :public_uid, unique: true
end
end


マイグレーションを適用します。

$ rails db:migrate


2-2. Userモデルがすでに存在する場合

$ rails g migration AddColumnPublicUidToUsers public_uid:string

マイグレーションファイルに検索性能をあげるためにpublic_uidへのindexをはります。


/db/migrate/YYYYMMDDhhmmss_add_column_public_uid_to_users.rb

class AddColumnPublicUidToUsers < ActiveRecord::Migration[5.2]

def change
add_column :users, :public_uid, :string
add_index :users, :public_uid, unique: true
end
end

マイグレーションを適用します。

bash

$ rails db:migrate


3. Userモデルにpublic_uidを自動生成するコードを付与

対象のモデルファイルにgenerate_public_uidというコードを1行追加するだけです。これによってモデルがDBに登録されるタイミングで自動的にpublic_uidに8桁のランダムな文字列が付与されるようになります。


/app/models/user.rb

class User < ApplicationRecord

generate_public_uid
end


4. to_paramメソッドをオーバーライド

to_paramメソッドを使ってURLの:id:public_uidに変更します。


/app/models/user.rb

class User < ApplicationRecord

generate_public_uid

def to_param
public_uid
end
end



5. コントローラ側でpublic_uidで検索

scaffoldでモデルを作成すると、コントローラファイルで以下のようにモデル検索がなされていると思います。


/app/controllers/users_controller.rb

private

def set_user
@user = User.find(params[:id])
end

to_paramをオーバーライドしたため、params[:id]にはpublic_uidの値が入るようになっており、findメソッドでは該当のidを持つUserモデルのレコードが存在しないためエラーになってしまいます。

そこで、以下のように編集してpublic_uidでUserモデルを検索するように変更します。


/app/controllers/users_controller.rb

private

def set_user
@user = User.find_by(public_uid: params[:id])
end

以上で、URLにidの代わりにランダムな文字列を表示させることができます。


おまけ


おまけ 1:ランダムな文字列でなくランダムな数字にしたい

public_uid gemでは、ランダムな数字を生成する仕組みとして、NumberRandomNumberSecureRandomという設定を用意してくれています。設定の方法は3で記載したgenerate_public_uidgeneratorを指定してあげるだけです。


/app/models/user.rb

class User < ApplicationRecord

# NumberRandomの場合
generate_public_uid generator: PublicUid::Generators::NumberRandom.new
# Number SecureRandomの場合
generate_public_uid generator: PublicUid::Generators::NumberSecureRandom.new
end


おまけ 2:public_uidの文字数を指定したい

8文字だとすぐ枯渇しかねないモデルの場合はpublic_uidの文字数を増やしたくなります。この場合も、generatorで指定してあげるだけです。例えば20文字を生成したい場合は以下の通りです。


/app/models/user.rb

class User < ApplicationRecord

generate_public_uid generator: PublicUid::Generators::HexStringSecureRandom.new(20)
end


Conclusion

便利。

他にも、全くランダムな文字列(a-z,A-Z,0-9)にする方法や、そもそもidをランダム値にする方法などもあるみたいです。詳しくはgemのREADMEをご覧いただければと思います。

私は、ここまでで満足!


Reference