Hi, I recently performed a major rewrite of my app to provide better encapsulation and more descriptive behavior for my models. But I also encountered very often a dilemma: does the logic I am writing belong to the class, or does it belong to an instance? I am running an ecommerce website, so I have the following models: User(id, name, ...) Order(id, user_id, status, amount) Item(id, order_id, product_id, quantity) Product(id, price, title, ...) Now I''d like to know if a given User has purchased a given Product. So I fetch my user and product objects user = User.find_by_name(''Fernando'') product = Product.find_by_title(''The book of Awesome'') Then, here is my problem. How would you search if the book was purchased by the user? There are few ways of doing it: * User#find_if_user_purchased_product?(product_id) And then the search makes a join with Item and Order, but it introduces the state of the Order in the condition, it could be ''paid'', ''unpaid''. And here is my problem: I recently migrated from an integer state (i.e: paid = 0, unpaid = 5) to strings. And that logic is in the User model, but a User shouldn''t be aware about how Order handles its payment status. Therefore this might mean that I should create a class method: Order.find_paid_order_for_product_by_user(product_id, user_id) This only has to know about the product_id and the user_id. Now this means that my User#find_if_user_purchased_product(product_id) code becomes something like: def find_if_user_purchased_product?(product_id) !!Order.find_paid_order_for_product_by_user(product_id, self.id) end Doesn''t it seem stupid that 1 method under hood, simply calls another method? Don''t I have a problem of how I am searching if my user has purchased this product? How would you handle such problem? It''s not only related to my ecommerce shop, it could be anything where a DB search is made across 3 tables of relationship. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I think the problem lies in the ability for the DB to perform a join across multiple tables which in fact breaks encapsulation! Maybe I should break down my DB query that uses joins into smaller queries. The code is obviously less efficient, but it''s more maintainable, as each model is only concerned with its own stuff, and therefore I don''t have to make edits across various models just because I changed the name or type of attributes in a single model. Using joins across multiple models doesn''t feel right anymore. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I think this is a common ''dilemma'' that comes across every now and then. "Which model should I ask for this information"? I also think there''s no right answer. But here''s my two cents: In your case, you want to know which a product was purchased by a user, so the question could go both ways: Did a user purchase this product, or was this product purchased by the user. I think it depends on your views and controllers, in other words, what is the main object you find out the relationship about? If you''re on a user view and want to list purchased products, you might just ask @user.orders.each { |o| o.products }.flatten (or some such thing). Similarly, @product.orders.each { |o| o.user }.flatten would give you all users who purchased that product. (this is all making some assumptions about your relationships). In either case, you are working with either an instance of User, or an instance of Product, so I think that that this should not be a class method, but an instance method. Same goes when you''re already dealing with a user instance and a product instance and you want to find out if that relationship exists, which is more to the point of your original post. How about an instance method on the User class called has_purchased?: @user.has_purchased?(@product) => true #or false Here''s a stab at that method... #class User < AR:Base def has_purchased?(product) !self.orders.products.find_by_id(product).nil? end About joins, I think they are absolutely needed. Some models are more "self contained" than others, but since you''re working with a relational model, it is natural that some models depend on others, and using :joins or :include in your finders makes life easier in many cases... On Apr 1, 5:05 am, Fernando Perez <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> I think the problem lies in the ability for the DB to perform a join > across multiple tables which in fact breaks encapsulation! > > Maybe I should break down my DB query that uses joins into smaller > queries. The code is obviously less efficient, but it''s more > maintainable, as each model is only concerned with its own stuff, and > therefore I don''t have to make edits across various models just because > I changed the name or type of attributes in a single model. > > Using joins across multiple models doesn''t feel right anymore. > -- > Posted viahttp://www.ruby-forum.com/.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
It also depends how optimal your SQL ends up. Blog: http://random8.zenunit.com/ Learn rails: http://sensei.zenunit.com/ On 01/04/2009, at 11:28 PM, Harold <harold.gimenez-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > I think this is a common ''dilemma'' that comes across every now and > then. "Which model should I ask for this information"? I also think > there''s no right answer. But here''s my two cents: > > In your case, you want to know which a product was purchased by a > user, so the question could go both ways: Did a user purchase this > product, or was this product purchased by the user. I think it depends > on your views and controllers, in other words, what is the main object > you find out the relationship about? If you''re on a user view and want > to list purchased products, you might just ask @user.orders.each { |o| > o.products }.flatten (or some such thing). Similarly, > @product.orders.each { |o| o.user }.flatten would give you all users > who purchased that product. (this is all making some assumptions about > your relationships). > > In either case, you are working with either an instance of User, or an > instance of Product, so I think that that this should not be a class > method, but an instance method. > > Same goes when you''re already dealing with a user instance and a > product instance and you want to find out if that relationship exists, > which is more to the point of your original post. How about an > instance method on the User class called has_purchased?: > > @user.has_purchased?(@product) => true #or false > > > Here''s a stab at that method... > #class User < AR:Base > def has_purchased?(product) > !self.orders.products.find_by_id(product).nil? > end > > About joins, I think they are absolutely needed. Some models are more > "self contained" than others, but since you''re working with a > relational model, it is natural that some models depend on others, and > using :joins or :include in your finders makes life easier in many > cases... > > > On Apr 1, 5:05 am, Fernando Perez <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> > wrote: >> I think the problem lies in the ability for the DB to perform a join >> across multiple tables which in fact breaks encapsulation! >> >> Maybe I should break down my DB query that uses joins into smaller >> queries. The code is obviously less efficient, but it''s more >> maintainable, as each model is only concerned with its own stuff, and >> therefore I don''t have to make edits across various models just >> because >> I changed the name or type of attributes in a single model. >> >> Using joins across multiple models doesn''t feel right anymore. >> -- >> Posted viahttp://www.ruby-forum.com/. > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---