今日はユーザーの登録機能を作りました。
いま僕が作っているお店の口コミサイトですが、基本的にユーザーログインしなくても口コミが出来るようにしています。(ログインしなきゃ口コミ出来ないなんて投稿に躊躇するからね)
でもユーザー登録&ログインしても使えるようにする予定です。
それでユーザーの登録、編集機能を付与する事にしました。
Railsの標準的な流れに従ってまずはモデルを作ります。モデルを作ったらルーター→コントローラー→ビューの順です。
userモデルを作る
以下のカラムを持たせています。
カラム | 型 | 内容 |
---|---|---|
nickname | string | ニックネーム |
string | メールアドレス | |
password_digest | string | パスワード |
image | string | プロフィール画像のURL |
introduce | string | 自己紹介文 |
age | tinyint | 年齢 |
sex | string | 性別 |
address | string | 住所 |
既にニックネームとメールアドレスとパスワードとプロフィール画像はカラムを作っていたので、後から残り4つのカラムを追加しました。
Railsでモデルにカラムを追加する
コンソールから生成
$ rails g migration AddAgeToUsers age:integer
$ rails g migration AddSexToUsers sex:string
$ rails g migration AddAddressToUsers address:string
構文は
rails g migration Addカラム名Toテーブル名 カラム名:型
です。カラム複数を同時に追加する事は出来ないようで、1個ずつ生成しました。
tinyintを指定するには、上記のように事前にintegerでマイグレーションファイルを生成しておいて、その後マイグレーションファイルを修正する必要があります。
t.integer :age #↓これをこのように指定するとtinyintになりました。 t.integer :age,:limit => 1
マイグレーションファイルを生成したら、migrateを実行。
ちゃんとテーブルにカラムが追加されているか確認
$ mysql -u root
mysql> show databases;
mysql> USE DB名_development;
mysql> show tables;
mysql> describe users;
Userクラスにバリデーションを入れる
入力されたユーザー情報の検証です。
class User < ApplicationRecord before_save { self.email.downcase! } validates :nickname, presence: true, length: { maximum: 50 } validates :introduce, length: { maximum: 255 } validates :address, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 }, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }, uniqueness: { case_sensitive: false } end
Userのストロングパラメーター
ストロングパラメーターはユーザー登録画面からPOSTされた情報のセキュリティのために実施。Railsの一般的なやり方。
private #ストロングパラメーター def user_params params.require(:user).permit(:nickname, :email, :password, :password_confirmation, :image, :introduce, :age, :sex, :address) end
パスワード暗号化のためのGemを追加
こちらの記事→password_digest:が暗号化されない問題
で色々失敗しながらもなんとか暗号化できました。
これでUserモデル関連の実装が完了!
User関連のルーティングを作成
以下のようにルーティングを作成。(routes.rb)
get 'signup', to: 'users#new' resources :users, only: [:index, :show, :create, :edit, :update]
ユーザー登録画面はsignupというURLで行うようにしました。
rails routesで見ると以下のようになりました。
users GET /users(.:format) users#index
POST /users(.:format) users#create
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
まぁよくあるユーザー機能でしょうか。
現時点ではdestroy(ユーザーの退会処理)は考えていません。
またindex(ユーザーの一覧表示画面)これもあるだけで実装は考えていません。
Userコントローラーの作成
これでapp/controllers/users_controller.rbと、app/views/users/配下にビューファイルが生成されました。
それぞれのメソッド(アクション)を実装していきます。
ユーザー登録機能 users#newアクションとnew.html.erb
users#newアクションの実装
新規ユーザー登録ボタンを押したら、このnewメソッドに来て、new.html.erb(ユーザ登録画面)に遷移していきます。
users_controller.rbのnewメソッドはユーザーのインスタンスを生成。
def new @user = User.new end
new.html.erbの実装
ユーザー登録画面(new.html.erb)はbootstrapのCSSを使用してちょっとデザインしつつ。
ニックネームとメールアドレスとパスワードを入力させます。プロフィール画像とかはログイン後の編集画面で登録できるようになります。
<%= form_for(@user) do |f| %> <div class="form-group"> <%= f.label :nickname, 'ニックネーム' %> <%= f.text_field :nickname, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :email, 'メールアドレス' %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password, 'パスワード' %> <%= f.password_field :password, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password_confirmation, 'パスワード(もう1度ご入力ください)' %> <%= f.password_field :password_confirmation, class: 'form-control' %> </div> <%= f.submit '登録する', class: 'btn btn-primary btn-block' %> <% end %>
これで以下の画面を出しています。
users#createアクション
ユーザー登録画面からPOSTされた情報を受け取るメソッドの実装です。
users_controller.rbのcreateメソッドを作っていきます。
def create @user = User.new(user_params) #user_paramsはPOSTデータをチェックするメソッド if @user.save flash[:success] = '新しいユーザーを登録しました。' redirect_to @user else flash.now[:danger] = 'ユーザーの登録に失敗しました。' render :new end end
これでユーザーの登録処理が出来ました。
実際にデータベース内を覗いてみるとちゃんと作成したユーザーが存在している事を確認できます。
> User.all
ユーザー情報表示機能
これは何のことはなく、users#showアクションとshow.html.erbを実装するだけでした。
/users/:id
このようなURLでアクセスしてきたらユーザーの情報を表示したいと思います。
コントローラーのメソッドは以下のように、URLから取ってきたidでuserテーブルを探してインスタンスを生成。
def show @user = User.find(params[:id]) end
show.html.erbでは、そのユーザー情報を画面に表示するだけ。
<div class="row"> <aside class="col-md-4"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"><%= @user.nickname %></h3> </div> <div class="panel-body al-c"> <% if @user.image? %> <%= image_tag @user.image, :class => "mw50" %> <% else %> <%= image_tag("/images/icon/icon-mama-02-160px.png") %> <% end %> </div> </div> </aside> <div class="col-md-8"> <p><span class="orange b"><%= @user.nickname %></span> <%= @user.live %> <% if @user.age.present? %> <%= @user.age %>代 <% end %> <%= @user.sex %></p> <p class="orange b">自己紹介</p> <p><%= @user.intro %></p> </div> </div>
この中では画像があれば表示し、無ければ自前のno-image画像を表示しています。
自前のCSSを利用しているのでこれで見栄え良く(?)できました。
個人的にはbootstrapのcssに使い慣れて無くあまり見栄えも良くない気がしています(; ・`ω・´)
ユーザー情報表示画面はいかのようになりました。
ユーザープロフィール編集機能・画面の作成
まずはusers#editアクションとedit.html.erbとusers#updateアクションの実装です。
users#editアクションの実装
users#editではやはりURLからidのパラメータを取ってきてuserインスタンスを生成し、それをedit.html.erbへ持ち回ります。
def edit @user = User.find(params[:id]) end
プロフィール編集画面の実装
簡単な部分を先に実装して現時点では以下のような画面が出ています。
ここにパスワードや性別、年齢の入力もできるようにします。
取り急ぎパスワード部分を先に実装!
edit.html.erbの実装
<div class="form-group"> <%= f.label :nickname, 'ニックネーム' %> <%= f.text_field :nickname, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :email, 'メールアドレス' %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="row"> <div class="col-xs-6 form-group"> <%= f.label :age, '年齢', class:'control-label' %> <%= f.select :age, [["",""], ["10代","10"], ["20代","20"], ["30代","30"], ["40代","40"], ["50代","50"], ["60代","60"], ["70代以上","70"] ], {}, class: 'form-control' %> </div> <div class="col-xs-6 form-group"> <%= f.label :sex, '性別', class:'control-label' %> <%= f.select :sex, [["",""], ["女性","女性"], ["男性","男性"]], {}, class: 'form-control' %> </div> </div> <div class="form-group"> <%= f.label :live, '地域(県や市など)' %> <%= f.text_field :live, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :intro, '自己紹介(255文字未満)' %> <%= f.text_area :intro, :maxlength => "255", :size => "80x5", class: 'form-control' %> </div> <div class="accbox"> <label for="label1">パスワードを変更するかたはこちら</label> <input type="checkbox" id="label1" class="cssacc" /> <div class="accshow"> <div class="form-group"> 新しいパスワード<br> <%= f.password_field :password, class: 'form-control' %> </div> <div class="form-group"> 新しいパスワードを再入力<br> <%= f.password_field :password_confirmation, class: 'form-control' %> </div> </div> </div> </div> <div class="al-c"><%= f.submit 'プロフィール編集を完了する', class: 'btn btn-primary' %></div>
これで実装しました。年齢や性別は必須じゃないので空白も作っています。
またパスワードもこの画面から変更できるようにしていますが、アコーディオンボックスを使って最初は見せないようにして、クリックしたら開くようにしています。
なんでパスワード部分をこのようにしたかというと、最初はパスワード入力欄に[●●●●●●●]みたいな感じで表示をさせたかったのです。
けど、初期値をパスワード欄に表示させる事が出来なかったからです。userのpassword_digestは暗号化されていてシステム管理者からも復元できないから表示できません。
で、初期値を表示することができないのでパスワード入力欄は空欄になってしまいます。これをユーザーが見た時に、「パスワードが空やんけ」と勘違いして再入力させてしまう事を懸念しました。
それで、パスワード入力部分は最初は隠すことにして、回避したわけです。
性別、年齢、地域を追加し、ここまででプロフィール編集画面はできました。
プロフィール画像のアップロード機能は次回にします。
users#updateアクションの実装
プロフィール編集画面からPOSTされたデータを受け取るのがこのメソッドです。ここでデータベースの更新を行います。
def update @user = User.find(params[:id]) #編集しようとしてるユーザーがログインユーザーとイコールかをチェック if current_user == @user if @user.update(user_params) flash[:success] = 'ユーザー情報を編集しました。' render :edit else flash.now[:danger] = 'ユーザー情報の編集に失敗しました。' render :edit end else redirect_to root_url end end
current_userメソッドはセッションを扱うメソッドで別途用意しています(割愛)。
ちゃんと更新ができたので、ここまででプロフィールの編集が出来るようになりました。