Like most modern frameworks and platforms, these days Magento embraces an Object Relational Mapping (ORM) approach over raw SQL queries. Though the underlying mechanism still comes down to SQL, we are now dealing strictly with objects. This makes our application code more readable, manageable, and isolated from vendor-specific SQL differences. Model, resource, and collection are three types of classes working together to allow us full entity data management, from loading, saving, deleting, and listing entities. The majority of our data access and management will be done via PHP classes called Magento models. Models themselves don't contain any code for communicating with the database.
The database communication part is decoupled into its own PHP class called resource class. Each model is then assigned a resource class. Calling load
, save
, or delete
methods on models get delegated to resource classes, as they are the ones to actually read, write, and delete data from the database. Theoretically, with enough knowledge, it is possible to write new resource classes for various database vendors.
Next to the model and resource classes, we have collection classes. We can think of a collection as an array of individual model instances. On a base level, collections extend from the MagentoFrameworkDataCollection
class, which implements IteratorAggregate
and Countable
from Standard PHP Library (SPL) and a few other Magento-specific classes.
More often than not, we look at model and resource as a single unified thing, thus simply calling it a model. Magento deals with two types of models, which we might categorize as simple and EAV models.
In this chapter, we will cover the following topics:
InstallSchema.php
)UpgradeSchema.php
)InstallData.php
)UpgradeData.php
)For the purpose of this chapter, we will create a miniature module called Foggyline_Office
.
The module will have two entities defined as follows:
Department
: a simple model with the following fields:entity_id
: primary keyname
: name of department, string valueEmployee
: an EAV model with the following fields and attributes:entity_id
: primary keydepartment_id
: foreign key, pointing to Department.entity_id
email
: unique e-mail of an employee, string valuefirst_name
: first name of an employee, string valuelast_name
: last name of an employee, string valueservice_years
: employee's years of service, integer valuedob
: employee's date of birth, date-time valuesalary
– monthly salary, decimal valuevat_number
: VAT number, (short) string valuenote
: possible note on employee, (long) string valueEvery module starts with the registration.php
and module.xml
files. For the purpose of our chapter module, let's create the app/code/Foggyline/Office/registration.php
file with content as follows:
<?php MagentoFrameworkComponentComponentRegistrar::register( MagentoFrameworkComponentComponentRegistrar::MODULE, 'Foggyline_Office', __DIR__ );
The registration.php
file is sort of an entry point to our module.
Now let's create the app/code/Foggyline/Office/etc/module.xml
file with the following content:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/ etc/module.xsd"> <module name="Foggyline_Office" setup_version="1.0.0"> <sequence> <module name="Magento_Eav"/> </sequence> </module> </config>
We will get into more details about the structure of the module.xml
file in later chapters. Right now, we will only focus on the setup_version
attribute and module
element within sequence
.
The value of setup_version
is important because we might use it within our schema install script (InstallSchema.php
) files, effectively turning the install script into an update script, as we will show soon.
The sequence
element is Magento's way of setting dependencies for our module. Given that our module will make use of EAV entities, we list Magento_Eav
as a dependency.
The Department
entity, as per requirements, is modeled as a simple model. We previously mentioned that whenever we talk about models, we implicitly think of model
class, resource
class, and collection
class forming one unit.
Let's start by first creating a model
class, (partially) defined under the app/code/Foggyline/Office/Model/Department.php
file as follows:
namespace FoggylineOfficeModel; class Department extends MagentoFrameworkModelAbstractModel { protected function _construct() { $this-> _init('FoggylineOfficeModel ResourceModelDepartment'); } }
All that is happening here is that we are extending from the MagentoFrameworkModelAbstractModel
class, and triggering the $this->_init
method within _construct
passing it our resource
class.
The AbstractModel
further extends MagentoFrameworkObject
. The fact that our model
class ultimately extends from Object
means that we do not have to define a property name on our model
class. What Object
does for us is that it enables us to get, set, unset, and check for a value existence on properties magically. To give a more robust example than name
, imagine our entity has a property called employee_average_salary
in the following code:
$department->getData('employee_average_salary'); $department->getEmployeeAverageSalary(); $department->setData('employee_average_salary', 'theValue'); $department->setEmployeeAverageSalary('theValue'); $department->unsetData('employee_average_salary'); $department->unsEmployeeAverageSalary(); $department->hasData('employee_average_salary'); $department->hasEmployeeAverageSalary();
The reason why this works is due to Object
implementing the setData
, unsetData
, getData
, and magic __call
methods. The beauty of the magic __call
method implementation is that it understands method calls like getEmployeeAverageSalary
, setEmployeeAverageSalary
, unsEmployeeAverageSalary
, and hasEmployeeAverageSalary
even if they do not exist on the Model
class. However, if we choose to implement some of these methods within our Model
class, we are free to do so and Magento will pick it up when we call it.
This is an important aspect of Magento, sometimes confusing to newcomers.
Once we have a model
class in place, we create a model resource
class, (partially) defined under the app/code/Foggyline/Office/Model/ResourceModel/Department.php
file as follows:
namespace FoggylineOfficeModelResourceModel; class Department extends MagentoFrameworkModelResourceModelDbAbstractDb { protected function _construct() { $this->_init('foggyline_office_department', 'entity_id'); } }
Our resource class that extends from MagentoFrameworkModelResourceModelDbAbstractDb
triggers the $this->_init
method call within _construct
. $this->_init
accepts two parameters. The first parameter is the table name foggyline_office_department
, where our model will persist its data. The second parameter is the primary column name entity_id
within that table.
AbstractDb
further extends MagentoFrameworkModelResourceModelAbstractResource
.
Finally, we create our collection
class, (partially) defined under the app/code/Foggyline/Office/Model/ResourceModel/Department/Collection.php
file as follows:
namespace FoggylineOfficeModelResourceModelDepartment; class Collection extends MagentoFrameworkModelResourceModel DbCollectionAbstractCollection { protected function _construct() { $this->_init( 'FoggylineOfficeModelDepartment', 'FoggylineOfficeModelResourceModelDepartment' ); } }
The collection
class extends from MagentoFrameworkModelResourceModelDbCollectionAbstractCollection
and, similar to the model
and resource
classes, does a $this->_init
method call within _construct
. This time, _init
accepts two parameters. The first parameter is the full model
class name FoggylineOfficeModelDepartment
, and the second parameter is the full resource class name FoggylineOfficeModelResourceModelDepartment
.
AbstractCollection
implements MagentoFrameworkAppResourceConnectionSourceProviderInterface
, and extends MagentoFrameworkDataCollectionAbstractDb
. AbstractDb
further extends MagentoFrameworkDataCollection
.
It is worth taking some time to study the inners of these collection
classes, as this is our go-to place for whenever we need to deal with fetching a list of entities that match certain search criteria.
The Employee
entity, as per requirements, is modeled as an EAV model.
Let's start by first creating an EAV model
class, (partially) defined under the app/code/Foggyline/Office/Model/Employee.php
file as follows:
namespace FoggylineOfficeModel; class Employee extends MagentoFrameworkModelAbstractModel { const ENTITY = 'foggyline_office_employee'; public function _construct() { $this-> _init('FoggylineOffice Model ResourceModelEmployee'); } }
Here, we are extending from the MagentoFrameworkModelAbstractModel
class, which is the same as with the simple model previously described. The only difference here is that we have an ENTITY
constant defined, but this is merely syntactical sugar for later on; it bears no meaning for the actual model
class.
Next, we create an EAV model resource
class, (partially) defined under the app/code/Foggyline/Office/Model/ResourceModel/Employee.php
file as follows:
namespace FoggylineOfficeModelResourceModel; class Employee extends MagentoEavModelEntityAbstractEntity { protected function _construct() { $this->_read = 'foggyline_office_employee_read'; $this->_write = 'foggyline_office_employee_write'; } public function getEntityType() { if (empty($this->_type)) { $this->setType(FoggylineOfficeModel Employee::ENTITY); } return parent::getEntityType(); } }
Our resource
class extends from MagentoEavModelEntityAbstractEntity
, and sets the $this->_read
, $this->_write
class properties through _construct
. These are freely assigned to whatever value we want, preferably following the naming pattern of our module. The read and write connections need to be named or else Magento produces an error when using our entities.
The getEntityType
method internally sets the _type
value to FoggylineOfficeModelEmployee::ENTITY
, which is the string foggyline_office_employee
. This same value is what's stored in the entity_type_code
column within the eav_entity_type
table. At this point, there is no such entry in the eav_entity_type
table. This is because the install schema script will be creating one, as we will be demonstrating soon.
Finally, we create our collection
class, (partially) defined under the app/code/Foggyline/Office/Model/ResourceModel/Employee/Collection.php
file as follows:
namespace FoggylineOfficeModelResourceModelEmployee; class Collection extends MagentoEavModelEntityCollectionAbstractCollection { protected function _construct() { $this->_init('FoggylineOfficeModelEmployee', 'FoggylineOfficeModelResourceModelEmployee'); } }
The collection
class extends from MagentoEavModelEntityCollectionAbstractCollection
and, similar to the model class, does a $this->_init
method call within _construct
. _init
accepts two parameters: the full model class name FoggylineOfficeModelEmployee
, and the full resource class name FoggylineOfficeModelResourceModelEmployee
.
AbstractCollection
has the same parent tree as the simple model collection class, but on its own it implements a lot of EAV collection-specific methods like addAttributeToFilter
, addAttributeToSelect
, addAttributeToSort
, and so on.
As we can see, EAV models look a lot like simple models. The difference lies mostly in the resource
class and collection
class implementations and their first level parent classes. However, we need to keep in mind that the example given here is the simplest one possible. If we look at the eav_entity_type
table in the database, we can see that other entity types make use of attribute_model
, entity_attribute_collection
, increment_model
, and so on. These are all advanced properties we can define alongside our EAV model making it closer to the implementation of the catalog_product
entity type, which is probably the most robust one in Magento. This type of advanced EAV usage is out of the scope of this book as it is probably worth a book on its own.
Now that we have simple and EAV models in place, it is time to look into installing the necessary database schema and possibly pre-fill it with some data. This is done through schema and data scripts.
3.137.218.215