Skip to content

Commit 3eea437

Browse files
committed
Merge remote-tracking branch 'l3/MC-39619' into PR-L3-20210405
2 parents 6e5e444 + f8ff639 commit 3eea437

File tree

11 files changed

+276
-1
lines changed

11 files changed

+276
-1
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\Bundle\Model\Product;
9+
10+
use Magento\Catalog\Model\Product;
11+
use Magento\Catalog\Model\Product\Type as BundleType;
12+
13+
/**
14+
* Service to check is bundle product has single choice (no customization possible)
15+
*/
16+
class SingleChoiceProvider
17+
{
18+
/**
19+
* Single choice availability
20+
*
21+
* @param Product $product
22+
* @return bool
23+
*/
24+
public function isSingleChoiceAvailable(Product $product) : bool
25+
{
26+
$result = false;
27+
if ($product->getTypeId() === BundleType::TYPE_BUNDLE) {
28+
$typeInstance = $product->getTypeInstance();
29+
$typeInstance->setStoreFilter($product->getStoreId(), $product);
30+
31+
if ($typeInstance->hasRequiredOptions($product)) {
32+
$options = $typeInstance->getOptions($product);
33+
$isNoCustomizations = true;
34+
foreach ($options as $option) {
35+
$optionId = $option->getId();
36+
$required = $option->getRequired();
37+
if ($isNoCustomizations && (int) $required === 1) {
38+
$selectionsCollection = $typeInstance->getSelectionsCollection(
39+
[$optionId],
40+
$product
41+
);
42+
$selections = $selectionsCollection->exportToArray();
43+
if (count($selections) > 1) {
44+
foreach ($selections as $selection) {
45+
if ($isNoCustomizations) {
46+
$isNoCustomizations = (int)$selection['is_default'] === 1
47+
&& (int)$selection['selection_can_change_qty'] === 0;
48+
} else {
49+
break;
50+
}
51+
}
52+
}
53+
} else {
54+
$isNoCustomizations = false;
55+
break;
56+
}
57+
}
58+
59+
$result = $isNoCustomizations;
60+
}
61+
}
62+
return $result;
63+
}
64+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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\Bundle\Plugin\Catalog\Model\Product\Type;
9+
10+
use Magento\Catalog\Model\Product\Type\AbstractType as Subject;
11+
use Magento\Catalog\Model\Product;
12+
use Magento\Catalog\Model\Product\Type;
13+
use Magento\Bundle\Model\Product\SingleChoiceProvider;
14+
15+
/**
16+
* Plugin to add possibility to add bundle product with single option from list
17+
*/
18+
class AbstractType
19+
{
20+
/**
21+
* @var SingleChoiceProvider
22+
*/
23+
private $singleChoiceProvider;
24+
25+
/**
26+
* @param SingleChoiceProvider $singleChoiceProvider
27+
*/
28+
public function __construct(
29+
SingleChoiceProvider $singleChoiceProvider
30+
) {
31+
$this->singleChoiceProvider = $singleChoiceProvider;
32+
}
33+
34+
/**
35+
* Add possibility to add to cart from the list in case of one required option
36+
*
37+
* @param Subject $subject
38+
* @param bool $result
39+
* @param Product $product
40+
* @return bool
41+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
42+
*/
43+
public function afterIsPossibleBuyFromList(Subject $subject, $result, $product)
44+
{
45+
if ($product->getTypeId() === Type::TYPE_BUNDLE) {
46+
$isSingleChoice = $this->singleChoiceProvider->isSingleChoiceAvailable($product);
47+
if ($isSingleChoice === true) {
48+
$result = $isSingleChoice;
49+
}
50+
}
51+
return $result;
52+
}
53+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\Bundle\Plugin\Catalog\ViewModel\Product;
9+
10+
use Magento\Catalog\Model\Product;
11+
use Magento\Catalog\ViewModel\Product\OptionsData as Subject;
12+
use Magento\Catalog\Model\Product\Type;
13+
use Magento\Bundle\Model\Product\SingleChoiceProvider;
14+
15+
/**
16+
* Plugin to add bundle options data
17+
*/
18+
class AddBundleOptionsData
19+
{
20+
/**
21+
* @var SingleChoiceProvider
22+
*/
23+
private $singleChoiceProvider;
24+
25+
/**
26+
* @param SingleChoiceProvider $singleChoiceProvider
27+
*/
28+
public function __construct(
29+
SingleChoiceProvider $singleChoiceProvider
30+
) {
31+
$this->singleChoiceProvider = $singleChoiceProvider;
32+
}
33+
34+
public function afterGetOptionsData(Subject $subject, array $result, Product $product) : array
35+
{
36+
if ($product->getTypeId() === Type::TYPE_BUNDLE) {
37+
if ($this->singleChoiceProvider->isSingleChoiceAvailable($product) === true) {
38+
$typeInstance = $product->getTypeInstance();
39+
$typeInstance->setStoreFilter($product->getStoreId(), $product);
40+
$options = $typeInstance->getOptions($product);
41+
foreach ($options as $option) {
42+
$optionId = $option->getId();
43+
$selectionsCollection = $typeInstance->getSelectionsCollection(
44+
[$optionId],
45+
$product
46+
);
47+
$selections = $selectionsCollection->exportToArray();
48+
$countSelections = count($selections);
49+
foreach ($selections as $selection) {
50+
$name = 'bundle_option[' . $optionId . ']';
51+
if ($countSelections > 1) {
52+
$name .= '[]';
53+
}
54+
$result[] = [
55+
'name' => $name,
56+
'value' => $selection['selection_id']
57+
];
58+
}
59+
}
60+
}
61+
}
62+
return $result;
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="StorefrontAddCategoryBundleProductWithSingleChoiceToCartActionGroup">
12+
<annotations>
13+
<description>Adds a Bundled Product with the single choice to the Cart from the Category page.</description>
14+
</annotations>
15+
<arguments>
16+
<argument name="product"/>
17+
<argument name="quantity" defaultValue="1" type="string"/>
18+
</arguments>
19+
20+
<moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/>
21+
<click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart"/>
22+
<waitForPageLoad stepKey="waitForPageLoad1"/>
23+
<waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/>
24+
<see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{product.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/>
25+
26+
<!--Open minicart and change Qty-->
27+
<scrollToTopOfPage stepKey="scrollToTheTopOfThePage"/>
28+
<waitForElementVisible selector="{{StorefrontMinicartSection.showCart}}" stepKey="waitForElementToBeVisible"/>
29+
<click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/>
30+
<waitForPageLoad stepKey="waitForPageToLoad2"/>
31+
<waitForElementVisible selector="{{StorefrontMinicartSection.quantity}}" stepKey="waitForElementQty"/>
32+
<pressKey selector="{{StorefrontMinicartSection.itemQuantity(product.name)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE]" stepKey="deleteFiled"/>
33+
<fillField selector="{{StorefrontMinicartSection.itemQuantity(product.name)}}" userInput="{{quantity}}" stepKey="changeQty"/>
34+
<conditionalClick selector="{{StorefrontMinicartSection.itemQuantityUpdate(product.name)}}" dependentSelector="{{StorefrontMinicartSection.itemQuantityUpdate(product.name)}}" visible="true" stepKey="updateQty"/>
35+
<waitForAjaxLoad stepKey="waitForAjaxLoad"/>
36+
<waitForText userInput="{{quantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/>
37+
38+
<!-- Close minicart -->
39+
<click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCartToClose"/>
40+
<waitForPageLoad stepKey="waitForPageToLoad3"/>
41+
</actionGroup>
42+
</actionGroups>

app/code/Magento/Bundle/etc/frontend/di.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,10 @@
1616
<type name="Magento\Catalog\Model\Product">
1717
<plugin name="add_bundle_child_identities" type="Magento\Bundle\Model\Plugin\Frontend\ProductIdentitiesExtender" sortOrder="100"/>
1818
</type>
19+
<type name="Magento\Catalog\Model\Product\Type\AbstractType">
20+
<plugin name="add_to_cart_single_option" type="Magento\Bundle\Plugin\Catalog\Model\Product\Type\AbstractType" />
21+
</type>
22+
<type name="Magento\Catalog\ViewModel\Product\OptionsData">
23+
<plugin name="add_bundle_options_data" type="Magento\Bundle\Plugin\Catalog\ViewModel\Product\AddBundleOptionsData" />
24+
</type>
1925
</config>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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\ViewModel\Product;
9+
10+
use Magento\Framework\View\Element\Block\ArgumentInterface;
11+
use Magento\Catalog\Model\Product;
12+
13+
/**
14+
* Product options data view model
15+
*/
16+
class OptionsData implements ArgumentInterface
17+
{
18+
/**
19+
* Returns options data array
20+
*
21+
* @param Product $product
22+
* @return array
23+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
24+
*/
25+
public function getOptionsData(Product $product) : array
26+
{
27+
return [];
28+
}
29+
}

app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,10 @@
5454
</arguments>
5555
<block class="Magento\Catalog\Block\Category\Rss\Link" name="rss.link" template="Magento_Catalog::category/rss.phtml"/>
5656
</referenceBlock>
57+
<referenceBlock name="category.products.list">
58+
<arguments>
59+
<argument name="viewModel" xsi:type="object">Magento\Catalog\ViewModel\Product\OptionsData</argument>
60+
</arguments>
61+
</referenceBlock>
5762
</body>
5863
</page>

app/code/Magento/Catalog/view/frontend/templates/product/list.phtml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ $_helper = $block->getData('outputHelper');
8787
data-product-sku="<?= $escaper->escapeHtml($_product->getSku()) ?>"
8888
action="<?= $escaper->escapeUrl($postParams['action']) ?>"
8989
method="post">
90+
<?php $optionsData = $block->getData('viewModel')->getOptionsData($_product); ?>
91+
<?php foreach ($optionsData as $optionItem): ?>
92+
<input type="hidden"
93+
name="<?= $escaper->escapeHtml($optionItem['name']) ?>"
94+
value="<?= $escaper->escapeHtml($optionItem['value']) ?>">
95+
<?php endforeach; ?>
9096
<input type="hidden"
9197
name="product"
9298
value="<?= /* @noEscape */ $postParams['data']['product'] ?>">

app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_advanced_result.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,10 @@
3636
<action method="setListCollection"/>
3737
</block>
3838
</referenceContainer>
39+
<referenceBlock name="search_result_list">
40+
<arguments>
41+
<argument name="viewModel" xsi:type="object">Magento\Catalog\ViewModel\Product\OptionsData</argument>
42+
</arguments>
43+
</referenceBlock>
3944
</body>
4045
</page>

app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
positions:list-secondary,grid-secondary,list-actions,grid-actions,list-primary,grid-primary
1717
-->
1818
<argument name="positioned" xsi:type="string">positions:list-secondary</argument>
19+
<argument name="viewModel" xsi:type="object">Magento\Catalog\ViewModel\Product\OptionsData</argument>
1920
</arguments>
2021
<block class="Magento\Catalog\Block\Product\ProductList\Toolbar" name="product_list_toolbar" template="Magento_Catalog::product/list/toolbar.phtml">
2122
<block class="Magento\Theme\Block\Html\Pager" name="product_list_toolbar_pager"/>

0 commit comments

Comments
 (0)