○witterもどきのアプリでハッシュタグ機能を実装してみた。

##はじめに
どうも、Pirikaraです。
今回は適当に作った○witterもどきのアプリで、ハッシュタグ機能を実装してみたので共有したいと思います。


##まずはrails new
まずはT○itterもどきのアプリを作ります。
tag機能を実装するので、rails newでtag_appを作ります（適当）

tweetモデルとtagモデルを作成しますが、
両者は多対多の関係なので、中間テーブルとしてtweet_tagモデルも作っておきます。

```ruby:tweet.rb
class Tweet < ApplicationRecord
  has_many :tweet_tags
  has_many :tags, through: :tweet_tags
  #写真の投稿も可能にするために、carrierwaveとminimagickを入れておいた
  mount_uploader :image, ImageUploader
end
```
```ruby:tag.rb
class Tag < ApplicationRecord
  has_many :tweet_tags
  has_many :tweets, through: :tweet_tags
end
```
```ruby:tweet_tag.rb
class TweetTag < ApplicationRecord
  belongs_to :tweet
  belongs_to :tag
end
```
まずはImageとTextのみ投稿ができるTw○tterもどきアプリを作りました。

[![Image from Gyazo](https://i.gyazo.com/919307491bc17fc14684cc680407cd1e.gif)](https://gyazo.com/919307491bc17fc14684cc680407cd1e)
コードはこちら。

```ruby:tweets_controller.rb
class TweetsController < ApplicationController
  def index
    @tweets = Tweet.all
  end

  def new
    @tweet = Tweet.new
  end

  def create
    tweet = Tweet.create(tweet_params)
    redirect_to tweets_path
  end

  private
  
  def tweet_params
    params.require(:tweet).permit(:image, :text, :tag_ids)
  end
end
```

現在『Tags』のプルダウンには何も入っていませんが、Tagsテーブルに登録されているものが表示される仕様です。
今回はtextarea内に『#(ハッシュタグ)』があれば、それ以下をtagとして認識、Tagsテーブルに保存される形でコードを書いていきたいと思います。

## 仕様と進め方の確認
1. textareaに『#(ハッシュタグ)』があれば『#』以下をtagとして認識し、Tagsテーブルに保存する
2. 一覧表示画面にて、tag名が『#○○○』の形で表示される
3. 新規投稿画面にて、Tagsのプルダウンにこれまで登録されたTagが表示される。

今回はtweets_controllerのcreateアクションでTweet.createされたあと、
model/tweet.rbでActiveRecordの『after_create』というコールバックを使用し、データベースに保存される直前にTagsテーブルに『#』タグ以下を登録させる感じでやっていきます。
after_create等のコールバックについては、下記のサイトを参考にしました。

[Railsガイド](https://railsguides.jp/active_record_callbacks.html)

それではいざ、実装。

## Tweet Modelにコールバックの記述

after_create do 〜 endの間に処理を記述していきます。
1. controller側でcreateしたTweetを取得
2. Tweetのtextカラムの値から、『#〇〇〇』に該当する箇所を検出
3. tagsテーブルに保存

加えて、view側で『#〇〇〇』をtagsテーブル経由で表示させまます。

4.『#〇〇〇』を含む形でtweetsテーブルに保存されているので、viewで出力する際に正規表現を使って取り除く。

コードは以下の通りです。

```ruby:models/tweet.rb
class Tweet < ApplicationRecord
  has_many :tweet_tags
  has_many :tags, through: :tweet_tags
  mount_uploader :image, ImageUploader
  
  #DBへのコミット直前に実行
  after_create do
    #1.controller側でcreateしたTweetを取得
    tweet = Tweet.find_by(id: self.id)
    #2.正規表現を用いて、Tweetのtext内から『#○○○』の文字列を検出
    tags  = self.text.scan(/[#＃][\w\p{Han}ぁ-ヶｦ-ﾟー]+/)
    #3.mapメソッドでtags配列の要素一つ一つを取り出して、先頭の#を取り除いてDBへ保存する
    tags.uniq.map do |t|
      tag = Tag.find_or_create_by(name: t.downcase.delete('#'))
      tweet.tags << tag
    end
  end
end
```

```html:tweets/index.html.erb
<div class="content">
  <% @tweets.each do |tweet| %>
    <div class="content__post">
      <%=image_tag "#{tweet.image}"%>
      <div class="content__post__tags">
        <!--4.gsub()で正規表現に合致する箇所を空欄にする-->
        <%= simple_format(tweet.text.gsub!(/[#＃][\w\p{Han}ぁ-ヶｦ-ﾟー]+/, "")) %>
        <!--4.tweetにタグがあればタグを出力する-->
        <% if tweet.tags.any? %>
          <% tweet.tags.each do |tag| %>
            <p>#<%= tag.name %></p>
          <% end %>
        <% end %>
      </div>
    </div>
  <% end %>
</div>
<div class="footer">
</div>
```

index view側でハッシュタグとともにtweetを表示させることができました。
[![Image from Gyazo](https://i.gyazo.com/f7aed0f92fd3fbe575c69cab542d3900.gif)](https://gyazo.com/f7aed0f92fd3fbe575c69cab542d3900)
DBにもちゃんと保存されていますね。
めでたしめでたし。
<img width="838" alt="スクリーンショット 2019-12-19 15.27.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493565/e15bb7ce-642a-43bf-4357-b2b2b4872755.png">
<img width="710" alt="スクリーンショット 2019-12-19 15.27.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493565/c56c11b0-8515-1a47-be81-34acaacff724.png">
<img width="674" alt="スクリーンショット 2019-12-19 15.28.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493565/55522a09-47a0-01cd-9b83-8b132d59808c.png">

##おわりに
今回はTwitte○もどきのアプリでハッシュタグ機能の実装にチャレンジしてみました。
View側で表示させる時に正規表現を使っているところが不恰好なのでヘルパーに突っ込んでもいいかもしれませんね。

応用としては、ハッシュタグからリンクを飛ばして紐づいたtweetを一覧表示できる・・・・・・とかですかね。
ぜひチャレンジしてみてください。

おわり。