beforeSave
functionality is incorrectly described #7786
Description
General issue
Directly following the general patterns described on this page - for an extension attribute on a product, with a plugin connected to the product repository - the afterSave
function will not update the extension attribute. For example, making an API call with updated extension attribute will leave it unchanged.
Description:
Essentially, the data in the product instance returned as the result of a save
call (passed as the 2nd parameter to an 'after' plugin function) is not what was sent via the API.
After a little digging, the data that reaches the beforeSave
function is correct. However, the final step in the product repository save process is a get call, presumably to refresh data, which correspondingly triggers the afterGet
function to (re-)populate the extension attributes on the product returned as the result.
The code example on the page is using the ProductInterface
that is the result returned from the function, due to the attribute sequence, hence why it can't / won't update the extension attribute.
However, it should also be noted that is specifically a peculiarity of the product repository and does not necessarily hold true for other repositories.
Possible solutions:
We can't simply return the original parameter object, because that would invalidate any processing carried out before / during save. Similarly, we can't just return the result object by itself, since that has the incorrect extension attributes and potentially might be used by something else. And we can't change this to a beforeSave
function, because that causes potential issues with product creation (hard to link an extension attribute to a product that doesn't exist in the database yet.)
To resolve this correctly, you actually need both the result object and the original parameter object:
public function afterSave
(
\Magento\Catalog\Api\ProductRepositoryInterface $subject,
\Magento\Catalog\Api\Data\ProductInterface $result, /** result from the save call **/
\Magento\Catalog\Api\Data\ProductInterface $entity /** original parameter to the call **/
/** other parameter not required **/
) {
$extensionAttributes = $entity->getExtensionAttributes(); /** get original extension attributes from entity **/
$ourCustomData = $extensionAttributes->getOurCustomData();
$this->customDataRepository->save($ourCustomData);
$resultAttributes = $result->getExtentionAttributes(); /** get extension attributes as they exist after save **/
$resultAttributes->setOurCustomData($ourCustomData); /** update the extension attributes with correct data **/
$result->setExtensionAttributes($resultAttributes);
return $result;
}
...