こんにちは。寒すぎて凍えているGeeksalonの限界メンターです。
「アーティストごとにアルバムを投稿したい」、「飲み会ごとに飲んだ量でランキングを実装したい」、「旅行プランごとに行き先を登録したい」etc...
そんな声に応えた機能を実装してみました。ユーザーのグループ機能はあったんですが、投稿をグループ化するのはぱっと見つからなかったので、初めてですが記事を書いてみました。未熟者ではありますが、温かい目で読んでみてください♪
【目次】
- 開発環境
- 概要
- アソシエーション
- 機能実装
➢ モデルの作成
➢ コントローラーの作成
➢ アソシエーション
➢ HTMLの実装
・ おまけ:ランキング
【 1. 開発環境 】
・ruby 3.0.4
・rails 6.1.6
- 前提 -
・Userの機能は実装済み
・TweetのCRUD機能は実装済み
【 2. 概要 】
- コントローラー・モデルは以下3つ
- User:ユーザー
- Tweet:投稿(好きなものに置き換えて実装してください)
- Room:グループ(Groupは予約語なのでRoomで実装しています)
- ユーザーは自由にルームを作成できる
- 投稿の際にルームを登録する。投稿は1つのルームに所属する。
- ユーザーは複数ルームに所属する。
- 今回の実装では、複数回所属してしまっている状態。これを逆手にとって後述のランキングを実装している。
- おいおいルームのページから投稿するボタンを実装して自動的にそのルームでの投稿になるようにしたい。
【3. アソシエーション】
- UserとTweetは1対多
- RoomとTweetは1対多
- UserとRoomは多対多
- Tweetが中間テーブルとなってくれます。
【4. 機能実装】
➢ モデルの作成
まずRoomモデルを作成しましょう。
$ rails g model Room roomname:text
$ rails db:migrate
➢ コントローラーの作成
コントローラーを作成します。
$ rails g controller rooms
できたら中身に以下を記述していきましょう。
class RoomsController < ApplicationController
def index
@rooms = Room.all
end
def new
@room = Room.new
end
def create
room = Room.new(room_params)
if room.save
redirect_to :action => "index"
else
redirect_to :action => "new"
end
end
def show
@room = Room.find(params[:id])
@users = User.all
#ランキング実装部分
@roomtweets = Room.find(params[:id]).tweets
@ranks = User.find(@roomtweets.group(:user_id).order('count(id) desc').pluck(:user_id))
end
private
def room_params
params.require(:room).permit(:roomname)
end
end
tweet投稿時にRoomを割り振れるように、tweet_controllerも書き加えます。
# ~ 省略 ~
def new
@tweet = Tweet.new
rooms = []
#tweet新規投稿の際、Roomを選択できるように配列にしてプルダウンに
Room.all.each do |r|
room = []
room.push(r.name)
room.push(r.id)
rooms.push(room)
end
@rooms = rooms
end
# ~ 省略 ~
private
def tweet_params
params.require(:tweet).permit(:body, :room_id)
end
# ~ 省略 ~
➢ アソシエーション
Tweetsテーブルにroom_idカラムを追加します。
$ rails g migration AddRoomIdToTweets room_id:integer
$ rails db:migrate
アソシエーションを記述していきます。
class User < ApplicationRecord
has_many :rooms, through: :tweets
#Tweetsテーブルを介してRoomを関連付ける
has_many :joined_rooms, through: :tweets, source: :room
# 所属しているRoomを表示する際、使用する
def already_joined?(room)
self.tweets.exists?(room_id: room.id)
end
end
class Tweet < ApplicationRecord
belongs_to :user
belongs_to :room
end
class Room < ApplicationRecord
has_many :tweets
#tweetsテーブルを介してUserを関連付ける
has_many :users, through: :tweets
#Tweetsテーブルを介して所属しているUserを関連付ける
has_many :joined_users, through: :tweets, source: :user
#所属しているユーザーを表示する際、使用する
def already_joining?(user)
self.tweets.exists?(user_id: user.id)
end
end
➢ HTMLの実装
まずはルーティングを記述しておきましょう。
resources :rooms
以下はRoom一覧ページです。
<div class="rooms-container">
<%= link_to "new", new_room_path %> #roomの新規作成
<br>
<% @rooms.each do |r| %> #roomの一覧表示
・<%= link_to room_path(r.id) do %><%= r.roomname %><% end %><br>
<% end %>
</div>
つぎはRoom新規作成ページです。
<%= form_for(@room, :url => { controller:'rooms', action:'create'}) do |f| %>
<%= f.label :room名 %>
<%= f.text_field :roomname, :size => 140, class: "imput" %>
<%= f.submit %>
<% end %>
<%= link_to "Room一覧に戻る", rooms_path %>
そして、Roomの詳細ページです。
<%= link_to "rooms一覧に戻る", rooms_path %>
<br>
<h4>room名:<%= @room.roomname %></h4>
<% @users.each do |u| %>
<% if u.already_joined?(@room) %>
<%= u.name %><br>
<% end %>
<% end %>
<br>
<h4>メンバーの投稿</h4>
<% @room.tweets.each do |t| %>
〇<%= t.body %>
/<%= t.user.name %><br>
<% end %>
<br>
<h4>Room内 投稿数ランキングTop3</h4>
<% @ranks.each.with_index(1) do |u, i| %>
第<%= i %>位:<%= u.name %>✨<br>
<% end %>
そして最後に、tweetの投稿ページも少し手を加えておきましょう。
<%= form_for(@tweet, :url => { controller:'tweets', action:'create'}) do |f| %>
<%# 中略 %>
<%# Roomの登録 %>
<%= f.label :room名 %>
<%= f.select :room_id, @rooms %>
<%# 中略 %>
<% end %>
まとめ
以上がグループ機能実装の簡単な説明です。基本的にはグループチャットの原理を利用していることが分かるかと思います。
しっかり細かいところまで考えられていない&実装も中途半端なので、今後詰めていければと思っています。