Model Best Practices.

Ok, although not entirely new to RoR, myself and a colleague have been "discussing" some best practices for `find`.

The scenario: - table of users - table of companies - a user can belong to multiple companies - end result is that the list will be used for a select list.

So, if I want to get a list of companies that a particular user has been with in the last X time frame, which model should have the specialized `find` (because we may / may not have to do a "GROUP BY" as well).

Should it be in the users model, because it's all the companies would have the user in common?

Or:

Should it be in the companies model, because it will be a collection of companies?

Thanks in adavance

Woops ... I should've also mentioned that the example I gave is quite a high level rendition. The reason I'm not using HABTM or another associate is because there are quite a few other tables between users & companies and some extra conditions. Originally we had it all setup through associations but it was just just too dang slow so doing SQL by hand to significantly speed things up - the main reason for this is because of the a GROUP BY, ORDER BY combo we got going on.

But the same principle question applies: which model should be returning the result?

Based on what you’ve shared, I’d put the finder on Company, since you want a set of companies.

If you've got:

class User < ActiveRecord::Base   has_and_belongs_to_many :companies end

class Company < ActiveRecord::Base   has_and_belongs_to_many :users end

All you need to do is:

user = User.find(params[:id]) user.companies

You don't need to write a special finder: it's built for you when you declare the model relationships.

KumaZatheef wrote:

@Brent: If the join was that straight forward then there wouldn't be such an issue, but as I mentioned in my immediately follow up to the original post, the join is actually quite complex ... the optimized SQL is as such: (as displayed with checks in the function)

    query = "SELECT c.id as company_id, c.name as company_name               FROM entries as e                 INNER JOIN project_tasks AS pt ON pt.id = e.project_task_id                 INNER JOIN projects as p ON p.id = pt.project_id                 INNER JOIN jobs as j on j.id = p.job_id                 INNER JOIN companies as c ON c.id = j.company_id               WHERE e.user_id = "+self.id.to_s+" "

    query += " AND entries.start_timestamp >= "+start_timestamp.to_s +" " if( !start_timestamp.nil? )     query += " AND entries.end_timestamp <= "+end_timestamp.to_s+" " if( !end_timestamp.nil? )

    query += " AND c.active = "+is_active.to_s+"                 GROUP BY c.id ORDER BY max( e.start_timestamp ) DESC"

Obviously this more complex than what I originally started polling about, but this gives insight into the end desired result.

So the poll question remains: should this be in the User model (since it's a user's list of Companies), or be in the Company model since that's what it's actually returning.

KumaZatheef wrote:

So the poll question remains: should this be in the User model (since it's a user's list of Companies), or be in the Company model since that's what it's actually returning.

In this case, I think it would belong in both places. I would make a pair of methods, that work together. One in User that asks Company for a list for itself, and then the method in Company that takes in the User's id and returns the list.

You wouldn't want to put the method just in Users, because it's stepping on Company's toes by pulling data from Company's table. On the other hand, you still want to be able to call user.companies.

class Company < ActiveRecord::Base   def self.find_for_user(id)     # excecute the correct SQL, wrapping the results up the ActiveRecord way   end end

class User < ActiveRecord::Base   def companies     Company.find_for_user(self.id)   end end

Hmmm ... that's a good way to cover possible scenarios. So the "real" call would be in Company, but Users would access it .... is that ok (ex: "standard") to have one Model accessing another's functions ... for some reason I figured that'd be crossing into each other's territory ...

KumaZatheef wrote:

is that ok (ex: "standard") to have one Model accessing another's functions ... for some reason I figured that'd be crossing into each other's territory ...

It is standard to have the models talk to each other. That's what "has_and_belongs_to_many" and those sorts of relationship declarations do; they're just meta-programming tools to create methods like the ones I'm suggesting.

oh yeah, right .... duh. That makes sense, thanks a bunch for your feedback, much appreciated!!

I'm curious what others have to say as well ... agree / disagree?

KumaZatheef wrote:

oh yeah, right .... duh. That makes sense, thanks a bunch for your feedback, much appreciated!!

I'm curious what others have to say as well ... agree / disagree?

On Jan 25, 4:30 pm, Brent Miller <rails-mailing-l...@andreas-s.net>

Disagree:

I would put that nasty looking query in the association itself. Sence that is something that is probably going to be a maintenance issue all by itsself, Id make it a module -- esp. because I bet you have more then one of these functions:

Hmmm ... interesting idea of putting it in a Module, never thought of that. I'll take a look at the feasibility of doing it that way, within the context of our existing code. You're right, we do have more than one of these functions; however, the others apply to associations between User and other Models (ex: jobs of a given company). Any other thoughts / ideas out there?

I'm liking all of these suggestions so far .... thank you so much to those that have contributed, very helpful, much appreciated!