Skip to content

Commit 0982a53

Browse files
authored
Merge pull request #3827 from magento-tsg/2.2-develop-pr80
[TSG] Backporting for 2.2 (pr80) (2.2-develop)
2 parents 9f04388 + 2ece592 commit 0982a53

File tree

44 files changed

+1078
-249
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1078
-249
lines changed

app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@ public function __construct(
2828
}
2929

3030
/**
31+
* Perform action on relation/extension attribute.
32+
*
3133
* @param object $entity
3234
* @param array $arguments
3335
* @return \Magento\Catalog\Api\Data\ProductInterface|object
3436
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
3537
*/
3638
public function execute($entity, $arguments = [])
3739
{
40+
if ($entity->getOptionsSaved()) {
41+
return $entity;
42+
}
43+
3844
$options = $entity->getOptions();
3945
$optionIds = [];
4046

app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
namespace Magento\Catalog\Model\ResourceModel;
1010

1111
use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
12+
use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend;
13+
use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend;
14+
use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
15+
use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface;
1216

1317
/**
1418
* Catalog entity abstract model
@@ -39,16 +43,18 @@ abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity
3943
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
4044
* @param \Magento\Catalog\Model\Factory $modelFactory
4145
* @param array $data
46+
* @param UniqueValidationInterface|null $uniqueValidator
4247
*/
4348
public function __construct(
4449
\Magento\Eav\Model\Entity\Context $context,
4550
\Magento\Store\Model\StoreManagerInterface $storeManager,
4651
\Magento\Catalog\Model\Factory $modelFactory,
47-
$data = []
52+
$data = [],
53+
UniqueValidationInterface $uniqueValidator = null
4854
) {
4955
$this->_storeManager = $storeManager;
5056
$this->_modelFactory = $modelFactory;
51-
parent::__construct($context, $data);
57+
parent::__construct($context, $data, $uniqueValidator);
5258
}
5359

5460
/**
@@ -88,14 +94,14 @@ protected function _isApplicableAttribute($object, $attribute)
8894
/**
8995
* Check whether attribute instance (attribute, backend, frontend or source) has method and applicable
9096
*
91-
* @param AbstractAttribute|\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend|\Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend|\Magento\Eav\Model\Entity\Attribute\Source\AbstractSource $instance
97+
* @param AbstractAttribute|AbstractBackend|AbstractFrontend|AbstractSource $instance
9298
* @param string $method
9399
* @param array $args array of arguments
94100
* @return boolean
95101
*/
96102
protected function _isCallableAttributeInstance($instance, $method, $args)
97103
{
98-
if ($instance instanceof \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
104+
if ($instance instanceof AbstractBackend
99105
&& ($method == 'beforeSave' || $method == 'afterSave')
100106
) {
101107
$attributeCode = $instance->getAttribute()->getAttributeCode();

app/code/Magento/Catalog/Model/ResourceModel/Product.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Magento\Catalog\Model\ResourceModel\Product\Website\Link as ProductWebsiteLink;
99
use Magento\Framework\App\ObjectManager;
1010
use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer;
11+
use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface;
1112

1213
/**
1314
* Product entity resource model
@@ -101,6 +102,7 @@ class Product extends AbstractResource
101102
* @param \Magento\Catalog\Model\Product\Attribute\DefaultAttributes $defaultAttributes
102103
* @param array $data
103104
* @param TableMaintainer|null $tableMaintainer
105+
* @param UniqueValidationInterface|null $uniqueValidator
104106
*
105107
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
106108
*/
@@ -115,7 +117,8 @@ public function __construct(
115117
\Magento\Eav\Model\Entity\TypeFactory $typeFactory,
116118
\Magento\Catalog\Model\Product\Attribute\DefaultAttributes $defaultAttributes,
117119
$data = [],
118-
TableMaintainer $tableMaintainer = null
120+
TableMaintainer $tableMaintainer = null,
121+
UniqueValidationInterface $uniqueValidator = null
119122
) {
120123
$this->_categoryCollectionFactory = $categoryCollectionFactory;
121124
$this->_catalogCategory = $catalogCategory;
@@ -127,7 +130,8 @@ public function __construct(
127130
$context,
128131
$storeManager,
129132
$modelFactory,
130-
$data
133+
$data,
134+
$uniqueValidator
131135
);
132136
$this->connectionName = 'catalog';
133137
$this->tableMaintainer = $tableMaintainer ?: ObjectManager::getInstance()->get(TableMaintainer::class);
@@ -289,7 +293,7 @@ protected function _afterSave(\Magento\Framework\DataObject $product)
289293
}
290294

291295
/**
292-
* {@inheritdoc}
296+
* @inheritdoc
293297
*/
294298
public function delete($object)
295299
{
@@ -575,7 +579,7 @@ public function countAll()
575579
}
576580

577581
/**
578-
* {@inheritdoc}
582+
* @inheritdoc
579583
*/
580584
public function validate($object)
581585
{
@@ -615,7 +619,7 @@ public function load($object, $entityId, $attributes = [])
615619
}
616620

617621
/**
618-
* {@inheritdoc}
622+
* @inheritdoc
619623
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
620624
* @since 101.0.0
621625
*/
@@ -657,6 +661,8 @@ public function save(\Magento\Framework\Model\AbstractModel $object)
657661
}
658662

659663
/**
664+
* Retrieve entity manager object.
665+
*
660666
* @return \Magento\Framework\EntityManager\EntityManager
661667
*/
662668
private function getEntityManager()
@@ -669,6 +675,8 @@ private function getEntityManager()
669675
}
670676

671677
/**
678+
* Retrieve ProductWebsiteLink object.
679+
*
672680
* @deprecated 101.1.0
673681
* @return ProductWebsiteLink
674682
*/
@@ -678,6 +686,8 @@ private function getProductWebsiteLink()
678686
}
679687

680688
/**
689+
* Retrieve CategoryLink object.
690+
*
681691
* @deprecated 101.1.0
682692
* @return \Magento\Catalog\Model\ResourceModel\Product\CategoryLink
683693
*/
@@ -692,9 +702,10 @@ private function getProductCategoryLink()
692702

693703
/**
694704
* Extends parent method to be appropriate for product.
705+
*
695706
* Store id is required to correctly identify attribute value we are working with.
696707
*
697-
* {@inheritdoc}
708+
* @inheritdoc
698709
* @since 101.1.0
699710
*/
700711
protected function getAttributeRow($entity, $object, $attribute)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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\Plugin\Model\Product\Option;
9+
10+
/**
11+
* Plugin for updating product 'has_options' and 'required_options' attributes.
12+
*/
13+
class UpdateProductCustomOptionsAttributes
14+
{
15+
/**
16+
* @var \Magento\Catalog\Api\ProductRepositoryInterface
17+
*/
18+
private $productRepository;
19+
20+
/**
21+
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
22+
*/
23+
public function __construct(\Magento\Catalog\Api\ProductRepositoryInterface $productRepository)
24+
{
25+
$this->productRepository = $productRepository;
26+
}
27+
28+
/**
29+
* Update product 'has_options' and 'required_options' attributes after option save.
30+
*
31+
* @param \Magento\Catalog\Api\ProductCustomOptionRepositoryInterface $subject
32+
* @param \Magento\Catalog\Api\Data\ProductCustomOptionInterface $option
33+
*
34+
* @return \Magento\Catalog\Api\Data\ProductCustomOptionInterface
35+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
36+
*/
37+
public function afterSave(
38+
\Magento\Catalog\Api\ProductCustomOptionRepositoryInterface $subject,
39+
\Magento\Catalog\Api\Data\ProductCustomOptionInterface $option
40+
) {
41+
$product = $this->productRepository->get($option->getProductSku());
42+
if (!$product->getHasOptions()
43+
|| ($option->getIsRequire()
44+
&& !$product->getRequiredOptions())
45+
) {
46+
$product->setCanSaveCustomOptions(true);
47+
$product->setOptionsSaved(true);
48+
$optionId = $option->getOptionId();
49+
$currentOptions = array_filter($product->getOptions(), function ($optionItem) use ($optionId) {
50+
return $optionId != $optionItem->getOptionId();
51+
});
52+
$currentOptions[] = $option;
53+
$product->setOptions($currentOptions);
54+
$product->save();
55+
}
56+
57+
return $option;
58+
}
59+
}

app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,24 @@
77
-->
88

99
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10-
xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd">
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
1111
<!--Action to delete product attribute-->
1212
<actionGroup name="DeleteProductAttribute">
1313
<arguments>
1414
<argument name="productAttribute"/>
1515
</arguments>
1616
<amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributesGridPage"/>
1717
<waitForPageLoad time="30" stepKey="waitForProductAttributesGridPageLoad"/>
18-
<click selector="{{AdminProductAttributeGridSection.resetFilter}}" stepKey="resetFilter"/>
18+
<conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersBeforeDelete"/>
1919
<fillField selector="{{AdminProductAttributeGridSection.gridFilterFrontEndLabel}}"
2020
userInput="{{productAttribute.default_label}}" stepKey="fillAttributeDefaultLabelInput"/>
21-
<click selector="{{AdminProductAttributeGridSection.search}}" stepKey="searchForAttribute"/>
21+
<click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="applyFilters"/>
2222
<click selector="{{AdminProductAttributeGridSection.firstRow}}" stepKey="clickFirstRow"/>
2323
<waitForPageLoad time="30" stepKey="waitForPageLoad"/>
24-
<click selector="{{AdminProductAttributeEditSection.deleteAttribute}}" stepKey="deleteProductAttribute"/>
24+
<click selector="{{AdminMainActionsSection.delete}}" stepKey="deleteProductAttribute"/>
2525
<waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitingForWarningModal"/>
26-
<click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreDelete"/>
26+
<click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/>
27+
<see selector="{{AdminMessagesSection.success}}" userInput="You deleted the product attribute." stepKey="seeSuccessMessage"/>
2728
</actionGroup>
2829

2930
<actionGroup name="navigateToProductAttribute">

app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,38 @@
7474
<click selector="{{AdminDataGridTableSection.row('1')}}" stepKey="clickOnAttributeRow"/>
7575
<waitForPageLoad stepKey="waitForAttributePageLoad" />
7676
</actionGroup>
77+
<actionGroup name="StartCreateProductAttribute">
78+
<arguments>
79+
<argument name="attributeCode" type="string"/>
80+
<argument name="attributeType" type="string" defaultValue="select"/>
81+
</arguments>
82+
<amOnPage url="{{AdminProductAttributeNewPage.url}}" stepKey="goToNewProductAttributePage"/>
83+
<fillField selector="{{AttributePropertiesSection.defaultLabel}}" userInput="{{attributeCode}}" stepKey="fillDefaultLabel"/>
84+
<selectOption selector="{{AttributePropertiesSection.inputType}}" userInput="{{attributeType}}" stepKey="selectInputType"/>
85+
<waitForElementVisible selector="{{AdminNewAttributePanelSection.addOption}}" stepKey="waitForElementVisible"/>
86+
</actionGroup>
87+
<actionGroup name="AddOptionToProductAttribute">
88+
<arguments>
89+
<argument name="optionName" type="string"/>
90+
<argument name="optionNumber" type="string"/>
91+
</arguments>
92+
<click selector="{{AdminNewAttributePanelSection.addOption}}" stepKey="clickAddOption"/>
93+
<waitForElementVisible selector="{{AdminNewAttributePanelSection.optionAdminValue('optionNumber')}}" time="30" stepKey="waitForOptionRow"/>
94+
<fillField selector="{{AdminNewAttributePanelSection.optionAdminValue('optionNumber')}}" userInput="{{optionName}}" stepKey="fillAdminLabel"/>
95+
</actionGroup>
96+
<actionGroup name="SetScopeToProductAttribute">
97+
<arguments>
98+
<argument name="scope" type="string" defaultValue="1"/>
99+
</arguments>
100+
<click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/>
101+
<selectOption selector="{{AttributePropertiesSection.scope}}" userInput="{{scope}}" stepKey="selectGlobalScope"/>
102+
</actionGroup>
103+
<actionGroup name="SetUseInLayeredNavigationToProductAttribute">
104+
<arguments>
105+
<argument name="useInLayeredNavigation" type="string" defaultValue="1"/>
106+
</arguments>
107+
<scrollToTopOfPage stepKey="scrollToTop"/>
108+
<click selector="{{StorefrontPropertiesSection.storefrontPropertiesTab}}" stepKey="goToStorefrontProperties"/>
109+
<selectOption selector="{{AttributePropertiesSection.useInLayeredNavigation}}" userInput="{{useInLayeredNavigation}}" stepKey="selectUseInLayeredNavigation"/>
110+
</actionGroup>
77111
</actionGroups>

app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<entity name="ProductAttributeFrontendLabelThree" type="FrontendLabel">
1717
<data key="store_id">0</data>
1818
<data key="label" unique="suffix">attributeThree</data>
19+
<data key="default_label" unique="suffix">attributeThree</data>
1920
</entity>
2021
<entity name="ColorAttributeFrontandLabel" type="FrontendLabel">
2122
<data key="store_id">0</data>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd">
10+
<page name="AdminProductAttributeNewPage" url="catalog/product_attribute/new/" area="admin" module="Magento_Catalog">
11+
<section name="AttributePropertiesSection"/>
12+
<section name="StorefrontPropertiesSection"/>
13+
<section name="AdvancedAttributePropertiesSection"/>
14+
<section name="AdminAttributeOptionsSection"/>
15+
<section name="AttributeManageSwatchSection"/>
16+
</page>
17+
</pages>

app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<element name="attributeSetDropDown" type="select" selector="div[data-index='attribute_set_id'] .action-select.admin__action-multiselect"/>
4141
<element name="requiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]&gt;.admin__field-label span'), ':after').getPropertyValue('content');"/>
4242
<element name="requiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]&gt;.admin__field-label span'), ':after').getPropertyValue('content');"/>
43+
<element name="customAttributeDropdownField" type="select" selector="select[name='product[{{attributeCode}}]']" parameterized="true"/>
4344
</section>
4445
<section name="ProductInWebsitesSection">
4546
<element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/>
@@ -180,6 +181,7 @@
180181
<element name="applySinglePriceToAllSkus" type="radio" selector=".admin__field-label[for='apply-single-price-radio']"/>
181182
<element name="singlePrice" type="input" selector="#apply-single-price-input"/>
182183
<element name="attributeByName" type="input" selector="//label[text()='{{var}}']/preceding-sibling::input" parameterized="true"/>
184+
<element name="checkboxByName" type="input" selector="//div[text()='{{var}}']//ancestor::tr//input" parameterized="true"/>
183185
</section>
184186
<section name="AdminNewAttributePanel">
185187
<element name="saveAttribute" type="button" selector="#save" timeout="30"/>

app/code/Magento/Catalog/etc/webapi_rest/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@
2020
<plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/>
2121
<plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/>
2222
</type>
23+
<type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface">
24+
<plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/>
25+
</type>
2326
</config>

app/code/Magento/Catalog/etc/webapi_soap/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@
1919
<plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/>
2020
<plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/>
2121
</type>
22+
<type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface">
23+
<plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/>
24+
</type>
2225
</config>

0 commit comments

Comments
 (0)