前回の記事ではタグをseed-fuやorder_as_specifiedといったライブラリを用いて作成していまが、今回はそのタグをユーザーが複数持てるようにして、上の画像のようにタグの一覧表示をした際に、そのタグがいくつ持たれているかを表示するための合計数を取得します。
考え方としてはユーザーは「複数のタグが持てるし、タグも複数のユーザーを持てる関係」になるので、railsのリレーションとしては多対多の関係にしていきます。
いろいろやり方はあるかと思いますが、今回はhas_and_belongs_to_manyを使う方法で実現していきたいと思います。
バージョンはこちらです
1 2 |
ruby 2.4.0 rails 5.1.6 |
Migration
userモデルとtagモデル、それぞれnameカラムを持っているとします。
1 2 |
$ rails g model user name:string $ rails g model tag name:string |
次にそれらの中間テーブルを作成します。create_tags_usersのところはテーブル名をアルファベット順でつなげます
1 |
$ rails g migration create_tags_users tag:references user:references |
中間テーブルはidカラムを作成しないようにするため、以下のようにマイグレーションファイルにid: false を追加します。
1 2 3 4 5 6 7 8 |
class CreateTagsUsers < ActiveRecord::Migration[5.1] def change create_table :tags_users, id: false do |t| t.references :tag, index: true, null: false t.references :user, index: true, null: false end end end |
1 |
$ rails db:migrate |
Models
それぞれのモデルにアソシエーション設定を追記します。
app/models/user.rb
1 2 3 4 |
class User < ApplicationRecord has_and_belongs_to_many :tags end |
app/models/tag.rb
1 2 3 4 5 |
class Tag < ApplicationRecord has_and_belongs_to_many :users extend OrderAsSpecified end |
※ extend OrderAsSpecifiedは前回の記事で入れたgem ‘order_as_specified’のextendです
Controller
ユーザーのストロングパラメータにtag_ids: []を追加しておきます。
app/controllers/users_controller.rb
1 2 3 |
def user_params params.require(:user).permit(:name, tag_ids: []) end |
Views
ユーザーがタグを選択できるように、タグのチェックボックスを作ります。
app/views/users/_form.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<%= form_with(model: user, local: true) do |form| %> <div class="field"> <%= form.label :name %> <%= form.text_field :name, id: :user_name %> </div> <div class="field"> <%= form.label :tag_ids, 'タグ' %> <%= form.collection_check_boxes :tag_ids, Tag.all, :id, :name do |b| %> <%= b.check_box + b.label %> <% end %> </div> <div class="actions"> <%= form.submit %> </div> <% end %> |
↓このようになるかと思います。
ユーザーとタグの一覧表示です。
app/views/users/index.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<h1>Users</h1> <table class="table"> <thead> <tr> <th>Name</th> <th>Tags</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @users.each do |user| %> <tr> <td><%= user.name %></td> <td> <% user.tags.each do |t| %> <span class="tag"><%= t.name %></span> <% end %> </td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> |
ここではサンプルとして、10のユーザーデータと、それぞれ適当にタグを持たせています。
タグのスタイルも記載しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
.tag { background-color: #f96781; color: #ffffff; margin: 15px; padding: 5px 5px 5px 10px; position: relative; } .tag::before { position: absolute; top: 0; left: -15px; content: ''; width: 0; height: 0; border-color: transparent #f96781 transparent transparent; border-style: solid; border-width: 13px 15px 13px 0; } .tag::after { position: absolute; top: 50%; left: 0; z-index: 2; display: block; content: ''; width: 6px; height: 6px; margin-top: -3px; background-color: #fff; border-radius: 100%; } |
最後にcountメソッドで合計数を取得したら完成です。
1 2 3 4 5 |
<% Tag.order_as_specified(id:[2,3,4,1,5,6]).each do |t| %> <span class="tag"> <%= "#{t.name}(#{t.users.count})" %> </span> <% end %> |
以上です!ご覧いただき、ありがとうございました!😁