【Ruby on Rails】初学者向け－開発の流れについて⑦　ユーザー認証機能追加編

## Rails開発の流れについて

・Railsプロジェクトの作成とDockerの設定
[【Ruby on Rails】初学者向け－開発の流れについて①　プロジェクトの作成編](https://qiita.com/y-kida/items/2c8634ceb28a93c84bc9 "Qiita Home")

・Gem・DB設定とルーティング
[【Ruby on Rails】初学者向け－開発の流れについて②　Gem・DB設定とルーティング編](https://qiita.com/y-kida/items/1d34bf72a3296579f213 "Qiita Home")

・Bootstrapの導入
[【Ruby on Rails】初学者向け－開発の流れについて③　Bootstrapの導入編](https://qiita.com/y-kida/items/cb3723a344766fcaf15e "Qiita Home")

・一覧、新規作成、参照、更新、削除画面機能の追加vol.1
[【Ruby on Rails】初学者向け－開発の流れについて④　一覧、新規作成、参照、更新、削除画面機能の追加vol.1](https://qiita.com/y-kida/items/2d5edbe3be1cd7b7289c "Qiita Home")

・一覧、新規作成、参照、更新、削除画面機能の追加vol.2
[【Ruby on Rails】初学者向け－開発の流れについて④　一覧、新規作成、参照、更新、削除画面機能の追加vol.2](https://qiita.com/y-kida/items/ec0a89bf4c10d3c66e7a "Qiita Home")

・検索機能の追加
[【Ruby on Rails】初学者向け－開発の流れについて⑤　検索機能の追加](https://qiita.com/y-kida/items/721ad89ed3a4a9704997 "Qiita Home")

・DBやモデル周りの整備 & バリデーション機能追加
[【Ruby on Rails】初学者向け－開発の流れについて⑥　DBやモデル周りの整備 & バリデーション機能追加](https://qiita.com/y-kida/items/e04048be539993087252 "Qiita Home")

https://qiita.com/y-kida/items/e04048be539993087252


・ユーザー認証機能追加（今回学習するのはこちら）**

（※ 他追加機能があれば随時記事にしていく予定です）

--------------------------------------------------------
**前提**
・Docker
・Ruby on Rails 7
・Mysql
・BootStrap
・VSCode

-------------------------------------------------------

**前回のおさらい**
…前回はモデルでバリデーションを設定し、ja.ymlの設定や
　エラーメッセージ用のパーシャル作成、といったエラーチェックの処理を追加しました。

<h1>ユーザー認証機能追加について</h1>

今回はユーザ認証機能についてまとめていきます。
流れとしては以下です。

・パスワードハッシュ化のためのGemをインストール
・Userテーブルの作成
・ユーザー登録画面とログイン画面の作成
・ログインユーザーによって変化する機能の作成

### ハッシュ化のためのGemをインストール
まず初めにユーザー認証機能において大事なのがパスワードのハッシュ化です。
ハッシュ化することで容易にパスワードを解読されないようにします。

このパスワードをハッシュ化して登録するために必要なのが**bcrypt**というGemです。

**bcrypt**
…パスワードを安全に保存するためのハッシュ関数です。

まずGemfileを開き、bcryptのGemを追記します。

```ruby:Gemfile
gem "bcrypt", "~> 3.1.7"

```
次にターミナルでdocker compose buildを実行しGemをインストールします
```docker:terminal
docker-compose build
```

これでパスワードをハッシュ化して保存するための準備ができました。

-------------------------------------------------------------

### Userテーブルの作成
次に、ユーザーテーブルの作成を行います。
ターミナルで下記コマンドを実行し、migrationファイルとモデルを作成します。

```docker:terminal
docker-compose run web rails generate model User name:string password:string
```

作成されたmigrationファイルを編集します。
```db\migrate\20241030071156_create_users.rb
class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :name, null:false
      t.string :password, null:false

      t.timestamps
    end
    add_index :users, :name, unique: true
  end
end

```

**null: false**
…このように記載することで、DBに対してnullを許さない（必ず何かしらの値が入る）ようにする制約をかけることができる。

**add_index :users, :name, unique: true**
…このように記載することで、DBに対してnameカラムの値が一意であるよう制約をかけることができる。

migrationファイルが編集できたら下記コマンドを実行してmigrateします。
```docker:terminal
docker-compose exec web rails db:migrate
```


-----------------------------------------
### ユーザー登録画面とログイン画面の作成
Userテーブルが作成できたら次にルーティングの確認を行います。
現在のconfig/routes.rbは以下のようになっています。
```ruby:config/routes.rb
Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  root "tasks#index"
  get 'tasks', to: 'tasks#index'
  get 'tasks/new', to: 'tasks#new'
  post 'tasks', to: 'tasks#create'
  get 'tasks/:id', to: 'tasks#show', as: 'task'
  get 'tasks/:id/edit', to: 'tasks#edit', as: 'edit_task'
  patch 'tasks/:id', to: 'tasks#update'
  delete 'tasks/:id', to: 'tasks#destroy'
end
```

ここまでのところで全てのルーティングを１行ずつ足しこんでいきましたが、
実はこれは以下のように簡略化して記載することが可能です。
```ruby:config/routes.rb
Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  root "tasks#index"

  resources :tasks
end
```

**resources :tasks**
…このように記述することによってtaskリソースに対する
　標準的なメソッド（追加・更新・削除等）をひとくくりに記載することができます。

（※もし画面遷移でエラーが発生したらhttp://localhost:3000/rails/info/routesで
　　新しく設定されたルーティングを確認しながら修正を行ってください。）


今回はユーザーの登録機能追加やログイン機能追加にあたってルーティングを追加する必要があるため、
以下のように記載します。
ログイン画面をルートとするため、今までルートとしていたtaskのindexに遷移する処理に関しては修正が必要となります。
```ruby:config/routes.rb
Rails.application.routes.draw do
  post 'login', to: 'sessions#create'
  delete 'logout', to: 'sessions#destroy'
  
  root 'home#index'
  resources :users, only: %i[new create]
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")

  resources :tasks
  resources :users
end
```

次にユーザー情報の登録を行うため、コマンドを実行します。
以前は直接VSCodeをかまってファイルを作成していましたが
今回は以下のようにコマンドで必要なファイルの作成を行います。
```docker:terminal
docker-compose exec web rails generate controller Users create new
```

  **docker-compose exec web rails generate controller <コントローラ名> <アクション名>...**
  …コントローラーのファイルを作成するコマンドです。後続にアクション名をつけると、
  コントローラ内のアクションや必要なファイルが作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/38696349-2a5a-bbe4-e523-edb41bda61e8.png)
-------------------------------------------------------------------

次にタスク一覧画面から、ユーザー登録画面に遷移できるように
app\views\tasks\index.html.erbに下記を追記します。

```html:app\views\tasks\index.html.erb
<!--~~~~~~~~~~~~~省略~~~~~~~~~~~~~-->
<div class="d-flex align-items-center">
  <h1>タスク一覧</h1>
  <div class="ms-auto">
    <%= link_to '新規作成', new_task_path, class: 'btn btn-outline-dark'%>
    <!--下記を追記-->
    <%= link_to 'ユーザー登録', users_new_path, class: 'btn btn-outline-dark'%>
  </div>
</div>
<!--#~~~~~~~~~~~~~省略~~~~~~~~~~~~~-->
```

ユーザー登録画面に遷移できるようになったら、
ユーザー登録画面の内容をapp\views\users\new.html.erbに記載していきます。
また、パーシャルとしてapp\views\users\_form.html.erbを作成し、フォームを作成します。

```html:app\views\users\new.html.erb
<h1>ユーザー登録</h1>
<%= render partial: 'form', locals: {user: @user} %>
```
```html:app\views\users\_form.html.erb
<%= form_with model: @user, local: true, data: { turbo: false } do |f| %>
  <div class="form-group">
    <%= f.label :name, 'ユーザー名' %>
    <%= f.text_field :name, 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, 'パスワード（確認）' %>
    <%= f.password_field :password, class: 'form-control' %>
  </div>

  <%= f.submit '作成', class: 'btn btn-primary' %>
<% end %>
```
**f.password_field**
…パスワード入力直後に、黒丸で文字を隠すことができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/9f585219-ae9b-ccf4-2dcf-fefffb586c95.png)
ユーザー登録画面はこのようになります。

次にログインページの作成を行います。まず、ログイン画面を表示するための
homeディレクトリのindexから作成し、パーシャルである_login_form.html.erbをレンダリングしてくるように記載します。
```app\views\home\index.html.erb
<%= render partial: 'users/login_form'%>
```


```html:app\views\users\_login_form.html.erb
<h1>ログイン</h1>

<%= form_with(scope: :session, url: login_path) do |f| %>
  <div class="form-group">
    <%= f.label :name, 'ユーザー名' %>
    <%= f.text_field :name, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :password, 'パスワード' %>
    <%= f.password_field :password, class: 'form-control' %>
  </div>
  <%= f.submit 'ログイン', class: 'btn btn-primary' %>
<% end %>
```

-------------------------------------------------------------------------------
画面からユーザーの登録が行えるようにapp\controllers\users_controller.rbの各アクションに処理を記載していきます。

```ruby:app\controllers\users_controller.rb
class UsersController < ApplicationController
  def new
    @user = User.new(flash[:user])
  end

  def create
    @user = User.new(user_params)
    if @user.save
      session[:user_id] = @user.id
      render :show
    else
      flash.now[:error_messages] = @user.errors.full_messages
      render :new, status: :unprocessable_entity
    end
  end

  def show

  end

  private
  def user_params
    params.require(:user).permit(:name, :password, :password)
  end
end

```


ユーザー登録後の画面としてapp\views\users\show.html.erbを作成します。
```html:app\views\users\show.html.erb
<div class="d-flex align-items-center">
    <h1>ユーザーの登録が完了しました</h1>
    <div class="ms-auto boards__linkBox">
            <%= link_to '一覧', root_path, class: 'btn btn-outline-dark'%>
    </div>
</div>
```
ここまででユーザーの登録が行えます。

次にログインするための処理を作成します。

```ruby:app\controllers\users_controller.rb
class SessionsController < ApplicationController
    def create
        user = User.find_by(name: params[:session][:name])
        if user 
            session[:user_id] = user.id
            # 一覧画面へ遷移
            redirect_to tasks_path
        else
            render 'home/index'
        end
    end

  # ログアウト時の処理
    def destroy
      session.delete(:user_id)
      redirect_to root_path
    end
  end
  
```

最後にログイン画面を表示するためのコントローラーのファイルを作成し、
処理を記載します。ここでは特にindexアクション内に処理を記載しなくてもよいです。
```ruby:app\controllers\home_controller.rb
class HomeController < ApplicationController
    def index
    end
end
```
これにてログイン画面が表示され、登録したユーザーでのみログインすることが可能となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/ea6281e9-e399-571c-4435-fad2b01b4d3d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906686/a7097f00-f058-81d6-cd64-30d91007c603.png)