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?
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.
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.
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.
You will also find that has_and_belongs_to_many has been deprecated in favor of has_many :though => model_name which gives the same (and more) functionality.
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.
Related
I am new to Rails, ORM and database building. I am currently building a 2 sided marketplace platform like AirBnb, but I am not quite sure how to lay out the relationships between the models properly for this type of platform, like one_to_many rel or such.
Here is what I wish to achieve.
Customer can signup, create an account/login/profile -> User Table
Customer can generate a request/order, such as, food order, cleaning order, etc -->??
the other gets displayed to service workers, who fits the criteria for the order, if the worker is a cook, then he/she gets to see all of the orders related to food order, sorted by cost, requirement, etc.
I am not really sure how to map out the association of these 3 models... Can anyone help me?
class User < ActiveRecord::Base
has_many :orders
end
class Worker < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :worker
scope :for_cook, where("category == cook")
scope :for_cleaner, where("category == cleaner")
end
Then when you want the list of all orders that a cook can fulfill:
available_cook_orders = Order.for_cook
And you'd add additional logic to handle the other criteria you mentioned (cost, etc.)
In my Rails (3.2) app, an Order has many LineItems. A LineItem has many LineItemPayments. A LineItemPayment has one Payment. (LineItems can potentially be payed for multiple times (subscriptions), which is why I have the join table there.)
I need to be able to query for order information from a payment record. I can get an array of orders via relationships, but I know they will always be the same order. Is there a way in Rails to set up the association to reflect this? If not, would it better to set up a method for retrieving the array and then picking the order out of that, or rather just storing the order_id with the payment and set up a direct relationship that sidesteps all this?
You'll need to work with the orders collection and narrow it down accordingly per your own logic. Although you certainly 'can' add the order_id to the payment directly, that will denormalize your data (as a cache) which is only recommended when you start hitting performance bottlenecks in your queries - otherwise it's asking for trouble in the area of data integrity:
class Payment < ActiveRecord::Base
has_many :line_item_payments
has_many :line_items, :through => :line_item_payments
has_many :orders, :through => :line_items
# use this to get the order quickly
def order
orders.first
end
# use this to narrow the scope on the query interface for additional modifications
def single_order
orders.limit(1)
end
end
class LineItemPayment < ActiveRecord::Base
belongs_to :line_item
belongs_to :payment
end
class LineItem < ActiveRecord::Base
belongs_to :order
has_many :line_item_payments
end
Let say we have 4 tables
Client has many Campaign
Campaign has many Adgroup
Adgroup has many Ad
Ad
The relationship is the same, one to many. Let say we have known Ad.id, and we want to get Client.id from that Ad.id.
Could anybody give an elegant solution (by elegant i mean without writing sql statement, instead use active records procedure)?
Make sure that Ad belongs_to an Adgroup, and has a reference to it in it's db structure! Adgroups should belongs_to Campaigns and so on, all the way up. Remember, the dude with belongs_to is the one that needs to hold a reference in his db table to the guy that has_one him, not the other way round!
If the resources are created OK to begin with, you should be able to call #ad.adgroup.campaign.client.id and get a particular ad's (obtained, say, by #ad = Ad.find(some_id)) id.
Hope this helps!
With Rails associations, you have to remember to specify belongs_to for the other side of the relationship, so an ad will most likely has_one Adgroup and so on and so forth up the chain. Once you've coupled the associations, you can use ActiveRecord to method chain these models starting from the bottom, going up to the top of the hierarchy. So you would start with ad and chain it like:
#ad = Ad.find(an_id_or_name_or_whatever).Adgroup.Campaign.Client.id
Looking at the above, you can chain the Adgroup onto an Ad because of the associative relationship which gives you access to the methods of that parent model, all the way up to the Client model, of which .id is a method, and you can call it.
Take a look at some association basics from Rails here:
http://guides.rubyonrails.org/association_basics.html
First, ensure you have your relationships setup as follows:
class Ad < ActiveRecord::Base
belongs_to :ad_group, inverse_of: :ads
end
class AdGroup < ActiveRecord::Base
belongs_to :campaign, inverse_of: :ad_groups
has_many :ads, inverse_of :ad_group
end
class Campaign < ActiveRecord::Base
belongs_to :client, inverse_of: :campaigns
has_many :ad_groups, inverse_of :campaign
end
class Client < ActiveRecord::Base
has_many :campaigns, inverse_of :client
end
Use joins and pluck if all you want is the client id and efficient SQL:
Client.joins(campaigns: {ad_groups: :ad}).where(
ads: { id: some_id }).pluck('clients.id').first
If you want the entire client and efficient SQL then just:
Client.joins(campaigns: {ad_groups: :ad}).where(ads: { id: some_id }).first
I'm trying to implement something similar to that below. Basic setup is a Schedule model
class Schedule < ActiveRecord::Base
has_many :events
end
The Event model will then belong_to a Schedule.
Events are comprised of a name, datetime, running length and a Room
A Room is chosen from a select list when the event is created, all Rooms are created beforehand through seeding or admin interface. Room will store information like seating count.
I've been trying to find a method implementing this that feels right. Solutions seem to range between habtm or has_many :through but I can't see the need for a join table for this situation.
Theoretically, an Event has_one :room but the reverse relationship isn't belongs_to :event as a Room may be used for many events, this also requires the foreign key to be on the rooms table.
I've considered handling this manually in the model using a room_id foreign key in the event, I could then query for the relevant Room. I think this would work since I currently cannot see a requirement for finding all events using a Room.
class Event < ActiveRecord::Base
belongs_to :schedule
def room
Room.find(room_id)
end
end
Everything I've done so far in Rails has felt 'right', but all the solutions I've come up with here doesn't and I feel like I'm missing something; or maybe I'm just expecting that extra bit of magic.
Is there a "Rails" way to do this?
Wouldn't Event just have a belongs_to relationship to Room in this case?
class Event < ActiveRecord::Base
belongs_to :schedule
belongs_to :room
end
If you think about it from a schema perspective, your "events" table almost certainly has "schedule_id" and "room_id" columns.
The other side of the relationship is that a Room has_many Events.
I'm a beginning to ROR, but here's what I'm trying to achieve. I have two items I want to associate: matters and people. Each matter can have many people. That is, I want to create people and matters separately and later be able to link them.
For example, I may create:
Bill Clinton
Barack Obama
I may create the matters:
Global warming
War on terror
I want to be able to associate the users Bill Clinton AND Barack Obama to BOTH matters. Can someone point me to a tutorial that can show me how to do this?
I think has_and_belongs_to_many is used less and less by the RoR community now. While still supported, I think it is now more common to have an intermediate model (in your case something like PoliticianMatter) to join your Politician and Matter models.
Then your politician_matter table will have a PK, a politician_id and a matter_id.
Then you have
class PoliticanMatter < ActiveRecord::Base
belongs_to :politician
belongs_to :matter
end
The advantage of this approach is that if there ever need to be future properties of the politician -> matter relationship (e.g importance, date of last occurrence) you have a model which affords this - has_and_belongs_to_many would not support the addition of these extra properties.
You can also access the many to many relationship directly from the Politician and Matter models like this
class Politician < ActiveRecord::Base
has_many :politician_matters
has_many :matters, :through => :politician_matters
end
class Matter < ActiveRecord::Base
has_many :politician_matters
has_many :politicians, :through => :politician_matters
end
You need a many2many relationship between these two entities.
A matter can be studied by many people
A person can studie several matters
Rails uses the has_and_belongs_to_many helper to do that. You'll find more about that in the documentation and many many blog posts!
has_and_belongs_to_many helper
class Politician < ActiveRecord::Base
has_and_belongs_to_many :tasks
end
class Task < ActiveRecord::Base
has_and_belongs_to_many :politicians
end
What you need are 3 tables:
politicians, tasks and politicians_tasks (having the two columns politician_id and task_id, no primary key)
Hope this helps
Seb