Skip to content
This repository was archived by the owner on Dec 19, 2019. It is now read-only.

Commit fffa041

Browse files
authored
Merge pull request #5098 from magento-mpi/PR-2019-12-10
MPI Bug Fix 2.3
2 parents f2af84c + 8a135c0 commit fffa041

File tree

32 files changed

+1087
-359
lines changed

32 files changed

+1087
-359
lines changed

app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
namespace Magento\Catalog\Controller\Adminhtml\Product\Initialization;
88

9-
use DateTime;
109
use Magento\Backend\Helper\Js;
1110
use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory as CustomOptionFactory;
1211
use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory as ProductLinkFactory;
@@ -15,6 +14,8 @@
1514
use Magento\Catalog\Api\ProductRepositoryInterface\Proxy as ProductRepository;
1615
use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter;
1716
use Magento\Catalog\Model\Product;
17+
use Magento\Catalog\Model\Product\Authorization as ProductAuthorization;
18+
use Magento\Catalog\Model\Product\Filter\DateTime as DateTimeFilter;
1819
use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks;
1920
use Magento\Catalog\Model\Product\Link\Resolver as LinkResolver;
2021
use Magento\Catalog\Model\Product\LinkTypeProvider;
@@ -24,13 +25,13 @@
2425
use Magento\Framework\Stdlib\DateTime\Filter\Date;
2526
use Magento\Store\Model\StoreManagerInterface;
2627
use Zend_Filter_Input;
27-
use Magento\Catalog\Model\Product\Authorization as ProductAuthorization;
2828

2929
/**
3030
* Product helper
3131
*
3232
* @api
3333
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
34+
* @SuppressWarnings(PHPMD.TooManyFields)
3435
* @since 100.0.2
3536
*/
3637
class Helper
@@ -89,11 +90,6 @@ class Helper
8990
*/
9091
private $linkResolver;
9192

92-
/**
93-
* @var \Magento\Framework\Stdlib\DateTime\Filter\DateTime
94-
*/
95-
private $dateTimeFilter;
96-
9793
/**
9894
* @var LinkTypeProvider
9995
*/
@@ -114,6 +110,11 @@ class Helper
114110
*/
115111
private $localeFormat;
116112

113+
/**
114+
* @var DateTimeFilter
115+
*/
116+
private $dateTimeFilter;
117+
117118
/**
118119
* Constructor
119120
*
@@ -130,6 +131,7 @@ class Helper
130131
* @param AttributeFilter|null $attributeFilter
131132
* @param FormatInterface|null $localeFormat
132133
* @param ProductAuthorization|null $productAuthorization
134+
* @param DateTimeFilter|null $dateTimeFilter
133135
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
134136
*/
135137
public function __construct(
@@ -145,7 +147,8 @@ public function __construct(
145147
LinkTypeProvider $linkTypeProvider = null,
146148
AttributeFilter $attributeFilter = null,
147149
FormatInterface $localeFormat = null,
148-
?ProductAuthorization $productAuthorization = null
150+
?ProductAuthorization $productAuthorization = null,
151+
?DateTimeFilter $dateTimeFilter = null
149152
) {
150153
$this->request = $request;
151154
$this->storeManager = $storeManager;
@@ -162,6 +165,7 @@ public function __construct(
162165
$this->attributeFilter = $attributeFilter ?: $objectManager->get(AttributeFilter::class);
163166
$this->localeFormat = $localeFormat ?: $objectManager->get(FormatInterface::class);
164167
$this->productAuthorization = $productAuthorization ?? $objectManager->get(ProductAuthorization::class);
168+
$this->dateTimeFilter = $dateTimeFilter ?? $objectManager->get(DateTimeFilter::class);
165169
}
166170

167171
/**
@@ -185,7 +189,6 @@ public function initializeFromData(Product $product, array $productData)
185189
}
186190

187191
$productData = $this->normalize($productData);
188-
$productData = $this->convertSpecialFromDateStringToObject($productData);
189192

190193
if (!empty($productData['is_downloadable'])) {
191194
$productData['product_has_weight'] = 0;
@@ -209,7 +212,7 @@ public function initializeFromData(Product $product, array $productData)
209212
foreach ($attributes as $attrKey => $attribute) {
210213
if ($attribute->getBackend()->getType() == 'datetime') {
211214
if (array_key_exists($attrKey, $productData) && $productData[$attrKey] != '') {
212-
$dateFieldFilters[$attrKey] = $this->getDateTimeFilter();
215+
$dateFieldFilters[$attrKey] = $this->dateTimeFilter;
213216
}
214217
}
215218
}
@@ -408,22 +411,6 @@ private function getLinkResolver()
408411
return $this->linkResolver;
409412
}
410413

411-
/**
412-
* Get DateTimeFilter instance
413-
*
414-
* @return \Magento\Framework\Stdlib\DateTime\Filter\DateTime
415-
* @deprecated 101.0.0
416-
*/
417-
private function getDateTimeFilter()
418-
{
419-
if ($this->dateTimeFilter === null) {
420-
$this->dateTimeFilter = ObjectManager::getInstance()
421-
->get(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class);
422-
}
423-
424-
return $this->dateTimeFilter;
425-
}
426-
427414
/**
428415
* Remove ids of non selected websites from $websiteIds array and return filtered data
429416
*
@@ -497,20 +484,4 @@ function ($valueData) {
497484

498485
return $product->setOptions($customOptions);
499486
}
500-
501-
/**
502-
* Convert string date presentation into object
503-
*
504-
* @param array $productData
505-
* @return array
506-
*/
507-
private function convertSpecialFromDateStringToObject($productData)
508-
{
509-
if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') {
510-
$productData['special_from_date'] = $this->getDateTimeFilter()->filter($productData['special_from_date']);
511-
$productData['special_from_date'] = new DateTime($productData['special_from_date']);
512-
}
513-
514-
return $productData;
515-
}
516487
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Product\Filter;
9+
10+
use Magento\Framework\Stdlib\DateTime as StdlibDateTime;
11+
use Magento\Framework\Stdlib\DateTime\Filter\DateTime as StdlibDateTimeFilter;
12+
13+
/**
14+
* Product datetime fields values filter
15+
*/
16+
class DateTime implements \Zend_Filter_Interface
17+
{
18+
/**
19+
* @var StdlibDateTimeFilter
20+
*/
21+
private $stdlibDateTimeFilter;
22+
23+
/**
24+
* Initializes dependencies.
25+
*
26+
* @param StdlibDateTimeFilter $stdlibDateTimeFilter
27+
*/
28+
public function __construct(StdlibDateTimeFilter $stdlibDateTimeFilter)
29+
{
30+
$this->stdlibDateTimeFilter = $stdlibDateTimeFilter;
31+
}
32+
33+
/**
34+
* Convert datetime from locale format to internal format;
35+
*
36+
* Make an additional check for MySql date format which is wrongly parsed by IntlDateFormatter
37+
*
38+
* @param mixed $value
39+
* @return mixed|string
40+
* @throws \Exception
41+
*/
42+
public function filter($value)
43+
{
44+
if (is_string($value)) {
45+
$value = $this->createDateFromMySqlFormat($value) ?? $value;
46+
}
47+
return $this->stdlibDateTimeFilter->filter($value);
48+
}
49+
50+
/**
51+
* Parse a string in MySql date format into a new DateTime object
52+
*
53+
* @param string $value
54+
* @return \DateTime|null
55+
*/
56+
private function createDateFromMySqlFormat(string $value): ?\DateTime
57+
{
58+
$datetime = date_create_from_format(StdlibDateTime::DATETIME_PHP_FORMAT, $value);
59+
if ($datetime === false) {
60+
$datetime = date_create_from_format(StdlibDateTime::DATE_PHP_FORMAT, $value);
61+
if ($datetime !== false) {
62+
$datetime->setTime(0, 0, 0, 0);
63+
}
64+
}
65+
return $datetime instanceof \DateTime ? $datetime : null;
66+
}
67+
}

app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ protected function setUp()
165165
->disableOriginalConstructor()
166166
->getMock();
167167

168+
$this->dateTimeFilterMock = $this->createMock(\Magento\Catalog\Model\Product\Filter\DateTime::class);
169+
168170
$this->helper = $this->objectManager->getObject(
169171
Helper::class,
170172
[
@@ -178,6 +180,7 @@ protected function setUp()
178180
'linkTypeProvider' => $this->linkTypeProviderMock,
179181
'attributeFilter' => $this->attributeFilterMock,
180182
'localeFormat' => $this->localeFormatMock,
183+
'dateTimeFilter' => $this->dateTimeFilterMock
181184
]
182185
);
183186

@@ -188,11 +191,6 @@ protected function setUp()
188191
$resolverProperty = $helperReflection->getProperty('linkResolver');
189192
$resolverProperty->setAccessible(true);
190193
$resolverProperty->setValue($this->helper, $this->linkResolverMock);
191-
192-
$this->dateTimeFilterMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class);
193-
$dateTimeFilterProperty = $helperReflection->getProperty('dateTimeFilter');
194-
$dateTimeFilterProperty->setAccessible(true);
195-
$dateTimeFilterProperty->setValue($this->helper, $this->dateTimeFilterMock);
196194
}
197195

198196
/**
@@ -226,6 +224,7 @@ public function testInitialize(
226224
];
227225
$specialFromDate = '2018-03-03 19:30:00';
228226
$productData = [
227+
'name' => 'Simple Product',
229228
'stock_data' => ['stock_data'],
230229
'options' => $optionsData,
231230
'website_ids' => $websiteIds,
@@ -235,30 +234,23 @@ public function testInitialize(
235234
$productData = array_merge($productData, ['tier_price' => $tierPrice]);
236235
}
237236

238-
$this->dateTimeFilterMock->expects($this->once())
237+
$this->dateTimeFilterMock
238+
->expects($this->once())
239239
->method('filter')
240-
->with($specialFromDate)
241-
->willReturn($specialFromDate);
242-
243-
$attributeNonDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
244-
->disableOriginalConstructor()
245-
->getMock();
246-
$attributeDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
247-
->disableOriginalConstructor()
248-
->getMock();
249-
250-
$attributeNonDateBackEnd =
251-
$this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\DefaultBackend::class)
252-
->disableOriginalConstructor()
253-
->getMock();
254-
$attributeDateBackEnd = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\Datetime::class)
255-
->disableOriginalConstructor()
256-
->getMock();
240+
->willReturnArgument(0);
257241

258-
$attributeNonDate->expects($this->any())->method('getBackend')->willReturn($attributeNonDateBackEnd);
259-
$attributeDate->expects($this->any())->method('getBackend')->willReturn($attributeDateBackEnd);
260-
$attributeNonDateBackEnd->expects($this->any())->method('getType')->willReturn('non-datetime');
261-
$attributeDateBackEnd->expects($this->any())->method('getType')->willReturn('datetime');
242+
$this->setProductAttributes(
243+
[
244+
[
245+
'code' => 'name',
246+
'backend_type' => 'varchar',
247+
],
248+
[
249+
'code' => 'special_from_date',
250+
'backend_type' => 'datetime',
251+
]
252+
]
253+
);
262254

263255
$useDefaults = ['attributeCode1', 'attributeCode2'];
264256

@@ -274,8 +266,6 @@ public function testInitialize(
274266
$this->productMock->expects($this->once())->method('isLockedAttribute')->with('media')->willReturn(true);
275267
$this->productMock->expects($this->once())->method('unlockAttribute')->with('media');
276268
$this->productMock->expects($this->once())->method('lockAttribute')->with('media');
277-
$this->productMock->expects($this->once())->method('getAttributes')
278-
->willReturn([$attributeNonDate, $attributeDate]);
279269
$this->productMock->expects($this->any())->method('getSku')->willReturn('sku');
280270
$this->productMock->expects($this->any())->method('getOptionsReadOnly')->willReturn(false);
281271

@@ -348,7 +338,35 @@ function () {
348338
}
349339

350340
$this->assertEquals($expectedLinks, $resultLinks);
351-
$this->assertEquals($specialFromDate, $productData['special_from_date']);
341+
$this->assertEquals($specialFromDate, $this->productMock->getSpecialFromDate());
342+
}
343+
344+
/**
345+
* Mock product attributes
346+
*
347+
* @param array $attributes
348+
*/
349+
private function setProductAttributes(array $attributes): void
350+
{
351+
$attributesModels = [];
352+
foreach ($attributes as $attribute) {
353+
$attributeModel = $this->createMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class);
354+
$backendModel = $attribute['backend_model']
355+
?? $this->createMock(\Magento\Eav\Model\Entity\Attribute\Backend\DefaultBackend::class);
356+
$attributeModel->expects($this->any())
357+
->method('getBackend')
358+
->willReturn($backendModel);
359+
$attributeModel->expects($this->any())
360+
->method('getAttributeCode')
361+
->willReturn($attribute['code']);
362+
$backendModel->expects($this->any())
363+
->method('getType')
364+
->willReturn($attribute['backend_type']);
365+
$attributesModels[$attribute['code']] = $attributeModel;
366+
}
367+
$this->productMock->expects($this->once())
368+
->method('getAttributes')
369+
->willReturn($attributesModels);
352370
}
353371

354372
/**

0 commit comments

Comments
 (0)