Railsでタグ機能を使い、ジャンルで絞れる検索機能を作成

#はじめに
railsでアプリを作っていて、投稿をタグ機能で整理したい！
タグ検索機能を実装して、ジャンルごとに投稿し、ジャンルごとに投稿を見たい！
ということはありませんか？
ぜひ作っちゃいましょう:tiger:！

###:sparkles: 完成図 :sparkles:
![完成図１ (200).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649591/a5c52702-672a-735c-7f54-f9001cfcc09f.png)

これで、「伝記」を押すと、伝記タグのついた投稿だけ出てきます！
![完成図２ (201).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649591/64f2c368-3084-f0e9-7190-d5e2809fb756.png)





#モデル
今回は、本の紹介サイトで、本をジャンル分けで表示するサイトを作成します。
作るモデルは以下の３つ！
Bookモデル以外は一緒に作っていきましょう！

| Book  | Book_tag | Tag  |
|:-----:|:--------:|:----:|
|  id   | id       | id   |
| title | book_id  | name |
| text  | tag_id   |      |

<b>テーブル</b>
・books
　投稿テーブル
・tags
　タグに関するテーブル
・book_tags
　投稿とタグを紐づけるための中間テーブル


#１．事前準備
この記事はbooksテーブル(投稿機能)は作成済みの前提なので注意してください。
下記のコントローラーでは投稿、削除だけ書いてます。詳細、編集はお好みで！
・booksコントローラー
・Bookモデル
・booksテーブル( id, title:string, body:text )
・app>views>books>indexとnewページ

　↓　booksコントローラー

```ruby:books_controller.rb
class BooksController < ApplicationController
  def index
    @books = Book.all
  end 

  def new
    @book = Book.new
  end

  def create
    book = Book.new(book_params)
    if book.save
      redirect_to :action => "index"
    else
      redirect_to :action => "new"
    end
  end

  def destroy
      book = Book.find(params[:id])
      book.destroy
      redirect_to action: :index
  end

  private
  def book_params
    params.require(:book).permit(:title, :body)
  end

end
```


　↓　app>views>books>index/new↓

```ruby:books/index.html.erb
<h1>本を探そう</h1>
<%= link_to "新規投稿へ", new_book_path %>
<div class="books-container">
  <% @books.each do |b| %>
    <div class="book">
      <%= b.title %>
      <%= b.body %>
    </div>
    <%= link_to "削除する", book_path(b.id), method: :delete %>
  <% end %>
</div>
```

```ruby:books/new.html.erb
<h1>新規投稿</h1>
<%= form_for @book do |b| %>
  <div class="field">
    <%= f.label :title %>
    <%= f.text_field :title, :size => 140 %>
  </div>
  <div class="field">
    <%= f.label :body %>
    <%= f.text_field :body, :size => 140 %>
  </div>
  <%= f.submit "保存する" %>
<% end %>
<%= link_to "本一覧に戻る", books_path %>
```

　↓　config>routes.rb

```ruby:routes.rb
Rails.application.routes.draw do
  resources :books
end
```

#2．モデル作成
次に、Tagモデルを作成しましょう！

```ruby:ターミナル
rails generate model Tag name:string
```

続いてBookモデルとTagモデルの中間テーブルとなるBook_tagモデルを作成して行きます。

中間テーブルの作成にはreferencesというパラメーターを用いてBookモデル及びTagモデルの中間テーブルであることを示し、`rails db:migrate` でDBに反映させます。

```ruby:ターミナル
rails generate model Book_tag book:references tag:references
rails db:migrate
```
#3．モデルのアソシエーション設定
```ruby:book_tag.rb
class BookTag < ApplicationRecord
 belongs_to :tag
 belongs_to :book
end
```

上記は`rails generate model Book_tag book:references tag:references`により既に記載されてるはず！

```ruby:book.rb
class Book < ApplicationRecord
 has_many :book_tags, dependent: :destroy
 has_many :tags, through: :book_tags, dependent: :destroy
end
```

```ruby:tag.rb
  has_many :book_tags, dependent: :destroy
  has_many :books, through: :book_tags, dependent: :destroy
```

#4．seed.rbにタグレコード追加
```ruby:db/seeds.rb
##省略
Tag.create([
    { name: '小説' },
    { name: '漫画' },
    { name: '伝記'},
    { name: '実用書'},
    { name: '教育書'},
    ])
```
ここで、DBに変更を反映します。

```ruby:ターミナル
rails db:seed
```
#5．viewのformでtagを選択できるようにする
```ruby:books/new.html.erb
#省略
#追記ここから
<div class="check_box">
  <%= form.collection_check_boxes(:tag_ids, Tag.all, :id, :name) do |tag| %>
    <div class='form-check'>
   　 <%= tag.label do %>
    　  <%= tag.check_box %>
    　  <%= tag.text %>
    　<% end %>
    </div>
  <% end %>
</div>
#追記ここまで

  <%= f.submit "保存する" %>
<% end %>
```
#6．コントローラーでストロングパラメーターを追記
```ruby:books/books_controller.rb
#省略
    def book_params
      params.require(:book).permit(:title, :body, tag_ids: [])
    end
end
```
#7 ．投稿にtagが表示されるようviewを追記
渡された複数のタグをeach文で表示させるためそれぞれshowとindexに以下を追記。

```ruby:books/show.html.erb
<% @book.tags.each do |tag| %>
    <%= tag.name %>
<% end %>
```

```ruby:books/index.html.erb
<% @books.each do |b| %>
   <div class="book">
     <%= b.title %>
     <%= b.body %>
#ここから追記
     <% b.tags.each do |tag| %>
        <%= tag.name %>
     <% end %>
#ここまで
   </div>
<% end %>
```
#8 ．タグ検索機能の実装
最後にここからタグ検索機能を実装して行きます
選択したtagの投稿のみ取得するようにindex.html.erbを以下のように編集して行きます。

```ruby:books/index.html.erb
<h1>本を探そう</h1>
<%= link_to "新規投稿へ", new_book_path %>

#ここから追記
<%= form_tag books_path, method: :get, class: 'boards__searchForm' do %>

  <% @tags.each do |tag| %>
      <div class="tag-box">
        <%= link_to tag.name, books_path(name: tag.id) %>
      </div>
  <% end %>
      <div class="tag-box">
        <%= link_to "全ての本", books_path %>
      </div>
#ここまで
 <div class="books-container">
  <% @books.each do |b| %>
    <div class="book">
      <%= b.title %>
      <%= b.body %>
      <% book.tags.each do |tag| %>
         <%= tag.name %>
      <% end %>
    </div>
  <% end %>
 </div>
#ここから追記(これ忘れずに！)
<% end %>
#ここまで
```
続いてposts_controllerのindexアクションを以下のように編集します。

```ruby:books_controller.rb
class BooksController < ApplicationController
  def index
#ここから追記
    @tags = Tag.all
    @books = params[:name].present? ? Tag.find(params[:name]).books : Book.all
#ここまで
  end
```

`Tag.find(params[:name]).books : Book.all`で、nameがセットされていたらTagから関連づけられたbooksを呼び、nameの指定がなければ、全ての投稿を表示するよう記述されています。


#9 ．SCSS
ここは好みですが、リンクを箱で囲う感じにして、

```ruby:application.scssかbooks.scss
.tag-box{
    background-color: aquamarine;
    display: inline;
    border: 1px solid #000000;

    a{
        text-decoration: none;
    }
}
```

##:sparkles: かんせーい :sparkles:
![完成図２ (201).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649591/64f2c368-3084-f0e9-7190-d5e2809fb756.png)
(あ、詳細へ、編集する、は今回触れてないのでここは無視してください。
あと教育書の右に「すべての本」というのもついてるはず！)

##終わりに
大学生限定プログラミングコミュニティのGeekSalonでメンターをしてます！
大学生でプログラミング気になる人はぜひHP見てみてね:yellow_heart:
要チェック！[大学生限定プログラミングコミュニティGeekSalon](https://bit.ly/2IVdvl7)



参考サイト: [railsでタグ検索機能を実装し多対多を理解する](https://qiita.com/Kairi_Yasunnde/items/935dcdb8ec88b9ed9d91#3%E3%82%BF%E3%82%B0%E6%A4%9C%E7%B4%A2%E6%A9%9F%E8%83%BD%E3%81%AE%E5%AE%9F%E8%A3%85)