„Традиционният“ начин да направите това е с модела Entity-Attribute-Value или EAV. Както подсказва името, ще създадете нова таблица с три колони: една за „субекта“, който в този случай е клиентът, една за името или ключа на „атрибута“ и една за стойността. Така че ще имате таблица като тази:
customer_properties
+----+-------------+--------------+------------+
| id | customer_id | key | value |
+----+-------------+--------------+------------+
| 1 | 1 | color | yellow |
| 2 | 1 | brand | nike |
| 3 | 1 | sales | 33 |
| 4 | 2 | color | red |
| 5 | 2 | phone_number | 1111111111 |
| 6 | 2 | purchases | 2 |
+----+-------------+--------------+------------+
Определено ще искате ИНДЕКС на key
и може би на value
(и customer_id
, разбира се, но Rails ще направи това вместо вас, когато използвате relation
или belongs_to
във вашата миграция).
След това във вашите модели:
# customer.rb
class Customer < ActiveRecord::Base
has_many :customer_properties
end
# customer_property.rb
class CustomerProperty < ActiveRecord::Base
belongs_to :customer
end
Това позволява използване по следния начин:
customer = Customer.joins(:customer_properties)
.includes(:customer_properties)
.where(customer_properties: { key: "brand", value: "nike" })
.first
customer.customer_properties.each_with_object({}) do |prop, hsh|
hsh[prop.key] = prop.val
end
# => { "color" => "yellow",
# "brand" => "nike",
# "sales" => "33" }
customer.customer_properties.create(key: "email", value: "[email protected]")
# => #<CustomerProperty id: 7, customer_id: 1, key: "email", ...>
Що се отнася до дизайна на базата данни, това е доста солидно, но както можете да видите, има някои ограничения: По-специално, това е тромаво. Освен това сте ограничени до един тип стойност (:string
/VARCHAR
е често срещано). Ако тръгнете по този път, вероятно ще искате да дефинирате някои удобни методи на Customer, за да направите достъпа и актуализирането на свойствата по-малко тромави. Предполагам, че вероятно има скъпоценни камъни специално за направата на шаблона EAV да работи добре с ActiveRecord, но не ги знам наизуст и се надявам да ми простите, че не гугълнах, тъй като съм мобилен.
Както отбелязва Брад Верт, ако просто трябва да съхранявате произволни свойства и да не правите заявки от тях, serialize
е чудесна алтернатива, а ако използвате PostgreSQL дори проблемът със заявките е преодолим благодарение на страхотната му функция hstore.
Късмет!
person
Jordan Running
schedule
16.04.2014