2

I create a custom entity and repository. All work well, but the POST API route does not create new entities. But if I call the PUT API route to change a property, it works well. Why my custom repository doesn't create new entities?

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/shipment_methods" method="GET">
        <service class="MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface" method="getList"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route url="/V1/shipment_methods/:code" method="GET">
        <service class="MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface" method="getByCode"/>
        <resources>
            <resource ref="MyVendorName_MyModuleName::shipment_method"/>
        </resources>
    </route>
    <route url="/V1/shipment_methods" method="POST">
        <service class="MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface" method="save"/>
        <resources>
            <resource ref="MyVendorName_MyModuleName::shipment_method_save"/>
        </resources>
    </route>
    <route url="/V1/shipment_methods/:code" method="PUT">
        <service class="MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface" method="save"/>
        <resources>
            <resource ref="MyVendorName_MyModuleName::shipment_method_save"/>
        </resources>
    </route>
    <route url="/V1/shipment_methods" method="DELETE">
        <service class="MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface" method="delete"/>
        <resources>
            <resource ref="MyVendorName_MyModuleName::shipment_method_delete"/>
        </resources>
    </route>
    <route url="/V1/shipment_methods/:code" method="DELETE">
        <service class="MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface" method="deleteByCode"/>
        <resources>
            <resource ref="MyVendorName_MyModuleName::shipment_method_delete"/>
        </resources>
    </route>
</routes>

ShipmentMethodRepository

<?php
declare(strict_types=1);

namespace MyVendorName\MyModuleName\Model;

use MyVendorName\MyModuleName\Api\Data\ShipmentMethodInterface;
use MyVendorName\MyModuleName\Api\Data\ShipmentMethodSearchResultsInterface;
use MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface;
use MyVendorName\MyModuleName\Model\ResourceModel\ShipmentMethod as ShipmentMethodResourceModel;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;

class ShipmentMethodRepository implements ShipmentMethodRepositoryInterface
{
    /**
     * @var ShipmentMethodFactory
     */
    private $shipmentMethodFactory;

    /**
     * @var ShipmentMethodResourceModel
     */
    private $shipmentMethodResourceModel;

    /**
     * ShipmentMethodRepository constructor.
     * @param ShipmentMethodFactory $shipmentMethodFactory
     * @param ShipmentMethodResourceModel $shipmentMethodResourceModel
     */
    public function __construct(
        ShipmentMethodFactory $shipmentMethodFactory,
        ShipmentMethodResourceModel $shipmentMethodResourceModel
    ) {
        $this->shipmentMethodFactory = $shipmentMethodFactory;
        $this->shipmentMethodResourceModel = $shipmentMethodResourceModel;
    }

    /**
     * Retrieve shipment methods which match a specified criteria
     * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
     * @return \MyVendorName\MyModuleName\Api\Data\ShipmentMethodSearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $searchCriteria): ShipmentMethodSearchResultsInterface
    {
        // TODO: Implement getList() method.
    }

    /**
     * Get shipment method by code
     * @param string $code
     * @return \MyVendorName\MyModuleName\Api\Data\ShipmentMethodInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getByCode(string $code): ShipmentMethodInterface
    {
        $shipmentMethod = $this->shipmentMethodFactory->create();
        $this->shipmentMethodResourceModel->load($shipmentMethod, $code);

        if (!$shipmentMethod->getCode()) {
            throw new NoSuchEntityException(__('Shipment method with code %1 does not exist.', $code));
        }

        return $shipmentMethod;
    }

    /**
     * Create or update a shipment method
     * @param \MyVendorName\MyModuleName\Api\Data\ShipmentMethodInterface $shipmentMethod
     * @return \MyVendorName\MyModuleName\Api\Data\ShipmentMethodInterface
     * @throws \Magento\Framework\Exception\CouldNotSaveException
     */
    public function save(ShipmentMethodInterface $shipmentMethod): ShipmentMethodInterface
    {
        try {
            $this->shipmentMethodResourceModel->save($shipmentMethod);
        } catch (\Exception $e) {
            throw new CouldNotSaveException(__($e->getMessage()));
        }

        return $shipmentMethod;
    }

    /**
     * Delete shipment method
     * @param \MyVendorName\MyModuleName\Api\Data\ShipmentMethodInterface $shipmentMethod
     * @return bool
     * @throws \Magento\Framework\Exception\CouldNotDeleteException
     */
    public function delete(ShipmentMethodInterface $shipmentMethod): bool
    {
        try {
            $this->shipmentMethodResourceModel->delete($shipmentMethod);
        } catch (\Exception $e) {
            throw new CouldNotDeleteException(__($e->getMessage()));
        }

        return true;
    }

    /**
     * Delete shipment method by code
     * @param string $code
     * @return bool
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     * @throws \Magento\Framework\Exception\CouldNotDeleteException
     */
    public function deleteByCode(string $code): bool
    {
        return $this->delete($this->getByCode($code));
    }
}

di.xml

<preference for="MyVendorName\MyModuleName\Api\Data\ShipmentMethodInterface" type="MyVendorName\MyModuleName\Model\ShipmentMethod"/>
<preference for="MyVendorName\MyModuleName\Api\ShipmentMethodRepositoryInterface" type="MyVendorName\MyModuleName\Model\ShipmentMethodRepository"/>

Model\ShipmentMethod

<?php
declare(strict_types=1);

namespace MyVendorName\MyModuleName\Model;

use MyVendorName\MyModuleName\Api\Data\ShipmentMethodInterface;
use MyVendorName\MyModuleName\Model\ResourceModel\ShipmentMethod as ShipmentMethodResourceModel;
use Magento\Framework\Model\AbstractModel;

class ShipmentMethod extends AbstractModel implements ShipmentMethodInterface
{
    /**
     * {@inheritDoc}
     */
    protected function _construct(): void
    {
        $this->_init(ShipmentMethodResourceModel::class);
    }

    /**
     * {@inheritDoc}
     */
    public function getCode(): string
    {
        return (string)$this->getData(self::CODE);
    }

    /**
     * {@inheritDoc}
     */
    public function setCode(string $code): ShipmentMethodInterface
    {
        $this->setData(self::CODE, $code);

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function getDescription(): string
    {
        return (string)$this->getData(self::DESCRIPTION);
    }

    /**
     * {@inheritDoc}
     */
    public function setDescription(string $description): ShipmentMethodInterface
    {
        $this->setData(self::DESCRIPTION, $description);

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function getCourierCode(): string
    {
        return (string)$this->getData(self::COURIER_CODE);
    }

    /**
     * {@inheritDoc}
     */
    public function setCourierCode(string $courierCode): ShipmentMethodInterface
    {
        $this->setData(self::COURIER_CODE, $courierCode);

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function getServiceCode(): string
    {
        return (string)$this->getData(self::SERVICE_CODE);
    }

    /**
     * {@inheritDoc}
     */
    public function setServiceCode(string $serviceCode): ShipmentMethodInterface
    {
        $this->setData(self::SERVICE_CODE, $serviceCode);

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function getIsExpress(): bool
    {
        return (bool)$this->getData(self::IS_EXPRESS);
    }

    /**
     * {@inheritDoc}
     */
    public function setIsExpress(bool $isExpress): ShipmentMethodInterface
    {
        $this->setData(self::IS_EXPRESS, $isExpress);

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function getTiming(): string
    {
        return (string)$this->getData(self::TIMING);
    }

    /**
     * {@inheritDoc}
     */
    public function setTiming(string $timing): ShipmentMethodInterface
    {
        $this->setData(self::TIMING, $timing);

        return $this;
    }
}

Model\ResourceModel\ShipmentMethod

<?php
declare(strict_types=1);

namespace MyVendorName\MyModuleName\Shipment\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

class ShipmentMethod extends AbstractDb
{
    public const TABLE_NAME = 'my_shipment_method';

    /**
     * {@inheritDoc}
     */
    protected function _construct(): void
    {
        $this->_init(self::TABLE_NAME, 'code');
    }
}
8
  • Are you getting an error in the response of the rest api request? What is the result of making that call?
    – sdaugherty
    Jun 29, 2019 at 2:28
  • @floorz I receive the ShipmentMethod object that I return after try-catch. I checked the logs, and there aren't any errors Jun 29, 2019 at 6:46
  • What if you remove that php return type on the save method? That's the only thing that stands out here without seeing more code.
    – sdaugherty
    Jun 29, 2019 at 7:17
  • That or capture the return result of the resource model save and return that instead.
    – sdaugherty
    Jun 29, 2019 at 7:19
  • 1
    Ohhhh interesting! Nice find! Happy coding :)
    – sdaugherty
    Jun 29, 2019 at 17:36

1 Answer 1

3

I found the solution through this post how-set-resource-model-for-not-primary-ai :). In nutshell, because my primary key is not auto-increment, the resource model fails the save method for new entities. To solve the problem you should set protected $_isPkAutoIncrement = false; inside your resource model class

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.