• Développement
  • Magento

La recette pour un bon modèle de données Magento 2- partie 2

kevin_2234efd45f
Kevin Weyhaupt, Lead Technique
Le 22 mars 2018
cooking_magento_3_1_9a0bceb26e
  • ecommerce

Lecture :9 minutes

EDIT : Le contenu de cet article peut ne plus être d'actualité suite aux nouvelles bonnes pratiques suggérées par Magento.

Partie 2 : Apporter de la saveur

Liant entre le modèle et les données

Rappel, retrouvez le code complet et commenté : https://github.com/blackbird-agency/magento-2-data-model-sample.

Pour le moment nous avons uniquement le côté métier et donc il manque la relation avec nos données. Pour cela nous allons créer nos « resource models ».
Les « resource models » permettent de faire le lien entre nos modèles et la base de données. Ces classes héritent ici d’AbstractDb afin de pouvoir effectuer des opérations avant ou après différentes actions sur la base de données, par exemple « after/beforeDelete », « after/beforeSave » mais surtout pour définir dans le constructeur la table et la clé primaire en lien avec notre objet à l’aide de la méthode “_init”.


<?php
namespace MyVendor\MyModule\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use MyVendor\MyModule\Api\Data\StudentInterface;

class Student extends AbstractDb
{
  protected function _construct()
  {
    $this->_init('student', StudentInterface::ID);
  }
}


Gestion des collections

Dans certains cas on a besoin de récupérer facilement la collection de toutes notre entités, pour par exemple l’afficher dans une grille. L’idéal est de se placer dans MyVendor/MyModule/Model/ResourceModel/Student.
En héritant d’AbstractCollection il suffit simplement d’appliquer la méthode “_init” pour lier le modèle métier et le “resource model” :


<?php

namespace MyVendor\MyModule\Model\ResourceModel\Student;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use MyVendor\MyModule\Model;

class Collection extends AbstractCollection
{

protected function _construct()
{
parent::construct();
$this->_init(Model\Student::class, Model\ResourceModel\Student::class);
}
}

Agrémenter le tout avec des “repositories”

Les « repositories »

Un « repository » est encore un morceau du service contract qui va permettre de gérer le CRUD de notre modèle. On y trouvera le plus souvent les méthodes telles que : getById, save, delete …
Avant les “repositories”, le CRUD d’un modèle était géré par le modèle lui même à l’aide des méthodes save/load/delete de l’AbstractModel (par héritage). Désormais ces méthodes sont dépréciées car un modèle n’est pas censé être responsable de sa gestion du CRUD, ainsi il est géré par les “repositories” utilisant les “resource models”.

<?php 
namespace MyVendor\MyModule\Api;
/**
* @api
*/
interface StudentRepositoryInterface
{
public function save(Data\StudentInterface $student);
public function getById($studentId);
public function delete(Data\StudentInterface $student);
public function deleteById($studentId);
}


Implémentation des « Repository »

Pour les “repository” voici un exemple d’implémentation des différentes méthodes :


<?php
namespace MyVendor\MyModule\Model;

use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\NoSuchEntityException;
use MyVendor\MyModule\Api\Data;
use MyVendor\MyModule\Api\StudentRepositoryInterface;
use MyVendor\MyModule\Model\ResourceModel;

class StudentRepository implements StudentRepositoryInterface
{
  private $resourceStudent;

  private $studentFactory;

  public function __construct(
    ResourceModel\Student $resourceStudent,
    Data\StudentInterfaceFactory $studentFactory
  ) {
      $this->resourceStudent = $resourceStudent;
      $this->studentFactory = $studentFactory;
  }
  public function save(Data\StudentInterface $student)
{
  try {
    $this->resourceStudent->save($student);
  } catch (\Exception $e) {
    throw new CouldNotSaveException(__($e->getMessage()));
  }
    return $student;
  }

  public function getById($studentId)
  {
    $student = $this->studentFactory->create();
    $this->resourceStudent->load($student, $studentId);
    if (!$student->getId()) {
      throw new NoSuchEntityException(__('Student with id "%1" does not exist', $studentId));
  }
    return $student;
}
public function delete(Data\StudentInterface $student)
{
  try {
    $this->resourceStudent->delete($student);
  } catch (\Exception $e) {
    throw new CouldNotDeleteException(__($e->getMessage()));
  }
    return $student;
  }
  public function deleteById($studentId)
  {
    return $this->delete($this->getById($studentId));
  }
}


L’un des avantages des “Repositories” est que l’on peut gérer le “Lazy Loading”. C’est à dire que l’on va pouvoir charger uniquement ce que l’on a besoin et seulement dès que ceci est nécessaire. De plus, on vérifie si l’entité que l’on veut utiliser n’a pas déjà été chargée auparavant, et ainsi on réutilise l’entité existante au lieu de la ré instancier. Cela ajoute ainsi un gain de performance car l’utilisation de la mémoire vive du serveur est optimisée.

Ensuite pour finir avec les Repositories, comme dans la partie 1, on va appliquer l’injection de dépendance entre l’interface de l’API et le modèle. En plus de ça, on ajoute aussi un “RepositoryFactory” pour spécifier sur quel entité va déprendre de quel Repository.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="MyVendor\MyModule\Api\StudentRepositoryInterface" type="MyVendor\MyModule\Model\StudentRepository"/>
<type name="Magento\Framework\Model\Entity\RepositoryFactory">
<arguments>
<argument name="entities" xsi:type="array">
<item name="MyVendor\MyModule\Api\Data\StudentInterface">MyVendor\MyModule\Api\StudentRepositoryInterface</item>
</argument>
</arguments>
</type>
</config>

Voilà la partie 2 touche à sa fin. Si vous avez la motivation et l’envie de perfectionner votre recette afin d’être le meilleur dans le domaine, je vous invite à finir cette recette avec la 3ème et dernière partie, intitulée : La touche du chef ! 

Nos articles

Découvrir aussi

Abonnez-vous au blog pour ne rien louper