Question

I've been looking into extension attributes from various articles and tutorials:

https://store.fooman.co.nz/blog/an-introduction-to-extension-attributes.html https://devdocs.magento.com/guides/v2.2/extension-dev-guide/extension_attributes/adding-attributes.html

Easy to explain, but the one thing that came up for me is adding an extension attribute to the CartInterface model. Basically, if I want to add a plugin for the save() repository model it does not return anything (void). How would I go about retrieving the recently saved Quote/Cart going by the above two methods. Right now I just have a method that puts data into the extension attribute, but the data is not persistent elsewhere.

    $quote = $this->quoteRepository->getActive($cartId);

    // Extension Attributes
    $quoteExtension = $quote->getExtensionAttributes();
    $quoteExtension->setFromApp(true);

    $this->quoteRepository->save($quote);
Was it helpful?

Solution

First time, you must create a file extension_attributes.xml

Vendor\Namespace\etc\extension_attributes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Quote\Api\Data\CartInterface">
    <attribute code="from_app" type="string"/>
</extension_attributes>

Then, you have to change the behavior of the get(), getList() and save() function of the repository.

Vendor\Namespace\etc\extension_attributes.xml

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Api\CartRepositoryInterface">
    <plugin name="vendor_namespace_plugin_cart_repository" type="Vendor\Namespace\Plugin\CartRepositoryPlugin"/>
</type>

Having the field in the table of DB quote, you can directly save the data by the magic method. If you do this, in the plugin of below, you do not need the function beforSave().

$quote->setFromApp(true);
$this->quoteRepository->save($quote);

You can do on this way also, in which, the function beforeSave() of the plugin of below is mandatory.

$quote = $this->quoteRepository->getActive($cartId);

// Extension Attributes
$quoteExtension = $quote->getExtensionAttributes();
$quoteExtension->setFromApp(true);
$quote->setExtensionAttributes($quoteExtension);

$this->quoteRepository->save($quote);

Vendor\Namespace\Plugin\CartRepositoryPlugin

<?php
namespace Vendor\Namespace\Plugin;

use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartExtension;
use Magento\Quote\Api\Data\CartExtensionFactory;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Quote\Api\Data\CartSearchResultsInterface;

/**
 * Class CartRepositoryPlugin
 */
class CartRepositoryPlugin
{
    /**
     * @var CartExtensionFactory
     */
    private $extensionFactory;

    /**
     * CartRepositoryPlugin constructor.
     *
     * @param CartExtensionFactory $orderExtensionFactory
     */
    public function __construct(
        CartExtensionFactory $orderExtensionFactory
    ) {
        $this->extensionFactory = $orderExtensionFactory;
    }

    /**
     * @param CartRepositoryInterface $subject
     * @param CartInterface $resultEntity
     * @return CartInterface
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function afterGet(
        CartRepositoryInterface $subject,
        CartInterface $resultEntity
    ) {
        /** @var CartExtension $extensionAttributes */
        $extensionAttributes = $resultEntity->getExtensionAttributes() ?: $this->extensionFactory->create();

        $extensionAttributes->setFromApp($resultEntity->getData('from_app'));

        $resultEntity->setExtensionAttributes($extensionAttributes);

        return $resultEntity;
    }

    /**
     * @param CartRepositoryInterface $subject
     * @param CartSearchResultsInterface $resultCart
     * @return CartSearchResultsInterface
     */
    public function afterGetList(
        CartRepositoryInterface $subject,
        CartSearchResultsInterface $resultCart
    ) {
        /** @var CartInterface $order */
        foreach ($resultCart->getItems() as $order) {
            $this->afterGet($subject, $order);
        }

        return $resultCart;
    }

    /**
     * @param CartRepositoryInterface $subject
     * @param CartInterface $result
     * @return array
     */
    public function beforeSave(
        CartRepositoryInterface $subject,
        CartInterface $quote
    ) {
        $extensionAttributes = $quote->getExtensionAttributes() ?: $this->extensionFactory->create();
        if ($extensionAttributes !== null && $extensionAttributes->getFromApp() !== null) {
            $quote->setFromApp($extensionAttributes->getFromApp());
        }

        return [$quote];
    }
}

How you can look, the function beforeSave() use the magic method setFromApp() to save the data, instead of this, you can use the magic method directly to save the quote how I explained before.

If the data were in another table of DB with its own model, the function of the plugin will be afterSave() and it will seem similar to this.

/**
 * @param CartRepositoryInterface $subject
 * @param CartInterface $result
 * @return CartInterface
 */
public function afterSave(
    CartRepositoryInterface $subject,
    CartInterface $result
) {
    $extensionAttributes = $result->getExtensionAttributes() ?: $this->storeExtensionFactory->create();
    if ($extensionAttributes !== null && $extensionAttributes->getFromApp() !== null) {
        /** @var CustomEntity $customEntity */
        $customEntity = $this->customEntityFactory->create();
        $customEntity->setQuoteId($result->getId());
        $customEntity->setFromApp($result->getFromApp());
        $this->customEntityRepository->save($customEntity);
    }

    return $result;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top