railsのキホンvol.8（ログイン機能）

##ログイン機能

###ログインページを作る
まず、ルーティングとコントローラを設定する。
usersコントローラのlogin_formアクションを定義する。

```routes.rb
get "login" => "users/login_form"
```

```users_controller.rb
def login_form
end
```

ビューでログインフォームを表示する。

```login_form.html.erb
<p>メールアドレス</p>
<input name="email" value="<%= @email %>">
<p>パスワード</p>
<input type="password" name="password" value="<%= @password %>">
<input type="submit" value="ログイン">
```
###フィームの送信
次に、フォームのあたいを送信できるようにloginアクションを作る。

```routes.rb
post "login" => "users/login"
```
```users_controller.rb
def login
end
```
ログインフォームの方もform_tagを追加しておく。

```login_form.html.erb
#/loginへフォームの値を送信
<%= form_tag("/login") do %>
<p>メールアドレス</p>
<input name="email" value="<%= @email %>">
<p>パスワード</p>
<input type="password" name="password" value="<%= @password %>">
<input type="submit" value="ログイン">
<% end %>
```
また/loginで被っているが、post,getで異なったルーティングと認識されるので問題ない。
##ユーザーを特定

loginアクションで、ユーザーを特定する処理をする。
情報が一致した場合はフラッシュメッセージとともに投稿一覧へ。
しなかった場合はエラーメッセージとともにログインフォームへ、初期値として入力済みのもの。

```users_controller.rb
def login
    #ログインフォームで入力されたものが、データベースの情報とあっているか確認する
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      @error_message = "メールアドレスまたはパスワードが間違っています"
      @email = params[:email]
      @password = params[:password]
      render("users/login_form")
    end
end
```
##session変数
ページを移動してもユーザー情報を保持するためにsession関数を使う。
```session[:キー] = 値```
と記述をする。
今回は、ログインに成功した時にユーザー情報を保持しts保持したいので、loginアクションを追加修正する。

```users_controller.rb
def login
    #ログインフォームで入力されたものが、データベースの情報とあっているか確認する
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      session[:user_id] = @user.id
      ...
end
```
また新規登録時にそのままログイン状態にして、セッション変数で情報を保持する。

```user_controller.rb
def create
    @user = User.new(
      name: params[:name],
      email: params[:email],
      image_name: "default_user.jpg",
      password: params[:password]
    )
    if @user.save
      #新規登録に成功すれば、ログイン状態にする
      session[:user_id] = @user.id
      flash[:notice] = "ユーザー登録が完了しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/new")
    end
  end
```


##ログアウト機能
ログアウトする場合はセッションの値をからにする。
セッションの値を変更するときはpostとする。

```routes.rb
post "logout" => "users#logout"
```
```users_controller.rb
def logout
  session[:user_id] = nil
  flash[:notice] = "ログアウトしました。"
  redirect_to("/login")
end
```
##ログイン中のユーザーを取得する
###before_action
各コントローラの全アクションで共通の処理がある場合に使う。

ユーザー情報は全アクションで使うためapplication_controller.rb で処理を書いておく。
ここでは、set_crrent_userメソッドを定義する。


```controllers/application_controller.rb
before_action :set_current_user
def set_current_user
  @current_user = User.find_by(id: session[:user_id])
end
```
##アクセス制限
###ログインしていない場合
ログインが必要なページを表示しようとした場合は、フラッシュメッセージとともにログインページに移動する、authenticate_userメソッドを作る。

```controllers/application_controller.rb
def authenticate_user
    #ユーザー情報がない場合
    if @current_user == nil
      flash[:notice] = "ログインが必要です"
      redirect_to("/login")
    end
end
```
onlyを用いて、指定したアクションのみでメソッドを実行する。

```users_controller.rb
before_action :authenticate_user, {only: [:index, :show, :edit, :update]}
```
```posts_controller.rb
before_action :authenticate_user
```

###ログインしている場合
ログインしている場合も同様に、アクセス制限のメソッドを作っておく。

```controllers/application_controller.rb
def forbid_login_user
    if @current_user
      flash[:notice] = "すでにログインしています"
      redirect_to("/posts/index")
    end
end
```
対応するアクションに対してメソッドを適応させる。

```home_controller.rb
before_action :forbid_login_user,{only: [:top]}
```
```users_controller.rb
before_action :forbid_login_user, {only: [:new, :create, :login_form, :login]}
```

##ユーザー編集の制限

自分以外のユーザーの編集をできないように制限をかける。

```views/users/show.html.erb
#詳細ページのユーザーidとログインユーザーidが一致している場合は編集ok
<% if @user.id == @current_user.id %>
  <%! linl_to("編集","users/#{@user.id}/edit") %>
<% end %>
```
次にアクションの方でも制限しておく。
ensure_correct_userアクションを用いる。
ログインしているユーザーと編集したいユーザーが等しくないとき、投稿一覧ページへとリダイレクトさせる。

```controllers/users_controller.rb
before_action :ensure_correct_user, {only: [:edit,:update]}
def ensure_correct_user
  if @current_user.id != params[:id].to_i
    flash[:notice] = "権限がありません。"
    redirect_to("/posts/index")
  end
end
```

###to_iメソッド

params[:id]で帰ってくる数値は文字列になっているため、to_iメソッドを使うことによって、数値としてidを取得できる。

続き
https://qiita.com/jonnyjonnyj1397/items/dbc91ac83aea40e868ef