【Ruby on Rails】sorceryを使用したログインで、ユーザーネーム・emailのどちらでもログインできるようにする
Deviseでの方法はいくらでもあるのですけど、sorceryについてはなかなか見つからなかったもので。
Rails version 4.2.5
Ruby version 2.3.0-p0 (x86_64-linux)
sorcery (0.9.1)
前提:公式のこちらのチュートリアルに沿って、emailでのログインは可能になっていること。つまり途中から導入する場合ということになります。
マイグレーションファイルの編集
db/migrate/****_sorcery_core.rbにて
class SorceryCore < ActiveRecord::Migration def change create_table :users do |t| t.string :email, :null => false t.string :username, :null => false # これを追加 t.string :crypted_password t.string :salt t.timestamps end add_index :users, :email, unique: true add_index :users, :username, unique: true # これを追加 end end
emailの他に、今回はusername
というカラムを作ります。
そして、rake db:migrate:reset
を行いました。もうすでにDBにユーザーを作っていたのですが、それにはusername
を設定していなかったので、リセットを行うことにしました。
設定ファイルの編集
config/initializers/sorcery.rb
にて
# --- user config --- config.user_config do |user| # -- core -- # specify username attributes, for example: [:username, :email]. # Default: `[:email]` # user.username_attribute_names = [:username, :email]
user.username_attribute_names
に:email
だけでなく、:username
も追加します。
Strong Parametersへの追加
app/controllers/users_controller.rb
def user_params params.require(:user).permit(:username, :email, :password, :password_confirmation) end
User modelにバリデーション追加
app/models/user.rb
validates :email, :username, presence: true . . validates :username, uniqueness: true
:username
にpresence: true
とuniqueness: true
のバリデーションをかけます。
ログインフォームの編集
ユーザーネーム用・email用のフォームを一つにまとめたい。
どうすればいいのかなあ、と思って試行錯誤したのですが、以下の方法にしました。
app/views/user_sessions/_form.html.erb
ログイン画面にてemail用のフィールドの代わりに、下記のコードに置き換えます。
<div class="field"> <%= label_tag :login %><br /> <%= text_field_tag :login, nil, placeholder: 'username or email' %> </div>
params[:login]というのをコントローラーに渡すようにしておきます。ここにユーザーネーム、emailのどちらかが入ることになります。
セッション用コントローラーの編集
app/controllers/user_sessions_contoller.rb
def create # params[:login] is username or email if @user = login(params[:login], params[:password], params[:remember_me]) redirect_back_or_to(:users, notice: 'Login successfully') else flash.now[:alert] = 'Login failed' render :new end end
デフォルトではlogin
メソッドの第1引数にparams[:email]
を渡していましたが、ここではparams[:login]
を渡します。
Processing by UserSessionsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"****", "login"=>"user1", "password"=>"[FILTERED]", "commit"=>"Login", "remember_me"=>"on"} User Load (1.9ms) SELECT "users".* FROM "users" WHERE ("users"."username" = 'user1' OR "users"."email" = 'user1') ORDER BY "users"."id" ASC LIMIT 1
フィールドにuser1
というユーザーネームでログインしたところをconsoleで見てみます。
usersテーブルのusername
かemail
のどちらかにuser1
があるかどうか、検索してくれていますね。
Processing by UserSessionsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"****", "login"=>"test@example.com", "password"=>"[FILTERED]", "commit"=>"Login", "remember_me"=>"on"} User Load (0.5ms) SELECT "users".* FROM "users" WHERE ("users"."username" = 'test@example.com' OR "users"."email" = 'test@example.com') ORDER BY "users"."id" ASC LIMIT 1
今度はtest@example.com
でログインしたところ。こちらでもOR
検索してくれています。
その他
- ユーザーネームも必須にしたので、ユーザー登録画面(app/views/users/_form.html.erb)に、
username
用のフォームを追加する。 - ユーザーの一覧・詳細画面にもユーザーネームを表示する
ということをしました。
参考
こちらの記事では、emailからユーザーネームでのログインに置き換える、ということをしています。
(追記)
うーん、記事を投稿してすぐに気がついたのですけど、もしユーザーネームのほうにふざけてuser@example.com
などと、emailと同じ形式で登録してしまうと、他のユーザーのemail
とかぶってしまうことが無きにしもあらず、のような気がしました。
ユーザーネームには@
, .
(+
も?)などは使えないようにバリデーションをかける必要があるかもです。
(さらに追記)
こう・・・かな?
app/models/user.rb
validates :username, format: { with: /\A\w+\z/ }
単語、数字、アンダースコアだけに制限すると。