diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/ArrayBackend.php b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/ArrayBackend.php index 47a998a95ecb5..c387631e693cd 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/ArrayBackend.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/ArrayBackend.php @@ -42,6 +42,8 @@ public function validate($object) $data = $object->getData($attributeCode); if (is_array($data)) { $object->setData($attributeCode, implode(',', array_filter($data))); + } elseif (empty($data)) { + $object->setData($attributeCode, null); } return parent::validate($object); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/ArrayTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/ArrayBackendTest.php similarity index 82% rename from app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/ArrayTest.php rename to app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/ArrayBackendTest.php index 0a8adb7534cde..e454f1f22f7bb 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/ArrayTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/ArrayBackendTest.php @@ -5,7 +5,7 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity\Attribute\Backend; -class ArrayTest extends \PHPUnit_Framework_TestCase +class ArrayBackendTest extends \PHPUnit_Framework_TestCase { /** * @var \Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend @@ -20,13 +20,13 @@ class ArrayTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->_attribute = $this->getMock( - 'Magento\Eav\Model\Entity\Attribute', + \Magento\Eav\Model\Entity\Attribute::class, ['getAttributeCode', '__wakeup'], [], '', false ); - $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger = $this->getMock(\Psr\Log\LoggerInterface::class); $this->_model = new \Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend($logger); $this->_model->setAttribute($this->_attribute); } @@ -37,9 +37,10 @@ protected function setUp() public function testValidate($data) { $this->_attribute->expects($this->atLeastOnce())->method('getAttributeCode')->will($this->returnValue('code')); - $product = new \Magento\Framework\DataObject(['code' => $data]); + $product = new \Magento\Framework\DataObject(['code' => $data, 'empty' => '']); $this->_model->validate($product); $this->assertEquals('1,2,3', $product->getCode()); + $this->assertEquals(null, $product->getEmpty()); } public static function attributeValueDataProvider() diff --git a/app/code/Magento/Ui/view/base/web/js/form/client.js b/app/code/Magento/Ui/view/base/web/js/form/client.js index 6b4c1a3262273..f1299bb54b352 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/client.js +++ b/app/code/Magento/Ui/view/base/web/js/form/client.js @@ -22,7 +22,7 @@ define([ function beforeSave(data, url, selectorPrefix, messagesClass) { var save = $.Deferred(); - data = utils.serialize(data); + data = utils.serialize(utils.filterFormData(data)); data['form_key'] = window.FORM_KEY; diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/multiselect.js b/app/code/Magento/Ui/view/base/web/js/form/element/multiselect.js index 37b389dcba027..c37c67c39d092 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/multiselect.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/multiselect.js @@ -13,7 +13,10 @@ define([ return Select.extend({ defaults: { size: 5, - elementTmpl: 'ui/form/element/multiselect' + elementTmpl: 'ui/form/element/multiselect', + listens: { + value: 'setDifferedFromDefault setPrepareToSendData' + } }, /** @@ -38,6 +41,21 @@ define([ return _.isString(value) ? value.split(',') : value; }, + /** + * Sets the prepared data to dataSource + * by path, where key is component link to dataSource with + * suffix "-prepared-for-send". + * + * @param {Array} data - current component value + */ + setPrepareToSendData: function (data) { + if (!data.length) { + data = ''; + } + + this.source.set(this.dataScope + '-prepared-for-send', data); + }, + /** * @inheritdoc */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index ede108bf7bbd4..a5a5e3ff5b118 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Catalog\Model\ProductRepository; + /** * @magentoAppArea adminhtml */ @@ -27,7 +29,7 @@ public function testSaveActionWithDangerRequest() public function testSaveActionAndNew() { $this->getRequest()->setPostValue(['back' => 'new']); - $repository = $this->_objectManager->create('Magento\Catalog\Model\ProductRepository'); + $repository = $this->_objectManager->create(ProductRepository::class); $product = $repository->get('simple'); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/new/')); @@ -43,7 +45,7 @@ public function testSaveActionAndNew() public function testSaveActionAndDuplicate() { $this->getRequest()->setPostValue(['back' => 'duplicate']); - $repository = $this->_objectManager->create('Magento\Catalog\Model\ProductRepository'); + $repository = $this->_objectManager->create(ProductRepository::class); $product = $repository->get('simple'); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/edit/')); @@ -100,7 +102,7 @@ public function testIndexAction() */ public function testEditAction() { - $repository = $this->_objectManager->create('Magento\Catalog\Model\ProductRepository'); + $repository = $this->_objectManager->create(ProductRepository::class); $product = $repository->get('simple'); $this->dispatch('backend/catalog/product/edit/id/' . $product->getEntityId()); $body = $this->getResponse()->getBody(); @@ -119,4 +121,31 @@ public function testEditAction() '"Save & Duplicate" button isn\'t present on Edit Product page' ); } + + /** + * Tests Validate product action. + * + * @magentoDataFixture Magento/Catalog/_files/products_with_multiselect_attribute.php + * + * @return void + */ + public function testValidateAction() + { + $expectedResult = json_encode(['error' => false]); + + $repository = $this->_objectManager->create(ProductRepository::class); + $product = $repository->get('simple_ms_2'); + $data = $product->getData(); + unset($data['multiselect_attribute']); + + $this->getRequest()->setPostValue(['product' => $data]); + $this->dispatch('backend/catalog/product/validate'); + $response = $this->getResponse()->getBody(); + + $this->assertJsonStringEqualsJsonString( + $expectedResult, + $response, + 'Validate action returned incorrect result.' + ); + } } diff --git a/lib/web/mage/utils/misc.js b/lib/web/mage/utils/misc.js index 5e92ab0ecbe07..c8f7a0481f600 100644 --- a/lib/web/mage/utils/misc.js +++ b/lib/web/mage/utils/misc.js @@ -205,6 +205,32 @@ define([ } return formData; + }, + + /** + * Filters data object. Finds properties with suffix + * and sets their values to properties with the same name without suffix. + * + * @param {Object} data - The data object that should be filtered + * @param {String} suffix - The string by which data object should be filtered + * @param {String} separator - The string that is separator between property and suffix + * + * @returns {Object} Filtered data object + */ + filterFormData: function (data, suffix, separator) { + data = data || {}; + suffix = suffix || 'prepared-for-send'; + separator = separator || '-'; + _.each(data, function (value, key) { + if (_.isObject(value) && !value.length) { + this.filterFormData(value, suffix, separator) + } else if (_.isString(key) && ~key.indexOf(suffix)) { + data[key.split(separator)[0]] = value; + delete data[key]; + } + }, this); + + return data; } }; });