Reputation: 259
I have the app, where user can have one of several different profiles. Some of profile data are always the same (like first and last name, gender etc). Other fields may vary (for example, doctor can have license number and text about himself, while patient can have phone number etc).
I found approach, that fits pretty well, but still have some doubts. The point of my approach looks like this:
User model contains a lot of system-specific data, controlled by Devise and has_one :person
Person model contains common profile data and belongs_to :profile, :polymorphic => true
Doctor/Patient/Admin/etc contains more specific profile data and has_one :person, :as => :profile
With this approach i can simply check in Person model:
def doctor?
self.profile_type == 'Doctor'
end
But there is the few things doesn't give me a rest.
First one is performance. This approach requires a lot of additional joins. For example, for reading doctor's license number, first/last name and email at the same time it will generate 2 additional joins.
Second one is different ids for profile-specific model (i.e. Doctor) and for Person/User models. There will be situations, when user with ID=1 will have Patient relation with different ID, but it would be logical to have same ID for all this associated models.
Maybe you guys will see any more pitfalls in this approach? Is there any better solution for my situation?
Upvotes: 0
Views: 433
Reputation: 211600
You've got four basic patterns you can use here that might work.
Omnirecord
In this model you have all the possible fields in a single record and then use STI to differentiate between profile types. This is the simplest to implement but looks the most messy as few people will have all fields populated. Keep in mind that NULL
string fields don't take up a lot of database space, typically one bit per column, so having a lot of them isn't a big deal.
Optional Joins
In this model you create a number of possible linkages to different profile types, like doctor_profile_id
linking to a DoctorProfile, patient_profile_id
linking to a PatientProfile, and so forth. Since each relationship is spelled out in a specific field, you can even enforce foreign key constraints if you prefer, and indexing is easy. This can come in handy when a single record requires multiple different profiles to be associated with it, as in the case of a patient that's also a doctor.
Polymorphic Join
In this model you link to a specific profile type and profile id using the :polymorphic
option, much like you've suggested. Indexing is more complicated and foreign keys are impossible. You're also limited to having one and only one profile. These tend to work as a starting point but may prove to be trouble down the road when you get a doctor + patient requirement.
Key/Value Store
In this model you abandon all efforts to organize things into singular records and instead build an associated ProfileField and ProfileValue table. The ProfileField identifies what fields are available on what kinds of profiles, such as label, allowed values, data type and so forth, while the ProfileValue is used to store specific values for specific profiles.
class User < ActiveRecord::Base
has_many :profile_fields
end
class ProfileField < ActiveRecord::Base
has_many :profile_values
end
class ProfileValue < ActiveRecord::Base
belongs_to :user
belongs_to :profile_field
end
Since this one is wide open you can allow the site administrator to redefine what field are required, add new fields, and so on, without having to make a schema change.
Upvotes: 3