読者です 読者をやめる 読者になる 読者になる

プログラミング 美徳の不幸

Ruby, Rails, JavaScriptなどのプログラミングまとめ、解説、備忘録。

単一テーブル継承のモデル側で表示する内容を変更する

やりたいこと
◯抽象クラスNewsを継承した、FollowStartNewsやTweetFavNewsなど具象クラスを使うことで、タイプコードを削減する。
◯News一覧を見るときに、News.all.each{|news| news.body}で、その内容が見たい。
◯なおかつnews.bodyの内容はFollowStartNewsやTweetFavNewsなどによって異なる。

もう少し詳しく説明すると、
◯news_tableだけがDBに存在し、そこにはuser_id(当事者), tweet_id(対象のツイート),target_user_id(対象のユーザ)などの参照が存在する。
◯FollowStartNews < News ・・・いわゆる単一テーブル継承。
◯FollowStartNewsのbodyは"#{self.user.name}が#{self.target_user.name}をフォローしました"
◯TweetFavNewsのbodyは"#{self.user.name}が#{self.tweet}をいいねといっています"

そもそもこのような設計にした理由は①Typeコードからポリモーフィズムへ、の実践 ②構造はやや異なるがなるべく使用するインターフェースを共通化したい、の2点。

で、実際単なる文字列を出すだけなら上の通りやればいい。しかし、実際は#{self.user.name}がuserのページへのリンクだったりするとうれしい。しかしモデルからは通常link_toなどは使えない。

かといって、Helperにその実装を委ねると@news.bodyという呼び出しがfollow_start_news_format(@news)とかになっちゃいそう(=インターフェースが統一できない)。それはダサい。。。


で、とりあえず結論。

◯app/models/follow_start_news.rb

class FollowStartNews < News
  include Rails.application.routes.url_helpers
  include ActionView::Helpers

  def body
    {
      "ja" => "#{link_to self.user.name, user_path(self.user)}#{link_to self.target_user.name, user_path(self.target_user)}をフォローしました"
    }
  end
end

もしモデルに表示系のロジックが入り込むのがイヤなら、これをFollowStartNewsHelperとかにぶち込んで、bodyからはそれを呼び出せばよいだろう。(そのときはたぶんinclude FollowStartNewsHelperだけで良いと思う)