Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow users to define arbitrary number of fields in Rails

I'm building a CMS where Administrators must be able to define an arbitrary number fields of different types (text, checkboxes, etc). Users then can fill those fields and generate posts.

How can this be done in Rails? I guess that the persistence of these "virtual" attributes will be done in a serialised attribute in the database. But I am not sure how to struct the views and controllers, or where the fields should be defined.

like image 339
Victor Avatar asked Sep 05 '16 10:09

Victor


2 Answers

What you describe is called Entity-Attribute-Value model. MattW. provided 2 possible solutions, but you can also use one of these gems that implement this data pattern instead of handling this yourself:

  • eav_hashes
  • hydra_attribute

I haven't used any of these gems before, so I can't suggest which one is best.

RefineryCMS has this extension for the feature you need. You might want to take a look for ideas.

There is also a similar older question here on Stackoverflow.

like image 162
Teoulas Avatar answered Nov 09 '22 00:11

Teoulas


As soon as it's user defined, it is never a column (field), it's always a row (entity) in the database – users don't get to define your data structure.

Here are two ideas. (1) is the more idiomatic "Rails Way". (2) is somewhat less complicated, but may tie you to your specific DBMS.

(1) Using four models: Posts (n:1) PostTypes (1:n) PostFields (1:n) PostValues (n:1 Posts):

create_table :post_types do |t|
  t.string :name
  t.text :description
  ....

end

create_table :post_fields do |t|
  t.references :post_type
  t.string :name
  t.integer :type
  t.boolean :required
  ....
end

create_table :posts do |t|
  t.references :post_type
  (... common fields for all posts, like user or timestamp)

end
 create_table :post_values do |t|
  t.references :post
  t.references :post_field
  t.string :value
  ....
end

Only problem is that you're limited to a single type for values in the database, You could do a polymorphic association and create different models for :boolean_post_values, :float_post_values etc.

  1. One other solution may be to use a json-based data structure, i. e. the same as above, but instead of PostValues to just save it in one field of Posts:

    create_table :posts do |t| t.references :post_type t.json :data ... end // ... no table :post_values

This is probably easier, but json is a postgres-specific datatype (although you could use string and do the de/encoding yourself).

like image 31
Matthias Winkelmann Avatar answered Nov 09 '22 00:11

Matthias Winkelmann