ログイン機能の作成(Ruby on Rails)

#プログラミングの勉強日記
2020年7月18日　Progate Lv.211
Ruby on RailsⅧ

#ログインページの作成
　ログインするためにはログインページからメールアドレスやパスワードなどの情報をRaisに送信する。送信された情報をもとに操作しているユーザを特定しログイン状態とする。ログインページを作るためにルーティング、アクション、ビューを追加する。

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

```ruby:users_controller.rb
def login_form
end
```

`<input>`タグのtype属性をpasswordとすると入力した文字が伏字となるパスワード用のフォームになる。

```erb:login_form.html.erb
<p>パスワード</p>
<input type="password">
```

#パスワードカラムの追加
　`rails generate migration`を用いて`add_password_to_users`というファイル名のマイグレーションファイルを作成する。

```terminal:ターミナル
$ rails generate add_password_to_users
マイグレーションを変更後
$ rails db:migrate
```

```ruby:..._add_password_to_users/rb
class AddPasswordToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :password, :string
  end
end
```

ユーザの情報には必ずパスワードが存在してほしいので、バリデーションをかける。

```ruby:models/user.rb
class User < ApplicationRecord
  validates :password, {presence: true}
end
```

#ユーザの特定
　フォームに入力されたメールアドレスとパスワードは、`params[:email]`と`params[:password]`で受け取れる。usersテーブルから入力された値に一致するユーザを取得し、変数@userに代入する。フォームから送信されたメールアドレスとパスワードに一致するユーザが存在する場合はフラッシュメッセージを表示し、投稿一覧ページにリダイレクトする。ユーザが存在しない場合はログインページを再表示する。

```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
```

#ユーザが存在しないときの処理
　エラーメッセージを表示したりフォームに初期値を入れたりする。
　バリデーションのエラーメッセージとは異なり`find_by`メソッドで検索したが存在しなかったとする。（自作で作る）変数@emailと@passwordを定義し、それぞれに`params[:email]`と`params[:password]`の値を代入し、フォームに入力した値が初期値となるようにする。（変数に代入した初期値を表示するためにフォームにvalue属性を追加する）

```ruby:users_controller.rb
def login
  if @user 
    ...
  else
    @error_message="メールアドレスまたはパスワードが間違っています"
    @email=params[:email]
    @password=paras[:password]
    render("users/login_form")
  end
end
```

```erb:login_form.html.erb
<% if @error_message %>
  <div class="form_error">
    <%= @error_message %>
  </div>
<% end %>
<% form_tag("/login") do %>
  <p>メールアドレス</p>
  <input name="email" value="<%= @email %>">
  <p>パスワード</p>
  <input type="password" name="password" value="<%= @password %>">
<% end %>
```

#ログイン処理
　ログインページでユーザを特定した後、そのユーザの情報を保持し続ける必要がある。ページを移動してもユーザの情報を保持し続けるために`session`という特殊な変数を扱う。`session`に代入された値はブラウザに保存される。`session`に値を`session[:キー名]=値`で代入した値がブラウザに代入される。

```ruby:users_controller.rb
def login
  @user=User.find_by(...)
  #@userが存在する場合
  if @user
    session[:user_id]=@user.id
```


```erb:application.html.erb
<li>
  ログインしているユーザのid：
  <%= session[:user_id] %>
</li>
```

#ログアウト機能の作成
　ログアウトする、つまりログイン状態でなくするには`session[:user_id]`の値を空にする。`session[:user_id]`に`nil`を代入することで、`session[:user_id]`を空にできる。

```ruby:users_controller.rb
def logout
  session[:user_id]=nil
  flash[:notice]="ログアウトしました"
  redirect_to("/login")
end
```

```ruby:routes.rb
post "logout" => "users#logout"
```

　`session`の値を変更する場合にもgetではなくpostを使う。

ヘッダーメニューの表示内容を切り替える。（ログイン状態がログアウト状態かで表示する内容を変える）

```erb:application.html.erb
<% if session[:user_id] %>
  <li>
    <%= session[:user_id] %>
  </li>
  <li>
    <%= link_to("...","/about") %>
  </li>
<% end %>
```

#ユーザ登録時のログイン
　ユーザ登録に成功したらそのままログイン状態となるようにする。そのためにユーザ登録時にパスワード用のフォームを追加する。また、ユーザ登録時にpaswordのカラムの値が設定されるようにする。

```erb:new.html.erb
<p>パスワード</p>
<input name="password" type="password" value="<%= @user.password %>">
```

```ruby:user_controller.rb
def create
  @user=User.new(
    name: params[:name],
    email: params[:emai],
    image_name: "default_user.jpg"
    password: params[:password]
  )
end
```

　ユーザ登録が成功したときに作成したユーザがそのままログイン状態になるように、usersコントローラのcreateアクション内で作成したユーザのidを`session[:user_id]`に代入する。

```ruby:user_controller.rb
if @user.save
  session[:user_id]=@user.id
```

#ユーザを表示する
　`session[:user_id]`の値をもとにログイン中のユーザ情報をデータベースから取得する。find_byメソッドを用いてusersテーブルからidカラムの値が`session[:user_id]`と等しいユーザを取得し、変数(current_user)とする。

```erb:layouts/application.html.erb
<% current_user=User.find_by(id: session[:user_id]) %>
<li>
  <%= link_to(current_user.name, "/users/#{current_user.id}") %>
</li>
```