Creación de una nueva entidad en Magento 2
10 Dic, 2017 / 8 MIN readActualización: 15/02/2018
En este post explicaré como crear desde cero una nueva entidad en Magento 2 con tres simples campos. Para ello, hablaré de todo lo relacionado con repositorios, API, interfaces y extensión de atributos y explicaremos dichos conceptos.
Cabe destacar que todo el módulo sigue las recomendaciones estándar de PHP.
A continuación pasaremos a explicar los pasos a seguir para conseguir nuestro objetivo.
Creación del módulo
app / code / Interactiv4 / Post / registration.php
app / code / Interactiv4 / Post / composer.json
{ "name": "interactiv4/post", "description": "Example Module of Custom Entity", "require": { "php": "~7.0.0", "magento/magento-composer-installer": "*" }, "type": "magento2-module", "autoload": { "files": [ "registration.php" ], "psr-4": { "Interactiv4\\Post\\\": "" } } }
app / code / Interactiv4 / Post / etc / module.xml
Interfaz de modelo de datos
Esta interfaz, contendrá todos los getters y setters y definiremos en constantes el nombre de la tabla y campos de nuestra entidad en la base de datos.
app / code / Interactiv4 / Post / Api / Data / EntityInterface.php
Extensión de atributos
Nuestra interfaz extiende de Magento \ Framework \ Api \ CustomAttributesDataInterface, la cual a su vez extiende de Magento \ Framework \ Api \ ExtensibleDataInterface.
La pregunta es, ¿para qué queremos que nuestra interfaz extienda de ExtensibleDataInterface? Esto sólo es necesario si deseamos que otros módulos puedan agregar atributos a nuestra entidad, ya que no podemos cambiar la interfaz cada vez que queramos añadir un nuevo atributo. Si es así, necesitamos agregar las funciones getExtensionAttributes() y setExtensionAttributes().
Es muy importante indicar de manera correcta la devolución y parámetro en estas 2 nuevas funciones. Magento generará esta interfaz si el nombre indicado es el correcto. Si la interfaz que queremos hacer extensible es Interactiv4 / Post / Api / Data / EntityInterface, entonces el tipo de extensión de atributos será Interactiv4 / Post / Api / Data / EntityExtensionInterface añadiendo la palabra Extension después del nombre de la entidad y antes del sufijo Interface.
Además de todo esto, cabe destacar que tanto la interfaz del modelo de datos como la del repositorio tienen que cumplir lo siguiente:
- Añadir todas las anotaciones PHPDoc para todos los parámetros y devoluciones de cada función.
- Usar el nombre completo de clases en el PHPDoc Block. Esto significa que la utilización del use al principio de las clases no funcionará.
Base de datos
El siguiente paso es crear el esquema de base de datos de nuestra nueva entidad.
app / code / Interactiv4 / Post / Setup / InstallSchema.php
startSetup(); $table = $installer->getConnection()->newTable( $installer->getTable(EntityInterface::TABLE) )->addColumn( EntityInterface::ID, Table::TYPE_INTEGER, null, ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], 'Id' )->addColumn( EntityInterface::NAME, Table::TYPE_TEXT, 255, ['nullable' => true], 'Name' )->addColumn( EntityInterface::DESCRIPTION, Table::TYPE_TEXT, null, ['nullable' => true], 'Description' )->setComment( 'Custom Entity' ); $installer->getConnection()->createTable($table); $installer->endSetup(); } }
Interfaz de repositorio
Los repositorios en Magento son los encargados de las funcionalidades CRUD. En Magento todos deben tener los métodos save, getById, delete y getList, en nuestro caso hemos añadido alguno más para hacerlo un poco más completo cubriendo tus necesidades.
app / code / Interactiv4 / Post / Api / EntityRepositoryInterface.php
SearchResults
El método getList del repositorio devuelve una instancia de SearchResultsInterface. Este método podría devolver un array de objetos que coincidan con el SearchCriteria especificado, pero crearemos esta interfaz para añadir información sobre los valores que debe devolver.
app / code / Interactiv4 / Post / Api / Data / EntitySearchResultsInterface.php
Implementación del modelo de datos
Si nuestra interfaz de modelo de datos extiende de Magento \ Framework \ Api \ ExtensibleDataInterface, su implementación deberá extender de Magento \ Framework \ Model \ AbstractExtensibleModel.
app / code / Interactiv4 / Post / Model / Entity.php
_init(ResourceModelEntity::class); } /** * @inheritdoc */ public function getName() { return $this->_getData(self::NAME); } /** * @inheritdoc */ public function setName($name) { return $this->setData(self::NAME, $name); } /** * @inheritdoc */ public function getDescription() { return $this->_getData(self::DESCRIPTION); } /** * @inheritdoc */ public function setDescription($description) { return $this->setData(self::DESCRIPTION, $description); } /** * @inheritdoc */ public function getExtensionAttributes() { return $this->_getExtensionAttributes(); } /** * @inheritdoc */ public function setExtensionAttributes(EntityExtensionInterface $extensionAttributes) { return $this->_setExtensionAttributes($extensionAttributes); } public function getIdentities() { return [self::CACHE_TAG . '_' . $this->getId()]; } }
En este ejemplo, nuestra clase implementa de Magento \ Framework \ DataObject \ IdentityInterface, esto hace que nuestro modelo use el método getIdentities(), el cual devuelve un único id para el modelo. IdentityInterface sólo se debe usar si nuestro modelo necesita refrescar la caché después de alguna operación en la base datos y mostrar la información en el frontend. El método getIdentities() es usado por Varnish para añadir la información en la cabecera como X-Magento-Tags.
- $_cacheTag: identificador único para usarlo dentro del almacenamiento de caché.
- $_eventPrefix: prefijo único usado a la hora de ejecutar los eventos. Esto hará que se creé un único evento para nuestra entidad, como por ejemplo interactiv4_post_entity_model_save_before.
Implementación de repositorio
app / code / Interactiv4 / Post / Model / EntityRepository.php
entityFactory = $entityFactory; $this->resourceModelEntity = $resourceModelEntity; $this->entityCollectionFactory = $entityCollectionFactory; $this->entitySearchResultsInterfaceFactory = $entitySearchResultsInterfaceFactory; $this->collectionProcessor = $collectionProcessor; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; } /** * @inheritdoc */ public function save(EntityInterface $entity) { $this->resourceModelEntity->save($entity); return $entity; } /** * @inheritdoc */ public function getById($entityId) { return $this->get($entityId); } /** * @inheritdoc */ public function get($value, $attributeCode = null) { /** @var Entity $entity */ $entity = $this->entityFactory->create()->load($value, $attributeCode); if (!$entity->getId()) { throw new NoSuchEntityException(__('Unable to find entity')); } return $entity; } /** * @inheritdoc */ public function delete(EntityInterface $entity) { $entityId = $entity->getId(); try { $this->resourceModelEntity->delete($entity); } catch (Exception $e) { throw new CouldNotDeleteException( __('Unable to remove entity %1', $entityId) ); } return true; } /** * @inheritdoc */ public function deleteById($entityId) { $entity = $this->getById($entityId); return $this->delete($entity); } /** * @inheritdoc */ public function getList(SearchCriteriaInterface $searchCriteria) { /** @var Collection $collection */ $collection = $this->entityCollectionFactory->create(); $this->extensionAttributesJoinProcessor->process($collection, EntityInterface::class); $this->collectionProcessor->process($searchCriteria, $collection); /** @var EntitySearchResultsInterface $searchResults */ $searchResults = $this->entitySearchResultsInterfaceFactory->create(); $searchResults->setSearchCriteria($searchCriteria); $searchResults->setItems($collection->getItems()); $searchResults->setTotalCount($collection->getSize()); return $searchResults; } }
Factory
En POO, un Factory es usado para instanciar un objeto. El nombre de la clase Factory es el nombre de la clase Model con el sufijo Factory. Esta clase Magento la genera automáticamente, por lo que no es necesario crearla. Para crear una instancia del objeto a partir de una clase Factory haremos lo siguiente:
$entity = $this->entityFactory->create();
Implementación SearchResults
Esta clase es la más simple de todas, ya que hereda toda su funcionalidad de Magento \ Framework \ Api \ SearchResults
app / code / Interactiv4 / Post / Model / EntitySearchResults.php
ResourceModel
Como sabes, el modelo contiene la lógica de base de datos general, pero no ejecuta consultas SQL, de esto se encarga el resourceModel.
app / code / Interactiv4 / Post / Model / ResourceModel / Entity.php
_init(EntityInterface::TABLE, EntityInterface::ID); } }
app / code / Interactiv4 / Post / Model / ResourceModel / Entity / Collection.php
_init(ModelEntity::class, ResourceModelEntity::class); } }
Preference
Después de realizar todo lo comentado anteriormente, aún nos queda un último paso. Especificar las interfaces como dependencias de otras clases.
app / code / Interactiv4 / Post / etc / di.xml
En todos los ejemplos escritos anteriormente, hacemos uso de las interfaces y no de sus clases, ya que una interfaz no debe cambiar en el futuro, pero una clase si puede hacerlo. De esta manera nos aseguramos que no haya problemas o conflictos en el futuro.
Habilitar API para el uso de terceros
app / code / Interactiv4 / Post / etc / webapi.xml
Es importante que el nombre de los parámetros de la API coincidan con el nombre del parámetro del repositorio. El param entityId debe coincidir en /V1/interactiv4_entities/:entityId con public function getById($entityId);
Permisos
- <resource ref=»anonymous»/> hace que el recurso esté abierto públicamente sin ningún tipo de restricción.
- <resource ref=»self»/> hace que el recurso esté abierto sólo a clientes conectados.
- Para que un recurso sea solo accesible para usuarios administradores, este recurso debe de estar definido en /etc/acl.xml. En nuestro caso hemos definido el resource Interactiv4_Post::manage
app / code / Interactiv4 / Post / etc / acl.xml
Espero que os haya sido de ayuda y cualquier duda podéis dejar un comentario.
En este link podréis descargar el código completo.