##新規登録とログイン
devise機能が入ってることが前提です。
【例】application.html.erb内に記述するとして
<%= link_to "ログイン", new_user_session_path, class: "XXXX" %>
<%= link_to "新規登録", new_user_registration_path, class: "YYYY" %>
と記述するとログインと新規登録ができるようになります。
##未ログイン時とログイン時でボタンの表示の変更の仕方
未ログイン時とログイン時を分けるにはuser_signed_in?メソッドを使います。
user_signed_in?メソッドはユーザーがサインインしているかどうか検証するメソッドです。
【例】deviseによって設定されるPrefixの一部です
| リクエスト | Prefix | パス |
| devise/sessions#new | new_user_session | /users/sign_in |
| devise/sessions#create | user_session | /users/sign_in |
| devise/sessions#destroy | destroy_user_session | /users/sign_out |
などがあり、実際に使うときはnew_user_session_pathのように_pathを記述します。
【例】
<% if user_signed_in? %>
<%= link_to "ログアウト", destroy_user_session_path, method: :delete, class"YYYY" %>
<% else %>
<%= link_to "ログイン", new_user_session_path, class: "XXXX" %>
<%= link_to "新規登録", new_user_registration_path, class: "YYYY" %>
<% end %>
上記みたいに記述すると最初はログインと新規登録しか表示されないがログインか新規登録するとログアウトの表示に切り替わります。
##devise用のビューを作成します
ターミナル
$ rails g devise:views
をターミナルで実行すると新規登録画面とログイン画面を作成できます。
新規登録画面はapp/views/devise/registrations/new.html.erb
ログイン画面はapp/views/devise/sessions/new.html.erb
です。
##usersテーブルにカラムを追加
例えば、ニックネームを登録するために、usersテーブルにニックネームを保存するためのカラムを追加します。追加したカラムに、サインアップ時に登録するニックネームを保存します。
ターミナル
$ rails g migration Addカラム名To追加先テーブル名 追加するカラム名:型
【例】カラム名をNickname、追加先テーブル名をUsers、追加するカラム名:型をnickname:stringとした場合
$ rails g migration AddNicknameToUsers nickname:string
$ rails db:migrate
追加したら
<% if user_signed_in? %>
<%= link_to "新規投稿", new_post_path, class: "XXXX" %>
<%= link_to "ログアウト", destroy_user_session_path, method: :delete, class: "YYYY" %>
<% else %>
<%= link_to "ログイン", new_user_session_path, class: "XXXX" %>
<%= link_to "新規登録", new_user_registration_path, class: "YYYY" %>
<% end %>
上記見たいに記述し次はアソシエーションを定義します。
##アソシエーションを定義
アソシエーションを定義することにより、モデル間を紐付けることができます。
例えばどのユーザーがどの文章を投稿したかのデータを紐付けられます。
モデル間の紐付けはhas_manyメソッドとbelongs_toメソッドがあります。
has_many
Twitterを思い浮かべてもらうとわかりやすいと思います。
まずUserモデルの視点で考えると、ユーザーの作成した投稿が複数個あるとします。それは1人のユーザーは複数の投稿ができるということです。
userと他のモデルとの間に「1対多」のつながりがあることを示すのがhas_manyメソッドです。
つまりユーザーは一人なので「1対多」の「1」、投稿は複数できるので「1対多」の「多」ということです。
【例】モデル名をPostとしてhas_manyを記述します。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :posts
end
postは複数形になるのでpostsになります。
##belongs_to
has_manyとは逆の考えです。
1つの投稿は、1人のユーザーが投稿したものです。つまり1つの投稿を複数人が投稿できないため、投稿は必ず1人のユーザーになります。
Postモデルと他のモデル(今回はUser)との間に「1対1」のつながりがあることを示すのがbelongs_toメソッドです。
【例】モデル名をPostとしてbelongs_toを記述します。
class Post < ApplicationRecord
belongs_to :user
end
これで、アソシエーションができました。
また違う記事に書こうと思いますが「多対多」もあります。
##投稿保存時にuser_idも登録できるようにするには
まだこのままだと投稿保存時にuser_idも登録できないのでcurrent_userメソッドとmergeメソッドを使います。
##current_userメソッドとはmergeメソッドとは
current_userメソッドはdeviseのヘルパーメソッドで、ログイン中のユーザー情報を取得できます。
ログイン中のユーザーidを取得したい場合、current_user.idと記述することで取得できます。
mergeメソッドを使用すると2つのハッシュを結合することができます。
【例】
hash1 = {title: "おはよう", comment: "今日は晴れです"}
hash2 = {user_id: 3}
puts hash1.merge(hash2)
{title: "おはよう", content: "今日は晴れです",user_id: 3}とハッシュは結合できます。
【例】モデル名がPostでカラム名がtitleとcommentのとき
private
def post_params
params.require(:post).permit(:title, :comment).merge(user_id: current_user.id)
end
private内の記述は上記のようになります。
mergeメソッドで、postというハッシュの中に、user_idというキーを結合して、user_idをpostsテーブルに登録できるようにしました。
user_idは、ログイン中のユーザーidが必要なため、current_user.idと記述することで取得が可能になりました。
##投稿者を登録したいとき
deviseだけだとメールとパスワードしか登ない状態ですので、投稿者も登録したいときはusersテーブルにカラムを追加します。
今回はnicknameカラムを追加します。
ターミナル
$ rails g migration AddNicknameToUsers nickname:string
$ rails db:migrate
でカラムが追加されます。追加したらregistrations/new.html.erbを追記します。
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :nickname %><br />
<%= f.text_field :nickname, autofocus: true %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
〜以下略〜
上記のようにnicknameを追記します。
あとはニックネームを受け取れるようにするにはconfigure_permitted_parametersメソッドを利用します。
##configure_permitted_parameters
deviseでは初期状態でサインアップ時にメールアドレスとパスワードのみを受け取るようにストロングパラメーターが設定してあるので、追加したキー(今回はnickname)のパラメーターは許可されてないので許可してあげます。
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
end
end
上記のように追記します。
これにより、nicknameというキーに対しても保存が許可がされ、テーブルに名前の情報を保存できるようになります。
あとはビューファイルの編集ですがpost.user.nicknameと記述することで、その投稿に紐づいたユーザーの投稿者を表示することができます。
しかしこのままだとN+1問題が起こります。
##N+1問題
アソシエーションを組んだことによって、データを1度呼び出し、さらにそれに紐付く別テーブルのデータを呼ぶ際に、紐付くデータを呼ぶためのSQLを毎回発行してしまう状態のことをN+1問題と言います。
簡単にいうと処理が重くなってしまうので軽くしようということです。
今回の場合はindexアクションで全投稿を取得し、アソシエーションを利用して全投稿に紐付くユーザー情報を呼び出しています。
そこで使うのがincludesメソッドです。
includesメソッドは指定された関連モデルをまとめて一緒に取得しておくことで、SQLの発行回数を減らすことができます。
includesメソッドはモデル名.includes(:紐付くモデル名)です。
モデルはPostの場合、controller.rb内のindexの@posts = Post.allを編集します。
class PostsController < ApplicationController
def index
@posts = Post.includes(:user)
end
〜以下略〜
これでN+1問題が解決しました。
次にルーティングに追記します。
マイページを表示する際にはusersコントローラーのshowアクションを動かします。
Rails.application.routes.draw do
devise_for :users
root to: 'posts#index'
resources :posts, except: :index
resources :users, only: :show
end
コントローラー内でparams[:id]と記述すれば/users/:idの:id部分の情報を使用することができます。
なのでターミナルで
$ rails g controller users
以下が新規作成されるファイルです。
app/controllers/users_controller.rb
test/controllers/users_controller_test.rb
app/helpers/users_helper.rb
app/assets/javascripts/users.js.coffee
app/assets/stylesheets/users.css.scss
作成したusers_controller.rbにshowアクションを定義します。
ビューに情報を受け渡す際にはインスタンス変数(@付きの変数)をコントローラーのアクション内で定義します。今回の場合はユーザー情報とログイン中のユーザーの投稿の2つを定義します。モデル名がPostの場合
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@posts = @user.posts
end
end
あとはapp/views/users/show.html.erbのファイルを作成し、例えば
<div class="AAAA">
<%= @user.nickname %>さんの投稿一覧
</div>
<% @posts.each do |post| %>
<div class=BBBB">
<%= link_to post.title, post_path(post.id), class: "YYYY" %>
</div>
<div class="CCCC">
<%= link_to post.user.nickname, user_path(post.user), class: "XXXX"%>
</div>
<% end %>
あくまで例ですが上記のように記述します。
次にマイページへのリンクを作成をします。マイページへのパスは/users/ログイン中のユーザーのidなので
<% if user_signed_in? %>
<%= link_to current_user.nickname, user_path(current_user), class: "YYYY" %>
<%= link_to "新規投稿", new_post_path, class: "XXXX" %>
<%= link_to "ログアウト", destroy_user_session_path, method: :delete, class: "YYYY" %>
<% else %>
<%= link_to "ログイン", new_user_session_path, class: "XXXX" %>
<%= link_to "新規登録", new_user_registration_path, class: "YYYY" %>
<% end %>
一覧表示の投稿者名からマイページにアクセスできるようにするには
〜略〜
<div class="YYYYY">
<%= link_to post.user.nickname, user_path(post.user), class: "XXXXX" %>
</div>
〜以下略〜
あくまで例ですが上記のように記述します。
これで新規登録とログインの実装ができました。まだユーザー制限はかけてないので誰でも編集・削除機能できてしまうので、それは次に書きます。