Wie implementiere ich einen Servicevertrag für ein benutzerdefiniertes Modul in Magento 2?

42

Wie in diesem Beitrag zu sehen: Veraltete Methoden zum Speichern und Laden in Abstract Model Die Methoden saveund loadsind im Entwicklungszweig von Magento 2 veraltet.

Daher besteht die bewährte Praxis nun darin, Serviceverträge für CRUD-Unternehmen umzusetzen.

Was ist der schrittweise Prozess, den ich ausführen muss, um Serviceverträge für meine benutzerdefinierten Moduleinheiten zu implementieren?

NB: Ich weiß, dass meine CRUD-Modelle Tausende von Methoden enthalten können. Ich frage nur nach den hier angegebenen offensichtlichen Methoden: http://devdocs.magento.com/guides/v2.0/extension-dev-guide /service-contracts/design-patterns.html :

  • get
  • save
  • getList
  • delete
  • deleteById
Raphael bei Digital Pianism
quelle

Antworten:

89

Ich möchte zusätzlich zu der hervorragenden Antwort von @ryanF noch ein bisschen mehr ins Detail gehen.

Ich möchte die Gründe für das Hinzufügen eines Repositorys für benutzerdefinierte Entitäten zusammenfassen, Beispiele dafür geben und auch erläutern, wie diese Repository-Methoden als Teil der Web-API verfügbar gemacht werden.

Haftungsausschluss: Ich beschreibe nur einen pragmatischen Ansatz, wie dies für Module von Drittanbietern durchgeführt wird - die Kernteams haben ihre eigenen Standards, denen sie folgen (oder nicht).

Im Allgemeinen besteht der Zweck eines Repository darin, die speicherbezogene Logik auszublenden.
Ein Client eines Repositorys sollte sich nicht darum kümmern, ob sich die zurückgegebene Entität in einem Array im Speicher befindet, aus einer MySQL-Datenbank abgerufen, von einer Remote-API oder aus einer Datei abgerufen wird.
Ich gehe davon aus, dass das Magento-Kernteam dies getan hat, um das ORM in Zukunft ändern oder ersetzen zu können. In Magento besteht der ORM derzeit aus den Modellen, Ressourcenmodellen und Sammlungen.
Wenn ein Modul eines Drittanbieters nur die Repositorys verwendet, kann Magento ändern, wie und wo Daten gespeichert werden, und das Modul funktioniert trotz dieser tiefgreifenden Änderungen weiterhin.

Repositorys hat in der Regel Methoden wie findById(), findByName(), put()oder remove().
In Magento werden diese häufig genannt getbyId(), save()und delete(), auch nicht vorgeben , sie tun etwas anderes als CRUD DB - Operationen.

Magento 2-Repository-Methoden können leicht als API-Ressourcen verfügbar gemacht werden, was sie für die Integration in Systeme von Drittanbietern oder kopflose Magento-Instanzen wertvoll macht.

Msgstr "Soll ich ein Repository für meine benutzerdefinierte Entität hinzufügen?"

Wie immer lautet die Antwort

"Es hängt davon ab, ob".

Um es kurz zu machen: Wenn Ihre Entitäten von anderen Modulen verwendet werden, möchten Sie wahrscheinlich ein Repository hinzufügen.

Ein weiterer Faktor spielt hier eine Rolle: In Magento 2 können Repositorys problemlos als Web-API-Ressourcen (REST- und SOAP-Ressourcen) verfügbar gemacht werden.

Wenn dies für Sie aufgrund von Systemintegrationen von Drittanbietern oder eines Headless-Magento-Setups interessant ist, möchten Sie wahrscheinlich ein Repository für Ihre Entität hinzufügen.

Wie füge ich ein Repository für meine benutzerdefinierte Entität hinzu?

Nehmen wir an, Sie möchten Ihre Entität als Teil der REST-API verfügbar machen. Wenn dies nicht der Fall ist, können Sie den nächsten Teil zum Erstellen der Schnittstellen überspringen und unten direkt mit "Repository- und Datenmodellimplementierung erstellen" fortfahren.

Erstellen Sie die Repository- und Datenmodellschnittstellen

Erstellen Sie die Ordner Api/Data/in Ihrem Modul. Dies ist nur eine Konvention, Sie könnten einen anderen Ort verwenden, aber Sie sollten nicht.
Das Repository wird in den Api/Ordner verschoben. Das Data/Unterverzeichnis ist für später.

In Api/erstellen belichten eine PHP - Schnittstelle mit den Methoden , die Sie wollen. Gemäß den Konventionen von Magento 2 enden alle Schnittstellennamen mit dem Suffix Interface.
Beispielsweise Hamburgerwürde ich für eine Entität die Schnittstelle erstellen Api/HamburgerRepositoryInterface.

Erstellen Sie die Repository-Schnittstelle

Magento 2-Repositorys sind Teil der Domänenlogik eines Moduls. Das heißt, es gibt keine festen Methoden, die ein Repository implementieren muss.
Es hängt ganz vom Zweck des Moduls ab.

In der Praxis sind jedoch alle Repositories sehr ähnlich. Sie sind Wrapper für die CRUD-Funktionalität.
Die meisten haben die Methoden getById, save, deleteund getList.
Es kann mehr geben, zum Beispiel CustomerRepositoryhat die eine Methode get, die einen Kunden per E-Mail getByIdabruft , wodurch ein Kunde nach Entitäts-ID abgerufen wird.

Hier ist ein Beispiel für eine Repository-Schnittstelle für eine Hamburger-Entität:

<?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);

}

Wichtig! Hier seid timesinks!
Es gibt ein paar Fallstricke, die schwer zu debuggen sind, wenn Sie sie falsch verstehen:

  1. DO NOT verwenden PHP7 Skalar Argumenttypen oder Rückgabetypen , wenn Sie diese in die REST - API Haken wollen!
  2. Fügen Sie allen Methoden PHPDoc-Annotationen für alle Argumente und den Rückgabetyp hinzu!
  3. Verwenden Sie im PHPDoc-Block vollständig qualifizierte Klassennamen!

Die Anmerkungen werden vom Magento Framework analysiert, um zu bestimmen, wie Daten in und aus JSON oder XML konvertiert werden. Klassenimporte (also useAnweisungen) werden nicht angewendet!

Jede Methode muss eine Annotation mit Argumenttypen und dem Rückgabetyp haben. Auch wenn eine Methode keine Argumente akzeptiert und nichts zurückgibt, muss sie die folgende Anmerkung haben:

/**
 * @return void
 */

Skalar - Typen ( string, int, floatund bool) haben auch festgelegt werden, sowohl für Argumente und als Rückgabewert.

Beachten Sie, dass im obigen Beispiel die Annotationen für Methoden, die Objekte zurückgeben, auch als Schnittstellen angegeben werden.
Die Rückgabetyp-Schnittstellen befinden sich alle im Api\DataNamespace / Verzeichnis.
Dies soll darauf hinweisen, dass sie keine Geschäftslogik enthalten. Sie sind einfach Datenmengen.
Als nächstes müssen wir diese Schnittstellen erstellen.

Erstellen Sie die DTO-Schnittstelle

Ich denke, Magento nennt diese Schnittstellen "Datenmodelle", einen Namen, den ich überhaupt nicht mag.
Diese Art von Klasse wird allgemein als Datenübertragungsobjekt oder DTO bezeichnet .
Diese DTO-Klassen haben nur Getter und Setter für alle ihre Eigenschaften.

Der Grund, warum ich DTO lieber als Datenmodell verwende, ist, dass es weniger leicht ist, mit den ORM-Datenmodellen, Ressourcenmodellen oder Ansichtsmodellen zu verwechseln. Zu viele Dinge sind bereits Modelle in Magento.

Die gleichen Einschränkungen in Bezug auf die PHP7-Typisierung, die für Repositorys gelten, gelten auch für DTOs.
Außerdem muss jede Methode eine Annotation mit allen Argumenttypen und dem Rückgabetyp haben.

<?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);
}

Wenn eine Methode ein Array abruft oder zurückgibt, muss der Typ der Elemente im Array in der PHPDoc-Annotation angegeben werden, gefolgt von einer öffnenden und schließenden eckigen Klammer [].
Dies gilt sowohl für skalare Werte (zB int[]) als auch für Objekte (zB IngredientInterface[]).

Beachten Sie, dass ich a Api\Data\IngredientInterfaceals Beispiel für eine Methode verwende, die ein Array von Objekten zurückgibt. Ich werde den Code der Zutaten nicht zu diesem Beitrag hinzufügen.

ExtensibleDataInterface?

Im obigen Beispiel HamburgerInterfaceerweitert das ExtensibleDataInterface.
Technisch ist dies nur erforderlich, wenn andere Module Ihrer Entität Attribute hinzufügen können sollen.
In diesem Fall müssen Sie auch ein weiteres Getter / Setter-Paar hinzufügen, das gemäß der Konvention mit getExtensionAttributes()und bezeichnet wird setExtensionAttributes().

Die Benennung des Rückgabetyps dieser Methode ist sehr wichtig!

Das Magento 2-Framework generiert die Schnittstelle, die Implementierung und die Factory für die Implementierung, wenn Sie sie genau richtig benennen. Die Details dieser Mechanik sind jedoch nicht Gegenstand dieses Beitrags.
Wissen Sie nur, wenn die Schnittstelle des Objekts, das Sie erweiterbar machen möchten, aufgerufen wird \VinaiKopp\Kitchen\Api\Data\HamburgerInterface, muss der Typ der Erweiterungsattribute sein \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface. Das Wort Extensionmuss also nach dem Entitätsnamen, direkt vor dem InterfaceSuffix , eingefügt werden .

Wenn Sie nicht möchten, dass Ihre Entität erweiterbar ist, muss die DTO-Schnittstelle keine andere Schnittstelle erweitern, und die Methoden getExtensionAttributes()und setExtensionAttributes()können weggelassen werden.

Genug von der DTO-Schnittstelle, Zeit, um zur Repository-Schnittstelle zurückzukehren.

Der getList () -Rückgabetyp SearchResults

Die Repository-Methode getListgibt einen weiteren Typ zurück, dh eine SearchResultsInterfaceInstanz.

Die Methode getListkönnte natürlich nur ein Array von Objekten zurückgeben, die mit dem angegebenen übereinstimmen SearchCriteria, aber das Zurückgeben einer SearchResultsInstanz ermöglicht das Hinzufügen einiger nützlicher Metadaten zu den zurückgegebenen Werten.

Wie das funktioniert, sehen Sie weiter unten in der getList()Implementierung der Repository- Methode.

Hier ist das Beispiel für eine Hamburger-Suchergebnisoberfläche:

<?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);
}

Diese Schnittstelle überschreibt lediglich die Typen der beiden Methoden getItems()und setItems()der übergeordneten Schnittstelle.

Zusammenfassung der Schnittstellen

Wir haben jetzt die folgenden Schnittstellen:

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

Das Repository erstreckt sich nichts,
die HamburgerInterfacesich die \Magento\Framework\Api\ExtensibleDataInterface,
und das HamburgerSearchResultInterfaceerstreckt sich das \Magento\Framework\Api\SearchResultsInterface.

Erstellen Sie die Repository- und Datenmodellimplementierungen

Der nächste Schritt besteht darin, die Implementierungen der drei Schnittstellen zu erstellen.

Das Repository

Im Wesentlichen verwendet das Repository das ORM, um seine Arbeit zu erledigen.

Die getById(), save() und delete()Methoden sind ziemlich geradlinig.
Das HamburgerFactorywird als Konstruktorargument in das Repository injiziert, wie weiter unten zu sehen ist.

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);
}

Nun zum interessantesten Teil eines Repositorys, der getList()Methode.
Die getList()Methode muss die SerachCriteriaBedingungen in Methodenaufrufe für die Sammlung übersetzen.

Der schwierige Teil davon wird die immer ANDund ORBedingungen für die Filter rechts, vor allem , da die Syntax für die Bedingungen für die Gewinnung unterschiedlich ist , je nachdem ob es sich um eine EAV oder eine flache Tischeinheit.

In den meisten Fällen getList()kann wie im folgenden Beispiel dargestellt implementiert werden.

<?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;
    }
}

Filter innerhalb eines FilterGroupmüssen mit einem OR- Operator kombiniert werden .
Separate Filtergruppen werden mit dem logischen AND- Operator kombiniert .

Puh
Das war die größte Arbeit. Die anderen Schnittstellenimplementierungen sind einfacher.

Das DTO

Magento hatte ursprünglich die Absicht, das DTO als separate Klassen zu implementieren, die sich vom Entitätsmodell unterscheiden.

Das Kernteam hat dies jedoch nur für das Kundenmodul getan ( \Magento\Customer\Api\Data\CustomerInterfacewird von implementiert \Magento\Customer\Model\Data\Customer, nicht \Magento\Customer\Model\Customer).
In allen anderen Fällen implementiert das Entitätsmodell die DTO-Schnittstelle (z. B. \Magento\Catalog\Api\Data\ProductInterfaceimplementiert von \Magento\Catalog\Model\Product).

Ich habe Mitglieder des Kernteams auf Konferenzen dazu befragt, aber ich habe keine klare Antwort erhalten, was als gute Praxis anzusehen ist.
Mein Eindruck ist, dass diese Empfehlung aufgegeben wurde. Es wäre jedoch schön, eine offizielle Stellungnahme dazu zu erhalten.

Im Moment habe ich die pragmatische Entscheidung getroffen, das Modell als Implementierung der DTO-Schnittstelle zu verwenden. Wenn Sie der Meinung sind, dass die Verwendung eines separaten Datenmodells sauberer ist, können Sie dies tun. Beide Ansätze funktionieren in der Praxis gut.

Wenn die DTO-Schnittstelle die erweitert Magento\Framework\Api\ExtensibleDataInterface, muss das Modell erweitert werden Magento\Framework\Model\AbstractExtensibleModel.
Wenn Sie sich nicht für die Erweiterbarkeit interessieren, kann das Modell die ORM-Modellbasisklasse einfach weiter erweitern Magento\Framework\Model\AbstractModel.

Da das Beispiel das HamburgerInterfaceerweitert, erweitert das ExtensibleDataInterfaceHamburger Modell das AbstractExtensibleModel, wie hier zu sehen ist:

<?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);
    }
}

Durch Extrahieren der Eigenschaftsnamen in Konstanten können Sie diese an einem Ort aufbewahren. Sie können sowohl vom Getter / Setter-Paar als auch vom Setup-Skript verwendet werden, das die Datenbanktabelle erstellt. Andernfalls hat es keinen Vorteil, sie in Konstanten zu extrahieren.

Das SearchResult

Dies SearchResultsInterfaceist die einfachste der drei zu implementierenden Schnittstellen, da sie die gesamte Funktionalität einer Framework-Klasse erben kann.

<?php

namespace VinaiKopp\Kitchen\Model;

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

class HamburgerSearchResult extends SearchResults implements HamburgerSearchResultInterface
{

}

Konfigurieren Sie die ObjectManager-Einstellungen

Obwohl die Implementierungen vollständig sind, können wir die Schnittstellen nicht als Abhängigkeiten anderer Klassen verwenden, da der Magento Framework-Objektmanager nicht weiß, welche Implementierungen verwendet werden sollen. Wir müssen eine etc/di.xmlKonfiguration für mit den Einstellungen hinzufügen .

<?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>

Wie kann das Repository als API-Ressource verfügbar gemacht werden?

Dieser Teil ist wirklich einfach, es ist die Belohnung für die Arbeit, die Schnittstellen, die Implementierungen und die Verkabelung zu erstellen.

Alles was wir tun müssen, ist eine etc/webapi.xmlDatei zu erstellen .

<?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>

Beachten Sie, dass diese Konfiguration nicht nur die Verwendung des Repositorys als REST-Endpunkt ermöglicht, sondern auch die Methoden als Teil der SOAP-API verfügbar macht.

In der ersten Beispielroute <route method="GET" url="/V1/vinaikopp_hamburgers/:id">muss der Platzhalter :idden Namen des Arguments mit der zugeordneten Methode abgleichen public function getById($id).
Die beiden Namen müssen übereinstimmen, /V1/vinaikopp_hamburgers/:hamburgerIdwürden beispielsweise nicht funktionieren, da das Methodenargument Variablenname lautet $id.

Für dieses Beispiel habe ich die Erreichbarkeit auf eingestellt <resource ref="anonymous"/>. Dies bedeutet, dass die Ressource ohne Einschränkung öffentlich verfügbar ist!
Verwenden Sie, um eine Ressource nur einem angemeldeten Kunden zur Verfügung zu stellen <resource ref="self"/>. In diesem Fall wird das spezielle Wort mein der URL des Ressourcenendpunkts verwendet, um eine Argumentvariable $idmit der ID des aktuell angemeldeten Kunden zu füllen.
Schauen Sie sich den Magento-Kunden an etc/webapi.xmlund CustomerRepositoryInterfacewenn Sie das brauchen.

Schließlich <resources>kann das auch verwendet werden, um den Zugriff auf eine Ressource auf ein Administratorkonto zu beschränken. Setzen Sie dazu den <resource>Verweis auf eine in einer etc/acl.xmlDatei definierte Kennung .
Zum Beispiel <resource ref="Magento_Customer::manage"/>würde Zugriff auf all Admin - Konto beschränken , die privilegiert ist es, Kunden zu verwalten.

Eine Beispiel-API-Abfrage mit curl könnte folgendermaßen aussehen:

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

Hinweis: Das Schreiben dieser als Antwort auf gestartet https://github.com/astorm/pestle/issues/195
Check out Stößel , kaufen Commercebug und zu einem Patreon von @alanstorm

Vinai
quelle
1
Danke für diese tolle Antwort. Tut mir leid, vielleicht fehlt mir etwas, aber wozu ist eine saubere Schnittstelle für eine Entität gut, wenn sie am Ende von AbstractModel mit der setData-Methode erweitert werden muss, was bedeutet, dass Sie dem Objekt unabhängig von der Schnittstelle alles hinzufügen können?
LDusan
Eine Klasse kann beliebig viele Interfaces implementieren und zusätzliche Methoden hinzufügen. Das Wichtige ist, dass jede andere Klasse nur von den Schnittstellenmethoden abhängt und daher keine der anderen kennt. Das macht die Nicht-Schnittstellen-Methoden-Implementierungsdetails, die jederzeit geändert werden können, ohne externe Klassen zu unterbrechen. Das ist die Idee hinter der Abhängigkeitsinversion. Sowohl die Klasse als auch alle Clients sind von der Schnittstelle abhängig und kennen die Implementierungsdetails nicht. Klärt das?
Vinai
Danke für die Antwort, ich verstehe was du meinst. Die Sache ist, dass setData eine öffentliche Methode ist, daher bin ich mir nicht sicher, ob es als Implementierungsdetail betrachtet werden kann. Wenn es wie eine öffentliche Methode verwendet werden soll, wie können wir dann sicher sein, dass es bei Änderungen keine externen Fehler verursacht?
LDusan
3
Ich entschuldige mich. Was Sie beschreiben, ist eine gemeinsame Sichtweise. Die Mechanismen von Abhängigkeiten sind nicht intuitiv und da PHP den Aufruf von Methoden erlaubt, die nicht Teil der abhängigen Schnittstelle sind, und da sie nicht kompiliert werden müssen, wird die Funktionsweise von Abhängigkeiten noch unscharfer und unübersichtlicher . Dies kann auch im Magento 2-Kern beobachtet werden, wo es viele Stellen gibt, an denen Implementierungsmethoden aufgerufen werden, die nicht Teil der von der Schnittstelle abhängigen sind. Diese dienen als schlechte Beispiele und erschweren ein klares und gesundes Verständnis.
Vinai
1
Lassen Sie uns diese Diskussion im Chat fortsetzen .
Vinai
35

@Raphael bei Digital Pianism:

Bitte beachten Sie die folgende Beispielmodulstruktur:

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. Erstellen Sie eine Repository-Schnittstelle (Servicevertrag)
    Namespace/Custom/Api/CustomRepositoryInterface.php: http://codepad.org/WognSKnH

  2. Erstellen Sie SearchResultsInterface
    Namespace/Custom/Api/Data/CustomSearchResultsInterface.php: http://codepad.org/zcbi8X4Z

  3. Erstellen Sie CustomInterface (Datencontainer)
    Namespace/Custom/Api/Data/CustomInterface.php: http://codepad.org/Ze53eT4o

  4. Erstellen Sie ein CustomRepository (Concrete Repository)
    Namespace/Custom/Model/CustomRepository.php: http://codepad.org/KNt5QAGZ
    Hier geschieht die "Magie". Über Konstruktor DI übergeben Sie das Ressourcenmodell / die Auflistungsfactory für Ihr benutzerdefiniertes Modul. Das Speichern CRUD Methode in diesem Repository, aufgrund Ihrer CustomRepositoryInterface Bezüglich Sie müssen in einem Parameter von Custom passieren. In der Datei di.xml Ihres Moduls wird eine Schnittstelle dieses Typs vorzugsweise durch ein Entitätsmodell ersetzt. Das Entitätsmodell wird an das Ressourcenmodell übergeben und gespeichert.

  5. Set Präferenz
    Namespace/Custom/etc/di.xml: http://codepad.org/KmcoOUeV

  6. Entitätsmodell zur Implementierung der benutzerdefinierten Schnittstelle (Datencontainer)
    Namespace/Custom/Model/Custom.php: http://codepad.org/xQiBU7p7 .

  7. Ressourcenmodell
    Namespace/Custom/Model/ResourceModel/Custom.php: http://codepad.org/IOsxm9qW

Ein paar Dinge zu beachten:

  • Haftungsausschluss!!! Ich habe „Namespace“ anstelle der benutzerdefinierten Herstellernamen, Agenturname, etc ... , was Namen Sie Ihre Module zusammen verwenden , um Gruppe ... die tatsächliche Verwendung der „Namespace“ ist vollständig nicht in Php gültig ... so Know dass ich dies der Einfachheit halber getan habe und dass ich nicht denke, dass dies funktionieren wird , und auch nicht, dass ich es in irgendeiner Weise vorschlage.

  • @ Ryan Street hat mir das beigebracht ... also möchte ich nicht den ganzen Kredit in Anspruch nehmen

  • Ändern Sie die Implementierung des Repositorys ganz nach Ihren Bedürfnissen

  • Sie implementieren die Interaktion mit Ihren benutzerdefinierten Entitätsmodellen / Ressourcenmodellen / Sammlungen im konkreten Repository ...

  • Ich weiß, dass ich nicht alle Methoden angesprochen habe, die Sie in Ihrer Frage aufgeführt haben, aber dies ist ein guter Anfang und sollte die Lücke zwischen den Dokumenten und der tatsächlichen Implementierung schließen.

ryanF
quelle
Ryan, sind die in den Serviceverträgen genannten Methoden für jede von uns erstellte benutzerdefinierte API für Seifen obligatorisch, dh save (), delete () usw.?
Sushivam
Könnten Sie mir bitte eine Idee geben, wie Sie eine benutzerdefinierte Soap-API in Magento 2 erstellen können?
Sushivam
@SachinS Leider habe ich keinen Einblick in SOAP. Ich habe es noch nicht untersucht und noch nicht implementiert. Das Beste, was ich vorschlagen könnte, wäre, hier eine neue Frage zu stellen. Ich würde sagen, überprüfen Sie auch die Dokumente, aber das ist leider nicht immer die beste Vorgehensweise (sie könnten fehlen). Sie können sich jederzeit die Kerncodebasis oder eine Erweiterung eines Drittanbieters ansehen, um zu sehen, ob dort Einsichten vorhanden sind. Viel Glück! Wenn Sie Ihre Antwort finden, ist es möglicherweise hilfreich, den Link hier hinzuzufügen. Danke
RyanF
Danke für die Antwort @ryan, trotzdem habe ich mein Modul mit REST implementiert, da es im Vergleich zu SOAP leichtgewichtig ist ... Wenn ich dasselbe in SOAP implementiere, werde ich es
posten
3
@ryanF Danke für diese sehr nützliche Antwort. Ich weiß, dass es sich nicht um Kopier- / Einfüge-Code handeln soll, aber hier sind ein paar Tippfehler zum Nutzen anderer, die mitmachen. Im Repository sollte CustomSearchResultsInterfaceFactory CustomSearchResultsFactory sein. $ searchResults-> setCriteria sollte $ searchResults-> setSearchCriteria sein. $ Customs [] in foreach sollte $ Customs [] sein. Ich denke das ist es.
Tetranz
3

komplette Dateien der Nutzung von Serviceverträgen

Benutzerdefiniert / Modul / 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
quelle