【rails】Twitterのツイート機能のモデル構造を考えてみた
みなさん、Twitterやってますか?
自分はそこそこいい歳ですがそこそこのツイ廃具合を発揮させてもらっています。(もし興味あればフォローどうぞ さどはら (@sado_not_maso) | Twitter )
今日は、最近Twitterのツイート機能(つぶやきを投稿する機能)のモデル構成をRailsで考えてみたのでそのことについて簡単にまとめようと思います。
(今回記事内でやたらTwitter, Twitterと言っているためTwitter公式から怒られると怖いのでリンク貼っておきます。Twitterやったことないよ!という方は以下のリンクから簡単に始められるのでどうぞ)
ツイート機能の概要
まずはモデル構成(モデル間のアソシエーション)を考える上で押さえておきたいTwitterのツイート機能の特徴について一度まとめておきます。
- ひとつのツイートに紐づくユーザーはただ一人のみ
- ツイートに対してリプライ(返信)ができる
- 返信も1つのツイートとしてカウントされる
- 他人のツイートをリツイート(共有・拡散)できる
細かいことを言い出すと「いいね」機能や140字制限などの特徴もありますが、ここではあくまで「モデル構成を考える上で考慮する必要のある機能」を挙げています。
それでは早速これらの機能を備えるためのモデル構成を考えていきたいと思います。
登場するモデル
今回のアソシエーションで登場するモデルとそのカラムをまとめておきます。
(カラムは今回のツイート機能を実装する上で必要となるものだけを記載しています。)
User
モデル
User |
---|
id |
Tweet
モデル
Tweet |
---|
id |
user_id |
ReplyRelationship
モデル
ReplyRelationship |
---|
id |
main_tweet_id |
reply_tweet_id |
ShareRelationship
モデル
※Tweet
とRetweet
では少しややこしいためShare
としています。
ShareRelationship |
---|
id |
origin_tweet_id |
share_tweet_id |
実際のコード
User
モデル
# users.rb class User < ActiveRecord::Base has_many :tweets, dependent: :destroy end
Tweet
モデル
# tweets.rb class Tweet < ActiveRecord::Base belongs_to :user has_one :replying, through: :replying_relationships, source: :main_tweet has_one :replying_relationships, class_name: 'ReplyRelationship', foreign_key: 'reply_tweet_id', dependent: :destroy has_many :replied, through: :replied_relationships, source: :reply_tweet has_many :replied_relationships, class_name: 'ReplyRelationship', foreign_key: 'main_tweet_id', dependent: :destroy has_one :sharing, through: :sharing_relationships, source: :origin_tweet has_one :sharing_relationships, class_name: 'Share', foreign_key: 'share_tweet_id', dependent: :destroy has_many :shared, through: :shared_relationships, source: :share_tweet has_many :shared_relationships, class_name: 'Share', foreign_key: 'origin_tweet_id', dependent: :destroy end
ReplyRelationship
モデル
# reply_relationships.rb class ReplyRelationship < ActiveRecord::Base belongs_to :main_tweet, class_name: 'Tweet' belongs_to :reply_tweet, class_name: 'Tweet' end
ShareRelationship
モデル
# share_relationships.rb class ShareRelationship < ActiveRecord::Base belongs_to :origin_tweet, class_name: 'Tweet' belongs_to :share_tweet, class_name: 'Tweet' end
解説
Tweet
とUser
について
# users.rb class User < ActiveRecord::Base has_many :tweets, dependent: :destroy end # tweets.rb class Tweet < ActiveRecord::Base belongs_to :user end
これはシンプルなhas_many
とbelongs_to
の関係です。
あるuser
に対して、user.tweets
でそのユーザーのツイート一覧を参照できます。
Tweet
とReplyRelationship
について
# tweets.rb class Tweet < ActiveRecord::Base has_one :replying, through: :replying_relationships, source: :main_tweet has_one :replying_relationships, class_name: 'ReplyRelationship', foreign_key: 'reply_tweet_id', dependent: :destroy has_many :replied, through: :replied_relationships, source: :reply_tweet has_many :replied_relationships, class_name: 'ReplyRelationship', foreign_key: 'main_tweet_id', dependent: :destroy end # reply_relationships.rb class ReplyRelationship < ActiveRecord::Base belongs_to :main_tweet, class_name: 'Tweet' belongs_to :reply_tweet, class_name: 'Tweet' end
ツイートのアソシエーションを考える上で一番特徴的なのはTweet
モデルとReplyRelationship
モデルの関係性かなと思います。
Twitterをやっている方はご存知かと思いますが、Twitterでのリプライ(返信)は
- リプライは主となるツイートに付随するものである
- リプライそのものも一つのツイートである
という二つの性質を同時に持ちます。ちょうど以下の画像のような感じです。
リプライの例:@◯◯から始まる内容は特定のユーザーに向けた返信
これがFacebookなどになると、一つの投稿(Post
)に対するコメント(Comment
)は主従の関係がはっきりと成立するため
Post -- has_many --> Comments
Comment -- belongs_to --> Post
というわかりやすいアソシエーションとなります。
しかし今回リプライはツイートに付随するコメントとしての性質と、それ自体が一つのツイートであるという性質をあわせ持つため、Tweet
モデル間を繋げる役目を持つReplyRelationship
モデルを用意しています。
このようにすることで、以前ユーザーのフォロー機能を解説した記事のようにアソシエーションを考えることができます。
Tweet
とShareRelationship
について
# tweets.rb class Tweet < ActiveRecord::Base has_one :sharing, through: :sharing_relationships, source: :origin_tweet has_one :sharing_relationships, class_name: 'Share', foreign_key: 'share_tweet_id', dependent: :destroy has_many :shared, through: :shared_relationships, source: :share_tweet has_many :shared_relationships, class_name: 'Share', foreign_key: 'origin_tweet_id', dependent: :destroy end # share_relationships.rb class ShareRelationship < ActiveRecord::Base belongs_to :origin_tweet, class_name: 'Tweet' belongs_to :share_tweet, class_name: 'Tweet' end
リツイートについては以下の性質を持っています。
- 表示上は他人のツイート
- 所属は自分のTweets群
つまるところ、あるuser
がリツイートした他人のツイートもuser.tweets
で参照できるtweets
群の中に含めたい、という感じです。
これについてはいろいろ方法があるかもしれませんが、僕が考えた方法ではTweets
群の中のあるtweet
がもしリツイートであればtweet.sharing
として別のツイートを参照できる、というものでした。(もっといい方法があればアドバイスいただけると幸いです。)
それによって必要になったのがShareRelationship
というモデルですね。
アソシエーション自体はTweet
とReplyRelationship
の場合とほとんど変わりません。
まとめ
今回はTwitterのツイート機能のモデル構成について考えてみました。
個人的にツイート機能まわりはリプライの扱いが少し独特かなーという感じでしたが、なんとなく理解していただけたでしょうか。
とてもざっくりとした内容になりましたが、今回の記事が少しでも何かの参考になれば幸いです。