Hi all, I''ll do my best to explain this... I have the possibility to have two customer types in my system: 1. A "mailing list" type: new records are saved when user enters email address in the mailing list subscription form. 2. A "full" type: new records are saved when the user buys something and provides all their billing/shipping details, etc. Model: - I''d prefer to have 1 customers table with a column to mark their status. Validation: - The customer email address is common to both so validation on this is easy. - I want the "full" type to have extended validation rules, first and last name for example. - I''d like to enforce a unique email address. Questions: - Rails looks to support model based validation well. Is there a way to specify validation rules depending on which customer type is being saved (as I only want email validated for a mailing list type, but for the full type I need validation on pretty much everything)? Should I have 2 models? How do I map this to a single database table? - How do I code an upgrade on a customer? For example, if the customer is new and does not have an existing record, then when they buy something and fill out their details there is no problem here (besides the validation issue above). But, if the customer does have an existing record, is there an elegant way to "merge" the extended details from the form with the existing email address marked record on the database? Currently I have the code: form = params[:new_customer] email = form[:email] tmp_customer = Customer.find_by_email(email) if tmp_customer != nil && (tmp_customer.level == ''mailinglist'' || tmp_customer.level == ''test'') @customer = Customer.new(params[:new_customer]) @customer.id = tmp_customer.id @customer.level = ''full'' if @customer.update_attributes(@customer) flash[:notice] = "Customer details upgraded successfully." redirect_to(:action => ''payment_details'') else render(:action => ''checkout'') end else @customer = Customer.new(params[:new_customer]) @customer.level = ''full'' if @customer.save flash[:notice] = "Customer details saved successfully." redirect_to(:action => ''payment_details'') else render(:action => ''checkout'') end end But with this I get the error: undefined method `stringify_keys!'' for #<Customer:0x38b1ac0> My attempt was to get the existing customer, set the id of the form data to the record on the database and then perform an update. I''m not sure however if I have covered all of the Rails requirements to do something like this. But, possibly (most likely due to my lack of Rails experience), there is a better way. Sorry for the long post, I hope it makes sense, can anyone help? Thanks, Dan
Hi Dan, I did something like this in the past, and I took the approach of separating list users from full users. Logically they just didn''t seem the same to me. I wrote a ListUser model which only had a name and email address, and a User model which had all the info for a regular user. When I wanted to send an email to everyone, I''d just pull all the email addresses from each table and mail it out. This setup also had the advantage of allowing me to direct different messages to previous customers and potential customers - something along the lines of, " Go to mysite.com/abcd to access the great deal available only to current customers!" and "I see you aren''t a customer yet..well you''re missing out on my ___ great deal. Make an order by 6pm on Tuesday and you''ll be able to take advantage of this killer offer" To avoid duplicate emails, when someone signed up for the site I would just delete their entry in the list users table if it exists. Worked pretty nicely for me. Pat On 3/14/06, Dan Harper <dan@danharper.org> wrote:> Hi all, > > I''ll do my best to explain this... I have the possibility to have two > customer types in my system: > 1. A "mailing list" type: new records are saved when user enters email > address in the mailing list subscription form. > 2. A "full" type: new records are saved when the user buys something and > provides all their billing/shipping details, etc. > > Model: > - I''d prefer to have 1 customers table with a column to mark their status. > > Validation: > - The customer email address is common to both so validation on this is > easy. > - I want the "full" type to have extended validation rules, first and > last name for example. > - I''d like to enforce a unique email address. > > Questions: > - Rails looks to support model based validation well. Is there a way > to specify validation rules depending on which customer type is being > saved (as I only want email validated for a mailing list type, but for > the full type I need validation on pretty much everything)? Should I > have 2 models? How do I map this to a single database table? > - How do I code an upgrade on a customer? For example, if the customer > is new and does not have an existing record, then when they buy > something and fill out their details there is no problem here (besides > the validation issue above). But, if the customer does have an existing > record, is there an elegant way to "merge" the extended details from the > form with the existing email address marked record on the database? > Currently I have the code: > > form = params[:new_customer] > email = form[:email] > tmp_customer = Customer.find_by_email(email) > if tmp_customer != nil && (tmp_customer.level == ''mailinglist'' > || tmp_customer.level == ''test'') > @customer = Customer.new(params[:new_customer]) > @customer.id = tmp_customer.id > @customer.level = ''full'' > if @customer.update_attributes(@customer) > flash[:notice] = "Customer details upgraded successfully." > redirect_to(:action => ''payment_details'') > else > render(:action => ''checkout'') > end > else > @customer = Customer.new(params[:new_customer]) > @customer.level = ''full'' > if @customer.save > flash[:notice] = "Customer details saved successfully." > redirect_to(:action => ''payment_details'') > else > render(:action => ''checkout'') > end > end > > But with this I get the error: undefined method `stringify_keys!'' for > #<Customer:0x38b1ac0> > My attempt was to get the existing customer, set the id of the form data > to the record on the database and then perform an update. I''m not sure > however if I have covered all of the Rails requirements to do something > like this. But, possibly (most likely due to my lack of Rails > experience), there is a better way. > > Sorry for the long post, I hope it makes sense, can anyone help? > > Thanks, > Dan > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
Interesting, thanks for your insight Pat. I''ll have a think about it. I actually got part of it to work whilst hacking away on the train. It seems I wasn''t thinking too straight on the first attempt and was over complicating things. If I change: form = params[:new_customer] email = form[:email] tmp_customer = Customer.find_by_email(email) if tmp_customer != nil && (tmp_customer.level == ''mailinglist'' || tmp_customer.level == ''test'') @customer = Customer.new(params[:new_customer]) @customer.id = tmp_customer.id @customer.level = ''full'' if @customer.update_attributes(@customer) to: form = params[:new_customer] email = form[:email] tmp_customer = Customer.find_by_email(email) if tmp_customer != nil && (tmp_customer.level == ''mailinglist'' || tmp_customer.level == ''test'') if tmp_customer.update_attributes(params[:new_customer]) and include the level=''full'' as a hidden field in the form it works beautifully! I think that the error was generated because the new customer (@customer) didn''t have an id value because Rails only generates it just before it is written to the database. Because I''m already checking that the email address matches, it doesn''t matter if it gets overwritten in the update (this issue led to a bit of reverse thinking in the first and broken solution). But still an issue is the validation issue. Pat''s solution certainly gets around the problem. Can anyone else chip in with some advice? Thanks, Dan Pat Maddox wrote:> Hi Dan, > > I did something like this in the past, and I took the approach of > separating list users from full users. Logically they just didn''t > seem the same to me. I wrote a ListUser model which only had a name > and email address, and a User model which had all the info for a > regular user. When I wanted to send an email to everyone, I''d just > pull all the email addresses from each table and mail it out. This > setup also had the advantage of allowing me to direct different > messages to previous customers and potential customers - something > along the lines of, " Go to mysite.com/abcd to access the great deal > available only to current customers!" and "I see you aren''t a customer > yet..well you''re missing out on my ___ great deal. Make an order by > 6pm on Tuesday and you''ll be able to take advantage of this killer > offer" > > To avoid duplicate emails, when someone signed up for the site I would > just delete their entry in the list users table if it exists. Worked > pretty nicely for me. > > Pat >
I think you want to check out some of the writings on Single Table Inheritance - http://wiki.rubyonrails.com/rails/pages/SingleTableInheritance - http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html Rails supports STI -- check out ActiveRecord::Base under STI...
Ah ha! That looks exactly like what I need. Thanks Kenneth. Dan Kenneth Lee wrote:> I think you want to check out some of the writings on Single Table Inheritance > > - http://wiki.rubyonrails.com/rails/pages/SingleTableInheritance > - http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html > > Rails supports STI -- check out ActiveRecord::Base under STI... >