views:

38

answers:

2

This is a beginner's question but for some reason I cannot find the answer elsewhere.

Customer has_many orders  
Order has_many order_items

I am in customer/show.html.erb and I want my customer to manipulate order_items.

Many orders have many order_items and I want to search ALL of those order_items to find those such that read == false.

#controller
@customer = Customer.find(params[:id])
@orders = @customer.orders

@order_items = @orders.order_items doesn't work. Given that I have multiple items in @orders, how can I collect all the order_items that belong to @orders?

=== EDIT ===

My entire database structure is a big complicated group of tables and I need to traverse that tree for this particular view.

customer has_many orders
orders has_many order_items
order_items belongs_to category

How do I, for example, find the number of my customer's order_items that belong to category X?

Last question: why doesn't @orders.find_all_by_x(...) work?

A: 

Hi @orders = @customer.orders gives you a collection of ActiveRecord objects You can't call object's methods on this collection You should use each iterator

#controller
@customer = Customer.find(params[:id])
@orders = @customer.orders
list = Array.new()
@orders.each do |order|
  //some your action(something like order.order_items)
  //you may add some condition here
  order.order_items.each do |item|  
    list << item
  end
end

Now search I'm not shure about count but it may work

OrderItem.find(:all,:conditions => {:category_id => "1"} ).count //if you know category id

or

OrderItem.all :joins => :categories, :conditions => {:categories => {:name => "X"}} //name is column in your table which holds category name

and the last one

For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called name on your Client model for example, you get find_by_name and find_all_by_name for free from Active Record. If you have also have a locked field on the Client model, you also get find_by_locked and find_all_by_locked.

You cant call @orders.find_all_by_x(...) but you can call Category.find_all_by_name("X")

Bohdan Pohorilets
Hi Bohdan, I would have used .each, except I need ALL of the order_items together so I can sort them... using .each naturally constrains the ordering.
sscirrus
If you ned collect all you should use << operator
Bohdan Pohorilets
you may write even list << order.order_items but then you'll get other result
Bohdan Pohorilets
A: 

1) Show all OrderItems for a customer

You can add a relation called "order_items" to your customer like this:

has_many :order_items, :through => :orders

You can then do:

customer = Customer.find(:first)
order_items = customer.order_items

To find all the unread OrderItems (where read == false), you can add a named_scope to your OrderItem:

named_scope :unread, :conditions => {:read => false}

you can then find all unread OrderItems for one customer by:

customer.order_items.unread

2) Show all OrderItems of a Customer belonging to a given Category

Again, a named scope in OrderItem (I assume OrderItem belongs_to Category):

named_scope :in_category, lambda { |name|
  {:conditions => {:categories => {:name => name}}, :include => :category}
}

Two Notes:

  1. You have to include :category in the conditon

  2. even though its belongs_to :category (singular), you have to use :categories (plural). This is because the :include adds the table as plural. (I don't know why it does that, and for me this seems like a hint that this is not the best solution.)

Last question: why doesn't @orders.find_all_by_x(...) work?

This does work as expected for me. What error message are you getting?

Calavera
@calavera: To part 1: While Customer has_many :order_items, :through => :orders, I cannot say that OrderItem has_many :customers, :through => :orders. My Order table certainly only has customer_id and NOT order_item id. Can you have a one-way has_many through?
sscirrus
maybe you need has_one :through
Bohdan Pohorilets
@sscirrus:Bohdan is right, you can use has_one :customer, :through => :order
Calavera