Brandon
Brandon

Reputation: 1875

Moving MySQL routine to model

I have a PHP-driven website that I am converting to Rails. I currently have the following logic in a MySQL routine.

DECLARE FirstWeighinDate DATE;
DECLARE FirstWeighinWeight DECIMAL(5,1);
DECLARE MostRecentWeighinDate DATE;
DECLARE MostRecentWeighinWeight DECIMAL(5,1);
DECLARE NumberOfWeighins INT(11);
DECLARE NumberOfDeficitRecords INT(11);
DECLARE AverageDeficit INT(11);
DECLARE UserHeight INT(11);
DECLARE MostRecentBMI DECIMAL(5,1);

SELECT date INTO FirstWeighinDate FROM tblDeficitEntries WHERE weight IS NOT NULL AND user_id = inUserId ORDER BY date ASC LIMIT 1;
SELECT weight INTO FirstWeighinWeight FROM tblDeficitEntries WHERE weight IS NOT NULL AND user_id = inUserId ORDER BY date ASC LIMIT 1;
SELECT date INTO MostRecentWeighinDate FROM tblDeficitEntries WHERE weight IS NOT NULL AND user_id = inUserId ORDER BY date DESC LIMIT 1;
SELECT weight INTO MostRecentWeighinWeight FROM tblDeficitEntries WHERE weight IS NOT NULL AND user_id = inUserId ORDER BY date DESC LIMIT 1;
SELECT Height INTO UserHeight FROM tblLogins WHERE id = inUserId LIMIT 1;
SELECT COUNT(id) INTO NumberOfWeighins FROM tblDeficitEntries WHERE weight IS NOT NULL AND user_id = inUserId;
SELECT COUNT(id) INTO NumberOfDeficitRecords FROM tblDeficitEntries WHERE user_id = inUserId;
SELECT (SUM(deficit) / NumberOfDeficitRecords) INTO AverageDeficit FROM tblDeficitEntries WHERE user_id = inUserId;
SELECT ((MostRecentWeighinWeight * 703) / (UserHeight * UserHeight)) INTO MostRecentBMI;

SELECT FirstWeighinDate, FirstWeighinWeight, MostRecentWeighinDate, MostRecentWeighinWeight, NumberOfWeighins, NumberOfDeficitRecords, AverageDeficit, MostRecentBMI;

I believe in a MVC situation, that logic belongs in the Model. I am unsure how to achieve this. I was thinking it should be done with a virtual accessor that is a hash. Is that correct? If I should have this somewhere besides the Model, where should it go? I don't expect the entire code to be converted in the answer, but I would greatly appreciate a couple of the lines to be converted so that I can get the idea. Thanks!

Upvotes: 0

Views: 62

Answers (1)

MrTheWalrus
MrTheWalrus

Reputation: 9700

So, you've got two models: There's a a User, and that the User has many associated Entry objects.

class User < ActiveRecord::Base
  has_many :entries
end

class Entry < ActiveRecord::Base
  belongs_to :user
  #attributes include weight, date
end

So, what about these values?

The most basic way to do what I believe you want would be to turn all of these into methods on your User model, using the associated models.

For instance,

SELECT date INTO FirstWeighinDate FROM tblDeficitEntries WHERE weight IS NOT NULL AND user_id = inUserId ORDER BY date ASC LIMIT 1;

could be replaced with the following, in the User class:

def first_weighin_date
  entries.order('date asc').first.date
end

Then, anywhere you have a User object, you can simply call the method:

u = User.find_by_name('Bob')
u.first_weighin_date

Now, if you're going to be displaying this information for a lot of users at once, you'll want to take advantage of eager loading. This, for instance, is going to be very inefficient:

User.all.map(&:first_weigh_in_date)

It will make individual calls to the database to load the associated entries for each user object. 100 users? 101 database hits. So tell Rails to go ahead and load all the objects, because you're going to need them:

User.includes(:entries).map(&:first_weigh_in_date)

Now there's just two calls to the database, one to load the users and one to load the deficit_entries.

Upvotes: 1

Related Questions