目標
- 投稿する際に事前に用意した選択肢からチェックボックスを生成、チェックをつけさせ、その情報をデータベースに保存する。
- 投稿を一覧表示した際にチェックを付けた項目も見れるようにする。
- 投稿する際に付けたチェックをもとに投稿を検索することができる。
環境
- Ruby 3.0.4
- Rails 6.1.7
前提
- 投稿機能実装済み。
- 今回は関係ないですが、User機能、いいね機能も実装済みなので、そこら辺の記述は読み飛ばしてください。
完成形
コントローラー(投稿を扱うもの:今回はtweet_controller.rb)
class TweetsController < ApplicationController
before_action :authenticate_user!
@@selections = ["てつや","しばゆー","ゆめまる","りょう","むしめがね","としみつ"]
#中略
def index
@tweets= Tweet.all
end
def search
@selections = @@selections
@tweets = []
search_checkboxes = []
for num in 0..@selections.length - 1 do
search_checkboxes_index = "search_checkbox_" + num.to_s
if !params[search_checkboxes_index].nil?
search_checkboxes.push(params[search_checkboxes_index])
end
end
if params[:search_word] == ''and search_checkboxes == []
@tweets= Tweet.all
elsif params[:search_word] == nil and search_checkboxes == []
@tweets= Tweet.all
else
tweets = Tweet.where("body LIKE ? ",'%' + params[:search_word] + '%')
tweets.each do |d|
union_checkbox = (d.checkboxes + search_checkboxes).uniq
if union_checkbox == d.checkboxes
@tweets.push(d)
end
end
end
end
def new
@tweet = Tweet.new
@selections = @@selections
end
def create
tweet = Tweet.new(tweet_params)
tweet.user_id = current_user.id
if tweet.save
redirect_to :action => "index"
else
redirect_to :action => "new"
end
end
#中略
private
def tweet_params
params.require(:tweet).permit(:body, :image,checkboxes:[])
end
#後略
ルーティング
#前略
get 'tweet/search' => 'tweets#search'
#後略
モデル
#前略
serialize :checkboxes, Array
#後略
ビューファイル
<%= stylesheet_link_tag 'new', :media => "all" %>
<div class="post-container">
<p class="title">投稿フォーム</p>
<%= form_for(@tweet, :url => { controller:'tweets', action:'create'})do |f| %>
<%= f.label :投稿内容 %><br>
<%= f.text_field :body,size: 140%>
<div class="field">
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<div class="field">
<% @selections.each_with_index do |checkboxes,i| %>
<label>
<%= check_box_tag 'tweet[checkboxes][]', checkboxes,false%>
<%= checkboxes %>
</label>
<% end %>
</div>
<%= f.submit "送信"%><br>
<% end %>
</div>
<%= link_to "投稿を検索", tweet_search_path %>
<div class="tweets-container">
<h3>Tweet一覧</h3>
<% @tweets.each do |t| %>
<div class="tweet">
<div class="main-box">
<div class="left-container">
<%= t.user.email %>
<a href="/users/<%= t.user.id %>"><%= t.user.name %></a>
<%= t.body %>
<%= image_tag t.image_url, size: "250x200" if t.image? %>
<br>
<%= t.checkboxes.join(' ')%>
</div>
<div class="right-container">
<%= link_to "詳細", tweet_path(t.id) %>
<% if user_signed_in? && current_user.id == t.user_id %>
<%= link_to "編集する", edit_tweet_path(t.id) %>
<%= link_to "削除する", tweet_path(t.id), method: :delete %>
<% end %>
</div>
</div>
<% if user_signed_in? %>
<% if current_user.already_liked?(t) %>
<%= link_to tweet_like_path(id: t.id, tweet_id: t.id), method: :delete do %>
<i class="fas fa-heart"></i><%= t.likes.count %>
<% end %>
<% else %>
<%= link_to tweet_likes_path(id: t.id, tweet_id: t.id), method: :post do %>
<i class="far fa-heart"></i><%= t.likes.count %>
<% end %>
<% end %>
<% else %>
<p>いいねの数 = </p><%= t.likes.count %>
<% end %>
<p class="time"><%= t.created_at %></p>
</div>
<% end %>
</div>
<h3>投稿を検索</h3>
<%= form_tag({controller:"tweets",action:"search"}, method: :get) do %>
<%= text_field_tag :search_word %>
<div class="field">
<% @selections.each_with_index do |item,i| %>
<% pram = "search_checkbox_" + i.to_s %>
<label>
<%= check_box_tag pram, item, false%>
<%= item %>
</label>
<% end %>
</div>
<%= submit_tag '検索する' %>
<% end %>
<h3>検索結果</h3>
<% @tweets.each do |t| %>
<div class="tweet">
<div class="main-box">
<div class="left-container">
<%= t.user.email %>
<a href="/users/<%= t.user.id %>"><%= t.user.name %></a>
<%= t.body %>
<%= image_tag t.image_url, size: "250x200" if t.image? %>
<br>
<%= t.checkboxes.join(' ')%>
</div>
<div class="right-container">
<%= link_to "詳細", tweet_path(t.id) %>
<% if user_signed_in? && current_user.id == t.user_id %>
<%= link_to "編集する", edit_tweet_path(t.id) %>
<%= link_to "削除する", tweet_path(t.id), method: :delete %>
<% end %>
</div>
</div>
<% if user_signed_in? %>
<% if current_user.already_liked?(t) %>
<%= link_to tweet_like_path(id: t.id, tweet_id: t.id), method: :delete do %>
<i class="fas fa-heart"></i><%= t.likes.count %>
<% end %>
<% else %>
<%= link_to tweet_likes_path(id: t.id, tweet_id: t.id), method: :post do %>
<i class="far fa-heart"></i><%= t.likes.count %>
<% end %>
<% end %>
<% else %>
<p>いいねの数 = </p><%= t.likes.count %>
<% end %>
<p class="time"><%= t.created_at %></p>
</div>
<% end %>
</div>
手順
1.カラムの追加
はじめに投稿のテーブル(今回はTweet)にカラムを追加します。名前は何でもいい(今回はcheckboxes)ですがデータ型はstring
で用意してください。
rails generate migration AddCheckboxesToTweets checkboxes:string
rails db:migrate
カラムの追加に関してはこの記事を参考にしてみてください。
railsのデータベースで配列を保存するためにシリアライズします。
#前略
serialize :checkboxes, Array
#後略
2.checkbox付投稿の実装
check_box_tag
を使ってチェックボックス機能を実装しました。複数あるのでeach_with_index
メソッドを使用しています。
<div class="post-container">
<p class="title">投稿フォーム</p>
<%= form_for(@tweet, :url => { controller:'tweets', action:'create'})do |f| %>
<%= f.label :投稿内容 %><br>
<%= f.text_field :body,size: 140%>
<div class="field">
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<div class="field">
<% @selections.each_with_index do |checkboxes,i| %>
<label>
<%= check_box_tag 'tweet[checkboxes][]', checkboxes,false%>
<%= checkboxes %>
</label>
<% end %>
</div>
<%= f.submit "送信"%><br>
<% end %>
</div>
選択肢に関しては、投稿ページや検索ページで複数回書くのは面倒なので、冒頭にクラス変数で定義して再利用できる形にしました。ストロングパラメーターは配列の場合書き方が違うので注意してください。
class TweetsController < ApplicationController
before_action :authenticate_user!
@@selections = ["てつや","しばゆー","ゆめまる","りょう","むしめがね","としみつ"]
#中略
def new
@tweet = Tweet.new
@selections = @@selections
end
def create
tweet = Tweet.new(tweet_params)
tweet.user_id = current_user.id
if tweet.save
redirect_to :action => "index"
else
redirect_to :action => "new"
end
end
#中略
private
def tweet_params
params.require(:tweet).permit(:body, :image,checkboxes:[])
end
#後略
3.checkbox付一覧ページの実装
特に難しいことはしていません。each do
を使ってcheckboxes
の中身を表示しようとしましたが思ったようにいかなかったため、join
メソッドを用いて間に半角スペースを入れた結合文字列にして表示しています。
<%= link_to "投稿を検索", tweet_search_path %>
<div class="tweets-container">
<h3>Tweet一覧</h3>
<% @tweets.each do |t| %>
<div class="tweet">
<div class="main-box">
<div class="left-container">
<%= t.user.email %>
<a href="/users/<%= t.user.id %>"><%= t.user.name %></a>
<%= t.body %>
<%= image_tag t.image_url, size: "250x200" if t.image? %>
<br>
<%= t.checkboxes.join(' ')%>
</div>
<div class="right-container">
<%= link_to "詳細", tweet_path(t.id) %>
<% if user_signed_in? && current_user.id == t.user_id %>
<%= link_to "編集する", edit_tweet_path(t.id) %>
<%= link_to "削除する", tweet_path(t.id), method: :delete %>
<% end %>
</div>
</div>
<% if user_signed_in? %>
<% if current_user.already_liked?(t) %>
<%= link_to tweet_like_path(id: t.id, tweet_id: t.id), method: :delete do %>
<i class="fas fa-heart"></i><%= t.likes.count %>
<% end %>
<% else %>
<%= link_to tweet_likes_path(id: t.id, tweet_id: t.id), method: :post do %>
<i class="far fa-heart"></i><%= t.likes.count %>
<% end %>
<% end %>
<% else %>
<p>いいねの数 = </p><%= t.likes.count %>
<% end %>
<p class="time"><%= t.created_at %></p>
</div>
<% end %>
</div>
#ここから追記
def index
@tweets= Tweet.all
end
#ここまで追記
4.checkbox付検索ページの実装
ここが今回の山場です。
今回は検索ページと検索結果ページを同じにしているので、<%= form_tag({controller:"tweets",action:"search"}, method: :get) do %>
として、ビューファイルからTweets
コントローラーのsearch
アクションにget
を飛ばしています。
まずはbodyカラム(主な内容を記述するカラム)であいまい検索をかけ、変数tweets
に入れます。そしてtweetを対象にeach do
を行い、checkboxの検索条件を満たしたものをインスタンス変数として用意しておいた配列@tweets
に格納していきます。
#ここから追記
def search
@selections = @@selections
@tweets = []
search_checkboxes = []
for num in 0..@selections.length - 1 do
search_checkboxes_index = "search_checkbox_" + num.to_s
if !params[search_checkboxes_index].nil?
search_checkboxes.push(params[search_checkboxes_index])
end
end
if params[:search_word] == ''and search_checkboxes == []
@tweets= Tweet.all
elsif params[:search_word] == nil and search_checkboxes == []
@tweets= Tweet.all
else
tweets = Tweet.where("body LIKE ? ",'%' + params[:search_word] + '%')
tweets.each do |d|
union_checkbox = (d.checkboxes + search_checkboxes).uniq
if union_checkbox == d.checkboxes
@tweets.push(d)
end
end
end
end
#ここまで追記
<h3>投稿を検索</h3>
<%= form_tag({controller:"tweets",action:"search"}, method: :get) do %>
<%= text_field_tag :search_word %>
<div class="field">
<% @selections.each_with_index do |item,i| %>
<% pram = "search_checkbox_" + i.to_s %>
<label>
<%= check_box_tag pram, item, false%>
<%= item %>
</label>
<% end %>
</div>
<%= submit_tag '検索する' %>
<% end %>
<h3>検索結果</h3>
<% @tweets.each do |t| %>
<div class="tweet">
<div class="main-box">
<div class="left-container">
<%= t.user.email %>
<a href="/users/<%= t.user.id %>"><%= t.user.name %></a>
<%= t.body %>
<%= image_tag t.image_url, size: "250x200" if t.image? %>
<br>
<%= t.checkboxes.join(' ')%>
</div>
<div class="right-container">
<%= link_to "詳細", tweet_path(t.id) %>
<% if user_signed_in? && current_user.id == t.user_id %>
<%= link_to "編集する", edit_tweet_path(t.id) %>
<%= link_to "削除する", tweet_path(t.id), method: :delete %>
<% end %>
</div>
</div>
<% if user_signed_in? %>
<% if current_user.already_liked?(t) %>
<%= link_to tweet_like_path(id: t.id, tweet_id: t.id), method: :delete do %>
<i class="fas fa-heart"></i><%= t.likes.count %>
<% end %>
<% else %>
<%= link_to tweet_likes_path(id: t.id, tweet_id: t.id), method: :post do %>
<i class="far fa-heart"></i><%= t.likes.count %>
<% end %>
<% end %>
<% else %>
<p>いいねの数 = </p><%= t.likes.count %>
<% end %>
<p class="time"><%= t.created_at %></p>
</div>
<% end %>
</div>
#前略
get 'tweet/search' => 'tweets#search'
#後略