Railsで管理画面 - ユーザー 編

前回の続き

michimo-10.hatenablog.com

・コントローラー


adminの時はbase_controllerを使っているので、今まで使っていたApplicationControllerで作ったboards_controllerやuser_controller。。。などは引き継がれない(引っ張ってくることが出来ない)

つまり、もう一度boards_controllerやuser_controllerを定義し直さなければならない

とは言え一度作成しているので使いたい中身はほぼ同じ。ただ挙動を変える必要があるくらいだ。

また、今課題には今までに無かったuserの一覧画面の作成があるのでuserの情報を取って来なければならない。その辺りはコントローラーで明記し、ビューで表示させる。

まずは管理者用のboardsコントローラーの作成




続いてboardと同じようにusers_controllerを作成

rails g controller admin::Users # この時オプションでビューも作っていいがビューは後から個別で作る

出来上がったら一覧表示も作りたいのでboardsの中身身をコピペし部分ごとに編集していく

・自分の回答 # controllers/admin/users_controller.rb

class Admin::UsersController < Admin::BaseController
  before_action :login_user, only: %i[edit show update destroy]

  def index
    @search = User.ransack(params[:q]) # ↓ includeはしていない
    @users = @search.result(distinct: true).order(created_at: :desc).page(params[:page])
  end

  def show; end

  def edit; end

  def update
    if @user.update(user_params)
      redirect_to admin_user_path, success: t('defaults.message.updated', item: User.model_name.human)
    else             # ↑ パスに注意                                               ↑ 各所翻訳も注意
      flash.now[:danger] = t('defaults.message.not_updated', item: User.model_name.human)
      render :edit
    end
  end

  def destroy
    @user.destroy!
    redirect_to admin_users_path, success: t('defaults.message.deleted', item: User.model_name.human)
  end             # ↑ パスに注意

  private

  def login_user
    @user = User.find(params[:id])
  end       # ↑で userのidを@userに渡している

  def user_params
    params.require(:user).permit(:email, :first_name, :last_name, :avatar, :role)
  end      # :roleを追加(管理者か一般かを判断する為)
end

・ビュー


コントローラーでuserのidを取ってくることが出来たので、 boardと同じよう ・一覧(index),詳細(show),編集(edit)ページを作成する。 ・ファイルとフォルダは新たに作成しadmin下にすること。

一覧表示と言っても今までのページの表示ではなく管理者用のページなのでリスト化された物で良い 課題詳細にあったように

ユーザー一覧では、ID、フルネーム、権限(管理者 or 一般)の項目を表示

が出来れば良い。

しかし権限が一番大変

board同様に参考にするのは以下のBootstrap

Tables

Buttons

一覧画面(index)


・自分の回答 # views/admin/users/index.html.erb

<%= content_for(:title, t('.title')) %>
  <h1><%= t('.title') %></h1>
<div class="col-lg-10 offset-lg-1">
  <!-- 検索フォーム -->
  <form>
  <%= render 'search_form', url: admin_users_path, search: @search %>
  </form>                           # ↑ パスに注意
</div>
<table class="table table-striped">
  <thead>
    <tr>
      <th scope="col">id</th>
      <th scope="col">フルネーム</th>
      <th scope="col">権限</th>
      <th scope="col">作成日時</th>
    </tr>
  </thead>
  <tbody>
    <%= render @users %> #パーシャル作成
  </tbody>
</table>

boardと同じようにページネーションがあり、翻訳もしっかり充てられている

パーシャル部分


・自分の回答 # views/admin/users/_user.html.erb

<tr>
  <th scope="row"><%= user.id %></th>
  <td>
    <%= link_to user.decorate.full_name, admin_user_path(user) %>
  </td>
  <td>
    <%= user.role_i18n %> # 後述するrole_helpのgem
  </td>
  <td>
    <%= l user.created_at, format: :long %>
  </td>
  <td>
    <%= link_to t('defaults.edit'), edit_admin_user_path(user), id: "button-edit-#{user.id}", class:"btn btn-success" %>
  </td>
  <td>
    <%= link_to t('defaults.delete'), admin_user_path(user), id: "button-delete-#{user.id}", class: "btn btn-danger",
        method: :delete, data: {confirm: t('defaults.message.d_confirm')} %>
  </td>    # パスに注意
</tr>

検索のパーシャル


boardとほぼ同じだが、日付の代わりに入れた権限の:role_eq部分は後述

・自分の書き方 # /views/admin/users/_search_form.html.erb

<div class="input-group mb-3">
  <%= search_form_for search, url: url do |f| %>
    <div class="form-inline align-items-center mx-auto">
      <div class="col-auto">
        <%= f.search_field :first_name_or_last_name_cont, class: "form-control", placeholder:"検索ワード" %>
      </div>
      <div class="col-auto">
        <%= f.select :role_eq, User.roles_i18n.invert.map{|key, value| [key, User.roles[value]]}, { include_blank: t('defaults.not_select') }, class:"form-control" %>
      </div>
      <div class="col-auto">
    <%= f.submit class: "btn btn-primary input-group-append" %>
      </div>
    </div>
  <% end %>
</div>

form_forで使用できるhtmlタグ

f.selectやf.date_selectを使うことで選択肢や日付選択ボックスを生成してくれる。 よく見かけるf.submitが送信ボタンの生成してくれる様に。

form_forの使い方をマスターしよう!

セレクトボックスの書き方は以下

【開発メモ】Ruby on Railsのform_forでドロップダウンリストの選択ボックスを設置する方法 | FREE SWORDER

詳細(show)


最初は権限がわからなかった(後述) レイアウトは元々のapp/views/profiles/show.html.erbを参照し権限の表示部分を追加

・自分の回答 # views/admin/users/show.html.erb

<%= content_for(:title, t('.title')) %>
<div class="container pt-3">
  <div class="row">
    <div class="col-md-10 offset-md-1">
      <h1 class="float-left mb-5"><%= t('.title') %></h1>
      <%= link_to t('defaults.delete'), admin_user_path, id: 'button-delete-#{@user.id}', class: 'btn btn-danger float-right',
        method: :delete, data: {confirm: t('defaults.message.d_confirm')} %>  # この上下のlink_toは編集と削除ボタン
      <%= link_to t('defaults.edit'), edit_admin_user_path, id: "button-edit-#{@user.id}", class:"btn btn-success float-right" %>
      <table class="table">
        <tr>
          <th scope="row"><%= User.human_attribute_name(:id) %></th>
          <td><%= @user.id %></td>
        </tr>
        <tr>
          <th scope="row"><%= User.human_attribute_name(:role) %></th>
          <td><%= @user.role_i18n %></td>  # この権限を表示する部分に悩んだ
        </tr>

        <tr>
          <th scope="row"><%= User.human_attribute_name(:email) %></th>
          <td><%= @user.email %></td>
        </tr>
        <tr>
          <th scope="row"><%= User.human_attribute_name(:full_name) %></th>
          <td><%= @user.decorate.full_name %></td>
        </tr>
        <tr>
          <th scope="row"><%= User.human_attribute_name(:avatar) %></th>
          <td><%= image_tag @user.avatar.url, size: '50x50' %></td>
        </tr>
      </table>
    </div>
  </div>
</div>

権限(role)

今回追加になった権限という部分をセレクトボックスを使い、更にi18n対応させるという課題

Gemfileに

gem 'enum_help'

を追加しbundle install 詳しいやり方は以下

【Rails】enum_helpを用いてi18n対応セレクトボックスを作成 - Qiita

編集(edit)

・自分の回答 # views/admin/users/edit.html.erb

<%= content_for(:title, t('.title')) %>
<div class="container">
  <div class="row">
    <div class="col-lg-8 offset-lg-2">
      <h1><%= t('.title') %></h1>
      <%= form_with model: [:admin, @user], url: admin_user_path, local: true do |f| %>
        <%= render 'layouts/error_messages', object: f.object %>
        <div class="form-group">
          <%= f.label :email %>
          <%= f.email_field :email, class:"form-control" %>
        </div>
        <div class="form-group">
          <%= f.label :last_name %>
          <%= f.text_field :last_name, class: "form-control" %>
        </div>
        <div class="form-group">
          <%= f.label :first_name %>
          <%= f.text_field :first_name, class:"form-control" %>
        </div>
        <div class="form-group">
          <%= f.label :avatar %>
          <%= f.file_field :avatar, onchange: 'previewImage()', class: 'form-control mb-3', accept: 'image/*' %>
          <%= f.hidden_field :avatar_cache %>
        </div>
        <div class='mt-3 mb-3'>
          <%= image_tag @user.avatar.url,
                        id: 'preview',
                        size: '100x100' %>
        </div>
        <div class="form-group">
          <%= f.select :role, User.roles_i18n.invert, {}, class:"form-control" %>
        </div>
        <div class="actions">
          <%= f.submit class:"btn btn-primary" %>
        </div>
      <% end %>
    </div>
  </div>
</div>

モデル部分の記載が違ったがそれ以外はほぼ同じ。元々のadminじゃない方のeditフォームをコピペしたのであまり大差はない。

エラーメッセージのファイルは自分はlayoutsに入れてるのでエラーは出なかった