現在、自分が関わっているサービスの開発中に試してていることについて簡単に紹介します。
作っているものはユーザーが記事を投稿するいわゆる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
ここまでで書きやすいものを安全側に倒すことができました!
正直やりすぎかな?とか逆にわかりづらくなってしまったかな?と思う部分もあるので、こうやったほうがいいよ!みたいなのがあったら教えてください!