views:

89

answers:

4

I can't seem to wrap my head around this, so I thought I'd post and see if anyone could help me out (please pardon the question if it's insultingly simple: it's complicated to me right now!)

I have these models:

order
service
customer

I think they speak for themselves: a service is what the customer buys when they place an order.

Ok.

So, naturally, I setup these relationships:

# a customer can have many orders
class Customer
   has_many :orders
end

# an order belongs to a single customer and can have many services
class Order
   belongs_to :customer
   has_many :services
end

... but here's where I trip up:

# a service can belong to many orders
class Service
   # belongs_to :order ???
end

Because my understanding of belongs_to is that--if I put it there--a service could only belong to one order (it would have only one value in the order_id key field--currently not present--tying it to only one order, where it needs to be able to belong to many orders).

What am I missing here?

+1  A: 
class Customer
   has_many :orders
end

class Service
   has_many :orders
end

class Order
   belongs_to :customer
   belongs_to :service
end

The Order should have customer_id and service_id, because it is in a many-to-one relationship with both.

Ben
It's always the simple things... thanks. +1 (for speed!)
neezer
Wait - doesn't this mean that `order` can only have one `service`? I need an `order` to have many `services`... ??
neezer
Oh, that wasn't apparent from your question. In that case, it's literally a textbook answer - this exact situation is the basis for the Agile Web Development with Rails book by DHH. Darryl's 2nd solution is what they do.
Ben
+1  A: 

There are two ways to handle this. The first is a rails-managed many-to-many relationship. In this case, you use a "has_and_belongs_to_many" relationship in both the Order and Service models. Rails will automatically create a join table which manages the relationships. The relationships look like this:

class Order
  has_and_belongs_to_many :services
end

class Service
  has_and_belongs_to_many :orders
end

The second way is to manage the join table yourself through an intermediate model. In this case, you might have another model called "LineItem" that represents a Service in the context of an Order. The relationships look like this:

class LineItem
  belongs_to :order
  belongs_to :service
end

class Order
  has_many :line_items
end

class Service
  has_many :line_items
end

I prefer the second myself. It's probably just me, but I don't get as confused about what's going on when it's explicit. Plus if I ever want to add some attributes to the relationship itself (like perhaps a quantity in your case) I'm already prepared to do that.

Darryl
Ahh, okay... I suspected it might be a habtm relationship, but wasn't sure. Thanks.
neezer
Take note that you'll still have to create the join tabel yourself in a HABTM relationship
EmFi
A: 

I think this Railscast will help you out - basically you have 2 options. You can use has_and_belongs_to_many or has_many :through.

Andy Gaskell
A: 

I think you have realized this but your order is really a composite domain model, sometimes called an aggregate in DDD speak.

Your Service is a really a listing of some product/service that someone can order. Your order aggregate records what someone ordered.

The aggregate as someone else said is made up of a header, the Order, which includes things like who ordered, the date, does it include taxes, shipping charge, etc. And the Order has_many OrderLineItem's. The OrderLineItem belongs_to Service and contains things like the quantity ordered, belongs_to the product/service, etc.

class Customer < ActiveRecord::Base has_many :orders end

class Order < ActiveRecord::Base belongs_to :customer end

class OrderLineItem < ActiveRecord::Base belongs_to :Order end

I personally use the OrderLineItem model name in deference to LineItem because in a system that needs to ship real products, as opposed to services, you might have allocations that link orders to inventory which would have line items and shipments that get the allocated product to the client, which also have line items. So the line item term can become very overloaded. This likely is not the case in your model because you're only doing services.

Mark Menard