Saya ingin memberikan sedikit lebih detail selain jawaban yang sangat bagus dari @ryanF.
Saya ingin meringkas alasan untuk menambahkan repositori untuk entitas kustom, memberikan contoh cara melakukannya, dan juga menjelaskan cara mengekspos metode repositori tersebut sebagai bagian dari Web API.
Penafian: Saya hanya menjelaskan pendekatan pragmatis bagaimana melakukan ini untuk modul pihak ketiga - tim inti memiliki standar sendiri yang mereka ikuti (atau tidak).
Secara umum, tujuan repositori adalah untuk menyembunyikan logika terkait penyimpanan.
Klien repositori tidak perlu peduli apakah entitas yang dikembalikan disimpan dalam memori dalam array, diambil dari database MySQL, diambil dari API jarak jauh atau dari file.
Saya menganggap tim inti Magento melakukan ini sehingga mereka dapat mengubah atau mengganti ORM di masa depan. Di Magento, ORM saat ini terdiri dari Model, Model Sumber Daya, dan Koleksi.
Jika modul pihak ketiga hanya menggunakan repositori, Magento dapat mengubah bagaimana dan di mana data disimpan, dan modul akan terus bekerja, meskipun ada perubahan yang dalam.
Repositori umumnya memiliki metode seperti findById()
, findByName()
, put()
atau remove()
.
Di Magento ini biasa disebut getbyId()
, save()
dan delete()
, bahkan tidak berpura-pura melakukan hal lain selain operasi CRUD DB.
Metode repositori Magento 2 dapat dengan mudah diekspos sebagai sumber daya API, membuatnya berharga untuk integrasi dengan sistem pihak ketiga atau instance Magento tanpa kepala.
"Haruskah saya menambahkan repositori untuk entitas kustom saya?"
Seperti biasa, jawabannya adalah
"Tergantung".
Untuk mempersingkat cerita, jika entitas Anda akan digunakan oleh modul lain, maka ya, Anda mungkin ingin menambahkan repositori.
Ada faktor lain yang diperhitungkan di sini: di Magento 2, repositori dapat dengan mudah diekspos sebagai API Web - yaitu REST dan SOAP - sumber daya.
Jika itu menarik bagi Anda karena integrasi sistem pihak ketiga atau pengaturan Magento tanpa kepala, sekali lagi, ya, Anda mungkin ingin menambahkan repositori untuk entitas Anda.
Bagaimana cara menambahkan repositori untuk entitas khusus saya?
Mari kita asumsikan Anda ingin mengekspos entitas Anda sebagai bagian dari API REST. Jika itu tidak benar, Anda dapat melewati bagian yang akan datang untuk membuat antarmuka dan langsung ke "Buat repositori dan implementasi model data" di bawah ini.
Buat repositori dan antarmuka model data
Buat folder Api/Data/
di modul Anda. Ini hanya konvensi, Anda bisa menggunakan lokasi yang berbeda, tetapi Anda tidak boleh.
Repositori masuk ke Api/
folder. The Data/
subdirektori adalah untuk nanti.
Di Api/
, buat antarmuka PHP dengan metode yang ingin Anda paparkan. Menurut konvensi Magento 2, semua nama antarmuka berakhir dengan akhiran Interface
.
Misalnya, untuk Hamburger
entitas, saya akan membuat antarmuka Api/HamburgerRepositoryInterface
.
Buat antarmuka repositori
Repositori Magento 2 adalah bagian dari logika domain modul. Itu berarti, tidak ada set metode tetap yang harus diterapkan oleh repositori.
Itu sepenuhnya tergantung pada tujuan modul.
Namun, dalam praktiknya semua repositori sangat mirip. Mereka adalah pembungkus untuk fungsionalitas CRUD.
Sebagian besar memiliki metode getById
, save
, delete
dan getList
.
Mungkin ada lebih banyak, misalnya CustomerRepository
memiliki metode get
, yang menjemput pelanggan melalui email, getById
yang digunakan untuk mengambil pelanggan dengan ID entitas.
Berikut ini contoh antarmuka repositori untuk entitas hamburger:
<?php
namespace VinaiKopp\Kitchen\Api;
use Magento\Framework\Api\SearchCriteriaInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerInterface;
interface HamburgerRepositoryInterface
{
/**
* @param int $id
* @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getById($id);
/**
* @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface $hamburger
* @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
*/
public function save(HamburgerInterface $hamburger);
/**
* @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface $hamburger
* @return void
*/
public function delete(HamburgerInterface $hamburger);
/**
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return \VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria);
}
Penting! Di sini ada timesinks!
Ada beberapa gotcha di sini yang sulit di-debug jika Anda salah paham:
- JANGAN gunakan jenis argumen skalar PHP7 atau ketik kembali jika Anda ingin menghubungkan ini ke REST API!
- Tambahkan penjelasan PHPDoc untuk semua argumen dan jenis kembali ke semua metode!
- Gunakan Nama Kelas yang Sepenuhnya Memenuhi Syarat di blok PHPDoc!
Anotasi diuraikan oleh Magento Framework untuk menentukan bagaimana mengkonversi data ke dan dari JSON atau XML. Impor kelas (yaitu, use
pernyataan) tidak diterapkan!
Setiap metode harus memiliki anotasi dengan jenis argumen apa pun dan jenis kembali. Bahkan jika suatu metode tidak mengambil argumen dan tidak menghasilkan apa-apa, itu harus memiliki anotasi:
/**
* @return void
*/
Jenis skalar ( string
, int
, float
dan bool
) juga harus ditentukan, baik untuk argumen dan sebagai nilai kembali.
Perhatikan bahwa pada contoh di atas, anotasi untuk metode yang mengembalikan objek juga ditentukan sebagai antarmuka.
Jenis antarmuka kembali semua dalam Api\Data
direktori namespace /.
Ini untuk menunjukkan bahwa mereka tidak mengandung logika bisnis. Mereka hanyalah sekumpulan data.
Kita harus membuat antarmuka ini selanjutnya.
Buat antarmuka DTO
Saya pikir Magento menyebut antarmuka ini "model data", nama yang saya tidak suka sama sekali.
Tipe kelas ini umumnya dikenal sebagai Obyek Transfer Data , atau DTO .
Kelas-kelas DTO ini hanya memiliki getter dan setter untuk semua propertinya.
Alasan saya lebih suka menggunakan DTO daripada model data adalah bahwa kurang mudah untuk bingung dengan model data ORM, model sumber daya atau model tampilan ... terlalu banyak hal sudah model di Magento.
Pembatasan yang sama terkait pengetikan PHP7 yang berlaku untuk repositori juga berlaku untuk DTO.
Juga, setiap metode harus memiliki anotasi dengan semua tipe argumen dan tipe pengembalian.
<?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);
}
Jika metode mengambil atau mengembalikan array, jenis item dalam array harus ditentukan dalam penjelasan PHPDoc, diikuti oleh tanda kurung buka dan tutup []
.
Ini berlaku untuk nilai skalar (misalnya int[]
) maupun objek (misalnya IngredientInterface[]
).
Perhatikan bahwa saya menggunakan Api\Data\IngredientInterface
contoh untuk metode mengembalikan array objek, saya tidak akan menambahkan kode bahan ke posting sulit ini.
ExtensibleDataInterface?
Dalam contoh di atas HamburgerInterface
meluas ExtensibleDataInterface
.
Secara teknis ini hanya diperlukan jika Anda ingin modul lain dapat menambahkan atribut ke entitas Anda.
Jika demikian, Anda juga perlu menambahkan pasangan pengambil / penyetel lain, dengan konvensi dipanggil getExtensionAttributes()
dan setExtensionAttributes()
.
Penamaan tipe pengembalian metode ini sangat penting!
Kerangka kerja Magento 2 akan menghasilkan antarmuka, implementasi, dan pabrik untuk implementasi jika Anda menamainya dengan tepat. Detail dari mekanika ini berada di luar cakupan tulisan ini.
Sekadar tahu, jika antarmuka objek yang ingin Anda buat extensible disebut \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
, maka jenis atribut ekstensi haruslah \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface
. Jadi kata Extension
tersebut harus dimasukkan setelah nama entitas, tepat sebelum Interface
akhiran.
Jika Anda tidak ingin entitas Anda dapat diperluas, maka antarmuka DTO tidak harus memperluas antarmuka lainnya, getExtensionAttributes()
dan setExtensionAttributes()
metode dan dapat dihilangkan.
Cukup tentang antarmuka DTO untuk saat ini, saatnya untuk kembali ke antarmuka repositori.
GetList () mengembalikan tipe SearchResults
Metode repositori getList
mengembalikan tipe lain, yaitu SearchResultsInterface
instance.
Metode getList
tentu saja bisa saja mengembalikan array objek yang cocok dengan yang ditentukan SearchCriteria
, tetapi mengembalikan sebuah SearchResults
instance memungkinkan menambahkan beberapa data meta yang berguna ke nilai yang dikembalikan.
Anda dapat melihat cara kerjanya di bawah ini dalam getList()
implementasi metode repositori .
Berikut ini contoh antarmuka hasil pencarian hamburger:
<?php
namespace VinaiKopp\Kitchen\Api\Data;
use Magento\Framework\Api\SearchResultsInterface;
interface HamburgerSearchResultInterface extends SearchResultsInterface
{
/**
* @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface[]
*/
public function getItems();
/**
* @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface[] $items
* @return void
*/
public function setItems(array $items);
}
Semua antarmuka ini dilakukan hanya mengganti tipe untuk dua metode getItems()
dan setItems()
antarmuka induk.
Ringkasan antarmuka
Kami sekarang memiliki antarmuka berikut:
\VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface
\VinaiKopp\Kitchen\Api\Data\HamburgerInterface
\VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface
Repositori meluas apa-apa,
yang HamburgerInterface
memperluas \Magento\Framework\Api\ExtensibleDataInterface
,
dan HamburgerSearchResultInterface
memperluas \Magento\Framework\Api\SearchResultsInterface
.
Buat repositori dan implementasi model data
Langkah selanjutnya adalah membuat implementasi dari tiga antarmuka.
Repositori
Intinya, repositori menggunakan ORM untuk melakukan tugasnya.
Metode getById()
, save()
dan delete()
metodenya cukup lurus ke depan.
Itu HamburgerFactory
disuntikkan ke repositori sebagai argumen konstruktor, seperti yang dapat dilihat sedikit lebih jauh di bawah ini.
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);
}
Sekarang ke bagian repositori, getList()
metode yang paling menarik .
The getList()
metode memiliki untuk menerjemahkan SerachCriteria
kondisi dalam metode panggilan pada koleksi.
Bagian rumit dari mendapatkan AND
dan OR
kondisi untuk filter yang tepat, terutama karena sintaks untuk mengatur kondisi pada koleksi berbeda tergantung pada apakah itu adalah EAV atau entitas tabel datar.
Dalam kebanyakan kasus, getList()
dapat diimplementasikan seperti diilustrasikan dalam contoh di bawah ini.
<?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 dalam a FilterGroup
harus dikombinasikan menggunakan operator ATAU .
Grup filter terpisah digabungkan menggunakan operator logika DAN .
Fiuh
Ini adalah pekerjaan terbesar. Implementasi antarmuka lainnya lebih sederhana.
DTO
Magento awalnya dimaksudkan pengembang untuk menerapkan DTO sebagai kelas yang terpisah, berbeda dari model entitas.
Tim inti hanya melakukan ini untuk modul pelanggan ( \Magento\Customer\Api\Data\CustomerInterface
diterapkan oleh \Magento\Customer\Model\Data\Customer
, bukan \Magento\Customer\Model\Customer
).
Dalam semua kasus lain, model entitas mengimplementasikan antarmuka DTO (misalnya \Magento\Catalog\Api\Data\ProductInterface
diimplementasikan oleh \Magento\Catalog\Model\Product
).
Saya telah bertanya kepada anggota tim inti tentang hal ini di konferensi, tetapi saya tidak mendapatkan jawaban yang jelas apa yang dianggap praktik yang baik.
Kesan saya adalah bahwa rekomendasi ini telah ditinggalkan. Akan menyenangkan untuk mendapatkan pernyataan resmi tentang ini.
Untuk saat ini saya telah membuat keputusan pragmatis untuk menggunakan model sebagai implementasi antarmuka DTO. Jika Anda merasa lebih bersih menggunakan model data terpisah, jangan ragu untuk melakukannya. Kedua pendekatan bekerja dengan baik dalam praktik.
Jika DTO inteface memperluas Magento\Framework\Api\ExtensibleDataInterface
, model harus diperluas Magento\Framework\Model\AbstractExtensibleModel
.
Jika Anda tidak peduli tentang ekstensibilitas, model hanya dapat terus memperluas kelas dasar model ORM Magento\Framework\Model\AbstractModel
.
Karena contoh HamburgerInterface
meluas ExtensibleDataInterface
model hamburger memperluas AbstractExtensibleModel
, seperti dapat dilihat di sini:
<?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);
}
}
Mengekstraksi nama properti menjadi konstanta memungkinkan untuk menyimpannya di satu tempat. Mereka dapat digunakan oleh pasangan pengambil / penyetel dan juga oleh skrip Penataan yang membuat tabel database. Kalau tidak, tidak ada untungnya mengekstraksi mereka menjadi konstanta.
SearchResult
Ini SearchResultsInterface
adalah yang paling sederhana dari tiga antarmuka untuk diimplementasikan, karena dapat mewarisi semua fungsionalitasnya dari kelas kerangka kerja.
<?php
namespace VinaiKopp\Kitchen\Model;
use Magento\Framework\Api\SearchResults;
use VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface;
class HamburgerSearchResult extends SearchResults implements HamburgerSearchResultInterface
{
}
Konfigurasikan preferensi ObjectManager
Meskipun implementasinya selesai, kami masih tidak dapat menggunakan antarmuka sebagai dependensi dari kelas lain, karena manajer objek Magento Framework tidak tahu implementasi apa yang digunakan. Kita perlu menambahkan etc/di.xml
konfigurasi untuk preferensi.
<?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>
Bagaimana repositori dapat diekspos sebagai sumber daya API?
Bagian ini sangat sederhana, ini adalah hadiah untuk melalui semua pekerjaan menciptakan antarmuka, implementasi dan menghubungkannya bersama-sama.
Yang perlu kita lakukan hanyalah membuat etc/webapi.xml
file.
<?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>
Perhatikan bahwa konfigurasi ini tidak hanya memungkinkan penggunaan repositori sebagai titik akhir REST, tetapi juga memperlihatkan metode sebagai bagian dari SOAP API.
Dalam contoh rute pertama <route method="GET" url="/V1/vinaikopp_hamburgers/:id">
,, placeholder :id
harus mencocokkan nama argumen dengan metode yang dipetakan public function getById($id)
,.
Kedua nama harus cocok, misalnya /V1/vinaikopp_hamburgers/:hamburgerId
tidak akan berfungsi, karena nama variabel argumen metode adalah $id
.
Untuk contoh ini saya telah mengatur aksesibilitas ke <resource ref="anonymous"/>
. Ini berarti sumber daya diekspos secara publik tanpa batasan apa pun!
Untuk membuat sumber daya hanya tersedia untuk pelanggan yang masuk, gunakan <resource ref="self"/>
. Dalam hal ini, kata khusus me
dalam URL titik akhir sumber daya akan digunakan untuk mengisi variabel argumen $id
dengan ID pelanggan yang saat ini masuk.
Lihatlah Pelanggan Magento etc/webapi.xml
dan CustomerRepositoryInterface
jika Anda membutuhkannya.
Akhirnya, <resources>
ini juga dapat digunakan untuk membatasi akses ke sumber daya ke akun pengguna admin. Untuk melakukan ini, setel <resource>
ref ke pengenal yang ditentukan dalam etc/acl.xml
file.
Misalnya, <resource ref="Magento_Customer::manage"/>
akan membatasi akses ke akun admin apa pun yang memiliki hak istimewa untuk mengelola pelanggan.
Contoh permintaan API menggunakan curl bisa terlihat seperti ini:
$ curl -X GET http://example.com/rest/V1/vinaikopp_hamburgers/123
Catatan: menulis ini dimulai sebagai jawaban untuk https://github.com/astorm/pestle/issues/195
Periksa alu , beli Commercebug dan menjadi pelindung @alanstorm