1

I wanted to know is it possible to do the following 2 things at once:

  • View all items with status published.
  • View items for current user with any status.

I have this code:

# Item model
scope :published, -> { where(status: 'published') }
scope :unpublished, -> { where.not(status: 'published') }
scope :by_user, -> (user_id) { where(user: user_id) }

# Item controller
def index
 @items = Item.published + Item.unpublished.by_user(current_user.id)
end

The problem is that @items are an Array, but I want ActiveRecord::Relation.

If you want to know why I need this, it's a simple answer:

@items.find(params[:id])
Anton Maminov
  • 463
  • 1
  • 6
  • 11

4 Answers4

2

Based on the discussion in Rails: How to chain scope queries with OR instead of AND? I think you have at least four options:

1) Write a scope that combines your existing ones, something like:

scope :published_or_unpublished_by_user, -> (user_id) { where('status = ? OR (status != ? and user = ?)', 'published', 'published', user_id) }

2) Use a gem like squeel.

3) Use arel.

4) Wait for the .or syntax coming in Rails 5.

Community
  • 1
  • 1
Brian
  • 5,300
  • 2
  • 26
  • 32
2

Rails 4.x doesn't support OR or UNION queries yet, therefore I would suggest a new scope that combines the other scopes with subqueries (the database should be able to optimize them):

# in the model
scope :visible_for_user, ->(user) {
  where(
    'items.id IN (?) OR items.id IN (?)',
    Item.published, Item.unpublished.by_user(user.id)
  )
}

# in the controller
@items = Item.visible_for_user(current_user)

Please note that the above is a general solution when you want to union existing scope. In this specific example you might get a better performance with a optimized scope:

scope :visible_for_user, ->(user) { 
  where("items.status = 'published' OR items.user_id = ?", user.id)
}
spickermann
  • 100,941
  • 9
  • 101
  • 131
1

I see, you are trying to find unpublished items from current user. You can do it in my scope.

scope :current_user_not_published, ->(user_id) 
      {where('status != ? AND user = ?', 'published', user_id)}

Controller:

# Item controller
def index
 @published_items = Item.published
 @current_user_unpublished_utems = Item.current_user_not_published(current_user.id)
end

OR

#in this case it will be @items[0] published, @items[1] c.not_published

def index
  @items = [Item.published, Item.current_user_not_published(current_user.id)]
end
7urkm3n
  • 6,054
  • 4
  • 29
  • 46
0

I know this is an old question. If your published is a boolean column inside the items table, then published/unpublished is determined by your published being true or false.

Then in your items_controller.rb you can so this.

def show
  @item = Item.find(params[:id])
  @item_published = @item.where(published: true)
  @item_unpublished = @item.where(published: false)
end

So now in your item show page you can do this

<h2>Your Published Items</h2>

<% @item_published.each do |item| %>
  <li><%= item.name %></li>
  ...
<% end %>

<hr>

<h2>Your Unpublished Items</h2>

<% @item_unpublished.each do |item| %>
  <li><%= item.name %></li>
  ...
<% end %>
Elias Glyptis
  • 470
  • 5
  • 9