LoginSignup
9
9

More than 5 years have passed since last update.

リレーションを安全側に倒してみる

Posted at

現在、自分が関わっているサービスの開発中に試してていることについて簡単に紹介します。
作っているものはユーザーが記事を投稿するいわゆるCMS系のサービスです。

よく見るこんなモデル

ユーザーとそのユーザーが書いた記事として以下のようなモデルがあります。

 class User < ActiveRecord::Base
   has_many :posts
 end

 class Post < ActiveRecord::Base
   belongs_to :user
 end

あるユーザーの記事を表示する際には @user.posts でアクセスすることになります。
ここまではよくある話。

記事に公開状態を追加する

要件として、記事に公開/非公開状態を持たせるという話が出てきました。

 class Post < ActiveRecord::Base
   belongs_to :user
+  enum status: { open: 0, close: 1 }
 end

この変更により先程の @user.posts は公開/非公開の記事が混在するようになります。
公開状態の記事だけを取るためには、以下のようにpostsに対して状態を明示するかopenの記事だけを取得するリレーションを貼るとかが考えられます。

@user.posts.open

# または
class User < ActiveRecord::Base
  has_many :posts
  has_many :open_posts, -> { self.open }, class_name: 'Post'
end

機能だけで言えばこの対応で十分ではあります。

間違えたときのリスクを考える

ここで呼び出しするリレーションを間違えることを考えます。

postsを呼ぶべき箇所でopen_postsを呼ぶ

postsが呼ばれるのは自分の書いた記事一覧を見る、所謂マイページのような箇所です
ここでopen_postsを呼んでしまうと、 自分で書いた非公開の記事が見つけられなくなる ことになります。

open_postsを呼ぶべき箇所でpostsを呼ぶ

open_postsが呼ばれるのは、たとえばあるユーザーの書いた記事一覧を見たいといった場合です。
ここでpostsを呼んでしまうと 非公開にしたはずの記事が他の人に見られる という危険性があります。

この2つを考えると避けるべきは確実に後者でしょう。
そして、書き間違えやすいのも圧倒的に後者ではないでしょうか。

書きやすいほうを安全にする

危険な挙動のほうがかきやすいのはいろいろ問題です。
そこで以下のように安全な書き方をしたほうがいいと考え採用しました。

 class User < ActiveRecord::Base
-   has_many :posts
-   has_many :open_posts, -> { self.open }, class_name: 'Post'
+   has_many :posts, -> { self.open }
+   has_many :all_posts, class_name: 'Post'
end

さらにいうと posts は編集画面に至る箇所ではなどでは使わないため、書き込みなどができる必要はありません。そこでreadonlyをつけることにしました。

 class User < ActiveRecord::Base
-  has_many :posts, -> { self.open }
+  has_many :posts, -> { self.open.readonly }
   has_many :all_posts, class_name: 'Post'
 end

ここまでで書きやすいものを安全側に倒すことができました!
正直やりすぎかな?とか逆にわかりづらくなってしまったかな?と思う部分もあるので、こうやったほうがいいよ!みたいなのがあったら教えてください!

9
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9