Chapter 4. Models and Collections

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:

  • Creating a miniature module
  • Creating a simple model
  • The EAV model
  • Understanding the flow of schema and data scripts
  • Creating an install schema script (InstallSchema.php)
  • Creating an upgrade schema script (UpgradeSchema.php)
  • Creating an install data script (InstallData.php)
  • Creating an upgrade data script (UpgradeData.php)
  • Entity CRUD actions
  • Managing collections

Creating a miniature module

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 key
    • name: name of department, string value
  • Employee: an EAV model with the following fields and attributes:
    • Fields:
      • entity_id: primary key
      • department_id: foreign key, pointing to Department.entity_id
      • email: unique e-mail of an employee, string value
      • first_name: first name of an employee, string value
      • last_name: last name of an employee, string value
    • Attributes:
      • service_years: employee's years of service, integer value
      • dob: employee's date of birth, date-time value
      • salary – monthly salary, decimal value
      • vat_number: VAT number, (short) string value
      • note: possible note on employee, (long) string value

Every 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.

Creating a simple model

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.

Note

The resource class is the key to communicating to the database. All it takes is for us to name the table and its primary key and our models can save, delete, and update entities.

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.

Creating an EAV model

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.

Note

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.218.215