railsでログイン・ログアウト機能を作る

# 仕様書

* ユーザーのログイン機能
* ログインした後の状態管理
* ログアウト機能

# ログインページのview作成

## ルーティングの設定

```ruby:routes.rb
get "login" => "users#login_form"
```

## コントローラの設定

```ruby:users_controller.rb
  def login_form
  end
```
# DBにパスワードカラムの追加

## マイグレーションファイルの設定

```ruby:test
rails g migration add_password_to_users
```

コマンドからmigrationファイルを作成し、マイグレーションファイルを下記に編集。

```ruby:rei
class AddPasswordToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :password, :string
  end
end
```

# バリデーションの設定

```ruby:users.rb
class User < ApplicationRecord
  validates :name, {presence: true}
  validates :email, {presence: true, uniqueness: true}
  # passwordカラムにバリデーションを設定してください
  validates :password, {presence: true}
end
```

passwordに空欄だとはじくバリデーションをmodelのuser.rbに記述。

# ログインフォームの作成

## ルーティングの設定

```ruby:routes.rb
post "login" => "users#login"
```
DBに変更を与えるのでpostで設定

## アクションの設定

```ruby:rei
  def login
  end
```

中身は空だがusers#loginに対応するアクションを設定。

## viewにおけるフォームの設定

```ruby:
<%= form_tag("/login") do %>
 <p>メールアドレス</p>
  <!-- name属性を追加してください -->
  <input name ="email">
  <p>パスワード</p>
  <!-- name属性を追加してください -->
  <input type="password" name ="password">
  <input type="submit" value="ログイン">
  <!-- form_tag用のendを追加してください -->
 <% end %>
```
ルーティングはroutes.rbでは最初に/が無いが他のファイルで設定する場合は/で始める。ルートは基本的にはroutes.rbで確認すると分かる。

このviewで/loginに値を送信するフォームを作成。inputにnameで値を設定。

## ユーザーを特定する

ログインしたユーザーを特定し、そのユーザーのページを表示するための機能。

```ruby:users_controller.rb
  def login
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      render("users/login_form")
    end
  end
```

User.find_by(email: params[:email], password: params[:password])でpassword、email両方一致した場合のみ引っ張ってくる。

redirect_toはURLを送って問い合わせるため基本はroutes.rbのURLで良い。ただrenderはviewの相対パスに直接問い合わせを行う。users/login_formもviewをhomeにした時の相対パス。なので最初に/がない。

### 特定したユーザーが登録済みユーザーじゃなかった場合

```ruby:test
  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
```

@userにもしなにも値が無かった場合、elseでeroorメッセージを流し、renderでlogin_formに流す。

```ruby:login_form.html.erb
<%= form_tag("/login") do %>
 <p>メールアドレス</p>
 <input name="email" value="<%= @email %>">
 <p>パスワード</p>
 <input type="password" name="<%= @password %>">
 <input type="submit" value="ログイン">
<% end %>
```
初期値に前に登録した値を持ってくる。

# ログインしたユーザー情報の保持（状態管理）

```ruby:user_controller.rb
  def login
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      session[:user_id] = @user.id
      
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      @error_message = "メールアドレスまたはパスワードが間違っています"
      @email = params[:email]
      @password = params[:password]
      render("users/login_form")
    end
  end
```

sessionにuser_idを入れる。viewに

```ruby:application.html.rb
<% if session[:user_id] %>
 <li>
  現在ログインしているユーザーのid: <%= session[:user_id] %>
 </li>
<% end %>
```
session[:user_id]は@つけなくてもそのままviewに表示できる。

# ログアウト機能

## ログアウトのメソッド

ログアウトはセッションにnilを代入することで行う。この時ルーティングはgetではなく、postを使う。

* get・・・データベースを変更しない場合
* post・・・データベース、sessionの値を変更する場合

```ruby:rei
session[:user_id] = nil
```

## controllerから権限の必要なページを限定する

before_actionで制限

```ruby:rei
before_action: メソッド名,{only:[:アクション名,:アクション名]}
```

# アクセス制限

## 非ログインユーザーに対してのアクセス制限

ログインしていないユーザーがURLを直接叩いても見れないようにアクセス制限をかけよう。application_controllerにforbid_login_userメソッドというログインを禁じるものを作り、各controllerに継承させる。

ensure_correct_userメソッドというログインユーザーと編集しようとしているユーザーのidが一致しない場合にアクセスを拒否するメソッドを作成し、before_actionで複数のアクションに用いる。

```ruby:users_controller.rb
  def ensure_correct_user
    if @current_user.id != params[:id].to_i
    flash[:notice] = "権限がありません"
    redirect_to("/posts/index")
   end
  end
```
params[:id]で取得できる値は文字列のため.to_iメソッドを使って数字に変換する。

## リンクなどの非表示

```ruby:rei
<% if @user.id == @current_user.id %>
<%= link_to("編集", "/users/#{@user.id}/edit") %>
<% end %>
```

上記でuser.idと現在ログインしているuser.idが違うと表示されないようにする。

# 他のユーザーの情報を編集できないように制限をかける

あとで追記