[Ruby on rails] アソシエーションを使ってタグ機能 

#はじめに
アソシエーションを使ってタグ機能を実装しました。
備忘録的な感じです。良かったら参考にしてみて下さいレベルです。ご了承ください。

**用意するもの**
簡単なCRUD機能がついたアプリ前提に書いています。

**環境**
Windows10
Ruby 2.6.6
Rails 6.0

#実装
**1.モデルを作る**

```Ruby:ターミナル
rails g model tag name:string
```

```Ruby:ターミナル
rails g model tweet_tag_relation tweet:references tag:references
```

カラムが空で保存されるのを防ぐために、<font color="Red">null: false </font>を先ほど作成したマイグレーションファイルに追加する。

```Ruby:db/migrate/20210101000000_create_tags.rb
class CreateTags < ActiveRecord::Migration[6.0]
  def change
    create_table :tags do |t|
      t.string :name, null: false #null; falseをnameが空で保存されないように追加
      t.timestamps
    end
  end
end
```
```Ruby:ターミナル
rails db:migrate
```
**2.アソシエーションとバリデーションを書く**

```Ruby:tweet.rb
has_many :tweet_tag_relations, dependent: :destroy
has_many :tags, through: :tweet_tag_relations, dependent: :destroy
```
上記を追加

```Ruby:tag.rb
class Tag < ApplicationRecord
   has_many :tweet_tag_relations, dependent: :destroy
   has_many :tweets, through: :tweet_tag_relations, dependent: :destroy
end
```
上記の通り記述

```Ruby:tweet_tag_relation.rb
class CafeTagRelation < ApplicationRecord
  belongs_to :tweet
  belongs_to :tag
end
```
上記の通り記述

**3.コントローラー**
createアクションの中身を少し変更

```Ruby:tweet_controller.rb
def create
      @tweet = current_user.tweets.new(tweet_params)
        tag_list = params[:tweet][:name].split(/[[:blank:]]+/).select(&:present?)
        @tweet.user_id = current_user.id
        if @tweet.save
          @tweet.save_tag(tag_list)
          redirect_to :action => "index"
        else
          redirect_to :action => "new"
        end
    end
```
一応、半角スペースをあけることで、複数タグつけれるように書いています。

**4.モデルに追加**

```Ruby:tweet.rb
def save_tag(tag_list)
        current_tags = self.tags.pluck(:name) unless self.tags.nil?
        old_tags = current_tags - tag_list
        new_tags = tag_list - current_tags
          # Destroy old taggings:
          old_tags.each do |old_name|
            self.tags.delete Tag.find_by(name: old_name)
          end
          # Create new taggings:
          new_tags.each do |new_name|
            tweet_tag = Tag.find_or_create_by(name: new_name)
            self.tags << tweet_tag
          end
end
```

**5.Viewに追加**

```Ruby:show.html.erb
<% @tweet.tags.each do |tag| %>
    <span><%= tag.name %></span>
<% end %>
```
上記を追加する。

```Ruby:new.html.erb
<div class="field">
    <%= c.label :タグ %>
    <%= c.text_field :name, :size => 140 %>
  </div>
```
自分のアプリに応じて、書き方は変えてください。

ここまでで、一旦タグ投稿して、詳細ページにて表示できたと思います。


**6.つけたタグから、タグ付いた投稿一覧ページに飛べるようにする**

```Ruby:routes.rb
 resources :tags do
    get 'tweets', to: 'tweets#search'
 end
```

```Ruby:tweet_controller.rb
 def search
        @tag = Tag.find(params[:tag_id])
      　@tweets = @tag.tweets.all 
 end
```

views/tweetsの中にsearch.html.erbを作り、

```Ruby:search.html.erb
<div class= tweetsearch>
<%= @tag.name %>
<% @tweets.each do |t| %>
    <div class="tweet">
      <%= t.body %>
    </div>
<% end %>
</div>
<%= link_to "Tweet一覧に戻る", tweets_path %>
```

以上で実装はできたかなと思います！

#もしかしたらエラー
もし、エラーでTweetのカラムにnameないやんけ！！ってエラー文が出たら、Tweetテーブルにnameのカラムを足しておいてください。タグのnameと関係はないのですが、私の場合はこれでなんか解決できました。（根本解決じゃないと思いますので参考までに）

#参考記事
[railsでタグ機能を実装する](https://qiita.com/you8/items/b2394104c6f9865f5d46)