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

### ■ 環境
+ windows 8.1
+ Rails 5.1.6
+ ruby 2.3.3p222 (2016-11-21 revision 56859) [i386-mingw32]


#1. 簡単な導入テスト

```bash
$ 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]

```

```bash
$ 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
```

```ruby:config/routes.rb
get 'tags/:tag', to: 'users#index', as: :tag #追加
```

```ruby:app/models/user.rb
acts_as_taggable #使用するmodelに追加
```

```ruby:app/controllers/users_controller.rb
#user_paramsを書き換え
def user_params
  params.require(:user).permit(:name, :tag_list)
end
```

```ruby:app/controllers/users_controller.rb
#indexアクション書き換え
  def index
    if params[:tag]
      @users = User.tagged_with(params[:tag])
    else
      @users = User.all
    end
  end
```

```eruby: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 %>
```

```eruby: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>
```

```eruby: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-image-store.s3.ap-northeast-1.amazonaws.com/0/467400/c27e9b56-b1f7-57ef-734c-58659d745e23.png)

##参照
[https://qiita.com/mishikunxo/items/537eea201542b9ee3fa7](https://qiita.com/mishikunxo/items/537eea201542b9ee3fa7)




#2.導入
## 実装イメージ
![acts-as-taggable-on_実装イメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467400/035bff49-4c31-02e2-d59d-5ba9043a50c1.png)

```bash
$ 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]

```

```bash
$ 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
```

```ruby: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)を事前に入れておく
#-------------------------------------------------------------------------------
```

```bash
$ rails db:seed
```

```ruby: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
```

```ruby: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
```

```ruby:app/models/user.rb
class User < ApplicationRecord
  acts_as_taggable
end
```

```eruby: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 %>
```

```eruby: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://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467400/a9893241-0ecf-07a4-47c4-33233092b183.png)


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

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

```eruby:railsで書かれたコード
<%= f.check_box :tag_list, { multiple: true }, "#{tag.name}", nil %>
<%= f.label " #{tag.name}(#{tag.taggings_count})" %>
```
```html: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のコードを変えると良い。

```eruby: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}`は強引できれいではないですが、これでうまくいきます。

```html: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](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をクリックしてチェックが可能
にしたい。

```eruby: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})" %>
```

```html: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>
```