Comment mettre en œuvre un contrat de service pour un module personnalisé dans Magento 2?

42

Comme on le voit dans cet article: Les méthodes d'enregistrement et de chargement obsolètes dans Abstract Model, les méthodes saveand loadsont obsolètes dans la branche de développement de Magento 2.

Ainsi, la bonne pratique consiste maintenant à mettre en œuvre des contrats de service pour traiter avec les entités CRUD.

Quel est le processus étape par étape que je dois suivre pour mettre en œuvre les contrats de service pour mes entités de module personnalisées?

NB: Je sais qu'il peut y avoir des milliers de méthodes dans mes modèles CRUD. Je demande simplement les méthodes évidentes décrites ci-dessous: http://devdocs.magento.com/guides/v2.0/extension-dev-guide /service-contracts/design-patterns.html :

  • get
  • save
  • getList
  • delete
  • deleteById
Raphael au pianisme numérique
la source

Réponses:

90

Je voudrais donner un peu plus de détails en plus de l'excellente réponse de @ryanF.

J'aimerais résumer les raisons d'ajouter un référentiel pour les entités personnalisées, de donner des exemples sur la manière de le faire et d'expliquer également comment exposer ces méthodes de référentiel dans le cadre de l'API Web.

Avertissement: je décris seulement une approche pragmatique de la manière de procéder pour les modules tiers - les équipes principales ont leurs propres normes qu’elles respectent (ou non).

En général, l'objectif d'un référentiel est de masquer la logique liée au stockage.
Un client d'un référentiel ne doit pas se soucier de savoir si l'entité retournée est conservée en mémoire dans un tableau, est extraite d'une base de données MySQL, extraite d'une API distante ou d'un fichier.
Je suppose que l'équipe principale de Magento a agi de la sorte pour pouvoir changer ou remplacer l'ORM à l'avenir. Dans Magento, l'ORM comprend actuellement les modèles, les modèles de ressources et les collections.
Si un module tiers utilise uniquement les référentiels, Magento peut modifier comment et où les données sont stockées et le module continuera à fonctionner malgré ces profonds changements.

Référentiels ont généralement des méthodes telles que findById(), findByName(), put()ou remove().
Dans ces Magento communément sont appelés getbyId(), save()et delete(), même pas prétendre qu'ils font quoi que ce soit d' autre que les opérations CRUD DB.

Les méthodes de référentiel Magento 2 peuvent facilement être exposées en tant que ressources API, ce qui les rend très utiles pour les intégrations avec des systèmes tiers ou les instances sans tête de Magento.

"Dois-je ajouter un référentiel pour mon entité personnalisée?".

Comme toujours, la réponse est

"Ça dépend".

En résumé, si vos entités seront utilisées par d'autres modules, alors oui, vous voudrez probablement ajouter un référentiel.

Il existe un autre facteur qui entre en ligne de compte: dans Magento 2, les référentiels peuvent facilement être exposés sous forme d’API Web - c’est-à-dire REST et SOAP - ressources.

Si cela vous intéresse, en raison d’intégrations système tierces ou d’une configuration Magento sans tête, alors oui, vous souhaitez probablement ajouter un référentiel pour votre entité.

Comment ajouter un référentiel pour mon entité personnalisée?

Supposons que vous souhaitiez exposer votre entité dans le cadre de l'API REST. Si ce n'est pas le cas, vous pouvez ignorer la partie suivante sur la création des interfaces et aller directement à "Créer l'implémentation du référentiel et du modèle de données" ci-dessous.

Créer le référentiel et les interfaces de modèle de données

Créez les dossiers Api/Data/dans votre module. Ce n'est que convention, vous pouvez utiliser un emplacement différent, mais vous ne devriez pas.
Le référentiel va dans le Api/dossier. Le Data/sous-répertoire est pour plus tard.

Dans Api/, créez une interface PHP avec les méthodes que vous souhaitez exposer. Selon les conventions de Magento 2, tous les noms d'interface se terminent par le suffixe Interface.
Par exemple, pour une Hamburgerentité, je créerais l'interface Api/HamburgerRepositoryInterface.

Créer l'interface de référentiel

Les référentiels Magento 2 font partie de la logique de domaine d'un module. Cela signifie qu’il n’existe pas d’ensemble de méthodes qu’un référentiel doit implémenter.
Cela dépend entièrement de l'objectif du module.

Cependant, dans la pratique, tous les référentiels sont assez similaires. Ce sont des wrappers pour la fonctionnalité CRUD.
La plupart ont des méthodes getById, save, deleteet getList.
Il peut y en avoir davantage, par exemple, CustomerRepositoryune méthode get, qui récupère un client par courrier électronique, getByIdest utilisée pour extraire un client par ID d'entité.

Voici un exemple d'interface de référentiel pour une entité hamburger:

<?php

namespace VinaiKopp\Kitchen\Api;

use Magento\Framework\Api\SearchCriteriaInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerInterface;

interface HamburgerRepositoryInterface
{
    /**
     * @param int $id
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getById($id);

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface $hamburger
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
     */
    public function save(HamburgerInterface $hamburger);

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface $hamburger
     * @return void
     */
    public function delete(HamburgerInterface $hamburger);

    /**
     * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface
     */
    public function getList(SearchCriteriaInterface $searchCriteria);

}

Important! Ici être timesinks!
Il existe quelques pièges difficiles à résoudre si vous vous trompez:

  1. NE PAS utiliser PHP7 types d'argument scalaire ou les types de retour si vous voulez accrocher ceci dans l'API REST!
  2. Ajoutez des annotations PHPDoc pour tous les arguments et le type de retour à toutes les méthodes!
  3. Utilisez des noms de classe entièrement qualifiés dans le bloc PHPDoc!

Les annotations sont analysées par le framework Magento pour déterminer comment convertir les données en JSON ou XML. Les importations de classe (c'est-à-dire les useinstructions) ne sont pas appliquées!

Chaque méthode doit avoir une annotation avec tous les types d'argument et le type de retour. Même si une méthode ne prend aucun argument et ne renvoie rien, elle doit avoir l'annotation suivante:

/**
 * @return void
 */

Types scalaires ( string, int, floatet bool) doivent également être spécifié, à la fois pour les arguments et en tant que valeur de retour.

Notez que dans l'exemple ci-dessus, les annotations des méthodes renvoyant des objets sont également spécifiées en tant qu'interfaces.
Les interfaces de type de retour sont toutes dans l' Api\Dataespace de noms / répertoire.
Cela indique qu'ils ne contiennent aucune logique métier. Ce sont simplement des sacs de données.
Nous devons ensuite créer ces interfaces.

Créer l'interface DTO

Je pense que Magento appelle ces interfaces "modèles de données", un nom que je n’aime pas du tout.
Ce type de classe est communément appelé objet de transfert de données ou DTO .
Ces classes DTO ne disposent que de getters et de setters pour toutes leurs propriétés.

La raison pour laquelle je préfère utiliser DTO par rapport à un modèle de données est qu'il est moins facile de confondre avec les modèles de données ORM, les modèles de ressources ou les modèles de vue ... trop de choses sont déjà des modèles dans Magento.

Les mêmes restrictions concernant le typage PHP7 qui s'appliquent aux référentiels s'appliquent également aux DTO.
De plus, chaque méthode doit avoir une annotation avec tous les types d'argument et le type de retour.

<?php

namespace VinaiKopp\Kitchen\Api\Data;

use Magento\Framework\Api\ExtensibleDataInterface;

interface HamburgerInterface extends ExtensibleDataInterface
{
    /**
     * @return int
     */
    public function getId();

    /**
     * @param int $id
     * @return void
     */
    public function setId($id);

    /**
     * @return string
     */
    public function getName();

    /**
     * @param string $name
     * @return void
     */
    public function setName($name);

    /**
     * @return \VinaiKopp\Kitchen\Api\Data\IngredientInterface[]
     */
    public function getIngredients();

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\IngredientInterface[] $ingredients
     * @return void
     */
    public function setIngredients(array $ingredients);

    /**
     * @return string[]
     */
    public function getImageUrls();

    /**
     * @param string[] $urls
     * @return void
     */
    public function setImageUrls(array $urls);

    /**
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface|null
     */
    public function getExtensionAttributes();

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface $extensionAttributes
     * @return void
     */
    public function setExtensionAttributes(HamburgerExtensionInterface $extensionAttributes);
}

Si une méthode récupère ou retourne un tableau, le type des éléments du tableau doit être spécifié dans l'annotation PHPDoc, suivi d'un crochet entre deux crochets [].
Cela vaut tant pour les valeurs scalaires (par exemple int[]) que pour les objets (par exemple IngredientInterface[]).

Notez que j'utilise Api\Data\IngredientInterfaceun exemple pour une méthode renvoyant un tableau d'objets, je ne vais pas ajouter le code des ingrédients à cette publication difficile.

ExtensibleDataInterface?

Dans l'exemple ci-dessus, la HamburgerInterfaceétend la ExtensibleDataInterface.
Techniquement, cela n’est requis que si vous voulez que d’autres modules puissent ajouter des attributs à votre entité.
Si c'est le cas, vous devez également ajouter une autre paire de getter / setter, appelée par convention getExtensionAttributes()et setExtensionAttributes().

La dénomination du type de retour de cette méthode est très importante!

Le framework Magento 2 générera l'interface, l'implémentation et l'usine pour l'implémentation si vous les nommez juste. Les détails de ces mécanismes sont hors de portée de ce post si.
Sachez simplement que si l'interface de l'objet que vous souhaitez rendre extensible est appelée \VinaiKopp\Kitchen\Api\Data\HamburgerInterface, le type d'attribut d'extension doit être \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface. Donc, le mot Extensiondoit être inséré après le nom de l'entité, juste avant le Interfacesuffixe.

Si vous ne souhaitez pas que votre entité soit extensible, l'interface de DTO n'a pas besoin d'étendre une autre interface et les méthodes getExtensionAttributes()et setExtensionAttributes()peuvent être omises.

Assez parlé de l'interface DTO pour l'instant, il est temps de revenir à l'interface du référentiel.

Le type de retour getList () SearchResults

La méthode repository getListrenvoie encore un autre type, à savoir une SearchResultsInterfaceinstance.

La méthode getListpourrait bien sûr simplement renvoyer un tableau d'objets correspondant à l'objet spécifié SearchCriteria, mais le retour d'une SearchResultsinstance permet d'ajouter des métadonnées utiles aux valeurs renvoyées.

Vous pouvez voir comment cela fonctionne ci-dessous dans l' getList()implémentation de la méthode de référentiel .

Voici l'exemple d'interface de résultat de recherche hamburger:

<?php

namespace VinaiKopp\Kitchen\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface HamburgerSearchResultInterface extends SearchResultsInterface
{
    /**
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface[]
     */
    public function getItems();

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface[] $items
     * @return void
     */
    public function setItems(array $items);
}

Toute cette interface ne remplace les types des deux méthodes getItems()et setItems()de l'interface parent.

Résumé des interfaces

Nous avons maintenant les interfaces suivantes:

  • \VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface
  • \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
  • \VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface

Le référentiel n’étend rien,
le HamburgerInterfaces'étend \Magento\Framework\Api\ExtensibleDataInterface,
et le HamburgerSearchResultInterfaceétend \Magento\Framework\Api\SearchResultsInterface.

Créer les implémentations du référentiel et du modèle de données

L'étape suivante consiste à créer les implémentations des trois interfaces.

Le référentiel

Essentiellement, le référentiel utilise l'ORM pour effectuer son travail.

Le getById(), save() et les delete()méthodes sont assez simples.
Le HamburgerFactoryest injecté dans le référentiel en tant qu'argument constructeur, comme on peut le voir un peu plus loin.

public function getById($id)
{
    $hamburger = $this->hamburgerFactory->create();
    $hamburger->getResource()->load($hamburger, $id);
    if (! $hamburger->getId()) {
        throw new NoSuchEntityException(__('Unable to find hamburger with ID "%1"', $id));
    }
    return $hamburger;
}

public function save(HamburgerInterface $hamburger)
{
    $hamburger->getResource()->save($hamburger);
    return $hamburger;
}

public function delete(HamburgerInterface $hamburger)
{
    $hamburger->getResource()->delete($hamburger);
}

Passons maintenant à la partie la plus intéressante d’un référentiel, la getList()méthode.
La getList()méthode doit traduire les SerachCriteriaconditions en appels de méthodes sur la collection.

La difficulté consiste à définir correctement les conditions ANDet ORpour les filtres, d’autant plus que la syntaxe de définition des conditions de la collection diffère selon qu’il s’agit d’une entité EAV ou d’une table à plat.

Dans la plupart des cas, getList()peuvent être implémentés comme illustré dans l'exemple ci-dessous.

<?php

namespace VinaiKopp\Kitchen\Model;

use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SortOrder;
use Magento\Framework\Exception\NoSuchEntityException;
use VinaiKopp\Kitchen\Api\Data\HamburgerInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterfaceFactory;
use VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface;
use VinaiKopp\Kitchen\Model\ResourceModel\Hamburger\CollectionFactory as HamburgerCollectionFactory;
use VinaiKopp\Kitchen\Model\ResourceModel\Hamburger\Collection;

class HamburgerRepository implements HamburgerRepositoryInterface
{
    /**
     * @var HamburgerFactory
     */
    private $hamburgerFactory;

    /**
     * @var HamburgerCollectionFactory
     */
    private $hamburgerCollectionFactory;

    /**
     * @var HamburgerSearchResultInterfaceFactory
     */
    private $searchResultFactory;

    public function __construct(
        HamburgerFactory $hamburgerFactory,
        HamburgerCollectionFactory $hamburgerCollectionFactory,
        HamburgerSearchResultInterfaceFactory $hamburgerSearchResultInterfaceFactory
    ) {
        $this->hamburgerFactory = $hamburgerFactory;
        $this->hamburgerCollectionFactory = $hamburgerCollectionFactory;
        $this->searchResultFactory = $hamburgerSearchResultInterfaceFactory;
    }

    // ... getById, save and delete methods listed above ...

    public function getList(SearchCriteriaInterface $searchCriteria)
    {
        $collection = $this->collectionFactory->create();

        $this->addFiltersToCollection($searchCriteria, $collection);
        $this->addSortOrdersToCollection($searchCriteria, $collection);
        $this->addPagingToCollection($searchCriteria, $collection);

        $collection->load();

        return $this->buildSearchResult($searchCriteria, $collection);
    }

    private function addFiltersToCollection(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
            $fields = $conditions = [];
            foreach ($filterGroup->getFilters() as $filter) {
                $fields[] = $filter->getField();
                $conditions[] = [$filter->getConditionType() => $filter->getValue()];
            }
            $collection->addFieldToFilter($fields, $conditions);
        }
    }

    private function addSortOrdersToCollection(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        foreach ((array) $searchCriteria->getSortOrders() as $sortOrder) {
            $direction = $sortOrder->getDirection() == SortOrder::SORT_ASC ? 'asc' : 'desc';
            $collection->addOrder($sortOrder->getField(), $direction);
        }
    }

    private function addPagingToCollection(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        $collection->setPageSize($searchCriteria->getPageSize());
        $collection->setCurPage($searchCriteria->getCurrentPage());
    }

    private function buildSearchResult(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        $searchResults = $this->searchResultFactory->create();

        $searchResults->setSearchCriteria($searchCriteria);
        $searchResults->setItems($collection->getItems());
        $searchResults->setTotalCount($collection->getSize());

        return $searchResults;
    }
}

Les filtres dans un FilterGroupdoivent être combinés à l'aide d'un opérateur OU .
Des groupes de filtres distincts sont combinés à l'aide de l' opérateur logique AND .

Ouf
C'était le plus gros travail. Les autres implémentations d'interface sont plus simples.

Le DTO

Magento avait initialement prévu que les développeurs implémentent le DTO en tant que classes distinctes, distinctes du modèle d'entité.

Cependant, l’équipe principale n’a fait cela que pour le module client (elle \Magento\Customer\Api\Data\CustomerInterfaceest mise en œuvre par \Magento\Customer\Model\Data\Customer, pas \Magento\Customer\Model\Customer).
Dans tous les autres cas, le modèle d'entité implémente l'interface DTO (par exemple, \Magento\Catalog\Api\Data\ProductInterfaceest implémenté par \Magento\Catalog\Model\Product).

J'ai posé des questions à ce sujet aux membres de l'équipe principale lors de conférences, mais je n'ai pas reçu de réponse claire sur ce qui doit être considéré comme une bonne pratique.
J'ai l'impression que cette recommandation a été abandonnée. Ce serait bien d’obtenir une déclaration officielle à ce sujet.

Pour le moment, j'ai pris la décision pragmatique d'utiliser le modèle comme implémentation d'interface DTO. Si vous estimez qu'il est préférable d'utiliser un modèle de données distinct, n'hésitez pas à le faire. Les deux approches fonctionnent bien dans la pratique.

Si l’interface DTO étend la Magento\Framework\Api\ExtensibleDataInterface, le modèle doit s’étendre Magento\Framework\Model\AbstractExtensibleModel.
Si l'extensibilité ne vous intéresse pas, le modèle peut simplement continuer à étendre la classe de base du modèle ORM Magento\Framework\Model\AbstractModel.

Comme l'exemple HamburgerInterfaces'étend, le ExtensibleDataInterfacemodèle hamburger étend le AbstractExtensibleModel, comme on peut le voir ici:

<?php

namespace VinaiKopp\Kitchen\Model;

use Magento\Framework\Model\AbstractExtensibleModel;
use VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerInterface;

class Hamburger extends AbstractExtensibleModel implements HamburgerInterface
{
    const NAME = 'name';
    const INGREDIENTS = 'ingredients';
    const IMAGE_URLS = 'image_urls';

    protected function _construct()
    {
        $this->_init(ResourceModel\Hamburger::class);
    }

    public function getName()
    {
        return $this->_getData(self::NAME);
    }

    public function setName($name)
    {
        $this->setData(self::NAME, $name);
    }

    public function getIngredients()
    {
        return $this->_getData(self::INGREDIENTS);
    }

    public function setIngredients(array $ingredients)
    {
        $this->setData(self::INGREDIENTS, $ingredients);
    }

    public function getImageUrls()
    {
        $this->_getData(self::IMAGE_URLS);
    }

    public function setImageUrls(array $urls)
    {
        $this->setData(self::IMAGE_URLS, $urls);
    }

    public function getExtensionAttributes()
    {
        return $this->_getExtensionAttributes();
    }

    public function setExtensionAttributes(HamburgerExtensionInterface $extensionAttributes)
    {
        $this->_setExtensionAttributes($extensionAttributes);
    }
}

Extraire les noms de propriétés en constantes permet de les conserver au même endroit. Ils peuvent être utilisés par la paire getter / setter et également par le script d'installation qui crée la table de base de données. Sinon, il ne sert à rien de les extraire en constantes.

Le résultat de recherche

La SearchResultsInterfaceplus simple des trois interfaces à implémenter, car elle peut hériter de toutes ses fonctionnalités d'une classe d'infrastructure.

<?php

namespace VinaiKopp\Kitchen\Model;

use Magento\Framework\Api\SearchResults;
use VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface;

class HamburgerSearchResult extends SearchResults implements HamburgerSearchResultInterface
{

}

Configurer les préférences d'ObjectManager

Même si les implémentations sont terminées, nous ne pouvons toujours pas utiliser les interfaces comme dépendances d'autres classes, car le gestionnaire d'objets de Magento Framework ne sait pas quelles implémentations utiliser. Nous devons ajouter une etc/di.xmlconfiguration pour avec les préférences.

<?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="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" type="VinaiKopp\Kitchen\Model\HamburgerRepository"/>
    <preference for="VinaiKopp\Kitchen\Api\Data\HamburgerInterface" type="VinaiKopp\Kitchen\Model\Hamburger"/>
    <preference for="VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface" type="VinaiKopp\Kitchen\Model\HamburgerSearchResult"/>
</config>

Comment le référentiel peut-il être exposé en tant que ressource API?

Cette partie est très simple, c'est la récompense d'avoir parcouru tout le travail de création d'interfaces, d'implémentations et de câblage.

Tout ce que nous avons à faire est de créer un etc/webapi.xmlfichier.

<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route method="GET" url="/V1/vinaikopp_hamburgers/:id">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="getById"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="GET" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="getList"/>
        <resources>
            <resource ref="anonymouns"/>
        </resources>
    </route>
    <route method="POST" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="save"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="PUT" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="save"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="DELETE" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="delete"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

Notez que cette configuration permet non seulement d'utiliser le référentiel en tant que points de terminaison REST, mais qu'elle expose également les méthodes dans le cadre de l'API SOAP.

Dans le premier exemple de route, <route method="GET" url="/V1/vinaikopp_hamburgers/:id">l'espace réservé :iddoit correspondre au nom de l'argument à la méthode cartographié, public function getById($id).
Les deux noms doivent correspondre, par exemple /V1/vinaikopp_hamburgers/:hamburgerId, ne fonctionneraient pas, car le nom de variable d'argument de méthode est $id.

Pour cet exemple, j'ai défini l'accessibilité sur <resource ref="anonymous"/>. Cela signifie que la ressource est exposée publiquement sans aucune restriction!
Pour qu'une ressource ne soit disponible que pour un client connecté, utilisez <resource ref="self"/>. Dans ce cas, le mot spécial medans l'URL du noeud final de la ressource sera utilisé pour renseigner une variable d'argument $idavec l'ID du client actuellement connecté.
Regardez le client Magento etc/webapi.xmlet CustomerRepositoryInterfacesi vous en avez besoin.

Enfin, le <resources>peut également être utilisé pour restreindre l'accès à une ressource à un compte d'utilisateur admin. Pour ce faire, définissez la <resource>référence sur un identifiant défini dans un etc/acl.xmlfichier.
Par exemple, <resource ref="Magento_Customer::manage"/>limiterait l'accès à tout compte administrateur disposant du privilège de gérer des clients.

Un exemple de requête d'API utilisant curl pourrait ressembler à ceci:

$ curl -X GET http://example.com/rest/V1/vinaikopp_hamburgers/123

Remarque: l'écriture de ceci a commencé comme une réponse à https://github.com/astorm/pestle/issues/195
Découvrez un pilon , achetez Commercebug et devenez un patron de @alanstorm

Vinai
la source
1
Merci pour cette excellente réponse. Désolé, il me manque peut-être quelque chose, mais à quoi sert-il d'avoir une interface propre pour une entité alors qu'à la fin, elle doit s'étendre depuis AbstractModel qui a la méthode setData, ce qui signifie que vous pouvez ajouter quelque chose à l'objet, quelle que soit l'interface?
LDusan
Une classe peut implémenter un nombre illimité d'interfaces et ajouter des méthodes supplémentaires. L'important est que toute autre classe ne dépende que des méthodes d'interface et ne connaisse donc aucune des autres. Cela rend les détails d'implémentation des méthodes non-interface, qui peuvent être modifiés à tout moment sans casser les classes externes. C'est l'idée derrière l'inversion de dépendance. La classe et tous les clients dépendent de l'interface et ignorent les détails de l'implémentation. Est-ce que cela clarifie?
Vinai
Merci pour la réponse, je vois ce que vous voulez dire. Le fait est que setData est une méthode publique, donc je ne suis pas sûr si cela peut être considéré comme un détail d'implémentation. Si c'est censé être utilisé comme une méthode publique, comment pouvons-nous être sûrs que rien ne sera cassé de l'extérieur si changé?
LDusan
3
Je m'excuse. Ce que vous décrivez est un point de vue commun. Les mécanismes des dépendances ne sont pas intuitifs et puisque PHP permet d'appeler des méthodes qui ne font pas partie de l'interface dépendante de l'interface, et comme il n'a pas besoin d'être compilé, le fonctionnement des dépendances est encore plus flou et difficile à voir clairement. . Cela peut également être observé dans le noyau de Magento 2, où il existe de nombreux endroits où les méthodes d'implémentation sont appelées et qui ne font pas partie de l'interface dépendante. Celles-ci sont de mauvais exemples et rendent encore plus difficile une compréhension claire et solide.
Vinai
1
Continuons cette discussion sur le chat .
Vinai
35

@Raphael at Digital Pianism:

Veuillez vous référer à l'exemple de structure de module suivant:

app/
   code/
  |    Namespace/
  |   |    Custom/
  |   |   |    Api/
  |   |   |   |    CustomRepositoryInterface.php
  |   |   |   |    Data/
  |   |   |   |   |    CustomInterface.php
  |   |   |   |   |    CustomSearchResultsInterface.php
  |   |   |    etc/
  |   |   |   |    di.xml
  |   |   |   |    module.xml
  |   |   |    Model/
  |   |   |   |    Custom.php
  |   |   |   |    CustomRepository.php
  |   |   |   |    ResourceModel/
  |   |   |   |   |    Custom.php
  1. Créer une interface de référentiel (contrat de service)
    Namespace/Custom/Api/CustomRepositoryInterface.php: http://codepad.org/WognSKnH

  2. Créez SearchResultsInterface
    Namespace/Custom/Api/Data/CustomSearchResultsInterface.php: http://codepad.org/zcbi8X4Z

  3. Créer une interface personnalisée (conteneur de données)
    Namespace/Custom/Api/Data/CustomInterface.php: http://codepad.org/Ze53eT4o

  4. Créer un référentiel personnalisé (référentiel concret)
    Namespace/Custom/Model/CustomRepository.php: http://codepad.org/KNt5QAGZ
    C'est ici que se passe la "magie". Via le constructeur DI, vous transmettez la fabrique de modèles / collections de ressources pour votre module personnalisé; En ce qui concerne la méthode de sauvegarde CRUD dans ce référentiel, en raison de votre CustomRepositoryInterface, vous devez transmettre un paramètre de CustomInterface. Le di.xml de votre module a une préférence pour remplacer une interface de ce type par un modèle d'entité. Le modèle d'entité est transmis au modèle de ressource et est enregistré.

  5. Définir la préférence dans
    Namespace/Custom/etc/di.xml: http://codepad.org/KmcoOUeV

  6. Modèle d'entité implémentant une interface personnalisée (conteneur de données)
    Namespace/Custom/Model/Custom.php: http://codepad.org/xQiBU7p7 .

  7. Modèle de ressource
    Namespace/Custom/Model/ResourceModel/Custom.php: http://codepad.org/IOsxm9qW

Quelques points à noter:

  • Avertissement!!! J'ai utilisé "Namespace" à la place de votre nom de fournisseur personnalisé, nom de l'agence, etc ... quel que soit le nom que vous utilisez pour regrouper vos modules ... l'utilisation réelle de "Namespace" n'est pas valide dans Php ... alors sachez que je l'ai fait pour des raisons de commodité, et que je ne pense pas que cela fonctionnera, et je ne le suggère en aucune manière.

  • @Ryan Street m'a appris cela ... alors je ne veux pas prendre tout le crédit

  • Modifiez clairement la mise en œuvre du référentiel en fonction de vos besoins

  • Vous implémentez l'interaction avec vos modèles d'entités personnalisés / modèles de ressources / collections dans le référentiel concret ...

  • Je sais que je n’ai pas abordé toutes les méthodes que vous avez énumérées dans votre question, mais c’est un bon début et il convient de combler le fossé entre la documentation et la mise en œuvre réelle.

ryanF
la source
Ryan, les méthodes mentionnées dans les contrats de service sont-elles obligatoires pour toute API de savon personnalisée que nous créons, à savoir save (), delete (), etc.?
Sushivam
Pourriez-vous me donner une idée de la création d'une api de savon personnalisée dans magento 2?
Sushivam
@SachinS Malheureusement, je n'ai aucune idée à offrir concernant SOAP. Je ne l'ai pas encore examiné et je ne l'ai pas encore mis en œuvre. Le mieux que je puisse suggérer serait de poser une nouvelle question à ce sujet. Je dirais aussi de vérifier les documents, mais ce n'est malheureusement pas toujours la meilleure solution (ils font peut-être défaut). Vous pouvez toujours jeter un coup d'œil à la base de code ou à une extension tierce et voir s'il y a un aperçu à ce sujet. Bonne chance! Si vous trouvez votre réponse, vous pouvez ajouter le lien ici. Merci
ryanF
Merci pour la réponse @ryan, de toute façon, j'ai implémenté mon module en utilisant REST, car il est léger, comparé à SOAP ... Si
j'implémente
3
@ryanF Merci pour cette réponse très utile. Je sais que ce n'est pas censé être un code de travail copier-coller, mais voici quelques fautes de frappe à l'avantage des autres utilisateurs. Dans le référentiel, CustomSearchResultsInterfaceFactory doit être CustomSearchResultsFactory. $ searchResults-> setCriteria devrait être $ searchResults-> setSearchCriteria. $ Douane [] dans le foreach devrait être $ douane []. Je crois que c'est à propos de ça.
Tetranz
3

dossiers complets d'utilisation des contrats de service

Personnalisé / Module / registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Custom_Module',
    __DIR__
);

../etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Custom_Module" setup_version="1.0.0" />
</config>

../Setup/InstallSchema.php

<?php
namespace Custom\Module\Setup;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Ddl\Table;
class InstallSchema implements InstallSchemaInterface {
    public function install( SchemaSetupInterface $setup, ModuleContextInterface $context ) {
        $installer = $setup;
        $installer->startSetup();
        $table = $installer->getConnection()->newTable(
            $installer->getTable( 'ad_shipping_quote' )
        )->addColumn(
            'entity_id',
            Table::TYPE_SMALLINT,
            null,
            [ 'identity' => true, 'nullable' => false, 'primary' => true ],
            'Post ID'
        )->addColumn(
            'product_id',
            Table::TYPE_SMALLINT,
            255,
            [ ],
            'Post ID'
        )
            ->addColumn(
            'customer_name',
            Table::TYPE_TEXT,
            255,
            [ 'nullable' => false ],
            'Post Title'
        )

            ->addColumn(
            'customer_email',
            Table::TYPE_TEXT,
            '2M',
            [ ],
            'Post Content'
        ) ->addColumn(
                'customer_comments',
                Table::TYPE_TEXT,
                255,
                [ 'nullable' => false ],
                'Post Title'
            )->addColumn(
                'date_added',
                Table::TYPE_TEXT,
                255,
                [ 'nullable' => false ],
                'Post Title'
            )->addColumn(
                'date_updated',
                Table::TYPE_TEXT,
                255,
                [ 'nullable' => false ],
                'Post Title'
            )
            ->setComment(
            'Ad Shipping Quote Table'
        );
        $installer->getConnection()->createTable( $table );
        $installer->endSetup();
    }
}

../etc/di.xml

<?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="Custom\Module\Api\ModelRepositoryInterface"
                type="Custom\Module\Model\ModelRepository" />
    <preference for="Custom\Module\Api\Data\ModelInterface"
                type="Custom\Module\Model\Model" />
    <preference for="Custom\Module\Api\Data\ModelSearchResultsInterface"
                type="Custom\Module\Model\ModelSearchResults" />
</config>

../etc/webapi.xml

  <?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">

    <route method="GET" url="/V1/model/:id">
        <service class="Custom\Module\Api\ModelRepositoryInterface" method="getById"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>


    <route method="GET" url="/V1/model">
        <service class="Custom\Module\Api\ModelRepositoryInterface" method="getList"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

../Api/ModelRepositoryInterface.php

  <?php
namespace Custom\Module\Api;

use \Custom\Module\Api\Data\ModelInterface;
use \Magento\Framework\Api\SearchCriteriaInterface;

interface ModelRepositoryInterface
{
    /**
     * @api
     * @param \Custom\Module\Api\Data\ModelInterface $model
     * @return \Custom\Module\Api\Data\ModelInterface
     */
    public function save(ModelInterface $model);

    /**
     * @api
     * @param \Custom\Module\Api\Data\ModelInterface $model
     * @return \Custom\Module\Api\Data\ModelInterface
     */
    public function delete(ModelInterface $model);

    /**
     * @api
     * @param \Custom\Module\Api\Data\ModelInterface $id
     * @return void
     */
    public function deleteById($id);

    /**
     * @api
     * @param int $id
     * @return \Custom\Module\Api\Data\ModelInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getById($id);

    /**
     * @api
     * @param \Magento\Framework\Api\SearchCriteriaInterface $criteria
     * @return \Custom\Module\Api\Data\ModelSearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $criteria);
}

../Api/Data/ModelInterface.php

<?php
namespace Custom\Module\Api\Data;

interface ModelInterface
{
    /**
     * Return the Entity ID
     *
     * @return int
     */
    public function getEntityId();

    /**
     * Set Entity ID
     *
     * @param int $id
     * @return $this
     */
    public function setEntityId($id);

    /**
     * Return the Product ID associated with Quote
     *
     * @return int
     */
    public function getProductId();

    /**
     * Set the Product ID associated with Quote
     *
     * @param int $productId
     * @return $this
     */
    public function setProductId($productId);

    /**
     * Return the Customer Name
     *
     * @return string
     */
    public function getCustomerName();

    /**
     * Set the Customer Name
     *
     * @param string $customerName
     * @return $this
     */
    public function setCustomerName($customerName);

    /**
     * Return the Customer Email
     *
     * @return string
     */
    public function getCustomerEmail();

    /**
     * Set the Customer Email
     *
     * @param string $customerEmail
     * @return $this
     */
    public function setCustomerEmail($customerEmail);

    /**
     * Return the Customer Comments
     *
     * @return string
     */
    public function getCustomerComments();

    /**
     * Set the Customer Comments
     *
     * @param string $customerComments
     * @return $this
     */
    public function setCustomerComments($customerComments);

    /**
     * Return the Date and Time of record added
     *
     * @return string
     */
    public function getDateAdded();

    /**
     * Set the Date and Time of record added
     *
     * @param string $date
     * @return $this
     */
    public function setDateAdded($date);

    /**
     * Return the Date and Time of record updated
     *
     * @return string
     */
    public function getDateUpdated();

    /**
     * Set the Date and Time of record updated
     *
     * @param string $date
     * @return $this
     */
    public function setDateUpdated($date);
}

..Api / Data / ModelSearchResultsInterface.php

<?php

namespace Custom\Module\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface ModelSearchResultsInterface extends SearchResultsInterface
{
    /**
     * @return \Custom\Module\Api\Data\ModelInterface[]
     */
    public function getItems();

    /**
     * @param \Custom\Module\Api\Data\ModelInterface[] $items
     * @return $this
     */
    public function setItems(array $items);
}

../Model/Model.php

    <?php

namespace Custom\Module\Model;

use Custom\Module\Api\Data\ModelInterface;

class Model extends \Magento\Framework\Model\AbstractModel implements
    \Custom\Module\Api\Data\ModelInterface
{
    protected function _construct()
    {
        $this->_init('Custom\Module\Model\ResourceModel\Model');
    }

    /**
     * @inheritdoc
     */
    public function getEntityId()
    {
        return $this->_getData('entity_id');
    }

    /**
     * @inheritdoc
     */
    public function setEntityId($id)
    {
        $this->setData('entity_id', $id);
    }

    /**
     * @inheritdoc
     */
    public function getProductId()
    {
        return $this->_getData('product_id');
    }

    /**
     * @inheritdoc
     */
    public function setProductId($productId)
    {
        $this->setData('product_id', $productId);
    }

    /**
     * @inheritdoc
     */
    public function getCustomerName()
    {
        return $this->_getData('customer_name');
    }

    /**
     * @inheritdoc
     */
    public function setCustomerName($customerName)
    {
        $this->setData('customer_name', $customerName);
    }

    /**
     * @inheritdoc
     */
    public function getCustomerEmail()
    {
        return $this->_getData('customer_email');
    }

    /**
     * @inheritdoc
     */
    public function setCustomerEmail($customerEmail)
    {
        $this->setData('customer_email', $customerEmail);
    }

    /**
     * @inheritdoc
     */
    public function getCustomerComments()
    {
        return $this->_getData('customer_comments');
    }

    /**
     * @inheritdoc
     */
    public function setCustomerComments($customerComments)
    {
        $this->setData('customer_comments', $customerComments);
    }

    /**
     * @inheritdoc
     */
    public function getDateAdded()
    {
        return $this->_getData('date_added');
    }

    /**
     * @inheritdoc
     */
    public function setDateAdded($date)
    {
        $this->setData('date_added', $date);
    }

    /**
     * @inheritdoc
     */
    public function getDateUpdated()
    {
        return $this->_getData('date_updated');
    }

    /**
     * @inheritdoc
     */
    public function setDateUpdated($date)
    {
        $this->setData('date_updated', $date);
    }
}

../Model/ResourceModel/Model.php

<?php

namespace Custom\Module\Model\ResourceModel;

class Model extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    protected $_idFieldName = 'entity_id';

    protected function _construct()
    {
        $this->_init('ad_shipping_quote','entity_id');
    }
}

../Model/ResourceModel/Model/Collection.php

<?php

namespace Custom\Module\Model\ResourceModel\Model;

class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    protected $_idFieldName = 'entity_id';
    protected $_eventPrefix = 'ad_shipping_quote_collection';
    protected $_eventObject = 'quote_collection';

    protected function _construct()
    {
        $this->_init('Custom\Module\Model\Model', 'Custom\Module\Model\ResourceModel\Model');
    }
}

../Model/ModelRepository.php

 <?php
    namespace Custom\Module\Model;

    use \Custom\Module\Api\Data\ModelInterface;
    use \Custom\Module\Model\ResourceModel\Model as ObjectResourceModel;
    use \Magento\Framework\Api\SearchCriteriaInterface;
    use \Magento\Framework\Exception\CouldNotSaveException;
    use \Magento\Framework\Exception\NoSuchEntityException;
    use \Magento\Framework\Exception\CouldNotDeleteException;

    class ModelRepository implements \Custom\Module\Api\ModelRepositoryInterface
    {
        protected $objectFactory;

        protected $objectResourceModel;

        protected $collectionFactory;

        protected $searchResultsFactory;

        public function __construct(
            \Custom\Module\Model\ModelFactory $objectFactory,
            ObjectResourceModel $objectResourceModel,
            \Custom\Module\Model\ResourceModel\Model\CollectionFactory $collectionFactory,
            \Magento\Framework\Api\SearchResultsInterfaceFactory $searchResultsFactory
        ) {
            $this->objectFactory        = $objectFactory;
            $this->objectResourceModel  = $objectResourceModel;
            $this->collectionFactory    = $collectionFactory;
            $this->searchResultsFactory = $searchResultsFactory;
        }

        public function save(ModelInterface $object)
        {
            $name = $object->getCustomerName();
            $hasSpouse = $object->getSpouse();
            if ($hasSpouse == true) {
                $name = "Mrs. " . $name;
            } else {
                $name = "Miss. " . $name;
            }
            $object->setCustomerName($name);
            try {
                $this->objectResourceModel->save($object);
            } catch (\Exception $e) {
                throw new CouldNotSaveException(__($e->getMessage()));
            }
            return $object;
        }

        /**
         * @inheritdoc
         */
        public function getById($id)
        {
            $object = $this->objectFactory->create();
            $this->objectResourceModel->load($object, $id);
            if (!$object->getId()) {
                throw new NoSuchEntityException(__('Object with id "%1" does not exist.', $id));
            }
            return $object;
        }

        public function delete(ModelInterface $object)
        {
            try {
                $this->objectResourceModel->delete($object);
            } catch (\Exception $exception) {
                throw new CouldNotDeleteException(__($exception->getMessage()));
            }
            return true;
        }

        public function deleteById($id)
        {
            return $this->delete($this->getById($id));
        }

        /**
         * @inheritdoc
         */
        public function getList(SearchCriteriaInterface $criteria)
        {
            $searchResults = $this->searchResultsFactory->create();
            $searchResults->setSearchCriteria($criteria);
            $collection = $this->collectionFactory->create();
            foreach ($criteria->getFilterGroups() as $filterGroup) {
                $fields = [];
                $conditions = [];
                foreach ($filterGroup->getFilters() as $filter) {
                    $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq';
                    $fields[] = $filter->getField();
                    $conditions[] = [$condition => $filter->getValue()];
                }
                if ($fields) {
                    $collection->addFieldToFilter($fields, $conditions);
                }
            }
            $searchResults->setTotalCount($collection->getSize());
            $sortOrders = $criteria->getSortOrders();
            if ($sortOrders) {
                /** @var SortOrder $sortOrder */
                foreach ($sortOrders as $sortOrder) {
                    $collection->addOrder(
                        $sortOrder->getField(),
                        ($sortOrder->getDirection() == SortOrder::SORT_ASC) ? 'ASC' : 'DESC'
                    );
                }
            }
            $collection->setCurPage($criteria->getCurrentPage());
            $collection->setPageSize($criteria->getPageSize());
            $objects = [];
            foreach ($collection as $objectModel) {
                $objects[] = $objectModel;
            }
            $searchResults->setItems($objects);
            return $searchResults;
        }
    }

../Model/ModelSearchResults.php

namespace Custom\Module\Model;

use \Magento\Framework\Api\SearchResults;
use \Custom\Module\Api\Data\ModelSearchResultsInterface;


class ModelSearchResults extends SearchResults implements ModelSearchResultsInterface
{

}

../Controller/Index/Save.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Save extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelFactory;
    /**
     * @var
     */
    private $modelRepository;


    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelFactory $modelFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelFactory $modelFactory,
        \Custom\Module\Model\ModelRepository $modelRepository
) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelFactory = $modelFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);


    }

    /**
     * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $data = [

            "product_id" => 201,
            "customer_name" => "Katrina",
            "customer_email" => "[email protected]",
            "spouse" => 1
        ];

        $obj = $this->modelFactory->create();
        $this->modelRepository->save($obj->addData($data)); // Service Contract


        //$obj->addData($data)->save(); // Model / Resource Model

        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Getlist.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Getlist extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelFactory;
    /**
     * @var
     */
    private $modelRepository;
    /**
     * @var
     */
    private $searchCriteriaBuilder;


    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelRepository $modelRepository,
        \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelRepository = $modelRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        return parent::__construct($context);
    }

    /**
     * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $_filter = $this->searchCriteriaBuilder
            ->addFilter("customer_name", "%na%", "like")->create();
        $list = $this->modelRepository->getList($_filter);
        $results = $list->getItems();
        foreach ($results as $result) {
            echo $result->getCustomerName() . "<br>";
        }




        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Getbyid.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Getbyid extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelRepository;

    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelRepository $modelRepository

) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);
    }

    public function execute()
    {

        $search = $this->modelRepository->getById(1);
        print_r($search->getData());

        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Deletebyid.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Deletbyid extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelRepository;

    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelRepository $modelRepository

) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);
    }

    public function execute()
    {

        $this->modelRepository->deleteById(1);

        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Del.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Del extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelRepository;

    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelFactory $modelFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelFactory $modelFactory,
        \Custom\Module\Model\ModelRepository $modelRepository

) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelFactory = $modelFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);
    }

    public function execute()
    {
        $obj = $this->modelFactory->create()->load(2);
         $this->modelRepository->delete($obj);

        $this->resultFactory->create("raw");
    }
}
Asad Ullah
la source