38
33

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 3 years have passed since last update.

大学生限定クリエイティブコミュニティGeekSalonAdvent Calendar 2021

Day 11

【タグ機能完全版】 ~ Rails

Last updated at Posted at 2021-07-18

#はじめに
この記事ではいわゆるタグをつけて投稿し、そのタグによって検索できる仕組みを実装しています。
前提としてTweetモデルにて投稿機能を扱う状態からタグ機能を追加していきます。

実装内容一覧

  1. タグモデルの作成
  2. モデルのアソシエーションの設定
  3. デフォルトのタグをseedファイルで用意する
  4. Tweet投稿の際、タグの投稿を可能にする
  5. コントローラーでタグ配列を受け取れるようにする
  6. 各Tweetにつけられたタグを表示する
  7. 複数タグの検索をできるようにする
  8. 新たなタグの追加をできるようにする

1. タグモデルの作成

まず、今回使用するモデルは以下の3つになります。

Tweets Tweet_tag_relations Tags
id id id
title tweet_id name
body tag_id
  • Tweets
    • 投稿テーブル
  • Tags
    • タグに関するテーブル
  • Tweet_tag_relations
    • 投稿とタグを紐づけるための中間テーブル

では、さっそくモデルを作成していきます。

Tagモデルのマイグレーションファイル生成

$ rails g model Tag name:string

Tweet_tag_relationモデルのマイグレーションファイル生成

$ rails g model Tweet_tag_relation tweet:references tag:references

DBに反映させます。

$ rails db:migrate

2. モデルのアソシエーションの設定

tweet_tag_relation.rb
class TweetTagRelation < ApplicationRecord
  belongs_to :tweet
  belongs_to :tag
end
tweet.rb
class Tweet < ApplicationRecord

  # 以下を追記
  #tweetsテーブルから中間テーブルに対する関連付け
  has_many :tweet_tag_relations, dependent: :destroy
  #tweetsテーブルから中間テーブルを介してTagsテーブルへの関連付け
  has_many :tags, through: :tweet_tag_relations, dependent: :destroy
end
tag.rb
class Tag < ApplicationRecord

  # 以下を追記
  #Tagsテーブルから中間テーブルに対する関連付け
  has_many :tweet_tag_relations, dependent: :destroy
  #Tagsテーブルから中間テーブルを介してArticleテーブルへの関連付け
  has_many :tweets, through: :tweet_tag_relations, dependent: :destroy
end

3. デフォルトのタグをseedファイルで用意する

db/seed.rbファイル中に以下のようにデフォルトのタグを記述します。

db/seeds.rb
Tag.create([
  { name: 'タグ1' },
  { name: 'タグ2' },
  { name: 'タグ3' },
  { name: 'タグ4' },
  { name: 'タグ5' }
])

以下のコマンドを打つと、tagsテーブルのレコードに初期のタグを保存できます。

$ rails db:seed

4. Tweet投稿の際、タグの投稿を可能にする

tweetのform_forの中で以下を追記します。

tweets/new.html.erb
<%= form_for @tweet do |f| %>

    <!-- 以下を追記 -->
    <div class='form-group'>
        <%= f.collection_check_boxes(:tag_ids, Tag.all, :id, :name) do |tag| %>
            <div class='form-check'>
                <%= tag.label class: 'form-check-label' do %>
                    <%= tag.check_box class: 'form-check-input' %>
                    <%= tag.text %>
                <% end %>
            </div>
        <% end %>
    </div>
    <!-- ここまで -->

    <%= f.submit '送信' %>
<% end %>

入力フォームにタグの選択チェックボックスを表示するのに、collection_check_boxes を使用します。
第一引数のtag_idsはタグIDのリストを渡し、複数のタグをtweetに紐づけることができます。
第二引数にはタグオブジェクトのリスト。
第三引数にチェックボックスのvalue、第四引数にタグオブジェクトのnameプロパティをラベル名に指定。

スクリーンショット 2019-11-27 16.33.11.png

先ほど投入したタグデータのチェックボックスが表示されていますね。

5. コントローラーでタグ配列を受け取れるようにする

private以下のparamsにtags_ids:[]を追加

tweets_controller.rb
 class TweetsController < ApplicationController

  private

  def article_params
    params.require(:article).permit(:body, tag_ids: [])
  end
end

ここで先ほどタグのチェックボックスで設定したtag_idsを許可します。
複数のtag_idが渡ってくるので配列の形式で記述しています。

6. 各Tweetにつけられたタグを表示する

チェックしたタグをshow.html.erbで表示してみたいと思います。

tweets/show.html.erb
<% @tweet.tags.each do |tag| %>
    <span><%= tag.name %></span>
<% end %>
スクリーンショット 2019-11-27 16.34.31.png

このように複数のタグを表示できれば完成です。

7. 複数タグの検索をできるようにする

投稿一覧ページでタグの検索用の情報を送信する項目を追記します。

views/tweets/index.html.erb

<%= form_tag({controller:"tweets",action:"index"}, method: :get) do %>
    <% Tag.all.each do |t| %>
        <li><%= check_box :tag_ids, t.name %><%= t.name %></li>
    <% end %>
    <%= submit_tag '検索' %>
<% end %>

image.png

上記のようなcheck_boxで情報を送信した場合、送信された情報は以下のようになります。

{"tag_ids"=>{"タグ1"=>"0", "タグ2"=>"1", "タグ3"=>"1", "タグ4"=>"0", "タグ5"=>"0"}

コントローラーに検索アルゴリズムを記述します。上記のデータにループ処理を施し、該当のタグを持つtweetを取得するといった流れで考えることができます。やや難解ですが以下のアルゴリズムを読解することはプログラミング学習で非常にためになると思うので、ぜひ自分で理解してみてください!

OR検索の場合

app/controllers/tweets_controller.rb
  def index  

    @tweets = Tweet.all

    #以下を追記
    if params[:tag_ids]
      @tweets = []
      params[:tag_ids].each do |key, value|      
        @tweets += Tag.find_by(name: key).tweets if value == "1"
      end
      @tweets.uniq!
    end
    #ここまで

  end

AND検索の場合

app/controllers/tweets_controller.rb
  def index

    @tweets = Tweet.all

    # 以下を追記
    if params[:tag_ids]
      @tweets = []
      params[:tag_ids].each do |key, value|
        if value == "1"
          tag_tweets = Tag.find_by(name: key).tweets
          @tweets = @tweets.empty? ? tag_tweets : @tweets & tag_tweets
        end
      end
    end
    #ここまで

  end

8. 新たなタグの追加をできるようにする

簡易的に投稿一覧ページでタグの追加をできるようにします。

views/tweets/index.html.erb

<!-- 以下を追記 -->
<%= form_tag({controller:"tweets",action:"index"}, method: :get) do %>
    <%= text_field_tag :tag %>
    <%= submit_tag 'タグを追加' %>
<% end %>
<!-- ここまで -->

image.png

上記でタグのパラメータを送信し、そのパラメータがあればindexアクション内でtagsテーブルに保存します。

app/controllers/tweets_controller.rb
  def index

    # 以下を追記
    if params[:tag]
      Tag.create(name: params[:tag])
    end
    #ここまで

  end

最後に

これにて包括的なタグ機能を実装することができました。難しい部分もあったと思いますが、パラメータの理解やアルゴリズムにいついて勉強しがいのある項目です。頑張りましょう!

38
33
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
38
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?