実装すること
今回は私が作成した、「お気に入りのコーヒーを投稿するポートフォリオ」で実装した「あなたへのおすすめ機能」のコードを書いていきます。
簡単にポートフォリオについて説明させて頂くと、
・ユーザーは飲んだコーヒーを投稿します。(Itemテーブル)
・投稿する際に飲んだコーヒーのテイスト(Taistテーブル)を16個のブロックから選びます。
・その投稿に対してユーザーはいいねをすることができます。(Likeテーブル)
・ログインユーザーはマイページに「あなたへのおすすめ」の投稿(Item)が表示されます。
「あなたへのおすすめ」を表示する流れは下記となります。
①ログインユーザーが一番最近いいねした投稿と同じテイストデータの投稿を取ってくる
②①の投稿の中でまだログインユーザーがいいねしていない投稿を取ってくる
③②の投稿の中で最新の投稿を1つ取ってくる(②が無い場合はランダムに1つ取ってくる)
※いいね機能に関しては、下記リンク先で説明しております。
https://qiita.com/yuto_1014/items/78d8b52d33a12ec33448
イメージ
新規投稿をする際に、飲んだコーヒーのテイスト(Taistテーブル)を16個のブロックから選びます。
16個のブロックには、それぞれTaistテーブルの「refresh,bitter,body,fruity」の4つのカラムに入る数字が割り振られています。
(例)左上なら、refresh=3, bitter=0, body=0, fruity=3
ER図
Taistテーブルは、投稿(コーヒー)のテイスト(味)データを格納します。
Itemテーブルは、投稿(コーヒー)のデータを格納します。
関係は、Taist:Item = 1:Nになります。
アソシエーションの確認
Taistモデル
accepts_nested_attributes_for
は、Railsが標準で提供している、ActiveRecordのメソッドの一つです。モデル同士が関連付けられている時に、ネストさせることで一度にまとめてレコードの更新ができるようになります。
今回はTaistレコードが作成・更新されるとそれに紐づいたItemレコードも作成・更新されるようにしています。
allow_destroy: true
をattributesメソッドに追加することで削除可能になります。削除するには対象となるレコードに_destroy: 1
のようなパラメーターを渡します。
class Taist < ApplicationRecord
#アソシエーション
has_many :items
#親子同時保存(親:taist/子:item)
accepts_nested_attributes_for :items, allow_destroy: true
end
Itemモデル
class Item < ApplicationRecord
belongs_to :taist
belongs_to :user
has_many :likes, dependent: :destroy
has_many :liked_users, through: :likes, source: :user
#refile
attachment :image
end
投稿機能の作成(コントローラー・ビュー)
taists_controller.rb
親子同時保存(Taistテーブル・Itemテーブル)にはcocoonを使用しています。
画像投稿はrefileを使用しています。
class TaistsController < ApplicationController
def new
#cocoonで親子同時保存
@taist = Taist.new
@taist.items.build
end
def create
@taist = Taist.new(taist_params)
if @taist.save
render :index
else
render :new
end
end
private
def taist_params
params.require(:taist).permit(:fruity, :refresh, :body, :bitter, items_attributes: [:id, :title, :taist_id, :image, :user_id])
end
end
taists/new.html.erb
・Taistモデルの子要素であるItemモデルは、fields_for
を使用してフォームを作成します。
・f.label '✔︎', for: 'refreshA'
は、id: 'refreshA'のラジオボタンに紐づいています。
・refresh以外のbiiter,body,fruityはhidden_field
で隠し、disabled: "disabled"
で初期状態では保存されないようにしています。javascriptを使用して、id=refreshAがチェックされていた時は、class=refreshAのhidden_fieldを持つdisabled: "disabled"
を解除することで、特定のデータのみ保存するようにします。
<h1>新規投稿</h1>
<h5>コーヒーマップ</h5>
<%= form_for(@taist, url: users_taists_path, remote: true) do |f| %>
<%= f.fields_for :items do |item| %>
<%= render 'item_fields', f: item %>
<% end %>
<div class="refresh_contentA">
<%= f.radio_button :refresh, 3, class: "radio_btn", id: 'refreshA' %>
<%= f.label '✔︎', for: 'refreshA' %>
<%= f.hidden_field :bitter, :value => 0, disabled: "disabled", class: 'hiddenA' %>
<%= f.hidden_field :body, :value => 0, disabled: "disabled", class: 'hiddenA' %>
<%= f.hidden_field :fruity, :value => 3, disabled: "disabled", class: 'hiddenA' %>
</div>
... <!-- refresh_contentがA-Qの16個あります。 -->
<%= f.submit "保存", class:"form-control" %>
<% end %>
taists/_item_fields.html.erb
・hidden_field
でuser_idにcurrent_user.idを代入しています。
<div class="nested-fields form-inline">
<p>画像投稿</p>
<%= f.attachment_field :image %>
<p>タイトル</p>
<%= f.text_field :title, class: "form-control" %>
<%= f.hidden_field :user_id, :value => current_user.id %>
</div>
application.js
チェックされたrefreshIDに紐づいた'disabled'を解除します。
$(document).on("turbolinks:load", function() {
$('#refreshA').click(function() {
$(".hiddenA").attr('disabled', false);
});
// #refreshA-Qまで記載しています。
});
おすすめを表示する(コントローラ・ビュー)
大変お待たせしました。おすすめ機能を表示するロジックを記載致します。
users_controller.rb
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
#@userがいいねした投稿
@likes = Like.where(user_id: @user.id).order(created_at: :desc)
if @likes.exists?
#like = @userがいいねしている投稿の最新1つ
like = @likes.last
#item = likeのitem_idが含まれるitem
item = like.item
#item_recommend = itemと同じtaist
taist_recommend = Taist.find_by(refresh: item.taist.refresh, bitter: item.taist.bitter, body: item.taist.body, fruity: item.taist.fruity)
#item_recommend = taist_recommendと同じitemを全て
item_recommend = taist_recommend.items
#いいねしたitem全てのitem.id
x = [] #xに配列を代入できるようにする
@likes.each do |like|
x << like.item_id #xにlike.item_idを全て代入して配列にする
end
#like_recommend = item_recommendの中で@userがいいねしていないitemの最新1つ
@like_recommend = item_recommend.where.not(id: x).last
else
@like_recommend = Item.order("RAND()").last
end
end
end
users/show.html.erb
<h1>あなたへのおすすめ</h1>
<h3>タイトル</h3>
<%= @like_recommend.title %>
<h3>投稿</h3>
<%= attachment_image_tag @like_recommend, :image %>
最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。