Yuki Matsumoto

To be engneer soon / Ruby on Rails

ログイン機能を作成する(パート3:ユーザー管理機能をもつログイン機能を実装する)

2019-03-06 Yuki MatsumotoRuby on Rails

ユーザーモデルにadminフラグを追加する

ユーザーが管理者かどうかを表すフラグを追加。まずはマイグレーションファイルの雛形を作成する。

bundler exec rails g migration add_admin_to_users

マイグレーションファイルを開き、データベースの中身を定義していく。

class AddAdminToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :admin, :boolean, default: false, null: false
  end
end

そして、migrateしてuserテーブルにadminカラムを追加。

ユーザー管理するためのコントローラーを作成する

ユーザー管理のためのコントローラーを作成します。今回はadminのモジュールの中にUsersControllerというクラスを持たせるので、Admin::UserControllerという名前をつけます。※Railsではモジュール階層をコードを保存するためのディレクトリ階層に対応させているため、app/controllers/admin/users_controller.rbといったファイルが対応する。これは管理系の機能を追加したいときに、admin::のついたコントローラーを追加していけばコードがadminディレクトリの下にまとまりわかりやすくなります。 ※複数の管理系コントローラーがあるアプリケーションの場合は共通処理を持たせるために基底クラス(例: Admin::BaseController)を作り、各管理系コントローラーがそれを継承するように作るのが一般的らしい。

ここからはAdmin::UserControllerにCRUD機能を実装していきます。まずはコントローラーを作成します。

bundler exec rails g controller Admin::Users new edit show index

routesが自動で生成されたのでこれを変更していく。(config/routes.rb)

Rails.application.routes.draw do
  namespace :admin do
    resources :users
  end
    #get 'ideas/index'
    #get 'ideas/show'
    #get 'ideas/new'
    #get 'ideas/edit'
  root 'ideas#index'  
  resources :ideas
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

そしてコントローラーで各アクションを設定します。(app/controllers/admin/users_controller.rb)

class Admin::UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to admin_users_path, notice: "ユーザーd「#{@user}」を登録しました。"
    else
      render :new
    end
  end

  def edit
  end

  def show
  end

  def index
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :admin, :password, :password_confirmation)
  end
end

次にviewを作成していきます。(app/views/admin/users/new.html.erb)

<div>
  <h1>ユーザー登録</h1>
    <%= form_with model: [:admin, @user], local: true do |f| %>
      <div class="form-group">
        <%= f.label :name, 'Name' %>
        <%= f.text_field :name, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :email, 'Email' %>
        <%= f.text_field :email, class: 'form-control' %>
      </div>

      <div class="form-check">
        <%= f.label :admin, class: 'form-check-label' %>
        <%= f.check_box :admin, class: 'form-check-input', title: 'Admin' %>
      </div>

      <div class="form-group">
        <%= f.label :password, 'Password' %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :password_confirmation, 'Confirm your password' %>
        <%= f.password_field :password_confirmation, class: 'form-control' %>
      </div>

      <div>
        <%= f.submit 'Sign up', class: 'btn btn-primary' %>
      </div>
    <% end %>
</div>

次にバリデーションをパスワードとメールにバリデーションを追加します。(app/models/user.rb)

class User < ApplicationRecord
  has_secure_password
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true
end

rails serverを起動すると以下のように画面が表示されます。

image1

チェックボックスの位置がおかしな場所にありますがあとで修正します。

そのほかのアクションを追加する

ここからindex, show, edit, update, destroyアクションを追加していきます。(app/controllers/admin/users_controller.rb)

def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def edit
    @user = User.find(params[:id])
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to admin_users_path, 
      notice: "ユーザー「#{@user.name}」を登録しました。"
    else
      render :new
    end
  end

  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
      redirect_to admin_user_path(@user), 
      notice: "ユーザー「#{@user.name}」を更新しました。"
    else
      render :new
    end
  end

  def destroy
    @user = User.find(params[:id])
    @user.destroy
    redirect_to admin_users_url, 
    notice: "ユーザー「#{@user.name}を削除しました。」"
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :admin, :password, :password_confirmation)
  end
end

index画面を作成する。(app/views/admin/users/index.html.erb)

<div>
  <h1>ユーザー一覧</h1>
  <div>
    <%= link_to 'Sign Up', new_admin_user_path, 
    class: 'btn btn-primary' %>

    <div class="mb-3">
      <table class="table table-hover">
        <thead>
          <tr>
            <th><% t '.name' %>></th>
            <th><% t '.email' %></th>
            <th><% t '.admin' %></th>
            <th><% t '.created_at' %></th>
            <th><<% t '.updated_at' %></th>
          </tr>
          <tbody>
            <%= @users.each do |user| %>
              <tr>
                <td><%= link_to user.name, [:admin, user] %></td>
                <td><% user.email %><td>
                <td><% user.admin? ? 'あり' : 'なし' %></td>
                <td><% user.created_at %></td>
                <td><% user.updated_at %></td>
                <td>
                  <%= link_to '編集', edit_admin_user_path(user), 
                  class: 'bot btn-primary mr-3' %>
                </td>
                <td>
                  <%= link_to '削除', [:admin, user], method: :delete, data: {confirm: "ユーザー「#{user.name}」を削除します。よろしいですか?"}, class: 'btn btn-danger' %>
                </td>
              </tr>
            <% end %>
          </tbody> 
        </thead>
      </table>
    </div>
  </div>
</div>

new.html.erbを少しいじる。(app/views/admin/users/new.html.erb) ※ちなみにこの状態でserverを起動するとpartialで指定したformファイルが存在しないためエラーが発生する。このあと追加するのでまだserverは実行しない

<h1>ユーザー登録</h1>
    <div class="nav.justify-content-end">
      <%= link_to 'Sing Up', admin_users_path, 
      class: 'nav-link' %>
      <%= render partial: 'form', locals: {user: @user}%>
    </div>

続いてedit.html.erbを作成する(app/views/admin/users/edit.html.erb)

<% if user.errors.present? %>    
  <ul>
    <div id="error_explanation">
      <%= user.errors.full_messages.each do |message| %>
        <li><% message %></li>
      <% end %>
    </div>
  </ul>
<% end %>  
    <%= form_with model: [:admin, @user], local: true do |f| %>
      <div class="form-group">
        <%= f.label :name, 'Name' %>
        <%= f.text_field :name, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :email, 'Email' %>
        <%= f.text_field :email, class: 'form-control' %>
      </div>

      <div class="form-check">
        <%= f.label :admin, class: 'form-check-label' %>
        <%= f.check_box :admin, class: 'form-check-input', title: 'Admin' %>
      </div>

      <div class="form-group">
        <%= f.label :password, 'Password' %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :password_confirmation, 'Confirm your password' %>
        <%= f.password_field :password_confirmation, class: 'form-control' %>
      </div>

      <div>
        <%= f.submit 'Sign up', class: 'btn btn-primary' %>
      </div>
    <% end %>

show.html.erbを編集します。(app/views/admin/users/show.html.erb)

<div>
  <h1>ユーザー詳細</h1>
  <div class="nav-justify-content-end">
    <%= link_to '一覧', admin_users_path, class: 'nav-link' %>
     <table class="table table-hover">
      <tbody>
        <tr>
          <th><% t '.id' %></th>
          <td><% @user.id %></td>
        </tr>
        <tr>
          <th><% t '.name' %></th>
          <td><% @user.name %></td>
        </tr>
        <tr>
          <th><% t '.email' %></th>
          <td><% @user.email %></td>
        </tr>
        <tr>
          <th><% t '.admin' %></th>
          <td><% @user.admin? 'あり' : 'なし' %></td>
        </tr>
        <tr>
          <th><% t '.created_at' %></th>
          <td><% @user.created_at %></td>
        </tr>
        <tr>
          <th><% t '.updated_at' %></th>
          <td><% @user.updated_at %></td>
        </tr>
      </tbody>
     </table>
     <%= link_to '編集', edit_admin_user_path, 
     class: 'btn btn-primary mr-3' %>
     <%= link_to '削除', [:admin, @user], method: :delete, 
     data: { confirm: "ユーザー「#{@user.name}」を削除します。よろしいですか?"}, 
     class: 'btn btn-danger' %>
  </div>
</div>

日本語を出力できるようにする

最後に使える言語に日本語を追加します。デフォルトでは英語が設定されているので、日本語にするための設定が必要です。

まずは新しいファイルを作成します。ファイル名はlocale.rbとします。(config/intializers/)

ファイルの中に以下を記載。

Rails.application.config.i18n.default_locale = :ja

そしてja.ymlの中に以下をコピペします。

ja:
  activerecord:
    errors:
      messages:
        record_invalid: 'バリデーションに失敗しました: %{errors}'
        restrict_dependent_destroy:
          has_one: "%{record}が存在しているので削除できません"
          has_many: "%{record}が存在しているので削除できません"
  date:
    abbr_day_names:
    ------- 土
    abbr_month_names:
    - 
    - 1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12月
    day_names:
    - 日曜日
    - 月曜日
    - 火曜日
    - 水曜日
    - 木曜日
    - 金曜日
    - 土曜日
    formats:
      default: "%Y/%m/%d"
      long: "%Y年%m月%d日(%a)"
      short: "%m/%d"
    month_names:
    - 
    - 1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12月
    order:
    - :year
    - :month
    - :day
  datetime:
    distance_in_words:
      about_x_hours:
        one:1時間
        other:%{count}時間
      about_x_months:
        one:1ヶ月
        other:%{count}ヶ月
      about_x_years:
        one:1年
        other:%{count}年
      almost_x_years:
        one: 1年弱
        other: "%{count}年弱"
      half_a_minute: 30秒前後
      less_than_x_seconds:
        one: 1秒以内
        other: "%{count}秒未満"
      less_than_x_minutes:
        one: 1分以内
        other: "%{count}分未満"
      over_x_years:
        one: 1年以上
        other: "%{count}年以上"
      x_seconds:
        one: 1秒
        other: "%{count}秒"
      x_minutes:
        one: 1分
        other: "%{count}分"
      x_days:
        one: 1日
        other: "%{count}日"
      x_months:
        one: 1ヶ月
        other: "%{count}ヶ月"
      x_years:
        one: 1年
        other: "%{count}年"
    prompts:
      second: 秒
      minute: 分
      hour: 時
      day: 日
      month: 月
      year: 年
  errors:
    format: "%{attribute}%{message}"
    messages:
      accepted: を受諾してください
      blank: を入力してください
      confirmation:%{attribute}の入力が一致しません
      empty: を入力してください
      equal_to:%{count}にしてください
      even: は偶数にしてください
      exclusion: は予約されています
      greater_than:%{count}より大きい値にしてください
      greater_than_or_equal_to:%{count}以上の値にしてください
      inclusion: は一覧にありません
      invalid: は不正な値です
      less_than:%{count}より小さい値にしてください
      less_than_or_equal_to:%{count}以下の値にしてください
      model_invalid: 'バリデーションに失敗しました: %{errors}'
      not_a_number: は数値で入力してください
      not_an_integer: は整数で入力してください
      odd: は奇数にしてください
      other_than:%{count}以外の値にしてください
      present: は入力しないでください
      required: を入力してください
      taken: はすでに存在します
      too_long:%{count}文字以内で入力してください
      too_short:%{count}文字以上で入力してください
      wrong_length:%{count}文字で入力してください
    template:
      body: 次の項目を確認してください
      header:
        one: "%{model}にエラーが発生しました"
        other: "%{model}に%{count}個のエラーが発生しました"
  helpers:
    select:
      prompt: 選択してください
    submit:
      create: 登録する
      submit: 保存する
      update: 更新する
  number:
    currency:
      format:
        delimiter: ","
        format: "%n%u"
        precision: 0
        separator: "."
        significant: false
        strip_insignificant_zeros: false
        unit: 円
    format:
      delimiter: ","
      precision: 3
      separator: "."
      significant: false
      strip_insignificant_zeros: false
    human:
      decimal_units:
        format: "%n %u"
        units:
          billion: 十億
          million: 百万
          quadrillion: 千兆
          thousand: 千
          trillion: 兆
          unit: ''
      format:
        delimiter: ''
        precision: 3
        significant: true
        strip_insignificant_zeros: true
      storage_units:
        format: "%n%u"
        units:
          byte: バイト
          eb: EB
          gb: GB
          kb: KB
          mb: MB
          pb: PB
          tb: TB
    percentage:
      format:
        delimiter: ''
        format: "%n%"
    precision:
      format:
        delimiter: ''
  support:
    array:
      last_word_connector: "、"
      two_words_connector: "、"
      words_connector: "、"
  time:
    am: 午前
    formats:
      default: "%Y年%m月%d日(%a) %H時%M分%S秒 %z"
      long: "%Y/%m/%d %H:%M"
      short: "%m/%d %H:%M"
    pm: 午後

ちなみに以下のリンクからコピペしてます。
rails-i18n/ja.yml at master · svenfuchs/rails-i18n · GitHub

最後に同じファイル内に以下を記述する

ja:
  activerecord:

  attributes:
    tasks:

      user:
        name: 名前
        email: メールアドレス
        admin: 管理者権限
        password: パスワード
        password_confirmation: パスワード(確認)
        created_at: 登録日時
        updated_at: 更新日時

これでユーザー管理機能の基礎ができました。

まとめ

今回は管理機能の箱を作成しました。現状誰でも自由に管理機能をいじることができてしまうので、後々、管理機能への制限を追加していきます。次回からはユーザーがログインするための機能を実装していきます。