#はじめに
この記事ではいわゆるタグをつけて投稿し、そのタグによって検索できる仕組みを実装しています。
前提としてTweetモデルにて投稿機能を扱う状態からタグ機能を追加していきます。
実装内容一覧
- タグモデルの作成
- モデルのアソシエーションの設定
- デフォルトのタグをseedファイルで用意する
- Tweet投稿の際、タグの投稿を可能にする
- コントローラーでタグ配列を受け取れるようにする
- 各Tweetにつけられたタグを表示する
- 複数タグの検索をできるようにする
- 新たなタグの追加をできるようにする
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. モデルのアソシエーションの設定
class TweetTagRelation < ApplicationRecord
belongs_to :tweet
belongs_to :tag
end
class Tweet < ApplicationRecord
# 以下を追記
#tweetsテーブルから中間テーブルに対する関連付け
has_many :tweet_tag_relations, dependent: :destroy
#tweetsテーブルから中間テーブルを介してTagsテーブルへの関連付け
has_many :tags, through: :tweet_tag_relations, dependent: :destroy
end
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ファイル中に以下のようにデフォルトのタグを記述します。
Tag.create([
{ name: 'タグ1' },
{ name: 'タグ2' },
{ name: 'タグ3' },
{ name: 'タグ4' },
{ name: 'タグ5' }
])
以下のコマンドを打つと、tagsテーブルのレコードに初期のタグを保存できます。
$ rails db:seed
4. Tweet投稿の際、タグの投稿を可能にする
tweetのform_forの中で以下を追記します。
<%= 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プロパティをラベル名に指定。
先ほど投入したタグデータのチェックボックスが表示されていますね。
5. コントローラーでタグ配列を受け取れるようにする
private以下のparamsにtags_ids:[]
を追加
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
で表示してみたいと思います。
<% @tweet.tags.each do |tag| %>
<span><%= tag.name %></span>
<% end %>
このように複数のタグを表示できれば完成です。
7. 複数タグの検索をできるようにする
投稿一覧ページでタグの検索用の情報を送信する項目を追記します。
<%= 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 %>
上記のようなcheck_boxで情報を送信した場合、送信された情報は以下のようになります。
{"tag_ids"=>{"タグ1"=>"0", "タグ2"=>"1", "タグ3"=>"1", "タグ4"=>"0", "タグ5"=>"0"}
コントローラーに検索アルゴリズムを記述します。上記のデータにループ処理を施し、該当のタグを持つtweetを取得するといった流れで考えることができます。やや難解ですが以下のアルゴリズムを読解することはプログラミング学習で非常にためになると思うので、ぜひ自分で理解してみてください!
OR検索の場合
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検索の場合
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. 新たなタグの追加をできるようにする
簡易的に投稿一覧ページでタグの追加をできるようにします。
<!-- 以下を追記 -->
<%= form_tag({controller:"tweets",action:"index"}, method: :get) do %>
<%= text_field_tag :tag %>
<%= submit_tag 'タグを追加' %>
<% end %>
<!-- ここまで -->
上記でタグのパラメータを送信し、そのパラメータがあればindexアクション内でtagsテーブルに保存します。
def index
# 以下を追記
if params[:tag]
Tag.create(name: params[:tag])
end
#ここまで
end
最後に
これにて包括的なタグ機能を実装することができました。難しい部分もあったと思いますが、パラメータの理解やアルゴリズムにいついて勉強しがいのある項目です。頑張りましょう!