3/22/2018 - #development #ecommerce #magento
by: Kévin Weyhaupt

Part 3 : The Chef's touch

The secret ingredient : the Entity Registry

Reminder, find all the commented code here : https://github.com/blackbird-agency/magento-2-data-model-sample.

The entity manager is available since version 2.1 of Magento. First and foremost I have to bring to your attention that since version 2.2, its use is not recommended by Magento anymore, here is what the framework says about the class “EntityManager” :
“It is not recommended to use the EntityManager and its infrastructure for the Persistence Entities. In the next version a new “PersistenceEntity Manager” will be created in order to cover the needs of the persistence with the API requests for the reading of the datas.”
Actually it is recommended to use the “Resource Model” by using Magento\Framework\Model\ResourceModel\Db\AbstractDb or also Magento\Eav\Model\Entity\AbstractEntity if it is a EAV model.For the collections and filters, it is recommended to use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection”

Despite all of this, we have to admit that the EntityManager can be really useful in some cases, especially in our case when we have to manage an association table in the data model. And thus, how it is build? 

Frist of all, we go back to the dependency injection, so let's go to di.xml. We will declare our entities for the "MetadataPool", that will allow us to do what we used to do previously in our Resource Model, ie. make a relation between our entity (using the API'sinterface again) and the data base specifying the table and its identifier.

<type name="Magento\Framework\EntityManager\MetadataPool">
 <arguments>
  <argument name="metadata" xsi:type="array">
    <item name="MyVendor\MyModule\Api\Data\StudentInterface" xsi:type="array">
     <item name="entityTableName" xsi:type="string">student</item>
     <item name="identifierField" xsi:type="string">student_id</item>
    </item>
    <item name="MyVendor\MyModule\Api\Data\TeacherInterface" xsi:type="array">
      <item name="entityTableName" xsi:type="string">teacher</item>
      <item name="identifierField" xsi:type="string">teacher_id</item>
    </item>
  </argument>
 </arguments>
</type>

Now let's deal with the Resource Models MyVendor\MyModule\Model\ResourceModel\Student and Teacher in order to override the methods of the CRUD to use the EntityManager. Of course, we have to keep in mind to use the EntityManager in our classes. 

public function save(AbstractModel $object)
{
  return $this->entityManager->save($object);
}

public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null)
{
  return <$this->entityManager->load($object, $value);
}

public function delete(\Magento\Framework\Model\AbstractModel $object)
{
  $this->entityManager->delete($object);
}

To finish with the EntityManager, we will hydrate our objects. What does it mean? It is about giving it the elements it needs to work properly, for instance to give values to its attributes (the same way a constructor does when instantiating an object).

<type name="Magento\Framework\EntityManager\HydratorPool">
  <arguments>
      <argument name="hydrators" xsi:type="array">
          <item name="MyVendor\MyModule\Api\Data\StudentInterface" xsi:type="string">Magento\Framework\EntityManager\AbstractModelHydrator</item>
          <item name="MyVendor\MyModule\Api\Data\TeacherInterface" xsi:type="string">Magento\Framework\EntityManager\AbstractModelHydrator</item>
      </argument>
  </arguments>
</type>

You want to learn even more about this EntityManager, especially about the possibilities to manage the associative tables? I Invite you to read the end of the article to learn about the icing on the cake.

The icing on the cake : the “handlers”

The “handlers” were brought by the entity Manager to manage the associative tables  (in our example “teacher_student”). The handlers are classes that allow to manage the CRUD on associative tables using the ‘Entity Manager’. And thus, thanks to the dependency injection, we avoid dealing with the CRUD in the resource model with the « after/beforeDelete/Save/Update ».
These are classes that extend ‘ExtensionInterface” of the Entity Manager, that will be called by their “execute” method, and that will do what you asked when reading, upgrading, creating or deleting an entity.

Before creating our handlers, we first establish a class that will allow us to get, for one “Student”, all the associated “Teacher”. This will be useful for the recovery, modification or backup of a “Student”.

protected function lookupTeacherIds($studentId)
  {
      $connection = $this->getConnection();

      $entityMetadata = $this->metadataPool->getMetadata(StudentInterface::class);
      $linkField = $entityMetadata->getLinkField();

      $select = $connection->select()
          ->from(['ts' => $this->getTable('teacher_students')], 'id_teacher')
          ->join(
              ['stud' => $this->getMainTable()],
              'stud.' . $linkField . ' = ts.' . $linkField,
              []
          )
          ->where('ts.' . $entityMetadata->getIdentifierField() . ' = :id_student');

      return $connection->fetchCol($select, ['id_student' => (int)$studentId]);
  }

Obviously, make sure that you have everything it needs for the method to work, especially the "MetadataPool". In our case, we should also do it for the Resource Model of "Teacher", eventhough we were not adding this code to the article, you'll find it here on Github : https://github.com/blackbird-agency/magento-2-data-model-sample.

The ReadHandler

In this case, The ReadHandler will enable to manage the reading of the “Teacher” associated to a “Student”.

<?php

namespace MyVendor\MyModule\Model\ResourceModel\Student\Relation;

use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
use MyVendor\MyModule\Model\ResourceModel\Student;

class ReadHandler implements ExtensionInterface
{
  private $metadataPool;

  private $resourceStudent;

  publicfunction __construct(
      MetadataPool $metadataPool,
      Student $resourceStudent
  ) {
      $this->metadataPool = $metadataPool;
      $this->resourceStudent = $resourceStudent;
  }

  publicfunction execute($entity, $arguments = [])
  {
      if ($entity->getStudentId()) {
          $studentTeacherIds = $this->resourceStudent->lookupTeacherIds((int)$entity->getStudentId());
          $entity->setData('teacher_ids', $studentTeacherIds);
      }
      return $entity;
  }
}

The SaveHandler

Here the SaveHandler will be used to create and update a “Student”. In case of an update, we will check if there was an addition or deletion of one “Teacher” or more and thus we modify the associative table.

<?php

namespace MyVendor\MyModule\Model\ResourceModel\Student\Relation;


use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
use MyVendor\MyModule\Api\Data\StudentInterface;
use MyVendor\MyModule\Model\ResourceModel\Student;

class SaveHandler implements ExtensionInterface
{
  protected $metadataPool;

  protected $resourceStudent;

  publicfunction __construct(
      MetadataPool $metadataPool,
      Student $resourceStudent
  ) {
      $this->metadataPool = $metadataPool;
      $this->resourceStudent = $resourceStudent;
  }

  publicfunction execute($entity, $arguments = [])
  {
      $entityMetadata = $this->metadataPool->getMetadata(StudentInterface::class);
      $linkField      = $entityMetadata->getLinkField();

      $connection = $entityMetadata->getEntityConnection();

      $oldTeachers = $this->resourceStudent->lookupTeacherIds((int)$entity->getStudentId());
      $newTeacher = (array)$entity->getTeacherIds();

      $table = $this->resourceStudent->getTable('teacher_students');

      $delete = array_diff($oldTeachers, $newTeacher);
      if ($delete) {
          $where = [
              $linkField . ' = ?'        => $entity->getStudentId(),
              'teacher_id IN (?)' => $delete,
          ];
          $connection->delete($table, $where);
      }

      $insert = array_diff($newTeacher, $oldTeachers);
      if ($insert) {
          $data = [];
          foreach ($insert as $teacherId) {
              $data[] = [
                  $linkField => $entity->getStudentId(),
                  'teacher_id' => (int)$teacherId
              ];
          }
          $connection->insertMultiple($table, $data);
      }

      return $entity;
  }
}

We could also use a “DeleteHandler” but in our case, if the data model has been actually built with “DeleteCascade”, it is not necessary to manage its deletion in the associative table.

Dependency injection for handlers

To finish, it is necessary to make some add-ons into to di.xml. in order to manage the CRUD and to use our handlers, we define “ExtensionPool”. Here we make the relation between the action with our Handler (create, read, update, delete) and the concerned model.



<type name="Magento\Framework\EntityManager\Operation\ExtensionPool">
  <arguments>
      <argument name="extensionActions" xsi:type="array">
          <item name="MyVendor\MyModule\Api\Data\StudentInterface" xsi:type="array">
              <item name="read" xsi:type="array">
                  <item name="studentReader" xsi:type="string">MyVendor\MyModule\Model\ResourceModel\Student\Relation\ReadHandler</item>
              </item>
              <item name="create" xsi:type="array">
                  <item name="studentCreator" xsi:type="string">MyVendor\MyModule\Model\ResourceModel\Student\Relation\SaveHandler</item>
              </item>
              <item name="update" xsi:type="array">
                  <item name="studentUpdater" xsi:type="string">MyVendor\MyModule\Model\ResourceModel\Student\Relation\SaveHandler</item>
              </item>
          </item>
          <item name="MyVendor\MyModule\Api\Data\TeacherInterface" xsi:type="array">
              <item name="read" xsi:type="array">
                  <item name="teacherReader" xsi:type="string">MyVendor\MyModule\Model\ResourceModel\Teacher\Relation\ReadHandler</item>
              </item>
              <item name="create" xsi:type="array">
                  <item name="teacherCreator" xsi:type="string">MyVendor\MyModule\Model\ResourceModel\Teacher\Relation\SaveHandler</item>
              </item>
              <item name="update" xsi:type="array">
                  <item name="teacherUpdater" xsi:type="string">MyVendor\MyModule\Model\ResourceModel\Teacher\Relation\SaveHandler</item>
              </item>
          </item>
      </argument>
  </arguments>
</type>


Here it is, our recipe is over. We wish you a “Bon appétit” and hope it won’t be too heavy to digest.

Write your comment

Ready
for take-off?

Blackbird is a web agency specialized in the development of eCommerce websites. We are Magento Experts and we offer our services and advice for e-marketing, strategy, branding, deployment and methodology.

Contact
+339 50 66 21 38


Legal terms
Agency 30, Avenue du Rhin
67100 Strasbourg
France
SEE MAP
FR - EN