Strategy for Handling Separate Shipping and Billing Addresses

Hi - Very new to RoR. I have looked around at different strategies
for handling shipping and billing addresses for an e-commerce
applications. What I am trying to do is to have a user registration
form where the user need to also fill out the default shipping
address. Ultimately, I would like for the users to be able to add
additional shipping address and billing addresses.

I am sort of going down the path outlined by ryanb's here and using
Single Table Inheritance where the shipping address and billing
address inherits from address class. Is this the best approach?

ryanb suggested something along the lines below:

    Customer
    has_many :addresses
    has_many :orders

    Address
    belongs_to :customer

    Order
    belongs_to :customer
    belongs_to :billing_address #...
    belongs_to :shipping_address #...

Is this an instance where I need to use both STI and polymorphic
association?

Can someone walk through an example of what is needed to do this or a
strategy to accomplish this?

Here is what I have so far for the registration form:

Hi - Very new to RoR. I have looked around at different strategies
for handling shipping and billing addresses for an e-commerce
applications. What I am trying to do is to have a user registration
form where the user need to also fill out the default shipping
address. Ultimately, I would like for the users to be able to add
additional shipping address and billing addresses.

I am sort of going down the path outlined by ryanb's here and using
Single Table Inheritance where the shipping address and billing
address inherits from address class. Is this the best approach?

ryanb suggested something along the lines below:

Customer
has_many :addresses
has_many :orders

Address
belongs_to :customer

Order
belongs_to :customer
belongs_to :billing_address #...
belongs_to :shipping_address #...

Is this an instance where I need to use both STI and polymorphic
association?

No I don't think you need either. You have a case where an Order can
have two different addresses, but they are both always Addresses.

I haven't created the order.rb model yet, but ryanb did suggest making
sure that...

"The billing_address_id column would go in the orders table. Same
with the shipping."

Is this the only table in the database that would contain this
column? Should they also exist in the address table or is this
handled by Rails through the type column?

I don't think that they need to be in the address table, in this case.
Let's see.

Here's my current code but I'm certain I'm missing some key concepts
here. I am providing some of the code below. Any help would be
greatly appreciated.

users_controller.rb
************************************************
class UsersController < ApplicationController

user.rb model:
************************************************
class User < ActiveRecord::Base
acts_as_authentic
has_many :addresses
accepts_nested_attributes_for :addresses, :allow_destroy => true

def address_attributes=(address_attributes)
address_attributes.each do |attributes|
addresses.build(attributes)
end
end

How do I make the default registration address be the default
shipping_address? I am using address here but should it be
shipping_address? Should I use a hidden field to set the type and
modify the create section of the code?

This isn't really an issue with the user model. User's don't have a
shipping address, they just have one or more addresses. Now they
might have one of those addresses be the one they normal want orders
shipped to.

I'll get to setting the defaults for a order at the end.

************************************************

addresses_controller.rb
************************************************
class AddressesController < ApplicationController
# GET /addresses
# GET /addresses.xml

def index
@addresses = Address.all

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @addresses }
end
end

# GET /addresses/1
# GET /addresses/1.xml
def show
@address = Address.find(params[:id])

respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @address }
end
end

# GET /addresses/new
# GET /addresses/new.xml
def new
@address = Address.new

respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @address }
end
end

# GET /addresses/1/edit
def edit
@address = Address.find(params[:id])
end

# POST /addresses
# POST /addresses.xml
def create
@address = Address.new(params[:address])

respond_to do |format|
if @address.save
flash[:notice] = 'Address was successfully created.'
format.html { redirect_to(@address) }
format.xml { render :xml => @address, :status
=> :created, :location => @address }
else
format.html { render :action => "new" }
format.xml { render :xml => @address.errors, :status
=> :unprocessable_entity }
end
end
end

# PUT /addresses/1
# PUT /addresses/1.xml
def update
@address = Address.find(params[:id])

respond_to do |format|
if @address.update_attributes(params[:address])
flash[:notice] = 'Address was successfully updated.'
format.html { redirect_to(@address) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @address.errors, :status
=> :unprocessable_entity }
end
end
end

# DELETE /addresses/1
# DELETE /addresses/1.xml
def destroy
@address = Address.find(params[:id])
@address.destroy

respond_to do |format|
format.html { redirect_to(addresses_url) }
format.xml { head :ok }
end
end
end
************************************************

The main comment I've got about this, is that if every address belongs
to a user, then the address controller should be scoped to a user, and
instead of Address.find, Address.new, Address.create you should find
instantiate and create addresses using the addresses association of
user.

address.rb model
************************************************
class Address < ActiveRecord::Base
belongs_to :user
end
************************************************

billing_address.rb
************************************************
class BillingAddress < Address
end
************************************************

shipping_address.rb
************************************************
class ShippingAddress < Address
end
************************************************

You don't need subclasses unless there is a behavioral difference
between billing and shipping addresses. I doubt that there is, they
are both addresses, it's just that they are playing different roles.

If you change the Order model slightly

Order
   belongs_to :customer
   belongs_to :billing_address, :class_name => 'Address'
   belongs_to :shipping_address, :class_name => 'Address'

Then you can use the Address class for both roles.

Now having gotten here, you could also model the notion of a customer
having a preferred shipping address (and even a preferred billing
address) in a similar way

User
   ...
  belongs_to :preferred_billing_address, :class_name => "Address"
  belongs_to :preferred_shipping_address, :class_name => "Address"

It might seem odd to have a User 'belonging to' two particular
addresses, but you can alternatively thing of belongs_to as
has_a_pointer_to, and has_many as has_many_pointing_to_me.

Now about the order. I think that there are at least two ways of
dealing with the billing and shipping addresses:

1) in the OrdersController

     def new
          @order = current_user.orders.build(
                               :billing_address =>
current_user.preferred_billing_address,
                               :shipping_address =>
current_user.preferred_shipping_address
                         )
        #...
      end

2) in the OrderModel

   class Order < ActiveRecord::Base
          belongs_to :customer
          belongs_to :billing_address, :class_name => 'Address'
          belongs_to :shipping_address, :class_name => 'Address'

          def after_initialize
              if new_record?
                 self.billing_address =
customer.preferred_billing_address unless billing_address
                  self.shipping_address =
customer.preferred_shipping_address unless shipping_address
              end
          end
    end

The first approach is probably more conventional.

HTH

Order
  belongs_to :customer
  belongs_to :billing_address, :class_name => 'Address'
  belongs_to :shipping_address, :class_name => 'Address'
....snip....
1) in the OrdersController

    def new
         @order = current_user.orders.build(
                              :billing_address =>
current_user.preferred_billing_address,
                              :shipping_address =>
current_user.preferred_shipping_address
                        )
       #...
     end

One thing that popped into my head while reading this is that given the above, if I update my shipping/billing address, any orders I've previously placed will have their addresses updated as well. Which may not be what you want -- since you'd lose the ability to tell where an order was shipped should I do that.

So, you might want to add some logic to the Address model that if the address currently being changed is associated with any orders that instead a new address should be created and the old one set to some sort of "archived, but need to keep around as it relates to an order" status.

This is similar to needing to store the price of the product in the order itself so that if you later change the price you aren't retroactively changing the order information...

-philip

No they won't.

When the order is saved, then it will have a shipping_address_id and
billing_address_id set to either the values intialized by the
controller in the new method (which is used as a template for the
values posted back to the create method by the form) or whatever the
user changed it to on the order form.

If the user changes his preferences later, it won't affect existing orders.

I think you might be better off just to write some code and test it
and see what happens, rather than continuing with what seem to be
gedanken experiments.

Order
  belongs_to :customer
  belongs_to :billing_address, :class_name => 'Address'
  belongs_to :shipping_address, :class_name => 'Address'
....snip....
1) in the OrdersController

    def new
         @order = current_user.orders.build(
                              :billing_address =>
current_user.preferred_billing_address,
                              :shipping_address =>
current_user.preferred_shipping_address
                        )
       #...
     end

One thing that popped into my head while reading this is that given
the above, if I update my shipping/billing address, any orders I've
previously placed will have their addresses updated as well.

No they won't.

When the order is saved, then it will have a shipping_address_id and
billing_address_id set to either the values intialized by the
controller in the new method (which is used as a template for the
values posted back to the create method by the form) or whatever the
user changed it to on the order form.

If the user changes his preferences later, it won't affect existing orders.

I think you might be better off just to write some code and test it
and see what happens, rather than continuing with what seem to be
gedanken experiments.

I think you misunderstood or I didn't make it clear... if the user changes his preferred address, everything will be fine. If there is functionality that lets a user update his old addresses then there will be a problem in that the order will be updated too. Say the user ships things to his work address. Then he changes jobs and updates his work address... any order that had a shipping address of that same id will now have the new address, not the old.

class Address < ActiveRecord::Base
end
class Customer < ActiveRecord::Base
   belongs_to :preferred_billing_address, :class_name => "Address"
   belongs_to :preferred_shipping_address, :class_name => "Address"
   has_many :orders
end
class Order < ActiveRecord::Base
   belongs_to :customer
   belongs_to :billing_address, :class_name => 'Address'
   belongs_to :shipping_address, :class_name => 'Address'
end

   create_table "addresses", :force => true do |t|
     t.string "street"
     t.datetime "created_at"
     t.datetime "updated_at"
   end

   create_table "customers", :force => true do |t|
     t.string "name"
     t.integer "preferred_billing_address_id"
     t.integer "preferred_shipping_address_id"
     t.datetime "created_at"
     t.datetime "updated_at"
   end

   create_table "orders", :force => true do |t|
     t.integer "customer_id"
     t.integer "billing_address_id"
     t.integer "shipping_address_id"
     t.datetime "created_at"
     t.datetime "updated_at"
   end

>> c = Customer.create(:name => 'Philip')
>> a1 = Address.create(:street => '123 Maple St')
>> a2 = Address.create(:street => '456 Syrup St')
>> c.preferred_shipping_address = a1
>> c.preferred_billing_address = a2
>> c.save
>> o = c.orders.build(:shipping_address => c.preferred_shipping_address,
                       :billing_address => c.preferred_billing_address)
>> c.preferred_shipping_address.street
=> "123 Maple St"
>> c.preferred_billing_address.street
=> "456 Syrup St"
>> o.shipping_address.street
=> "123 Maple St"
>> o.billing_address.street
=> "456 Syrup St"
>> a1.street = '789 Blueberry St'
=> "789 Blueberry St"
>> a1.save
>> c.preferred_shipping_address.street
=> "789 Blueberry St"
>> o.shipping_address.street
=> "789 Blueberry St"

Rick, thanks for responding. I appreciate the help.

I wasn't familiar with scoping but after re

> Hi - Very new to RoR. I have looked around at different strategies
> for handling shipping and billing addresses for an e-commerce
> applications. What I am trying to do is to have a user registration
> form where the user need to also fill out the default shipping
> address. Ultimately, I would like for the users to be able to add
> additional shipping address and billing addresses.

> I am sort of going down the path outlined by ryanb's here and using
> Single Table Inheritance where the shipping address and billing
> address inherits from address class. Is this the best approach?

> ryanb suggested something along the lines below:

> Customer
> has_many :addresses
> has_many :orders

> Address
> belongs_to :customer

> Order
> belongs_to :customer
> belongs_to :billing_address #...
> belongs_to :shipping_address #...

> Is this an instance where I need to use both STI and polymorphic
> association?

No I don't think you need either. You have a case where an Order can
have two different addresses, but they are both always Addresses.

How do I distinguish between when an address is a shipping and when it
is a billing address? How does it get stored in the database?

> I haven't created the order.rb model yet, but ryanb did suggest making
> sure that...

> "The billing_address_id column would go in the orders table. Same
> with the shipping."

> Is this the only table in the database that would contain this
> column? Should they also exist in the address table or is this
> handled by Rails through the type column?

I don't think that they need to be in the address table, in this case.
Let's see.

So, I'm assuming the type column will be used.

> Here's my current code but I'm certain I'm missing some key concepts
> here. I am providing some of the code below. Any help would be
> greatly appreciated.

> users_controller.rb
> ************************************************
> class UsersController < ApplicationController
> user.rb model:
> ************************************************
> class User < ActiveRecord::Base
> acts_as_authentic
> has_many :addresses
> accepts_nested_attributes_for :addresses, :allow_destroy => true

> def address_attributes=(address_attributes)
> address_attributes.each do |attributes|
> addresses.build(attributes)
> end
> end

> How do I make the default registration address be the default
> shipping_address? I am using address here but should it be
> shipping_address? Should I use a hidden field to set the type and
> modify the create section of the code?

This isn't really an issue with the user model. User's don't have a
shipping address, they just have one or more addresses. Now they
might have one of those addresses be the one they normal want orders
shipped to.

I'll get to setting the defaults for a order at the end.

> ************************************************

> addresses_controller.rb
> ************************************************
> class AddressesController < ApplicationController
> # GET /addresses
> # GET /addresses.xml

> def index
> @addresses = Address.all

> respond_to do |format|
> format.html # index.html.erb
> format.xml { render :xml => @addresses }
> end
> end

> # GET /addresses/1
> # GET /addresses/1.xml
> def show
> @address = Address.find(params[:id])

> respond_to do |format|
> format.html # show.html.erb
> format.xml { render :xml => @address }
> end
> end

> # GET /addresses/new
> # GET /addresses/new.xml
> def new
> @address = Address.new

> respond_to do |format|
> format.html # new.html.erb
> format.xml { render :xml => @address }
> end
> end

> # GET /addresses/1/edit
> def edit
> @address = Address.find(params[:id])
> end

> # POST /addresses
> # POST /addresses.xml
> def create
> @address = Address.new(params[:address])

> respond_to do |format|
> if @address.save
> flash[:notice] = 'Address was successfully created.'
> format.html { redirect_to(@address) }
> format.xml { render :xml => @address, :status
> => :created, :location => @address }
> else
> format.html { render :action => "new" }
> format.xml { render :xml => @address.errors, :status
> => :unprocessable_entity }
> end
> end
> end

> # PUT /addresses/1
> # PUT /addresses/1.xml
> def update
> @address = Address.find(params[:id])

> respond_to do |format|
> if @address.update_attributes(params[:address])
> flash[:notice] = 'Address was successfully updated.'
> format.html { redirect_to(@address) }
> format.xml { head :ok }
> else
> format.html { render :action => "edit" }
> format.xml { render :xml => @address.errors, :status
> => :unprocessable_entity }
> end
> end
> end

> # DELETE /addresses/1
> # DELETE /addresses/1.xml
> def destroy
> @address = Address.find(params[:id])
> @address.destroy

> respond_to do |format|
> format.html { redirect_to(addresses_url) }
> format.xml { head :ok }
> end
> end
> end
> ************************************************

The main comment I've got about this, is that if every address belongs
to a user, then the address controller should be scoped to a user, and
instead of Address.find, Address.new, Address.create you should find
instantiate and create addresses using the addresses association of
user.

Yes, you are correct. I was not familiar with the scoping aspect of
rails and after some reading it appears that it is probably good
practice since without it any user can change another users address.
That's sort of what I gathered from reading about it.

> address.rb model
> ************************************************
> class Address < ActiveRecord::Base
> belongs_to :user
> end
> ************************************************

> billing_address.rb
> ************************************************
> class BillingAddress < Address
> end
> ************************************************

> shipping_address.rb
> ************************************************
> class ShippingAddress < Address
> end
> ************************************************

You don't need subclasses unless there is a behavioral difference
between billing and shipping addresses. I doubt that there is, they
are both addresses, it's just that they are playing different roles.

If you change the Order model slightly

Order
belongs_to :customer
belongs_to :billing_address, :class_name => 'Address'
belongs_to :shipping_address, :class_name => 'Address'

Then you can use the Address class for both roles.

Again, I understand what you are saying here, but where (i.e. - in the
address table and using the type column or in their own tables) would
the shipping and billing addresses be stored or designated in the
database?

Hi Philip - Thanks for responding. Yeah, I thought about what happens
when they go to change an old address. Not sure how to handle it at
this point. I did find a plugin written by Ryan Bates that at least
handled "freezing" an association.

http://github.com/ryanb/association-freezer/blob/4691b3247332ac9a5d534206e80080a54ff0a48d/association-freezer.gemspec

Seems like an interesting piece of code and I'll have to learn more
about it.

I see that you ran a test but I noticed that you used:

class Customer < ActiveRecord::Base
   belongs_to :preferred_billing_address, :class_name => "Address"
   belongs_to :preferred_shipping_address, :class_name => "Address"
   has_many :orders
end

What happens if you use has_many instead of belongs_to? It just makes
more sense to me that a Customer can have many addresses regardless of
whether they are of type shipping or billing.