API REST de Magento 2 utilizando un módulo simple como ejemplo

¡Os doy la bienvenida, queridos habravitas! Como he estado desarrollando en la plataforma de comercio electrónico de Magento desde 2013, habiendo reunido valor y creyendo que en esta área me puedo llamar, al menos, un desarrollador confiable, decidí escribir mi primer artículo en el centro sobre este sistema. Y comenzaré con la implementación de la API REST en Magento 2. Aquí, de forma inmediata, hay una funcionalidad para procesar solicitudes y trataré de demostrarlo usando un ejemplo de un módulo simple. Este artículo está más dirigido a aquellos que ya han trabajado con Magenta. Entonces, ¿a quién le interesa, por favor, debajo del gato?

Empate


Mi imaginación es muy mala, así que se me ocurrió el siguiente ejemplo: imagina que necesitamos implementar un blog, solo los usuarios del panel de administración pueden escribir artículos. De vez en cuando, algún CRM nos llama y carga estos artículos para sí mismo (por qué no está claro, pero así es como justificaremos el uso de la API REST). Para simplificar el módulo, omití específicamente la implementación de mostrar artículos en la interfaz y en el panel de administración (puede implementarlo usted mismo, le recomiendo un buen artículo sobre cuadrículas). Solo la funcionalidad de procesamiento de consultas se verá afectada aquí.

Desarrollo de la acción


Primero, cree la estructura del módulo, llamémoslo AlexPoletaev_Blog (la falta de imaginación no ha desaparecido). Colocamos el módulo en el directorio de la aplicación / código .

AlexPoletaev / Blog / 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="AlexPoletaev_Blog" setup_version="1.0.0"/> </config> 


AlexPoletaev / Blog / Registration.php
 <?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'AlexPoletaev_Blog', __DIR__ ); 


Estos dos archivos son los mínimos necesarios para el módulo.

Si todo se hace en Feng Shui, entonces necesitamos crear contratos de servicio (qué es dentro del Magenta y cómo funciona, puede leer aquí y aquí ), lo que haremos:

AlexPoletaev / Blog / Api / Data / PostInterface.php
 <?php namespace AlexPoletaev\Blog\Api\Data; /** * Interface PostInterface * @package AlexPoletaev\Api\Data * @api */ interface PostInterface { /**#@+ * Constants * @var string */ const ID = 'id'; const AUTHOR_ID = 'author_id'; const TITLE = 'title'; const CONTENT = 'content'; const CREATED_AT = 'created_at'; const UPDATED_AT = 'updated_at'; /**#@-*/ /** * @return int */ public function getId(); /** * @param int $id * @return $this */ public function setId($id); /** * @return int */ public function getAuthorId(); /** * @param int $authorId * @return $this */ public function setAuthorId($authorId); /** * @return string */ public function getTitle(); /** * @param string $title * @return $this */ public function setTitle(string $title); /** * @return string */ public function getContent(); /** * @param string $content * @return $this */ public function setContent(string $content); /** * @return string */ public function getCreatedAt(); /** * @param string $createdAt * @return $this */ public function setCreatedAt(string $createdAt); /** * @return string */ public function getUpdatedAt(); /** * @param string $updatedAt * @return $this */ public function setUpdatedAt(string $updatedAt); } 


AlexPoletaev / Blog / Api / PostRepositoryInterface.php
 <?php namespace AlexPoletaev\Blog\Api; use AlexPoletaev\Blog\Api\Data\PostInterface; use Magento\Framework\Api\SearchCriteriaInterface; /** * Interface PostRepositoryInterface * @package AlexPoletaev\Api * @api */ interface PostRepositoryInterface { /** * @param int $id * @return \AlexPoletaev\Blog\Api\Data\PostInterface */ public function get(int $id); /** * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @return \AlexPoletaev\Blog\Api\Data\PostSearchResultInterface */ public function getList(SearchCriteriaInterface $searchCriteria); /** * @param \AlexPoletaev\Blog\Api\Data\PostInterface $post * @return \AlexPoletaev\Blog\Api\Data\PostInterface */ public function save(PostInterface $post); /** * @param \AlexPoletaev\Blog\Api\Data\PostInterface $post * @return bool */ public function delete(PostInterface $post); /** * @param int $id * @return bool */ public function deleteById(int $id); } 


Analicemos estas dos interfaces con más detalle. La interfaz PostInterface muestra una tabla con artículos de nuestro blog. Crea una tabla a continuación. Cada columna de la base de datos debe tener su propio getter y setter en esta interfaz, descubriremos por qué esto es importante más adelante. La interfaz PostRepositoryInterface proporciona un conjunto estándar de métodos para interactuar con la base de datos y almacenar entidades cargadas en la memoria caché. Se utilizan los mismos métodos para la API. Otra nota importante, se requiere la presencia de PHPDocs correctos en estas interfaces, ya que Magenta, al procesar una solicitud REST, utiliza la reflexión para determinar los parámetros de entrada y los valores de retorno en los métodos.

Usando el script de instalación, cree una tabla donde se almacenarán las publicaciones del blog:

AlexPoletaev / Blog / Setup / InstallSchema.php
 <?php namespace AlexPoletaev\Blog\Setup; use AlexPoletaev\Blog\Api\Data\PostInterface; use AlexPoletaev\Blog\Model\ResourceModel\Post as PostResource; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; use Magento\Security\Setup\InstallSchema as SecurityInstallSchema; /** * Class InstallSchema * @package AlexPoletaev\Blog\Setup */ class InstallSchema implements InstallSchemaInterface { /** * Installs DB schema for a module * * @param SchemaSetupInterface $setup * @param ModuleContextInterface $context * @return void */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); $table = $setup->getConnection() ->newTable( $setup->getTable(PostResource::TABLE_NAME) ) ->addColumn( PostInterface::ID, Table::TYPE_INTEGER, null, ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], 'Post ID' ) ->addColumn( PostInterface::AUTHOR_ID, Table::TYPE_INTEGER, null, ['unsigned' => true, 'nullable' => true,], 'Author ID' ) ->addColumn( PostInterface::TITLE, Table::TYPE_TEXT, 255, [], 'Title' ) ->addColumn( PostInterface::CONTENT, Table::TYPE_TEXT, null, [], 'Content' ) ->addColumn( 'created_at', Table::TYPE_TIMESTAMP, null, ['nullable' => false, 'default' => Table::TIMESTAMP_INIT], 'Creation Time' ) ->addColumn( 'updated_at', Table::TYPE_TIMESTAMP, null, ['nullable' => false, 'default' => Table::TIMESTAMP_INIT_UPDATE], 'Update Time' ) ->addForeignKey( $setup->getFkName( PostResource::TABLE_NAME, PostInterface::AUTHOR_ID, SecurityInstallSchema::ADMIN_USER_DB_TABLE_NAME, 'user_id' ), PostInterface::AUTHOR_ID, $setup->getTable(SecurityInstallSchema::ADMIN_USER_DB_TABLE_NAME), 'user_id', Table::ACTION_SET_NULL ) ->addIndex( $setup->getIdxName( PostResource::TABLE_NAME, [PostInterface::AUTHOR_ID], AdapterInterface::INDEX_TYPE_INDEX ), [PostInterface::AUTHOR_ID], ['type' => AdapterInterface::INDEX_TYPE_INDEX] ) ->setComment('Posts') ; $setup->getConnection()->createTable($table); $setup->endSetup(); } } 


La tabla tendrá las siguientes columnas (no lo olvide, tenemos todo lo más simple posible):

  • id: incremento automático
  • author_id: identificador de usuario administrador (clave externa en el campo user_id de la tabla admin_user)
  • título - título
  • contenido - texto del artículo
  • created_at - fecha de creación
  • updated_at - editar fecha

Ahora necesita crear un conjunto estándar de clases de modelo magenta, modelo de recursos y colección . Por qué no pintaré estas clases, este tema es extenso y va más allá del alcance de este artículo, quienes están interesados ​​pueden buscar en Google por su cuenta. En pocas palabras, estas clases son necesarias para manipular entidades (artículos) de la base de datos. Le aconsejo que lea sobre los patrones del Modelo de Dominio, el Repositorio y la Capa de Servicio.

AlexPoletaev / Blog / Model / Post.php
 <?php namespace AlexPoletaev\Blog\Model; use AlexPoletaev\Blog\Api\Data\PostInterface; use AlexPoletaev\Blog\Model\ResourceModel\Post as PostResource; use Magento\Framework\Model\AbstractModel; /** * Class Post * @package AlexPoletaev\Blog\Model */ class Post extends AbstractModel implements PostInterface { /** * @var string */ protected $_idFieldName = PostInterface::ID; //@codingStandardsIgnoreLine /** * @inheritdoc */ protected function _construct() //@codingStandardsIgnoreLine { $this->_init(PostResource::class); } /** * @return int */ public function getAuthorId() { return $this->getData(PostInterface::AUTHOR_ID); } /** * @param int $authorId * @return $this */ public function setAuthorId($authorId) { $this->setData(PostInterface::AUTHOR_ID, $authorId); return $this; } /** * @return string */ public function getTitle() { return $this->getData(PostInterface::TITLE); } /** * @param string $title * @return $this */ public function setTitle(string $title) { $this->setData(PostInterface::TITLE, $title); return $this; } /** * @return string */ public function getContent() { return $this->getData(PostInterface::CONTENT); } /** * @param string $content * @return $this */ public function setContent(string $content) { $this->setData(PostInterface::CONTENT, $content); return $this; } /** * @return string */ public function getCreatedAt() { return $this->getData(PostInterface::CREATED_AT); } /** * @param string $createdAt * @return $this */ public function setCreatedAt(string $createdAt) { $this->setData(PostInterface::CREATED_AT, $createdAt); return $this; } /** * @return string */ public function getUpdatedAt() { return $this->getData(PostInterface::UPDATED_AT); } /** * @param string $updatedAt * @return $this */ public function setUpdatedAt(string $updatedAt) { $this->setData(PostInterface::UPDATED_AT, $updatedAt); return $this; } } 


AlexPoletaev / Blog / Model / ResourceModel / Post.php
 <?php namespace AlexPoletaev\Blog\Model\ResourceModel; use AlexPoletaev\Blog\Api\Data\PostInterface; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; /** * Class Post * @package AlexPoletaev\Blog\Model\ResourceModel */ class Post extends AbstractDb { /** * @var string */ const TABLE_NAME = 'alex_poletaev_blog_post'; /** * Resource initialization * * @return void */ protected function _construct() //@codingStandardsIgnoreLine { $this->_init(self::TABLE_NAME, PostInterface::ID); } } 


AlexPoletaev / Blog / Model / ResourceModel / Post / Collection.php
 <?php namespace AlexPoletaev\Blog\Model\ResourceModel\Post; use AlexPoletaev\Blog\Model\Post; use AlexPoletaev\Blog\Model\ResourceModel\Post as PostResource; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; /** * Class Collection * @package AlexPoletaev\Blog\Model\ResourceModel\Post */ class Collection extends AbstractCollection { /** * @inheritdoc */ protected function _construct() //@codingStandardsIgnoreLine { $this->_init(Post::class, PostResource::class); } } 


Un lector atento notará que nuestro modelo implementa la interfaz creada previamente y todos sus captadores y establecedores.

Al mismo tiempo, implementamos el repositorio y sus métodos:

AlexPoletaev / Blog / Model / PostRepository.php
 <?php namespace AlexPoletaev\Blog\Model; use AlexPoletaev\Blog\Api\Data\PostInterface; use AlexPoletaev\Blog\Api\Data\PostSearchResultInterface; use AlexPoletaev\Blog\Api\Data\PostSearchResultInterfaceFactory; use AlexPoletaev\Blog\Api\PostRepositoryInterface; use AlexPoletaev\Blog\Model\ResourceModel\Post as PostResource; use AlexPoletaev\Blog\Model\ResourceModel\Post\Collection as PostCollection; use AlexPoletaev\Blog\Model\ResourceModel\Post\CollectionFactory as PostCollectionFactory; use AlexPoletaev\Blog\Model\PostFactory; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; /** * Class PostRepository * @package AlexPoletaev\Blog\Model */ class PostRepository implements PostRepositoryInterface { /** * @var array */ private $registry = []; /** * @var PostResource */ private $postResource; /** * @var PostFactory */ private $postFactory; /** * @var PostCollectionFactory */ private $postCollectionFactory; /** * @var PostSearchResultInterfaceFactory */ private $postSearchResultFactory; /** * @param PostResource $postResource * @param PostFactory $postFactory * @param PostCollectionFactory $postCollectionFactory * @param PostSearchResultInterfaceFactory $postSearchResultFactory */ public function __construct( PostResource $postResource, PostFactory $postFactory, PostCollectionFactory $postCollectionFactory, PostSearchResultInterfaceFactory $postSearchResultFactory ) { $this->postResource = $postResource; $this->postFactory = $postFactory; $this->postCollectionFactory = $postCollectionFactory; $this->postSearchResultFactory = $postSearchResultFactory; } /** * @param int $id * @return PostInterface * @throws NoSuchEntityException */ public function get(int $id) { if (!array_key_exists($id, $this->registry)) { $post = $this->postFactory->create(); $this->postResource->load($post, $id); if (!$post->getId()) { throw new NoSuchEntityException(__('Requested post does not exist')); } $this->registry[$id] = $post; } return $this->registry[$id]; } /** * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @return \AlexPoletaev\Blog\Api\Data\PostSearchResultInterface */ public function getList(SearchCriteriaInterface $searchCriteria) { /** @var PostCollection $collection */ $collection = $this->postCollectionFactory->create(); foreach ($searchCriteria->getFilterGroups() as $filterGroup) { foreach ($filterGroup->getFilters() as $filter) { $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq'; $collection->addFieldToFilter($filter->getField(), [$condition => $filter->getValue()]); } } /** @var PostSearchResultInterface $searchResult */ $searchResult = $this->postSearchResultFactory->create(); $searchResult->setSearchCriteria($searchCriteria); $searchResult->setItems($collection->getItems()); $searchResult->setTotalCount($collection->getSize()); return $searchResult; } /** * @param \AlexPoletaev\Blog\Api\Data\PostInterface $post * @return PostInterface * @throws StateException */ public function save(PostInterface $post) { try { /** @var Post $post */ $this->postResource->save($post); $this->registry[$post->getId()] = $this->get($post->getId()); } catch (\Exception $exception) { throw new StateException(__('Unable to save post #%1', $post->getId())); } return $this->registry[$post->getId()]; } /** * @param \AlexPoletaev\Blog\Api\Data\PostInterface $post * @return bool * @throws StateException */ public function delete(PostInterface $post) { try { /** @var Post $post */ $this->postResource->delete($post); unset($this->registry[$post->getId()]); } catch (\Exception $e) { throw new StateException(__('Unable to remove post #%1', $post->getId())); } return true; } /** * @param int $id * @return bool */ public function deleteById(int $id) { return $this->delete($this->get($id)); } } 


El método \AlexPoletaev\Blog\Model\PostRepository::getList() debería devolver datos de cierto formato, por lo que también necesitaremos esta interfaz:

AlexPoletaev / Blog / Api / Data / PostSearchResultInterface.php
 <?php namespace AlexPoletaev\Blog\Api\Data; use Magento\Framework\Api\SearchResultsInterface; /** * Interface PostSearchResultInterface * @package AlexPoletaev\Blog\Api\Data */ interface PostSearchResultInterface extends SearchResultsInterface { /** * @return \AlexPoletaev\Blog\Api\Data\PostInterface[] */ public function getItems(); /** * @param \AlexPoletaev\Blog\Api\Data\PostInterface[] $items * @return $this */ public function setItems(array $items); } 


Para facilitar la prueba de nuestro módulo, crearemos dos scripts de consola que agregarán y eliminarán datos de prueba de la tabla:

AlexPoletaev / Blog / Console / Command / DeploySampleDataCommand.php
 <?php namespace AlexPoletaev\Blog\Console\Command; use AlexPoletaev\Blog\Api\PostRepositoryInterface; use AlexPoletaev\Blog\Model\Post; use AlexPoletaev\Blog\Model\PostFactory; use Magento\User\Api\Data\UserInterface; use Magento\User\Model\User; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** * Class DeploySampleDataCommand * @package AlexPoletaev\Blog\Console\Command */ class DeploySampleDataCommand extends Command { /**#@+ * @var string */ const ARGUMENT_USERNAME = 'username'; const ARGUMENT_NUMBER_OF_RECORDS = 'number_of_records'; /**#@-*/ /** * @var PostFactory */ private $postFactory; /** * @var PostRepositoryInterface */ private $postRepository; /** * @var UserInterface */ private $user; /** * @param PostFactory $postFactory * @param PostRepositoryInterface $postRepository * @param UserInterface $user */ public function __construct( PostFactory $postFactory, PostRepositoryInterface $postRepository, UserInterface $user ) { parent::__construct(); $this->postFactory = $postFactory; $this->postRepository = $postRepository; $this->user = $user; } /** * @inheritdoc */ protected function configure() { $this->setName('alex_poletaev:blog:deploy_sample_data') ->setDescription('Blog: deploy sample data') ->setDefinition([ new InputArgument( self::ARGUMENT_USERNAME, InputArgument::REQUIRED, 'Username' ), new InputArgument( self::ARGUMENT_NUMBER_OF_RECORDS, InputArgument::OPTIONAL, 'Number of test records' ), ]) ; parent::configure(); } /** * @inheritdoc */ protected function execute(InputInterface $input, OutputInterface $output) { $username = $input->getArgument(self::ARGUMENT_USERNAME); /** @var User $user */ $user = $this->user->loadByUsername($username); if (!$user->getId() && $output->getVerbosity() > 1) { $output->writeln('<error>User is not found</error>'); return null; } $records = $input->getArgument(self::ARGUMENT_NUMBER_OF_RECORDS) ?: 3; for ($i = 1; $i <= (int)$records; $i++) { /** @var Post $post */ $post = $this->postFactory->create(); $post->setAuthorId($user->getId()); $post->setTitle('test title ' . $i); $post->setContent('test content ' . $i); $this->postRepository->save($post); if ($output->getVerbosity() > 1) { $output->writeln('<info>Post with the ID #' . $post->getId() . ' has been created.</info>'); } } } } 


AlexPoletaev / Blog / Console / Command / RemoveSampleDataCommand.php
 <?php namespace AlexPoletaev\Blog\Console\Command; use AlexPoletaev\Blog\Model\ResourceModel\Post as PostResource; use Magento\Framework\App\ResourceConnection; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** * Class RemoveSampleDataCommand * @package AlexPoletaev\Blog\Console\Command */ class RemoveSampleDataCommand extends Command { /** * @var ResourceConnection */ private $resourceConnection; /** * @param ResourceConnection $resourceConnection */ public function __construct( ResourceConnection $resourceConnection ) { parent::__construct(); $this->resourceConnection = $resourceConnection; } /** * @inheritdoc */ protected function configure() { $this->setName('alex_poletaev:blog:remove_sample_data') ->setDescription('Blog: remove sample data') ; parent::configure(); } /** * @inheritdoc */ protected function execute(InputInterface $input, OutputInterface $output) { $connection = $this->resourceConnection->getConnection(); $connection->truncateTable($connection->getTableName(PostResource::TABLE_NAME)); if ($output->getVerbosity() > 1) { $output->writeln('<info>Sample data has been successfully removed.</info>'); } } } 


La característica principal de Magento 2 es el uso generalizado de su propia implementación de Dependency Injection . Para que Magenta sepa a qué interfaz corresponde la implementación, debemos especificar estas dependencias en el archivo di.xml. Al mismo tiempo, registraremos los scripts de consola recién creados en este archivo:

AlexPoletaev / Blog / 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="AlexPoletaev\Blog\Api\Data\PostInterface" type="AlexPoletaev\Blog\Model\Post"/> <preference for="AlexPoletaev\Blog\Api\PostRepositoryInterface" type="AlexPoletaev\Blog\Model\PostRepository"/> <preference for="AlexPoletaev\Blog\Api\Data\PostSearchResultInterface" type="Magento\Framework\Api\SearchResults" /> <type name="Magento\Framework\Console\CommandList"> <arguments> <argument name="commands" xsi:type="array"> <item name="deploy_sample_data" xsi:type="object">AlexPoletaev\Blog\Console\Command\DeploySampleDataCommand</item> <item name="remove_sample_data" xsi:type="object">AlexPoletaev\Blog\Console\Command\RemoveSampleDataCommand</item> </argument> </arguments> </type> </config> 


Ahora registre las rutas para la API REST, esto se hace en el archivo webapi.xml:

AlexPoletaev / Blog / 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 url="/V1/blog/posts" method="POST"> <service class="AlexPoletaev\Blog\Api\PostRepositoryInterface" method="save"/> <resources> <resource ref="anonymous"/> </resources> </route> <route url="/V1/blog/posts/:id" method="DELETE"> <service class="AlexPoletaev\Blog\Api\PostRepositoryInterface" method="deleteById"/> <resources> <resource ref="anonymous"/> </resources> </route> <route url="/V1/blog/posts/:id" method="GET"> <service class="AlexPoletaev\Blog\Api\PostRepositoryInterface" method="get"/> <resources> <resource ref="anonymous"/> </resources> </route> <route url="/V1/blog/posts" method="GET"> <service class="AlexPoletaev\Blog\Api\PostRepositoryInterface" method="getList"/> <resources> <resource ref="anonymous"/> </resources> </route> </routes> 


Aquí le decimos a Magente qué interfaz y qué método de esta interfaz usar al solicitar una URL específica y con un método http específico (POST, GET, etc.). Además, para simplificar, se utiliza un recurso anonymous , que permite que cualquiera pueda acceder a nuestra API; de lo contrario, debe configurar los derechos de acceso (ACL).

Climax


Todos los pasos posteriores suponen que tiene habilitado el modo desarrollador. Esto evita manipulaciones innecesarias con el despliegue de estadísticas de contenido y compilación DI.

Registre nuestro nuevo módulo, ejecute el comando: php bin/magento setup:upgrade .

Compruebe que se haya creado una nueva tabla alex_poletaev_blog_post .

A continuación, cargue los datos de prueba con nuestro script personalizado:

 php bin/magento -v alex_poletaev:blog:deploy_sample_data admin 

El parámetro admin en este script es el nombre de usuario de la tabla admin_user (puede ser diferente para usted), en una palabra, el usuario del panel de administración, que se escribirá en la columna author_id.

Ahora puedes comenzar a probar. Para las pruebas, utilicé Magento 2.2.4, dominio http://m224ce.local/ .

Una forma de probar la API REST es abrir http://m224ce.local/swagger y usar la funcionalidad swagger, pero recuerde que el método getList no funciona correctamente allí. También probé todos los métodos con curl, ejemplos:

Obtenga un artículo con id = 2

 curl -X GET -H "Accept: application/json" "http://m224ce.local/rest/all/V1/blog/posts/2" 

La respuesta es:

 {"id":2,"author_id":1,"title":"test title 2","content":"test content 2","created_at":"2018-06-06 21:35:54","updated_at":"2018-06-06 21:35:54"} 

Obtenga una lista de artículos con author_id = 2

 curl -g -X GET -H "Accept: application/json" "http://m224ce.local/rest/all/V1/blog/posts?searchCriteria[filterGroups][0][filters][0][field]=author_id&searchCriteria[filterGroups][0][filters][0][value]=1&searchCriteria[filterGroups][0][filters][0][conditionType]=eq" 

La respuesta es:

 {"items":[{"id":1,"author_id":1,"title":"test title 1","content":"test content 1","created_at":"2018-06-06 21:35:54","updated_at":"2018-06-06 21:35:54"},{"id":2,"author_id":1,"title":"test title 2","content":"test content 2","created_at":"2018-06-06 21:35:54","updated_at":"2018-06-06 21:35:54"},{"id":3,"author_id":1,"title":"test title 3","content":"test content 3","created_at":"2018-06-06 21:35:54","updated_at":"2018-06-06 21:35:54"}],"search_criteria":{"filter_groups":[{"filters":[{"field":"author_id","value":"1","condition_type":"eq"}]}]},"total_count":3} 

Eliminar artículo con id = 3

 curl -X DELETE -H "Accept: application/json" "http://m224ce.local/rest/all/V1/blog/posts/3" 

La respuesta es:

 true 

Guarda el nuevo artículo

 curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{"post": {"author_id": 1, "title": "test title 4", "content": "test content 4"}}' "http://m224ce.local/rest/all/V1/blog/posts" 

La respuesta es:

 {"id":4,"author_id":1,"title":"test title 4","content":"test content 4","created_at":"2018-06-06 21:44:24","updated_at":"2018-06-06 21:44:24"} 

Tenga en cuenta que para una solicitud con el método http POST, debe pasar la clave de publicación , que en realidad corresponde al parámetro de entrada ($ post) para el método

 \AlexPoletaev\Blog\Api\PostRepositoryInterface::save() 

Denouement


Para aquellos que estén interesados ​​en lo que sucede durante la solicitud y cómo lo procesa Magenta, a continuación les daré algunos enlaces a métodos con mis comentarios. Si algo no funciona, entonces estos métodos deben cargarse primero.

El controlador responsable de procesar la solicitud.
\ Magento \ Webapi \ Controller \ Rest :: dispatch ()

Siguiente llamado
\ Magento \ Webapi \ Controller \ Rest :: processApiRequest ()

Muchos otros métodos se denominan dentro de processApiRequest , pero el siguiente más importante
\ Magento \ Webapi \ Controller \ Rest \ InputParamsResolver :: resolve ()

\ Magento \ Webapi \ Controller \ Rest \ Router :: match () : se determina una ruta específica (dentro, a través del \Magento\Webapi\Model\Rest\Config::getRestRoutes() , todas las rutas adecuadas se extraen de la solicitud de la solicitud). El objeto de ruta contiene todos los datos necesarios para procesar la solicitud: clase, método, derechos de acceso, etc.

\ Magento \ Framework \ Webapi \ ServiceInputProcessor :: process ()
- usa \Magento\Framework\Reflection\MethodsMap::getMethodParams() , donde los parámetros del método se extraen mediante la reflexión

\ Magento \ Framework \ Webapi \ ServiceInputProcessor :: convertValue () - varias opciones para convertir una matriz en un DataObject o en una matriz desde un DataObject

\ Magento \ Framework \ Webapi \ ServiceInputProcessor :: _ createFromArray () - conversión directa, donde a través de la reflexión se verifica la presencia de captadores y establecedores (recuerde, dije anteriormente que volveremos a ellos?) Y que tienen un alcance público. A continuación, el objeto se llena de datos a través de los colocadores.

Al final, en el método
\ Magento \ Webapi \ Controller \ Rest :: processApiRequest () , a través de call_user_func_array método del objeto del repositorio.

Epílogo


Repositorio de módulos de Github

Hay dos formas de instalar:

1) Vía compositor. Para hacer esto, agregue el siguiente objeto a la matriz de repositories en el archivo composer.json

 { "type": "git", "url": "https://github.com/alexpoletaev/magento2-blog-demo" } 

Luego escriba el siguiente comando en la terminal:

 composer require alexpoletaev/magento2-blog-demo:dev-master 

2) Descargue los archivos del módulo y cópielos manualmente en el directorio app/code/AlexPoletaev/Blog

Independientemente del método que elija, al final debe ejecutar la actualización:

 php bin/magento setup:upgrade 

Espero que este artículo sea útil para alguien. Si tiene algún comentario, sugerencia o pregunta, bienvenido a comentar. Gracias por su atencion

Source: https://habr.com/ru/post/es413463/


All Articles