How to make database entries stick to a User's account if those entries were created before the User account was

StackOverflow https://stackoverflow.com/questions/16502842

Question

I'm trying to think of the best way to allow users to create database objects without creating an account but then associating those objects with a user's account when they decide to sign up. I will describe an example application, how I would like it to behave, and the approaches I have come up with. I am looking for criticisms/enhancements to my approaches or a better approach purposed by one of you, my fellow programmers.

Example Application

Let's say we have a Rails application called Suggestion Box. This application allows users to visit the site and enter a suggestion. A Suggestion requires a description and an email address. The user does not need to be logged in. Here are the models:

app/models/suggestion.rb

class Suggestion < ActiveRecord::Base
  # == DB Columns
  # id           :integer
  # user_id      :integer
  # description  :text
  # email        :string

  belongs_to :user

  validates :description, :email, :presence => true    
end

app/models/user.rb

class User < ActiveRecored::Base
  # == DB Columns
  # id           :integer
  # name         :string
  # email        :string

  has_many :suggestions

  validates :name, :email, :presence => true    
end

Use Case

Frank comes to the Suggestion Box application and enters in a suggestion. The next day Frank enters in another. If we ask the Rails console for all of suggestions we will get this:

# rails console
Suggestion.all
=> [#<Suggestion id: 1, user_id: nil, email: "frank@example.com", description: "Don't worry">, 
#<Suggestion id: 2, user_id: nil, email: "frank@example.com", description: "Be happy">]

On the third day Frank decides to bite the bullet and sign up for an account. He then creates another suggestion. At this point if we ask the console for User objects we get Frank:

# rails console
User.all
=> [#<User id: 1, name: "Frank", email: "frank@example.com" >]

If we ask for Frank's suggestions we get all 3 suggestions:

# rails console
frank = User.last
=> [#<User id: 1, name: "Frank", email: "frank@example.com" >
frank.suggestions
=> #<ActiveRecord::Associations::CollectionProxy [#<Suggestion id: 1, user_id: nil, email: "frank@example.com", description: "Don't worry">,
#<Suggestion id: 2, user_id: nil, email: "frank@example.com", description: "Be happy">,
#<Suggestion id: 3, user_id: nil, email: "frank@example.com", description: "Don't worry be happy now!">]

Now how do we make this connection between Frank and his past suggestions automatic?

Approaches

#1 Cookies

When a non logged in user submits a session I could store an indicator in a cookie about the created suggestions. Then when they sign up I could use this info to find the suggestions and assign them to the user.

I don't really like this approach because it breaks down when the user accesses more than one device.

#2 After Create Callback

When a user signs up I could run an after create callback that would look like this:

app/models.user.rb

class User < ActiveRecored::Base
  ...

  after_create :assign_suggestions

  def assign_suggestions
    suggestions = Suggestion.where(:user_id => nil, :email => self.email)
    suggestions.each do |s|
      s.user = self
      s.save
    end 
  end   
end

I really like this approach but there is one major downside. If Frank creates a fourth suggestion when not logged in it will not be added to his user object. To fix this I could write a callback on the Suggestion class that could check for matching emails.

app/models/suggestion.rb

class Suggestion < ActiveRecord::Base
  ...  

  after_create :assign_users

  def assign_users
    if self.user.present?
      user = User.where(:email => self.email).first
      if user
        self.user = user
      end
    end
  end
end

This is pretty good but if Frank creates a suggestion with a different email address (which could happen, I have many) then the suggestion will not be connected to Frank's user object.

Question

What do you guys think of Approach #2? (good or bad) Is there another trick on top of this I could use to connect Frank to suggestions he makes with other email addresses? Can you think of a different approach that would work better than mine?

Thanks for the advice!

Était-ce utile?

La solution

It seems you're trying to solve two scenarios at once: assigning the user is you main question, and handling multiple email addresses to one account.

The email address field is duplicated in two models. Initially I would have suggested leave the email address in User, and create anonymous (no name) Users and then build from there. That would change your Suggestion create process since you would be associating to the User record immediately. Since you're trying to handle the multiple email question, I would suggest breaking the email address out into a separate model:

class EmailAddress << ActiveRecord::Base
  # == DB Columns
  # id           :integer
  # user_id      :integer
  # email        :string

  belongs_to :user
  has_many :suggestions
  validates :email, presence :true
end

change the other models accordingly:

class Suggestion << ActiveRecord::Base
  ...

  belongs_to :email
end

class User << ActiveRecord::Base
  ...

  has_many :email
end

It would be your option to either create User immediately or defer it until they actually sign up for an account (I would defer).

As for the scenario of someone creating a suggestion when not signed up and having to make that association after the fact. This structure makes that easier BUT I would actually not allow that personally. I would inform the user something like this email address is already registered, please sign in to submit your suggestion. Assuming you want to make sure that people aren't falsely creating suggestions under other people's names.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top