diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/JsonEncoded.php b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/JsonEncoded.php index 5ea8e97e7004f..156c0326f2b6f 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/JsonEncoded.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/JsonEncoded.php @@ -41,7 +41,7 @@ public function beforeSave($object) { // parent::beforeSave() is not called intentionally $attrCode = $this->getAttribute()->getAttributeCode(); - if ($object->hasData($attrCode)) { + if ($object->hasData($attrCode) && !$this->isJsonEncoded($object->getData($attrCode))) { $object->setData($attrCode, $this->jsonSerializer->serialize($object->getData($attrCode))); } return $this; @@ -61,4 +61,24 @@ public function afterLoad($object) $object->setData($attrCode, $this->jsonSerializer->unserialize($object->getData($attrCode) ?: '{}')); return $this; } + + /** + * Returns true if given value is a valid json value, and false otherwise. + * + * @param string|int|float|bool|array|null $value + * @return bool + */ + private function isJsonEncoded($value): bool + { + $result = is_string($value); + if ($result) { + try { + $this->jsonSerializer->unserialize($value); + } catch (\InvalidArgumentException $e) { + $result = false; + } + } + + return $result; + } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/JsonEncodedTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/JsonEncodedTest.php index d94d25e7fd180..3388cbd3052a8 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/JsonEncodedTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/JsonEncodedTest.php @@ -82,6 +82,25 @@ public function testBeforeSave() $this->assertEquals(json_encode([1, 2, 3]), $product->getData('json_encoded')); } + /** + * Test before save handler with already encoded attribute value + */ + public function testBeforeSaveWithAlreadyEncodedValue() + { + $product = new \Magento\Framework\DataObject( + [ + 'json_encoded' => [1, 2, 3] + ] + ); + + // save twice + $this->model->beforeSave($product); + $this->model->beforeSave($product); + + // check it is encoded only once + $this->assertEquals(json_encode([1, 2, 3]), $product->getData('json_encoded')); + } + /** * Test after load handler */