Rails 活动记录 where OR 子句与祖父母 属性

Rails active record where OR clause with grandparent property

我有一个 Rails Pundit 政策范围,我正在努力弄清楚。

如果我有这3个模型:

User - has many stories
Story - belongs to a user and has many chapters
Chapter - belongs to a story 

我希望任何用户都能够查看任何 已发布的章节或用户编写的任何章节(甚至 non-published 个)

显然,如果我是写故事的作者,我应该能够查看我自己的故事以及我的故事的所有章节,即使它们没有出版。不过,其他人只能查看我发表的故事和那些发表的故事中发表的章节。

我试过这个:

class ChapterPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
     scope.where(published: true).or(scope.where("story.user.id = ?", @user.id))
    end
  end
end

据说我们需要使用 JOINS 我读过,所以我也试过这个:

scope.where(published: true).or(scope.joins(:story).where(stories: { user: @user }))

但我得到错误:

Relation passed to #or must be structurally compatible. Incompatible values: [:joins, :references]

知道如何编写查询来执行我想要的操作吗?

我想我终于明白了:

scope.joins(story: :user).where("(stories.published = ? AND chapters.published = ?) OR stories.user_id = ?", true, true, @user.id)

希望有一种更非 SQL 的声明方式,但是哦,好吧,除非有人知道非 sql 的方式,否则我猜这会起作用。

你看过 Arel 了吗?

你可以有这样的东西:

chapters = Arel::Table.new(:chapter)
stories = Arel::Table.new(:story)
rel = chapters.joins(stories)
    .where(chapters[:published].eq(true)
    .where(stories[:published].eq(true))
    .or(stories[:user_id].eq(@user.id))
scope.where(rel)

你可以用 Arel 表达式来写这个:

Chapter.joins(story: :user)
.where(Chapter.arel_table[:published].eq(true))
.where(Story.arel_table[:published].eq(true))
.or(Chapter.joins(story: :user).where(Story.arel_table[:user_id].eq(user.id)))

该表达式生成此查询:

SELECT  "chapters".* FROM "chapters" INNER JOIN "stories" ON "stories"."id" = "chapters"."story_id" INNER JOIN "users" ON "users"."id" = "stories"."user_id" WHERE ("chapters"."published" = 't' AND "stories"."published" = 't' OR "stories"."user_id" = 1)