Skip to content

Commit ad0299b

Browse files
author
Oleksii Korshenko
authored
Merge pull request #1768 from magento-engcom/develop-prs
Public Pull Requests #12397 Attribute with "Catalog Input Type for Store Owner" equal "Fixed Product Tax" for Multi-store by @klakovskiy #12376 10058: Tablerate->getCsvFile() fails with non-default PHP upload_tmp_dir(2.3) by @RomaKis #12044 [Backport 2.3] Add swatch option: Prevent loosing data and default value if data is not populated via adminhtml by @gomencal Fixed Public Issues #12393 Attribute with "Catalog Input Type for Store Owner" equal "Fixed Product Tax" for Multi-store #10058 Tablerate->getCsvFile() fails with non-default PHP upload_tmp_dir #9410 Impossible to add swatch options via Service Contracts if there is no existing swatch option for attribute #10707 Create attribute option via API for swatch attribute fails #10737 Can't import attribute option over API if option is 'visual swatch' #11032 Unable to add new options to swatch attribute
2 parents 46ba281 + 0eca759 commit ad0299b

File tree

8 files changed

+334
-9
lines changed

8 files changed

+334
-9
lines changed

app/code/Magento/OfflineShipping/Model/ResourceModel/Carrier/Tablerate.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Magento\OfflineShipping\Model\ResourceModel\Carrier;
1313

1414
use Magento\Framework\Filesystem;
15-
use Magento\Framework\Filesystem\DirectoryList;
1615
use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\Import;
1716
use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\RateQuery;
1817
use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\RateQueryFactory;
@@ -321,9 +320,13 @@ public function getConditionName(\Magento\Framework\DataObject $object)
321320
*/
322321
private function getCsvFile($filePath)
323322
{
324-
$tmpDirectory = $this->filesystem->getDirectoryRead(DirectoryList::SYS_TMP);
325-
$path = $tmpDirectory->getRelativePath($filePath);
326-
return $tmpDirectory->openFile($path);
323+
$pathInfo = pathInfo($filePath);
324+
$dirName = isset($pathInfo['dirname']) ? $pathInfo['dirname'] : '';
325+
$fileName = isset($pathInfo['basename']) ? $pathInfo['basename'] : '';
326+
327+
$directoryRead = $this->filesystem->getDirectoryReadByPath($dirName);
328+
329+
return $directoryRead->openFile($fileName);
327330
}
328331

329332
/**
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\OfflineShipping\Test\Unit\Model\ResourceModel\Carrier;
8+
9+
use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate;
10+
use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\Import;
11+
use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\RateQueryFactory;
12+
13+
/**
14+
* Unit test for Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
15+
*
16+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
17+
*/
18+
class TablerateTest extends \PHPUnit\Framework\TestCase
19+
{
20+
/**
21+
* @var Tablerate
22+
*/
23+
private $model;
24+
25+
/**
26+
* @var \PHPUnit_Framework_MockObject_MockObject
27+
*/
28+
private $storeManagerMock;
29+
30+
/**
31+
* @var \PHPUnit_Framework_MockObject_MockObject
32+
*/
33+
private $filesystemMock;
34+
35+
/**
36+
* @var \PHPUnit_Framework_MockObject_MockObject
37+
*/
38+
private $resource;
39+
40+
/**
41+
* @var \PHPUnit_Framework_MockObject_MockObject
42+
*/
43+
private $importMock;
44+
45+
protected function setUp()
46+
{
47+
$contextMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\Context::class);
48+
$loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class);
49+
$coreConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
50+
$this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class);
51+
$carrierTablerateMock = $this->createMock(\Magento\OfflineShipping\Model\Carrier\Tablerate::class);
52+
$this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class);
53+
$this->importMock = $this->createMock(Import::class);
54+
$rateQueryFactoryMock = $this->createMock(RateQueryFactory::class);
55+
$this->resource = $this->createMock(\Magento\Framework\App\ResourceConnection::class);
56+
57+
$contextMock->expects($this->once())->method('getResources')->willReturn($this->resource);
58+
59+
$this->model = new Tablerate(
60+
$contextMock,
61+
$loggerMock,
62+
$coreConfigMock,
63+
$this->storeManagerMock,
64+
$carrierTablerateMock,
65+
$this->filesystemMock,
66+
$this->importMock,
67+
$rateQueryFactoryMock
68+
);
69+
}
70+
71+
public function testUploadAndImport()
72+
{
73+
$_FILES['groups']['tmp_name']['tablerate']['fields']['import']['value'] = 'some/path/to/file';
74+
$object = $this->createPartialMock(
75+
\Magento\OfflineShipping\Model\Config\Backend\Tablerate::class,
76+
['getScopeId']
77+
);
78+
79+
$websiteMock = $this->createMock(\Magento\Store\Api\Data\WebsiteInterface::class);
80+
$directoryReadMock = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class);
81+
$fileReadMock = $this->createMock(\Magento\Framework\Filesystem\File\ReadInterface::class);
82+
$connectionMock = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class);
83+
84+
$this->storeManagerMock->expects($this->once())->method('getWebsite')->willReturn($websiteMock);
85+
$object->expects($this->once())->method('getScopeId')->willReturn(1);
86+
$websiteMock->expects($this->once())->method('getId')->willReturn(1);
87+
88+
$this->filesystemMock->expects($this->once())->method('getDirectoryReadByPath')
89+
->with('some/path/to')->willReturn($directoryReadMock);
90+
$directoryReadMock->expects($this->once())->method('openFile')
91+
->with('file')->willReturn($fileReadMock);
92+
93+
$this->resource->expects($this->once())->method('getConnection')->willReturn($connectionMock);
94+
95+
$connectionMock->expects($this->once())->method('beginTransaction');
96+
$connectionMock->expects($this->once())->method('delete');
97+
$connectionMock->expects($this->once())->method('commit');
98+
99+
$this->importMock->expects($this->once())->method('getColumns')->willReturn([]);
100+
$this->importMock->expects($this->once())->method('getData')->willReturn([]);
101+
102+
$this->model->uploadAndImport($object);
103+
unset($_FILES['groups']);
104+
}
105+
}

app/code/Magento/Swatches/Model/Plugin/EavAttribute.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,17 @@ protected function setProperOptionsArray(Attribute $attribute)
130130
$swatchesArray = $attribute->getData('swatchtext');
131131
}
132132
if ($canReplace == true) {
133-
$attribute->setData('option', $optionsArray);
134-
$attribute->setData('default', $defaultValue);
135-
$attribute->setData('swatch', $swatchesArray);
133+
if (!empty($optionsArray)) {
134+
$attribute->setData('option', $optionsArray);
135+
}
136+
if (!empty($defaultValue)) {
137+
$attribute->setData('default', $defaultValue);
138+
} else {
139+
$attribute->setData('default', [0 => $attribute->getDefaultValue()]);
140+
}
141+
if (!empty($swatchesArray)) {
142+
$attribute->setData('swatch', $swatchesArray);
143+
}
136144
}
137145
}
138146

app/code/Magento/Weee/Ui/DataProvider/Product/Form/Modifier/Manager/Website.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public function getWebsites(ProductInterface $product, EavAttribute $eavAttribut
6868
if ($storeId = $this->locator->getStore()->getId()) {
6969
/** @var WebsiteInterface $website */
7070
$website = $this->storeManager->getStore($storeId)->getWebsite();
71-
$websites[$website->getId()] = [
71+
$websites[] = [
7272
'value' => $website->getId(),
7373
'label' => $this->formatLabel(
7474
$website->getName(),
@@ -81,7 +81,7 @@ public function getWebsites(ProductInterface $product, EavAttribute $eavAttribut
8181
if (!in_array($website->getId(), $product->getWebsiteIds())) {
8282
continue;
8383
}
84-
$websites[$website->getId()] = [
84+
$websites[] = [
8585
'value' => $website->getId(),
8686
'label' => $this->formatLabel(
8787
$website->getName(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Catalog\Api;
7+
8+
use Magento\Eav\Api\Data\AttributeOptionInterface;
9+
use Magento\Eav\Api\Data\AttributeOptionLabelInterface;
10+
use Magento\TestFramework\TestCase\WebapiAbstract;
11+
12+
class ProductSwatchAttributeOptionManagementInterfaceTest extends WebapiAbstract
13+
{
14+
const SERVICE_NAME = 'catalogProductAttributeOptionManagementV1';
15+
const SERVICE_VERSION = 'V1';
16+
const RESOURCE_PATH = '/V1/products/attributes';
17+
18+
/**
19+
* @magentoApiDataFixture Magento/Swatches/_files/swatch_attribute.php
20+
* @dataProvider addDataProvider
21+
*/
22+
public function testAdd($optionData)
23+
{
24+
$testAttributeCode = 'color_swatch';
25+
$serviceInfo = [
26+
'rest' => [
27+
'resourcePath' => self::RESOURCE_PATH . '/' . $testAttributeCode . '/options',
28+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
29+
],
30+
'soap' => [
31+
'service' => self::SERVICE_NAME,
32+
'serviceVersion' => self::SERVICE_VERSION,
33+
'operation' => self::SERVICE_NAME . 'add',
34+
],
35+
];
36+
37+
$response = $this->_webApiCall(
38+
$serviceInfo,
39+
[
40+
'attributeCode' => $testAttributeCode,
41+
'option' => $optionData,
42+
]
43+
);
44+
45+
$this->assertTrue($response);
46+
$updatedData = $this->getAttributeOptions($testAttributeCode);
47+
$lastOption = array_pop($updatedData);
48+
$this->assertEquals(
49+
$optionData[AttributeOptionInterface::STORE_LABELS][0][AttributeOptionLabelInterface::LABEL],
50+
$lastOption['label']
51+
);
52+
}
53+
54+
/**
55+
* @return array
56+
*/
57+
public function addDataProvider()
58+
{
59+
$optionPayload = [
60+
AttributeOptionInterface::LABEL => 'new color',
61+
AttributeOptionInterface::SORT_ORDER => 100,
62+
AttributeOptionInterface::IS_DEFAULT => true,
63+
AttributeOptionInterface::STORE_LABELS => [
64+
[
65+
AttributeOptionLabelInterface::LABEL => 'DE label',
66+
AttributeOptionLabelInterface::STORE_ID => 1,
67+
],
68+
],
69+
AttributeOptionInterface::VALUE => ''
70+
];
71+
72+
return [
73+
'option_without_value_node' => [
74+
$optionPayload
75+
],
76+
'option_with_value_node_that_starts_with_text' => [
77+
array_merge($optionPayload, [AttributeOptionInterface::VALUE => 'some_text'])
78+
],
79+
'option_with_value_node_that_starts_with_a_number' => [
80+
array_merge($optionPayload, [AttributeOptionInterface::VALUE => '123_some_text'])
81+
],
82+
83+
];
84+
}
85+
86+
/**
87+
* @param $testAttributeCode
88+
* @return array|bool|float|int|string
89+
*/
90+
private function getAttributeOptions($testAttributeCode)
91+
{
92+
$serviceInfo = [
93+
'rest' => [
94+
'resourcePath' => self::RESOURCE_PATH . '/' . $testAttributeCode . '/options',
95+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
96+
],
97+
'soap' => [
98+
'service' => self::SERVICE_NAME,
99+
'serviceVersion' => self::SERVICE_VERSION,
100+
'operation' => self::SERVICE_NAME . 'getItems',
101+
],
102+
];
103+
return $this->_webApiCall($serviceInfo, ['attributeCode' => $testAttributeCode]);
104+
}
105+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Swatches\Model;
7+
8+
use Magento\Catalog\Api\ProductAttributeOptionManagementInterface;
9+
use Magento\Eav\Api\Data\AttributeOptionInterface;
10+
use Magento\Eav\Api\Data\AttributeOptionInterfaceFactory;
11+
12+
/**
13+
* Test add option of swatch attribute
14+
*
15+
*/
16+
class SwatchAttributeOptionAddTest extends \PHPUnit\Framework\TestCase
17+
{
18+
/**
19+
* @var \Magento\Framework\ObjectManagerInterface
20+
*/
21+
private $objectManager;
22+
23+
protected function setUp()
24+
{
25+
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
26+
}
27+
28+
/**
29+
* @magentoAppArea adminhtml
30+
* @magentoDbIsolation enabled
31+
* @magentoDataFixture Magento/Swatches/_files/swatch_attribute.php
32+
*/
33+
public function testSwatchOptionAdd()
34+
{
35+
/** @var \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute */
36+
$attribute = $this->objectManager
37+
->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
38+
->load('color_swatch', 'attribute_code');
39+
$optionsPerAttribute = 3;
40+
41+
$data['options']['option'] = array_reduce(
42+
range(10, $optionsPerAttribute),
43+
function ($values, $index) use ($optionsPerAttribute) {
44+
$values[] = [
45+
'label' => 'option ' . $index,
46+
'value' => 'option_' . $index
47+
];
48+
return $values;
49+
},
50+
[]
51+
);
52+
53+
/** @var AttributeOptionInterface[] $options */
54+
$options = [];
55+
foreach ($data['options']['option'] as $optionData) {
56+
$options[] = $this->objectManager
57+
->get(AttributeOptionInterfaceFactory::class)
58+
->create(['data' => $optionData]);
59+
}
60+
61+
/** @var ProductAttributeOptionManagementInterface $optionManagement */
62+
$optionManagement = $this->objectManager->get(ProductAttributeOptionManagementInterface::class);
63+
foreach ($options as $option) {
64+
$optionManagement->add(
65+
$attribute->getAttributeCode(),
66+
$option
67+
);
68+
}
69+
70+
$items = $optionManagement->getItems($attribute->getAttributeCode());
71+
array_walk(
72+
$items,
73+
function (&$item) {
74+
/** @var AttributeOptionInterface $item */
75+
$item = $item->getLabel();
76+
}
77+
);
78+
foreach ($options as $option) {
79+
$this->assertTrue(in_array($option->getLabel(), $items));
80+
}
81+
}
82+
}

lib/internal/Magento/Framework/Filesystem.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ public function getDirectoryRead($directoryCode, $driverCode = DriverPool::FILE)
7070
return $this->readInstances[$code];
7171
}
7272

73+
/**
74+
* Create an instance of directory with read permissions by path.
75+
*
76+
* @param string $path
77+
* @param string $driverCode
78+
*
79+
* @return \Magento\Framework\Filesystem\Directory\ReadInterface
80+
*
81+
*/
82+
public function getDirectoryReadByPath($path, $driverCode = DriverPool::FILE)
83+
{
84+
return $this->readFactory->create($path, $driverCode);
85+
}
86+
7387
/**
7488
* Create an instance of directory with write permissions
7589
*

lib/internal/Magento/Framework/Test/Unit/FilesystemTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public function testGetDirectoryRead()
4343
$this->assertEquals($dirReadMock, $this->_filesystem->getDirectoryRead(DirectoryList::ROOT));
4444
}
4545

46+
public function testGetDirectoryReadByPath()
47+
{
48+
/** @var \Magento\Framework\Filesystem\Directory\ReadInterface $dirReadMock */
49+
$dirReadMock = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class);
50+
$this->_dirReadFactoryMock->expects($this->once())->method('create')->will($this->returnValue($dirReadMock));
51+
$this->assertEquals($dirReadMock, $this->_filesystem->getDirectoryReadByPath('path/to/some/file'));
52+
}
53+
4654
public function testGetDirectoryWrite()
4755
{
4856
/** @var \Magento\Framework\Filesystem\Directory\WriteInterface $dirWriteMock */

0 commit comments

Comments
 (0)