Skip to content

Introduction

rm edited this page Apr 17, 2016 · 3 revisions

Eavquent was intended to work as invisible as possible. It was supposed to have the less impact as possible into an Eloquent model in terms of usability. Is for this reason it was developed as a trait and the unique set up required to provide EAV functionality to an Eloquent model is to use that trait.

Basics

Entity

An entity represents a real element which needs to extend its attributes dynamically. Example: elemenents such as Product, Customer or Sale are likely to be entities.

In this case an entity will be represented by an Eloquent model.

Attribute

The attribute act as the "column" we would like to add to an entity. An attribute gets a name such as price, city or colors to get identified and will be linked to an entity object. It will also play very closely with a data type instance which will cast or format its value when writing or reading from database.

This element will also be responsible of defining some default behaviour like data validation or default values.

Value

This parameter is responsible of storing data values related to a certain attribute and to a particular entity instance (row).

In Eavquent implementation, a Value instance will represent the content of an attribute related to a particular entity instance. It will contain the real value of the price attribute we have registered for a product[id=1] entity.

Values are stored in different tables based on their data type. String values will be stored in a table called (by default) eav_values_varchar when integer values would use eav_values_integer instead. Both tables columns are identical except the data type of the content column which is adapted to the data type they store.

The performance loss

EAV modeling is known for its lack of performance. It is also known for its complexity in terms of querying data if compared with the cost of querying any other horizontal structure. This paradigm has been tagged as anti-pattern in many articles and there is a lot of polemic about whether it should be used.

Since we are storing our entity, attribute and value in different tables, it's required to perform multiple queries to perform any operation. This means if we have 4 attributes registered for an entity, the package will perform at least 5 queries:

select * from `companies`
select * from `eav_values_varchar` where `attribute_id` = '1' and `eav_values_varchar`.`entity_id` in ('1', '2', '3', '4', '5') and `eav_values_varchar`.`entity_type` = 'App\Company'
select * from `eav_values_varchar` where `attribute_id` = '2' and `eav_values_varchar`.`entity_id` in ('1', '2', '3', '4', '5') and `eav_values_varchar`.`entity_type` = 'App\Company'
select * from `eav_values_varchar` where `attribute_id` = '3' and `eav_values_varchar`.`entity_id` in ('1', '2', '3', '4', '5') and `eav_values_varchar`.`entity_type` = 'App\Company'
select * from `eav_values_varchar` where `attribute_id` = '4' and `eav_values_varchar`.`entity_id` in ('1', '2', '3', '4', '5') and `eav_values_varchar`.`entity_type` = 'App\Company'

The gained flexibility

Despite the performance issues, EAV provides a very high flexibility. It let developers use dynamic attributes that can be handled at any time without affecting the database structure. It also helps when working with columns that will mainly store NULL values. Considering that the user accepts the lack of performance EAV comes with, the package has been developed with flexibility in mind so at least the user can fight that performance issue.

Performance can be improved by loading all entities related values in a single query and using PHP to organise them into relationships but decided not to, in favour of making database querying more flexible.

As explained below, this package loads the entity values as if they were custom Eloquent relationships. It is for this reason you can easily query through them as if they were a regular Eloquent relation.

Loading values as relationships lets you load only those values you may require for a certain situation, leaving the rest unloaded. It also lets you make use of the powerful Eloquent tools for querying relations so you can easily filter the entities you're fetching from database based on conditions you directly apply to the values content.