Як реалізувати контракт на обслуговування користувальницького модуля в Magento 2?


42

Як видно на цій посаді: Застаріле зберегти і методи навантаження в абстрактній моделіsave і loadметоди є застарілими в Magento 2 розвивати галузь.

Таким чином, належним звичним методом є впровадження контрактів на обслуговування, що стосуються організацій CRUD.

Який покроковий процес потрібно виконати, щоб реалізувати контракти на обслуговування для моїх спеціальних модулів?

NB: Я знаю, що в моїх CRUD-моделях може бути тисячі методів, я просто прошу очевидних методів, як зазначено тут: http://devdocs.magento.com/guides/v2.0/extension-dev-guide /service-contracts/design-patterns.html :

  • get
  • save
  • getList
  • delete
  • deleteById

Відповіді:


90

Я хотів би розповісти трохи більше деталей на додаток до чудової відповіді @ryanF.

Я хотів би підсумувати причини додавання сховища для користувацьких сутностей, навести приклади, як це зробити, а також пояснити, як відкрити ці методи репозиторію як частину веб-API.

Відмова: Я лише описую прагматичний підхід, як це зробити для сторонніх модулів - основні команди мають свої стандарти, яких вони дотримуються (чи ні).

Загалом, мета сховища - приховати логіку, пов'язану із зберіганням.
Клієнт сховища не повинен дбати про те, чи зберігається об'єкт зберігається в пам'яті в масиві, отримується з бази даних MySQL, отриманої з віддаленого API або з файлу.
Я припускаю, що основна команда Magento зробила це, щоб вони могли змінити або замінити ORM у майбутньому. Зараз у Магенто ORM складається з Моделей, Моделей ресурсів та Колекцій.
Якщо сторонній модуль використовує лише сховища, Magento може змінити, як і де зберігаються дані, і модуль продовжить працювати, незважаючи на ці глибокі зміни.

Сховища , як правило, такі методи , як findById(), findByName(), put()або remove().
В Magento вони зазвичай називаються getbyId(), save()і delete(), навіть не роблячи вигляд , що вони роблять що -то ще , але операції CRUD DB.

Методи сховища Magento 2 можуть бути легко викриті як ресурси API, що робить їх цінними для інтеграції з сторонніми системами або безголовими екземплярами Magento.

Msgstr "Чи слід додати сховище для моєї спеціальної сутності?"

Як завжди, відповідь така

"Це залежить".

Якщо коротко розповісти, якщо ваші сутності будуть використовуватися іншими модулями, то так, ви, ймовірно, хочете додати сховище.

Тут є ще один фактор, який враховується: у Magento 2 сховища можуть бути легко відкриті як Web API - тобто REST та SOAP - ресурси.

Якщо це цікаво для вас через сторонні інтеграції системи або безголове налаштування Magento, то знову ж таки, так, ви, ймовірно, хочете додати сховище для вашої сутності.

Як додати сховище для моєї спеціальної сутності?

Припустимо, ви хочете викрити свою організацію як частину REST API. Якщо це не відповідає дійсності, ви можете пропустити майбутню частину щодо створення інтерфейсів та перейти безпосередньо до "Створити сховище та реалізацію моделі даних" нижче.

Створіть інтерфейси сховища та моделі даних

Створіть папки Api/Data/у своєму модулі. Це просто умова, ви можете використовувати інше місце, але не слід.
Репозиторій переходить у Api/папку. Data/Підкаталог для пізніше.

У Api/створіть PHP інтерфейс з методами , які ви хочете виставити. Згідно з умовами Magento 2, всі назви інтерфейсу закінчуються суфіксом Interface.
Наприклад, для Hamburgerсутності я створив би інтерфейс Api/HamburgerRepositoryInterface.

Створіть інтерфейс сховища

Репозиторії Magento 2 є частиною доменної логіки модуля. Це означає, що немає фіксованого набору методів, які репозиторій повинен реалізовувати.
Це повністю залежить від призначення модуля.

Однак на практиці всі сховища досить схожі. Вони є обгортками для функціональності CRUD.
Більшість з них мають методи getById, save, deleteі getList.
Можливо, може бути і більше, наприклад CustomerRepository, метод методу get, який отримує клієнта електронною поштою, за допомогою getByIdякого використовується клієнт за ідентифікатором особи.

Ось приклад інтерфейсу сховища для об'єкта гамбургерів:

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

}

Важливо! Ось час посмішки!
Тут є декілька проблем, які важко налагодити, якщо ви їх неправильно:

  1. НЕ використовуйте скалярні типи аргументів PHP7 або типи повернення, якщо ви хочете підключити це до API REST!
  2. Додайте анотації PHPDoc до всіх аргументів та тип повернення до всіх методів!
  3. Використовуйте повністю кваліфіковані імена класів у блоці PHPDoc!

Анотації аналізуються рамкою Magento, щоб визначити, як перетворити дані в JSON або XML і з них. Імпорт класу (тобто useзаяви) не застосовується!

Кожен метод повинен мати примітку до будь-яких типів аргументів та типу повернення. Навіть якщо метод не бере аргументів і нічого не повертає, він повинен мати примітку:

/**
 * @return void
 */

Скалярні типи ( string, int, floatі bool) також повинні бути визначені, як аргументи і в якості значення, що повертається.

Зауважте, що у наведеному вище прикладі анотації для методів, що повертають об'єкти, вказані також як інтерфейси.
Інтерфейси типу повернення - всі в Api\Dataпросторі імен / каталозі.
Це означає, що вони не містять жодної логіки бізнесу. Вони просто пакети даних.
Ми повинні створити ці інтерфейси далі.

Створіть інтерфейс DTO

Я думаю, що Magento називає ці інтерфейси "моделями даних", ім'ям, яке мені зовсім не подобається.
Цей тип класу широко відомий як Об'єкт передачі даних або DTO .
Ці класи DTO мають лише геттери та сетери для всіх своїх властивостей.

Причина, яку я вважаю за краще використовувати модель DTO над даними, полягає в тому, що її не так просто переплутати з моделями даних ORM, моделями ресурсів або переглядати моделі ... надто багато речей вже є моделями в Magento.

Ті ж обмеження щодо типізації PHP7, які застосовуються до сховищ, застосовуються і до DTO.
Крім того, кожен метод повинен мати примітку до всіх типів аргументів та типу повернення.

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

Якщо метод отримує або повертає масив, тип елементів в масиві повинен бути вказаний в анотації PHPDoc з подальшим квадратним дужкою, що відкривається і закривається [].
Це справедливо як для скалярних значень (наприклад int[]), так і для об'єктів (наприклад IngredientInterface[]).

Зауважте, що я використовую Api\Data\IngredientInterfaceяк приклад для методу повернення масиву об'єктів, я не буду додавати код інгредієнтів до цієї публікації важко.

ExtensibleDataInterface?

У прикладі вище HamburgerInterfaceрозширюється ExtensibleDataInterface.
Технічно це потрібно лише в тому випадку, якщо ви хочете, щоб інші модулі могли додати атрибути до вашої сутності.
Якщо це так, вам також потрібно додати ще одну геттер / пара задач, за умовою, що називається getExtensionAttributes()та setExtensionAttributes().

Найменування зворотного типу цього методу дуже важливо!

Рамка Magento 2 генерує інтерфейс, реалізацію та фабрику для впровадження, якщо ви їх правильно назвете. Деталі цих механізмів, однак, не входять у рамки цієї посади, хоча.
Тільки знайте, якщо інтерфейс об'єкта, який ви хочете зробити розширюваним, викликається \VinaiKopp\Kitchen\Api\Data\HamburgerInterface, то типом атрибутів розширення повинно бути \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface. Отже, слово Extensionмає бути вставлено після назви сутності, прямо перед Interfaceсуфіксом.

Якщо ви не хочете, щоб ваша сутність була розширюваною, тоді інтерфейс DTO не повинен поширювати жоден інший інтерфейс, getExtensionAttributes()і setExtensionAttributes()методи та способи можна опустити.

Зараз достатньо інтерфейсу DTO, час повернутися до інтерфейсу сховища.

Тип повернення getList () типу SearchResults

Метод репозиторію getListповертає ще один тип, тобто SearchResultsInterfaceекземпляр.

Звичайно, метод getListможе просто повернути масив об'єктів, що відповідають вказаному SearchCriteria, але повернення SearchResultsекземпляра дозволяє додавати корисні метадані до повернених значень.

Ви можете бачити, як це працює нижче в getList()реалізації методу репозиторію .

Ось приклад інтерфейсу результатів пошуку гамбургерів:

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

Все, що робиться в цьому інтерфейсі - це переміна типів для двох методів getItems()та setItems()батьківського інтерфейсу.

Підсумок інтерфейсів

Тепер у нас є такі інтерфейси:

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

Сховище не минає нічого, розширює , і розширює .
HamburgerInterface\Magento\Framework\Api\ExtensibleDataInterface
HamburgerSearchResultInterface\Magento\Framework\Api\SearchResultsInterface

Створіть реалізацію сховища та моделі даних

Наступним кроком є ​​створення реалізацій трьох інтерфейсів.

Репозиторій

По суті, сховище використовує ORM для виконання своєї роботи.

В getById(), save() і delete()методи досить прямо вперед.
Аргумент HamburgerFactoryвводиться в сховище як аргумент конструктора, як видно трохи нижче.

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

Тепер до найцікавішої частини репозиторію, getList()методу. Метод повинен перевести умови в виклики методів зі збору.
getList()SerachCriteria

Складною частиною цього є отримання правильності ANDта ORумов для фільтрів, тим більше, що синтаксис встановлення умов колекції відрізняється залежно від того, чи це об'єкт EAV або плоска таблиця.

У більшості випадків це getList()може бути реалізовано, як показано в прикладі нижче.

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

Фільтри в межах FilterGroupобов'язкового потрібно комбінувати за допомогою оператора АБО .
Окремі групи фільтрів об'єднуються за допомогою логічного оператора AND .

Уф
Це був найбільший шматок роботи. Інші реалізації інтерфейсу простіші.

DTO

Magento спочатку передбачав розробників впроваджувати DTO як окремі класи, відмінні від сутнісної моделі.

Основна команда зробила це лише для модуля клієнта ( \Magento\Customer\Api\Data\CustomerInterfaceреалізується \Magento\Customer\Model\Data\Customer, не \Magento\Customer\Model\Customer).
У всіх інших випадках модель об'єкта реалізує інтерфейс DTO (наприклад \Magento\Catalog\Api\Data\ProductInterface, реалізований компанією \Magento\Catalog\Model\Product).

Я запитав членів основної команди про це на конференціях, але я не отримав чіткої відповіді, що слід вважати хорошою практикою.
Моє враження, що від цієї рекомендації відмовилися. Хоча було б отримати офіційну заяву з цього приводу.

Наразі я прийняв прагматичне рішення використовувати модель як інтерфейс DTO. Якщо ви вважаєте, що чистіше використовувати окрему модель даних, сміливо робіть це. Обидва підходи на практиці прекрасно працюють.

Якщо інтерфейс DTO розширюється Magento\Framework\Api\ExtensibleDataInterface, модель повинна розширюватися Magento\Framework\Model\AbstractExtensibleModel.
Якщо вас не хвилює розширюваність, модель може просто продовжувати розширювати базовий клас моделі ORM Magento\Framework\Model\AbstractModel.

Оскільки приклад HamburgerInterfaceпоширює ExtensibleDataInterfaceмодель гамбургерів, це поширюється AbstractExtensibleModel, як видно тут:

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

Витяг імен властивостей у константи дозволяє тримати їх в одному місці. Вони можуть використовуватися парою getter / setter, а також сценарієм настройки, який створює таблицю бази даних. В іншому випадку вилучення їх у константи не має ніякої користі.

Результат пошуку

Це SearchResultsInterfaceнайпростіший з трьох інтерфейсів, який можна реалізувати, оскільки він може успадкувати всю його функціональність від базового класу.

<?php

namespace VinaiKopp\Kitchen\Model;

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

class HamburgerSearchResult extends SearchResults implements HamburgerSearchResultInterface
{

}

Налаштування параметрів ObjectManager

Незважаючи на те, що реалізація завершена, ми все ще не можемо використовувати інтерфейси як залежності інших класів, оскільки менеджер об'єктів Magento Framework не знає, які програми використовувати. Нам потрібно додати 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="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>

Як можна відкрити сховище як ресурс API?

Ця частина справді проста, це нагорода за проходження всієї роботи по створенню інтерфейсів, реалізації та їх з'єднанню.

Все, що нам потрібно зробити - це створити 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/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>

Зауважте, що ця конфігурація не тільки дозволяє використовувати сховище як кінцевих точок REST, але також розкриває методи як частину SOAP API.

У першому прикладі маршрут, <route method="GET" url="/V1/vinaikopp_hamburgers/:id">заповнювач :idповинен збігатися з ім'ям аргументу перетвореного методу public function getById($id).
Два імені повинні збігатися, наприклад /V1/vinaikopp_hamburgers/:hamburgerId, не працюватимуть, оскільки ім'я змінної аргументу методу є $id.

Для цього прикладу я встановив доступність <resource ref="anonymous"/>. Це означає, що ресурс відкривається публічно без будь-яких обмежень!
Щоб зробити ресурс доступним лише для зареєстрованого клієнта, використовуйте <resource ref="self"/>. У цьому випадку спеціальне слово meв URL-адресі кінцевої точки ресурсу буде використано для заповнення змінної аргументу $idідентифікатором поточного входу в систему клієнта.
Погляньте на клієнта Magento etc/webapi.xmlі CustomerRepositoryInterfaceякщо вам це потрібно.

Нарешті, <resources>також можна використовувати обмеження доступу до ресурсу до облікового запису користувача адміністратора. Для цього встановіть <resource>ref на ідентифікатор, визначений у etc/acl.xmlфайлі.
Наприклад, <resource ref="Magento_Customer::manage"/>обмежив би доступ до будь-якого облікового запису адміністратора, який має пільгу керувати клієнтами.

Приклад API-запиту за допомогою curl може виглядати так:

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

Примітка. Написання цього почалося як відповідь на https://github.com/astorm/pestle/isissue/195
Перевірте пестик , купіть Commercebug і станьте патроном @alanstorm


1
Дякую за цю чудову відповідь. Вибачте, можливо, мені щось не вистачає, але який сенс мати чистий інтерфейс для сутності, коли в кінці він повинен поширюватися на AbstractModel, який має метод setData, а це означає, що ви можете додати що-небудь до об'єкта незалежно від інтерфейсу?
LDusan

Клас може реалізувати будь-яку кількість інтерфейсів і також додати додаткові методи. Важливим є те, що будь-який інший клас залежить тільки від методів інтерфейсу і, отже, не знає про жоден з інших. Це робить деталі впровадження неінтерфейсних методів, які можна змінити будь-коли, не порушуючи зовнішніх класів. У цьому полягає ідея інверсії залежності. І клас, і будь-які клієнти залежать від інтерфейсу, і не знають про деталі реалізації. Це уточнює?
Вінай

Дякую за відповідь, я бачу, що ви маєте на увазі. Вся справа в тому, що setData - це публічний метод, тому я не впевнений, чи можна його розглядати як деталі реалізації. Якщо він призначений для використання як публічний метод, як ми можемо бути впевнені, що він не порушить нічого зовнішнього при зміні?
LDusan

3
Я прошу вибачення. Те, що ви описуєте, є загальною точкою зору. Механіка залежностей не є інтуїтивно зрозумілою, і оскільки PHP дозволяє викликати методи, які не входять в залежність від інтерфейсу, а також оскільки її не потрібно складати, це робить залежність роботи ще більш розмитою і важко чітко бачити . Це також можна спостерігати в ядрі Magento 2, де є багато місць, де називаються методи реалізації, які не є частиною залежної від інтерфейсу. Вони слугують поганими прикладами і ускладнюють чітке розуміння.
Вінай


35

@Raphael у Digital Pianism:

Будь ласка, зверніться до наступної структури зразкового модуля:

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. Створіть інтерфейс сховища (контракт на обслуговування)
    Namespace/Custom/Api/CustomRepositoryInterface.php: http://codepad.org/WognSKnH

  2. Створіть SearchResultsInterface
    Namespace/Custom/Api/Data/CustomSearchResultsInterface.php: http://codepad.org/zcbi8X4Z

  3. Створіть користувальницький інтерфейс (контейнер даних)
    Namespace/Custom/Api/Data/CustomInterface.php: http://codepad.org/Ze53eT4o

  4. Створіть CustomRepository (Concrete Repository)
    Namespace/Custom/Model/CustomRepository.php: http://codepad.org/KNt5QAGZ
    Тут відбувається "магія". Через конструктор DI ви переходите до моделі ресурсів / фабрики колекції для вашого спеціального модуля; Що стосується методу збереження CRUD у цьому репозиторії, то через ваш CustomRepositoryInterface ви повинні передавати параметр CustomInterface. Di.xml модуля має перевагу замінити інтерфейс такого типу на модель сутності. Модель сутності передається в модель ресурсу і зберігається.

  5. Встановіть перевагу в
    Namespace/Custom/etc/di.xml: http://codepad.org/KmcoOUeV

  6. Модель сутності, що реалізує спеціальний інтерфейс (контейнер даних)
    Namespace/Custom/Model/Custom.php: http://codepad.org/xQiBU7p7 .

  7. Модель ресурсу
    Namespace/Custom/Model/ResourceModel/Custom.php: http://codepad.org/IOsxm9qW

Кілька речей, які слід зазначити:

  • Відмова від відповідальності !!! Я використовував "Простір імен" замість вашого власного імені постачальника, імені агентства тощо ... незалежно від того, яке ім'я ви використовуєте для згрупування своїх модулів разом ... фактичне використання "Простір імен" повністю не вірно в Php ... так що знайте що я зробив це заради зручності і що я не думаю, що це спрацює, і я не пропоную це жодним чином.

  • @Ryan Street навчила мене цього ... тому я не хочу брати всі заслуги

  • Чітко змінити реалізацію сховища відповідно до ваших потреб

  • Ви реалізуєте взаємодію з вашими власними моделями сутності / моделями ресурсів / колекціями в конкретному сховищі ...

  • Я знаю, що я не звертався до всіх методів, які ви вказали у своєму запитанні, але це чудовий початок і повинен подолати розрив між документами та реальною реалізацією.


Райан, чи є способи, зазначені в Договорах на обслуговування, обов'язковими для будь-яких створених нами мильних програм, тобто зберегти (), видалити () тощо?
Sushivam

Скажіть, будь ласка, уявлення про те, як створити власні мильні api в magento 2?
Sushivam

@SachinS На жаль, я не маю жодної уявлення про SOAP. Я цього ще не вивчав, а ще не реалізував. Найкраще, що я міг би запропонувати, - це відкрити тут нове питання щодо цього. Я б сказав, перевірте документи, але це, на жаль, не завжди найкращий спосіб дії (їх може не вистачати). Ви завжди можете подивитися на основну кодову базу чи розширення сторонніх розробників і побачити, чи є там якась інформація. Удачі! Якщо ви знайдете свою відповідь, може бути непогано додати посилання тут. Спасибі
ryanF

Дякую за відповідь @ryan, у будь-якому разі я реалізував мій модуль за допомогою REST, оскільки його світло зважило порівняно з SOAP ... Якщо я реалізую те саме в SOAP,
невдало

3
@ryanF Дякую за цю дуже корисну відповідь. Я знаю, що це не повинен бути копіювати / вставляти робочий код, але ось кілька помилок на користь інших. У сховищі CustomSearchResultsInterfaceFactory має бути CustomSearchResultsFactory. $ searchResults-> setCriteria має бути $ searchResults-> setSearchCriteria. $ Customs [] в передбаченні має бути $ Customs []. Я думаю, що саме про це.
тетранз

3

повні файли використання контрактів на обслуговування

Спеціальні / Модуль / 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 / Дані / 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" => "karina@kapoor.com",
            "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");
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.