I'm developing a stock and warehouse management system using relational databases (MySQL) and PHP. Due to the fact that the stock products will have multiple characteristics (widths, heights, weights, measures, colors, etc) there raises the need of having a database model approach of storing the attributes and the possibility to add/edit new attributes, alter product types and so on.
So, in the current concept I can see only 3 viable models:
store all attributes in a single table, as separated column and
based on product type (probably category) to serve them to the end
user to fill
the EAV (Entity - Attribute - Value) model that will involve
something like this:
a category table containing classes of attributes
a class of attributes table that will contain separate classes with multiple attributes (in this manner we ensure that we can add to a category a class of attributes without the need to manually add to similar categories attributes one after the other)
a attributes table responsible for the attribute itself
a attributes values table where we store the values
Store all common attributes in a single table and create multiple tables for all different category type: this model would require to change the database every time we encounter a new category type
The second model is inspired from here.
After reading a lot regarding the EAV model I now have doubts over this model and I am little concern regarding the ways I will have to connect different product attributes in orders / invoices and so on.. Even the validation of forms seems that it will be a real pain of using the EAV model, but still.. I wouldn't like to have a single table with 100+ columns and then to be ready to add new columns whenever a new attribute is to be added..
So, the question would be: is there a cheaper solution? Or could the EAV model be improved?
I know it's a long and old debate, but everybody is just pointing to NoSQL and I only rely on RDBMS..
EDIT:
The downside of those approaches (or of most of the approaches found) is that:
for a specified attribute there probably should exist a measure unit
(eq. attribute weight should have a drop down with measuring units)
a specified attribute should be mandatory or not
all attributes should have a validation on form submit
Until now, the only feasible solution would be to create a new table for every new category, and deal in that table with all custom attributes and rules. But, yet again, it would end up to a real pain when a new category is to be set up.
EDIT 2:
The option of using a Json column in MySQL, does not solve from my point of view any of the downsides mentioned above.. OR, maybe I am wrong and I don't clearly see the big picture..
I gather that these are your primary requirements:
Flexible attributes
Your exact need here is unclear: it sounds like you either expect the attributes to change, or at least expect that all attributes will not always be applicable to all products (i.e. a sparse matrix)
Products are also categorized, and the category will (at least partially) determine what attributes are applicable to a product
The attributes themselves may have additional properties aside from their value, that must be provided by the user (i.e. a unit that goes with a weight)
Input validation is a must, and checks things like:
All required attributes are present
Attributes which are not applicable are not present
Attributes have valid values
User-provided attribute properties have valid values
You probably also want to make sure you can search/filter efficiently by attributes
These different requirements all result in different technical needs, and different technical solutions. Some are matters of database, and some will have to be solved in code regardless of database choice. Obviously you are aware of some of these issues, but I think it is worth really breaking it down:
Flexible Attributes
Having a list of flexible attributes (as you know) does not work well with RDBMS systems where your table schema has to be pre-defined. This includes pretty much all of the SQLs, and definitely MySQL. The issue is that changing the table schema is expensive and for large tables can take minutes or hours, making it practically impossible to add attributes if you have to add a column to a table to do it.
Even if your list of attributes rarely changes, a large table of attributes is very inefficient if most products don't have a value for most attributes (i.e. a sparse matrix).
In the long run, you just won't get anywhere if your attributes are stored as a column in tables. Even if you break it down per-category, you are still going to have large empty tables that you can't add columns to dynamically.
If you stick with an RDBMS your only option is really an EAV system. Having considered, researched, and implemented EAV systems, I wouldn't worry too much about all the hype you hear about them on the internet. I know that there are lots of articles out there talking about the EAV "anti-pattern", and I'm the kind of person who takes proper use of software design patterns seriously, but EAV does have a perfectly valid time and place, and this is it. In the long run you will not be able to do this on an RDBMS without EAV. You could certainly look at a NoSQL system that is designed for this specific kind of problem, but when the rest of your database is in a standard RDBMS, installing or switching to a NoSQL system just to store your attribute values is almost certainly overkill. You certainly aren't going to want to lose the ACID compliance that a RDMBS comes with, and most NoSQL systems don't guarantee ACID compliance. There is a wave of NewSQL systems out there that are designed to get the best of both worlds, but if this is just one part of a larger application (which I'm sure is the case), it probably isn't worth investigating completely new technologies just to make this one feature happen. You could also consider using something like JSON storage inside MySQL to store your attribute values. That is a viable option now that MySQL has better JSON support, but that only makes a small change to the big picture: you would still need all your other EAV tables to keep track of allowed attributes, categories, etc. It is only the attribute values that you would be able to place inside of the JSON data, so the potential benefits of JSON storage are relatively small (and have other issues that I will mention down the road).
So in summary, I would say that as long as the rest of your application runs on a RDBMS, it is perfectly reasonable to use EAV to manage flexible attributes. If you were trying to build your entire system in an EAV inside of a RDBMS, then you would definitely be wasting your time and I'd tell you to go find a good NoSQL database that fits the problem you are trying to solve. The disadvantages of EAV do still apply though: you can't easily perform consistency checks within your RDBMS system, and will have to do that yourself in code.
Categorized products with category-specific attributes
You've pretty much got it here. This is relatively straight-forward inside an EAV system. You will have your attributes table, you will have a category table, and then you will need a standard one-to-many or many-to-many relationship between the attributes and categories table which will determine which attributes are available to which category. You obviously also have a relationship between products and categories, so you know which products therefore need which attributes.
Your option #3 is designed to fulfill this requirement, but having a table with each attribute as a column will scale very poorly as your system grows, and will definitely break if you ever need to dynamically add attributes. You don't want to be running ALTER TABLE statements on the fly, especially if you have more than a few thousand records.
Managing attribute properties
It is one thing to store dynamic attributes and values. It is another problem entirely to store dynamic attributes, values, and associated meta data (i.e. store a weight as well as the unit the weight is in). This however is no longer a database problem, but rather a code problem. In terms of actually storing the information your best bet is to probably store your meta data inside your attribute values table, and rely upon some code abstractions to handle the input validation as well as form building. That can get quite complicated quite fast, especially if done wrong, and talking through such a system would take another entire post. However, I think you are on the right track: for a fancier attribute that requires both a value and meta data, you need to somehow assign a class that is responsible for input processing and form validation. For instance for a simple text field you have a "text" class that reads the user's value out of the form and stores it in the proper "attribute_values" table, with no meta data stored. Then for your "weight" attribute you would have a "weight" attribute that stores the number given by the user (i.e. 0.5) but then also stores the unit the user specified with that number (i.e. 'lbs') and persists both to the "attribute_values" table (in pseudo-SQL): INSERT INTO attribute_values value='0.5', meta_data='{"unit":"lbs"}', product_id=X, attribute_id=X. Ironically JSON probably would be a good way to store this meta data, since the exact meta data kept will also vary by attribute type, and I doubt you would another level of tables to handle that variation in your EAV tables.
Again, this is more of a code problem than storage problem. If you decided to do JSON tables the overall picture to meet this requirement wouldn't change: your "attribute type classes" would simply store the meta data in a different way. That would probably look something like: UPDATE products SET attributes='{"weight":0.5,"unit":"lbs"}' WHERE id=X
Input Validation
This will have to be handled exclusively by code regardless of how you store your data, so this requirement doesn't matter much in terms of deciding your database structure. A class-based system as described above will also be able to handle input validation, if properly executed.
Sort/Search/Filter
This doesn't matter if you are exclusively using your attributes for data storage/retrieval, but will you be searching on attributes at all? With a proper EAV system and good indexes, you can actually search/sort efficiently in an RDBMS system (although it can start to get painful if you search by more than a handful of indexes at a time). I haven't looked in detail, but I'm pretty sure that using JSON for storage won't scale well when it comes to searching. While MySQL can work with JSON now and search the columns directly, I seriously doubt that such searching/sorting makes use of MySQL indexes, which means that it won't work with large databases. I could be wrong on that one though. It would be worth digging into before committing to a MySQL/JSON storage setup, if you were going to do something like that.
Depending on your needs, this is also a good place to compliment an RDBMS system with a NoSQL system. Having managed large-ish (~1.5 million product) e-commerce systems before, I have found that MySQL tends to fall flat in the searching/sorting category, especially if you are doing any kind of text searching. In an e-commerce system a query like: "Show me the results that best match the term 'blue truck' and have the attribute 'For ages 3-5'" is common, but doing something like that in MySQL is about impossible, primarily because of the need for relevancy based sorting and scoring. We solved this problem by using Apache Solr (Elastic is a similar solution) and it managed our searching/sorting/search term scoring very well. In this case it was a two database solution. MySQL kept all the actual data and stored attributes in EAV tables, and anytime something got updated we pushed a record of everything to Apache Solr for additional storage. When a query came in from a user we would query Apache Solr which was an expert at text searching and could also handle the attribute filtering with no trouble, and then we would pull the full product record out of our MySQL database. The system worked beautifully. We had 1.5 million products, thousands of custom attributes, and had no trouble running the whole thing off of a single virtual server. Obviously there was a lot of code going on behind the scenes, but the point is that it definitely worked and wasn't difficult to maintain. Never had any issues with performance from either MySQL or Solr.
Well, this is just one approach. You could simplify this if you don't need or want all of this.
You could, for example, use a Json column in Mysql, to store all of the extra attributes. Another idea, in the product type, add a json column to store the custom attributes and types, and use this to draw the form on the screen.
I would recommend you to go through an EAV database first in order to understand the database creation & its values.
You can follow magento DB structure which uses EAV model.
EAV stands for Entity attribute and value model. Let’s closely have a look at all parts.
Entity: Data items are represented as entity, it can be a product or customer or a category. In the database each entity have a record.
Attribute: These are belongs to different entity, for example a Customer entity have attributes like Name, Age, Address etc. In Magento database all attributes are listed in a single table.
Value: Simply the values of the attributes, for example for the Name attribute the value will be “Rajat”.
EAV is used when you have many attributes for an entity and these attribute are dynamic (added/removed).
Also there is a high possibility that many of these attribute would have empty or null value most of the time.
In such a situation EAV structure has many advantages mainly with optimized mysql storage
For Your case - Category can also have attributes, products can also have attributes so on with customers etc ...
Let's take an example of categories. Following are the tables provided by magento:
1. catalog_category_entity
2. catalog_category_entity_datetime
3. catalog_category_entity_decimal
4. catalog_category_entity_int
5. catalog_category_entity_text
6. catalog_category_entity_varchar
7. catalog_category_flat
Follow this link to know more about table
Magento Category Tables
For attributes which are select box. You can put dropdown values under option values.
Follow this to link to understand magento eav structure which will give you clear picture about how EAV model work & how you can make a best use of it.
magento table structure
There are three approaches if you want to stick with a relational database.
The first is best if you know in advance the attributes for all the products. You chose one of the three ways to store polymorphic data in a relational model.
It's "clean" from a relational point of view - you're just using rows and columns, but each of the 3 options has its own benefits and drawbacks.
If you don't know your attributes at development time, I'd recommend against these solutions - they'd require significant additional tooling.
The next option is EAV. The benefits and drawbacks are well documented - but your focus on "validating input forms" is only one use case for the data, and I think you could easily find your data becomes "write only". Providing sorting/filtering, for instance, becomes really hard ("find all products with a height of at least 12, and sort by material_type" is almost impossible using the EAV model).
The option I prefer is a combination of relational data for the core, invariant data, and document-centric (JSON/XML) for the variant data.
MySQL can query JSON natively - so you can sort/filter by the variant attributes. You'd have to create your own validation logic, though - perhaps by integrating JSON Schema in your data entry applications.
By using JSON Schema, you can introduce concepts that "belong together", and provide lookup values. For instance, if you have product weight, your schema might say weight always must have a unit of measure, with the valid options being kilogram, milligram, ounce, pound etc.
If you have foreign key relationships in the variant data, you have a problem - for instance, "manufacturer" might link to a manufacturers table. You can either model this as an explicit column, or in the JSON and do without SQL's built-in foreign key tools like joins.
I want to store some user settings and I thought of 3 options, since it's my first project and I want to start it the right way, I can't figure out which is the the optimal way of doing this..
Storing single settings directly in columns in the user table
Having a user_settings table with the columns setting_name, setting_value and user_id
Storing a JSON string in the user table, in a column named something like "user_settings_json"
On a design analysis, I noticed wordpress stores it in a separate table, but I'm not sure that's necessary for every application (since mine does not have nearly as many user settings as wp does)
I'm using Laravel, PHP, javascript/jquery.
Which do you guys think would be, most useful, overall better, in terms of design, serviceability and performance?
Storing a JSON string ? NO !
You want to be able to isolate the settings, just query what you need. Therefore, keep it in separate columns!
As for the question if you have to make a separate table, no, you don't have to. When you just got some simple settings you can just add the columns to the existing table of users. Be aware of the limitations here. If you do need advanced settings, i would recommend using a separate table. Better do it too early. Not every setting might apply to every user. For example when you've got premium accounts who can have more settings. So, keeping it separate is what I would do.
Btw, I wouldn't let the columns start with 'setting_' if they are already in a separate table containing 'setting' in the name.
Conclusion: Option 2 :)
Recently I've made package for my project which uses Laravel 9 and it allows you to add settings to any Laravel model. It can cast values to primitive types like bool, int, but also to custom classes. Eg.:
$user->settings->get('is_gamer');
$user->settings->set('games_count', 10);
// or global site scoped
Settings::get('display_annoucement');
// more advanced usage with definition of custom class
$address = $user->settings->get('address');
$address->country = 'Poland';
$address->zip = '11-222';
$address->city = 'Warsaw';
$user->settings->put('address', $address);
// any model that implements trait
$article->settings->get('show_breadcrumbs');
$post->settings->get('allow_replies');
You can find it here: https://github.com/npabisz/laravel-settings
We are working on a platform which allows the user to create 'promotion' instances, wherein there are an arbitrary amount of pages and 'modules' associated with those pages. Each module has its own customizable collection of attributes. One of the modules I am developing is the entry form, which is the main component.
The form includes some default fields e.g. name, date of birth and email address. The module then allows the user to add as many additional fields of any type that they require (e.g. a '25 words or less' text field, extra opt-in checkboxes, etc).
I'm trying to plan out how I will deal with these X additional fields in terms of storage in MySQL. Sorting and filtering on these fields will be a requirement.
This seems like something that would be a known and solved problem, but I haven't had any luck either wording it correctly or coming across relevant information. I've had some thoughts while searching; but each has a downfall that makes me think there must be a better way:
Create a new table for each form module which contains the additional fields as new rows - this seems really messy / clunky.
Store the additional information in an extra row as JSON (or some other data format). Pull all the data into PHP, expand the JSON and work with all the data in PHP - we envision a high number of entries (5-10k) so I assume this would be too inefficient.
Having an upper limit on additional fields and appending a bunch of rows to the entries table i.e. 'custom1', 'custom2', 'custom3', etc. This seems very messy as well.
Looking again at point 2, I thought there might be a way to take the block of data in the extra row and create a derived table from it, but I haven't had any luck finding information around whether that's possible. For example:
SELECT * FROM( JSON_DECODE(entries.extra) ) ...
If this were possible, that would probably be my preference.
What is the correct way to approach this problem of needing a dynamic amount of additional rows?
The correct solution depends a lot on how you're going to use the data. All solutions have strengths and weaknesses, and you have to understand that you're basically trying to do something that relational databases were not designed for.
I gave a presentation about this topic earlier this year called Extensible Data Modeling, in which I tried to provide a survey of different solutions, and their pros and cons.
You could also throw in the towel, and use a non-relational database to store non-relational data.
I'm developing a page with C5 needing various data to be attached to the user accounts. There are two types of users, having different data. Some of the data is multi dimensional and therefor needs custom DB tables. My question is now if it makes sense to store all data in custom DB tables or to use user attributes for the one dimensional data.
Probably there is no general answer to this, but maybe some pros and cons?
I'm often asking myself where to store data in Concrete5 and would be interested how others decide ...
Yeah. I'd definitely store as user attributes for similar reasons to the one you've already identified (visible, searchable, etc).
concrete5 is extensible, but not super extensible; you can attach data to a user using attributes, but not through some totally custom object / db table that you also expect to, e.g., show up on the user profile page.
Oftentimes in c5 (like any other framework), doing it the Right way (attribute) is more difficult (especially for the first "object", but also for each additional one) than just creating a db table and linking to a user id. But, like in all frameworks, you'll reap benefits down the road that you hadn't even considered. This is in searching, upgradability, and things that might only occur to the guy who takes over development next year.
So, with all that being said, go with attributes. And not just for the one dimensional data. You can configure the attribute controller (and the db schema behind it) to store any data you wish. Look at the Address attribute. This contains multiple fields (though it's still 1D). I think there's an opensource "multi address" attribute out there which stores 1-n addresses as a single attribute. You can do this with an additional linked table, but I've recently gotten lazy with c5 and done no-mysql by dumping json_encode()ed (multi-dimensional) arrays in the "data" field. (In this case, your attribute doesn't even need its own table -- it can use the Default table.) You can then configure the editing interface and also the display value (so, e.g., it just shows a list of each sub-object's Name property). Similarly, you can configure the text that gets indexed for searching purposes.
You asked for pros/cons. Doing this custom will be quicker and more straightforward. Extending an attribute, especially to create something complex, isn't super simple, and there isn't a lot of good documentation. Also, the attribute-editing UI (on the user dashboard page) is a bit kludgy. Yes, you get to "design" whatever you want within the "table cell", but you're still limited to making the admin click on the attribute name, using your editing interface within the cell, and then (ideally) clicking on the little disk icon. (Creating a javascript dialog might solve some issues here.)
An online application we are building (php & mysql) requires users to be able to create their own forms for data capture and record this data in a database, respecting the existing ORM's.
If the forms where "hard coded" then we would simply set the db tables up to store the normalised data ourselves however as our users define the form fields contained in the forms, we're not sure what is the best way to proceed to implement this functionality.
Do we need to think about some kind of meta data or data abstraction layer for our DB? Google hasn't been too much help as we're unsure about how we need to go about this.
Any pointers in the right direction would be gratefully appreciated!
Many content management systems address this problem in different ways.
For example, in Drupal, users can create their own custom content (with custom forms) through the CCK module. The module defines different types of fields that the user can create, then generates tables with specific data types to store the data.
Some tips:
Define your field types - Think about giving the users a choice of different field types (e.g., select box, string, radio).
Create tables for user defined fields - Each field type will have a specific SQL data type. Define a table using these data types. For example, a select box might be mapped to an enum and a input text element might be mapped to a varchar column.
Add data to the new tables - use the new tables to store the data in a somewhat normalized way.
Obviously there are many different approaches, but these are just a few suggestions.
I think I've found a solution to my problem, so for all those people who come along a similar problem have a look at the following artcles -
http://www.adaniels.nl/articles/an-alternative-way-of-eav-modeling/
http://weblogs.sqlteam.com/davidm/articles/12117.aspx
Hope this helps.