12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

rails acts-as-taggable-on でタグ付け後、タグを使って検索

Last updated at Posted at 2019-08-07

■ 環境

  • windows 8.1
  • Rails 5.1.6
  • ruby 2.3.3p222 (2016-11-21 revision 56859) [i386-mingw32]

#1. 簡単な導入テスト

$ rails _5.1.6_ new hoge
Gemfile
source 'https://rubygems.org'

gem 'rails',        '5.1.6'
gem 'puma',         '3.9.1'
gem 'sass-rails',   '5.0.6'
gem 'uglifier',     '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks',   '5.0.1'
gem 'jbuilder',     '2.7.0'

gem 'acts-as-taggable-on', '~> 6.0' #追加

group :development, :test do
  gem 'sqlite3', '1.3.13'
  gem 'byebug',  '9.0.6', platform: :mri
end

group :development do
  gem 'web-console',           '3.5.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.0.2'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'rails-controller-testing', '1.0.2'
  gem 'minitest',                 '5.10.3'
  gem 'minitest-reporters',       '1.1.14'
  gem 'guard',                    '2.13.0'
  gem 'guard-minitest',           '2.4.4'
end

group :production do
  gem 'pg', '0.20.0'
end

# Windows環境ではtzinfo-dataというgemを含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

$ bundle update
$ bundle install

$ rails generate scaffold user name:string
$ rails acts_as_taggable_on_engine:install:migrations
$ rails db:migrate
$ rails db:migrate RAILS_ENV=test
config/routes.rb
get 'tags/:tag', to: 'users#index', as: :tag #追加
app/models/user.rb
acts_as_taggable #使用するmodelに追加
app/controllers/users_controller.rb
#user_paramsを書き換え
def user_params
  params.require(:user).permit(:name, :tag_list)
end
app/controllers/users_controller.rb
#indexアクション書き換え
  def index
    if params[:tag]
      @users = User.tagged_with(params[:tag])
    else
      @users = User.all
    end
  end
app/users/index.html.erb
<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= raw(user.tag_list.map { |t| link_to t, tag_path(t) }.join(', ')) %></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 %>
app/views/users/_form.html.erb
  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name, id: :user_name %>
    <%= form.label :tag_list %> <!--追加-->
    <%= form.text_field :tag_list, id: :user_name %> <!--追加-->
  </div>
app/users/show.html.erb
<p>tags: <%= raw(@user.tag_list.map { |t| link_to t, tag_path(t) }.join(', ')) %></p> <!--追加-->

test1.png

##参照
https://qiita.com/mishikunxo/items/537eea201542b9ee3fa7

#2.導入

実装イメージ

acts-as-taggable-on_実装イメージ.png

$ rails _5.1.6_ new hoge
Gemfile
source 'https://rubygems.org'

gem 'rails',        '5.1.6'
gem 'puma',         '3.9.1'
gem 'sass-rails',   '5.0.6'
gem 'uglifier',     '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks',   '5.0.1'
gem 'jbuilder',     '2.7.0'

gem 'acts-as-taggable-on', '~> 6.0' #追加

group :development, :test do
  gem 'sqlite3', '1.3.13'
  gem 'byebug',  '9.0.6', platform: :mri
end

group :development do
  gem 'web-console',           '3.5.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.0.2'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'rails-controller-testing', '1.0.2'
  gem 'minitest',                 '5.10.3'
  gem 'minitest-reporters',       '1.1.14'
  gem 'guard',                    '2.13.0'
  gem 'guard-minitest',           '2.4.4'
end

group :production do
  gem 'pg', '0.20.0'
end

# Windows環境ではtzinfo-dataというgemを含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

$ bundle update
$ bundle install

$ rails generate controller Users index search
$ rails generate model User name:string
$ rails acts_as_taggable_on_engine:install:migrations
$ rails db:migrate
$ rails db:migrate RAILS_ENV=test
db/seeds.rb
array = %w(aaa bbb ccc ddd eee fff ggg)
array.each{ |tag|
  tag_list = ActsAsTaggableOn::Tag.new
  tag_list.name = tag
  tag_list.save
}
#-------------------------------------------------------------------------------
# Userに紐づいていないタグ(aaa, bbb, ... ggg)を事前に入れておく
#-------------------------------------------------------------------------------
$ rails db:seed
config/routes.rb
Rails.application.routes.draw do
  root 'users#index'
  get 'users/index', to: 'users#index'
  post 'users/create', to: 'users#create'
  get 'users/search', to: 'users#search'
  post 'users/search', to: 'users#search'
  get 'tags/:tag', to: 'users#index', as: :tag
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    if params[:tag]
      @users = User.tagged_with(params[:tag])
    else
      @users = User.all
    end
    @new_user = User.new
    @tags = ActsAsTaggableOn::Tag.all
#-------------------------------------------------------------------------------
# もともと @tags = User.all としていたが、これだとUserに紐づいて登録されているタグのみ表示される
# Userと紐づいていないタグを参照する場合 @tags = ActsAsTaggableOn::Tag.all としておくとうまくいった
#-------------------------------------------------------------------------------
  end

  def create
    @user = User.new(user_params)
    @user.save
    redirect_to users_index_path
  end

  def search
    @users = User.where("name LIKE ?", "%#{user_params[:name]}%")
    @users = @users.tagged_with(user_params[:tag_list], :match_all => false)
  end
#-------------------------------------------------------------------------------
# 「:match_all => false」←これにかなりてこずりました。
# やりたい事は以下のようなフィルタリングです。
# ↓ 登録データ
# yamadaさん   タグaaa,タグbbb,タグccc
# yamahaさん   タグaaa,タグbbb
# yamamotoさん タグaaa,タグccc
# tanakaさん   タグaaa,タグbbb
#
# ↓ 名前yama でフィルタリング(名前にyamaを持つ)
# yamadaさん   タグaaa,タグbbb,タグccc
# yamahaさん   タグaaa,タグbbb
# yamamotoさん タグaaa,タグccc
# 
# ↓ タグaaa,タグbbbでフィルタリング(タグaaaとタグbbbの両方を持つ)
# yamadaさん   タグaaa,タグbbb,タグccc
# yamahaさん   タグaaa,タグbbb

# @users = User.where("name LIKE ?", "%#{user_params[:name]}%")これは見たままの意味です。
# 問題なのは「:match_all => false」これです。
# サイトで探した限り「:match_all => true」の事しかありませんでした。
# 「:match_all => true」では完全一致したものしか取りだしません。
# 上の例で言えばyamadaさんはタグcccが余計にあるため除外されます。
#-------------------------------------------------------------------------------

  private

  def user_params
    params.require(:user).permit(:name, tag_list: [])
#-------------------------------------------------------------------------------
# "tag_list: []" はviewのform_for内のcheckboxがpostする値が配列になっているため、
# 1.簡単な導入テストの ":tag_list" から "tag_list: []" に変更した。
#
# :tag_list は "aaa,bbb,ccc" の文字列を扱う
# tag_list: [] は ["aaa","bbb","ccc"] の配列を扱う
#-------------------------------------------------------------------------------
  end
end
app/models/user.rb
class User < ApplicationRecord
  acts_as_taggable
end
app/users/index.html.erb
<table>
  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= raw(user.tag_list.map { |t| link_to t, tag_path(t) }.join(' ')) %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<%= form_for(@new_user, url: {controller: 'users', action: 'create' }) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>

  <% @tags.each do |tag| %>
    <%= f.check_box :tag_list, { multiple: true }, "#{tag.name}", nil %>
    <%= f.label " #{tag.name}(#{tag.taggings_count})" %>
<!-- ------------------------------------------------------------------
"#{tag.name}" これだとタグ名が Aaaとなる(一文字目が大文字になる)
" #{tag.name}" これだと(スペース入れる)タグ名は "aaa"となる 
----------------------------------------------------------------------- -->
  <% end %>
  <%= f.submit "作成" %>
<% end %>

<%= form_for(@new_user, url: {controller: 'users', action: 'search' }) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>

  <% @tags.each do |tag| %>
    <%= f.check_box :tag_list, { multiple: true }, "#{tag.name}", nil %>
    <%= f.label " #{tag.name}(#{tag.taggings_count})" %>
  <% end %>

  <%= f.submit "検索" %>
<% end %>
app/users/serach.html.erb
<table>
  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= raw(user.tag_list.map { |t| link_to t, tag_path(t) }.join(' ')) %></td>
      </tr>
    <% end %>
  </tbody>
</table>

test3.png

##参照
https://github.com/mbleigh/acts-as-taggable-on
https://higelog.brassworks.jp/93
https://qiita.com/takeyuweb/items/3e313a7842997ba15345
https://stackoverflow.com/questions/6697540/acts-as-taggable-on-and-checkbox-tags
https://qiita.com/tomomomo1217/items/5c3c0ff92eff36e85b20

#3.補足 check_boxのlabelをクリックしてチェックを入れる
railsで自動生成されたhtmlコードを確認する

railsで書かれたコード
<%= f.check_box :tag_list, { multiple: true }, "#{tag.name}", nil %>
<%= f.label " #{tag.name}(#{tag.taggings_count})" %>
railsで生成されたhtmlコード
<input type="checkbox" value="aaa" name="group[tag_list][]" id="user_tag_list_aaa" />
<label for="user_ aaa(1)"> aaa(1)</label>

htmlコードのcheckbox のid="user_tag_list_aaa"とlabelのlabel for="user_ aaa(1)"が一致していない場合、labelをクリックしてもチェックボックスは無反応になる。そのため以下のようにrailsのコードを変えると良い。

railsで書かれたコード(labelをクリックしてチェックする)
<%= f.check_box :tag_list, { multiple: true }, tag.name, nil %>
<%= f.label "tag_list_#{tag.name}", "#{tag.name}(#{tag.taggings_count})" %>

tag_list_#{tag.name}は強引できれいではないですが、これでうまくいきます。

railsで生成されたhtmlコード(labelをクリックしてチェックする)
<input type="checkbox" value="aaa" name="user[tag_list][]" id="user_tag_list_aaa" />
<label for="user_tag_list_aaa"> aaa(1)</label>

##参照
http://gpsoft.dip.jp/hiki/?Rails%E3%81%A7%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%81%AE%E3%83%A9%E3%83%99%E3%83%AB%E3%82%92%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B

#4.補足 check_boxのlabelをクリックしてチェックを入れる(idとvalueに違う値を入れる)
check_boxの
value = tag.name
id = tag.id
のような形でかつ、
labelをクリックしてチェックが可能
にしたい。

railsで書かれたコード
<%= f.check_box :tag_list, { multiple: true, id: "user_tag_list_#{tag.id}" }, tag.name, nil %>
<%= f.label "tag_list_#{tag.id}", "#{tag.name}(#{tag.taggings_count})" %>
railsで生成されたhtmlコード
<input type="checkbox" value="aaa" name="user[tag_list][]" id="user_tag_list_1" />
<label for="user_tag_list_1">aaa(1)</label>
12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?