Skip to content

Commit 001aafe

Browse files
Merge branch '1.1-develop' of github.com:magento/magento2-page-builder into PB-219
2 parents b2ba423 + d66384f commit 001aafe

File tree

20 files changed

+1314
-50
lines changed

20 files changed

+1314
-50
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\PageBuilder\Controller\Adminhtml\Form\Element;
10+
11+
use Exception;
12+
use Magento\Backend\App\Action;
13+
use Magento\Backend\App\Action\Context;
14+
use Magento\Framework\App\Action\HttpPostActionInterface;
15+
use Magento\Framework\Controller\Result\JsonFactory;
16+
17+
/**
18+
* Returns the number of products that match the provided conditions
19+
*/
20+
class ProductTotals extends Action implements HttpPostActionInterface
21+
{
22+
const ADMIN_RESOURCE = 'Magento_Catalog::products';
23+
24+
/**
25+
* @var \Magento\PageBuilder\Model\Catalog\ProductTotals
26+
*/
27+
private $productTotals;
28+
29+
/**
30+
* @var JsonFactory
31+
*/
32+
private $jsonFactory;
33+
34+
/**
35+
* @param Context $context
36+
* @param \Magento\PageBuilder\Model\Catalog\ProductTotals $productTotals
37+
* @param JsonFactory $jsonFactory
38+
*/
39+
public function __construct(
40+
Context $context,
41+
\Magento\PageBuilder\Model\Catalog\ProductTotals $productTotals,
42+
JsonFactory $jsonFactory
43+
) {
44+
$this->jsonFactory = $jsonFactory;
45+
$this->productTotals = $productTotals;
46+
parent::__construct($context);
47+
}
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
public function execute()
53+
{
54+
$conditions = $this->getRequest()->getParam('conditionValue');
55+
56+
try {
57+
$totals = $this->productTotals->getProductTotals($conditions);
58+
$response = [
59+
'total' => $totals['total'],
60+
'disabled' => $totals['disabled'],
61+
'notVisible' => $totals['notVisible'],
62+
'outOfStock' => $totals['outOfStock'],
63+
];
64+
} catch (Exception $e) {
65+
$response = [
66+
'total' => 0,
67+
'disabled' => 0,
68+
'notVisible' => 0,
69+
'outOfStock' => 0,
70+
];
71+
}
72+
73+
return $this->jsonFactory->create()->setData($response);
74+
}
75+
}
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\PageBuilder\Model\Catalog;
10+
11+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
12+
use Magento\Catalog\Model\Product\Visibility;
13+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
14+
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
15+
use Magento\CatalogInventory\Helper\Stock;
16+
use Magento\CatalogWidget\Model\Rule;
17+
use Magento\Framework\Exception\LocalizedException;
18+
use Magento\Rule\Model\Condition\Combine;
19+
use Magento\Rule\Model\Condition\Sql\Builder;
20+
use Magento\Widget\Helper\Conditions;
21+
use Zend_Db_Select_Exception;
22+
23+
/**
24+
* Product totals for Products content type
25+
*/
26+
class ProductTotals
27+
{
28+
/**
29+
* @var CollectionFactory
30+
*/
31+
private $productCollectionFactory;
32+
33+
/**
34+
* @var Builder
35+
*/
36+
private $sqlBuilder;
37+
38+
/**
39+
* @var Rule
40+
*/
41+
private $rule;
42+
43+
/**
44+
* @var Conditions
45+
*/
46+
private $conditionsHelper;
47+
48+
/**
49+
* @var Stock
50+
*/
51+
private $stockFilter;
52+
53+
/**
54+
* @param CollectionFactory $productCollectionFactory
55+
* @param Builder $sqlBuilder
56+
* @param Rule $rule
57+
* @param Conditions $conditionsHelper
58+
* @param Stock $stockFilter
59+
*/
60+
public function __construct(
61+
CollectionFactory $productCollectionFactory,
62+
Builder $sqlBuilder,
63+
Rule $rule,
64+
Conditions $conditionsHelper,
65+
Stock $stockFilter
66+
) {
67+
$this->productCollectionFactory = $productCollectionFactory;
68+
$this->sqlBuilder = $sqlBuilder;
69+
$this->rule = $rule;
70+
$this->conditionsHelper = $conditionsHelper;
71+
$this->stockFilter = $stockFilter;
72+
}
73+
74+
/**
75+
* Decode the provided conditions
76+
*
77+
* @param string $conditions
78+
* @return Combine
79+
*/
80+
private function decodeConditions(string $conditions): Combine
81+
{
82+
if ($conditions) {
83+
$conditions = $this->conditionsHelper->decode($conditions);
84+
}
85+
86+
foreach ($conditions as $key => $condition) {
87+
if (!empty($condition['attribute'])
88+
&& in_array($condition['attribute'], ['special_from_date', 'special_to_date'])
89+
) {
90+
$conditions[$key]['value'] = date('Y-m-d H:i:s', strtotime($condition['value']));
91+
}
92+
}
93+
94+
$this->rule->loadPost(['conditions' => $conditions]);
95+
return $this->rule->getConditions();
96+
}
97+
98+
/**
99+
* Retrieve product collection based on provided conditions
100+
*
101+
* @param string $conditions
102+
* @return Collection
103+
* @throws LocalizedException
104+
*/
105+
private function getProductCollection(string $conditions): Collection
106+
{
107+
/** @var $collection Collection */
108+
$collection = $this->productCollectionFactory->create();
109+
110+
/** @var Combine $collectionConditions */
111+
$collectionConditions = $this->decodeConditions($conditions);
112+
$collectionConditions->collectValidatedAttributes($collection);
113+
$this->sqlBuilder->attachConditionToCollection($collection, $collectionConditions);
114+
115+
/**
116+
* Prevent retrieval of duplicate records. This may occur when multiselect product attribute matches
117+
* several allowed values from condition simultaneously
118+
*/
119+
$collection->distinct(true);
120+
121+
return $collection;
122+
}
123+
124+
/**
125+
* Retrieve count of all disabled products
126+
*
127+
* @param Collection $baseCollection
128+
* @return int number of disabled products
129+
*/
130+
private function getDisabledCount(Collection $baseCollection): int
131+
{
132+
/** @var Collection $disabledCollection */
133+
$disabledCollection = clone $baseCollection;
134+
$disabledCollection->addAttributeToFilter('status', Status::STATUS_DISABLED);
135+
return $disabledCollection->getSize();
136+
}
137+
138+
/**
139+
* Retrieve count of all not visible individually products
140+
*
141+
* @param Collection $baseCollection
142+
* @return int number of products not visible individually
143+
*/
144+
private function getNotVisibleCount(Collection $baseCollection): int
145+
{
146+
$notVisibleCollection = clone $baseCollection;
147+
$notVisibleCollection->addAttributeToFilter('status', Status::STATUS_ENABLED);
148+
$notVisibleCollection->addAttributeToFilter(
149+
'visibility',
150+
[
151+
Visibility::VISIBILITY_NOT_VISIBLE,
152+
Visibility::VISIBILITY_IN_SEARCH
153+
]
154+
);
155+
return $notVisibleCollection->getSize();
156+
}
157+
158+
/**
159+
* Retrieve count of all out of stock products
160+
*
161+
* @param Collection $baseCollection
162+
* @return int number of out of stock products
163+
* @throws Zend_Db_Select_Exception
164+
*/
165+
private function getOutOfStockCount(Collection $baseCollection): int
166+
{
167+
// Retrieve in stock products, then subtract them from the total
168+
$outOfStockCollection = clone $baseCollection;
169+
$this->stockFilter->addIsInStockFilterToCollection($outOfStockCollection);
170+
// Remove existing stock_status where condition from query
171+
$outOfStockWhere = $outOfStockCollection->getSelect()->getPart('where');
172+
$outOfStockWhere = array_filter(
173+
$outOfStockWhere,
174+
function ($whereCondition) {
175+
return !stristr($whereCondition, 'stock_status');
176+
}
177+
);
178+
$outOfStockCollection->getSelect()->setPart('where', $outOfStockWhere);
179+
$outOfStockCollection->getSelect()->where(
180+
'stock_status_index.stock_status = ?',
181+
\Magento\CatalogInventory\Model\Stock\Status::STATUS_OUT_OF_STOCK
182+
);
183+
return $outOfStockCollection->getSize();
184+
}
185+
186+
/**
187+
* Retrieve product totals for collection
188+
*
189+
* @param string $conditions
190+
* @return array
191+
* @throws LocalizedException
192+
* @throws Zend_Db_Select_Exception
193+
*/
194+
public function getProductTotals(string $conditions): array
195+
{
196+
/** @var Collection $collection */
197+
$collection = $this->getProductCollection($conditions);
198+
199+
// Exclude any linked products, e.g. simple products assigned to a configurable, bundle or group
200+
$collection->getSelect()->joinLeft(
201+
['super_link_table' => $collection->getTable('catalog_product_super_link')],
202+
'super_link_table.product_id = e.entity_id',
203+
['product_id']
204+
)->joinLeft(
205+
['link_table' => $collection->getTable('catalog_product_link')],
206+
'link_table.product_id = e.entity_id',
207+
['product_id']
208+
)->where('link_table.product_id IS NULL OR super_link_table.product_id IS NULL');
209+
210+
$disabledCount = $this->getDisabledCount($collection);
211+
$notVisibleCount = $this->getNotVisibleCount($collection);
212+
$outOfStockCount = $this->getOutOfStockCount($collection);
213+
214+
return [
215+
'total' => $collection->getSize(),
216+
'disabled' => $disabledCount,
217+
'notVisible' => $notVisibleCount,
218+
'outOfStock' => $outOfStockCount,
219+
];
220+
}
221+
}

app/code/Magento/PageBuilder/Test/Mftf/ActionGroup/ContentTypeProductsActionGroup.xml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
<seeElement selector="{{page.productNameInCarouselNotClonedBySlick(productGroupIndex, product.name)}}" stepKey="seeProduct"/>
4545
</actionGroup>
4646
<actionGroup name="validateCannotSeeProductInDefaultProductCarousel" extends="validateCanSeeProductInDefaultProductCarousel">
47-
<waitForElement selector="{{page.productNameInCarouselNotClonedBySlick(productGroupIndex, product.name)}}" stepKey="waitForProduct"/>
47+
<waitForElementNotVisible selector="{{page.productNameInCarouselNotClonedBySlick(productGroupIndex, product.name)}}" stepKey="waitForProduct"/>
4848
<dontSeeElement selector="{{page.productNameInCarouselNotClonedBySlick(productGroupIndex, product.name)}}" stepKey="seeProduct"/>
4949
</actionGroup>
5050
<actionGroup name="validateCanSeeProductInContinuousProductCarousel">
@@ -58,7 +58,7 @@
5858
<seeElement selector="{{page.productNameInCarouselBySlickSlideIndex(productGroupIndex, productItemIndex, product.name)}}" stepKey="seeProduct"/>
5959
</actionGroup>
6060
<actionGroup name="validateCannotSeeProductInContinuousProductCarousel" extends="validateCanSeeProductInContinuousProductCarousel">
61-
<waitForElement selector="{{page.productNameInCarouselBySlickSlideIndex(productGroupIndex, productItemIndex, product.name)}}" stepKey="waitForProduct"/>
61+
<waitForElementNotVisible selector="{{page.productNameInCarouselBySlickSlideIndex(productGroupIndex, productItemIndex, product.name)}}" stepKey="waitForProduct"/>
6262
<dontSeeElement selector="{{page.productNameInCarouselBySlickSlideIndex(productGroupIndex, productItemIndex, product.name)}}" stepKey="seeProduct"/>
6363
</actionGroup>
6464
<actionGroup name="validateProductInProducts">
@@ -495,6 +495,14 @@
495495
<actualResult type="variable">differenceBetweenCenterPositions</actualResult>
496496
</assertLessThanOrEqual>
497497
</actionGroup>
498+
<actionGroup name="validateProductTotals">
499+
<arguments>
500+
<argument name="productsTotals" defaultValue="of 0 total" type="string"/>
501+
</arguments>
502+
<waitForPageLoad stepKey="waitForPageLoad"/>
503+
<waitForElement time="2" selector="{{EditPanelForm.panelFieldControl(PageBuilderProductsTotalProductsProperty.section, PageBuilderProductsTotalProductsProperty.fieldName)}}" stepKey="waitForFieldVisible"/>
504+
<see selector="{{EditPanelForm.panelFieldControl(PageBuilderProductsTotalProductsProperty.section, PageBuilderProductsTotalProductsProperty.fieldName)}}" userInput="{{productsTotals}}" stepKey="seeProductTotals"/>
505+
</actionGroup>
498506
<actionGroup name="validateSwatchInProducts">
499507
<annotations>
500508
<description>Verify a product swatch is displayed within the Product preview within Page Builder.</description>

app/code/Magento/PageBuilder/Test/Mftf/Data/CatalogProductData.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,28 @@
4040
<requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity>
4141
<requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity>
4242
</entity>
43+
<entity name="SimpleProductDisabled2" type="product">
44+
<data key="sku" unique="suffix">simple_product_disabled</data>
45+
<data key="type_id">simple</data>
46+
<data key="attribute_set_id">4</data>
47+
<data key="name" unique="suffix">Simple Product Disabled</data>
48+
<data key="price">123.00</data>
49+
<data key="visibility">4</data>
50+
<data key="status">2</data>
51+
<data key="quantity">1001</data>
52+
<requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity>
53+
<requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity>
54+
</entity>
55+
<entity name="SimpleProductNotVisibleIndividually2" type="product">
56+
<data key="sku" unique="suffix">simple_product_not_visible_individually</data>
57+
<data key="type_id">simple</data>
58+
<data key="attribute_set_id">4</data>
59+
<data key="name" unique="suffix">Simple Product Not Visible Individually</data>
60+
<data key="price">123.00</data>
61+
<data key="visibility">1</data>
62+
<data key="status">1</data>
63+
<data key="quantity">1000</data>
64+
<requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity>
65+
<requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity>
66+
</entity>
4367
</entities>

app/code/Magento/PageBuilder/Test/Mftf/Data/ProductsData.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@
233233
<data key="value"/>
234234
<data key="errorMessage">This is a required field.</data>
235235
</entity>
236+
<!-- Total Products -->
237+
<entity name="PageBuilderProductsTotalProductsProperty" type="pagebuilder_products_grid_total_products">
238+
<data key="section">appearance_fieldset</data>
239+
<data key="fieldName">product_totals</data>
240+
</entity>
236241
<!-- Carousel Settings: Carousel Mode -->
237242
<entity name="PageBuilderProductsCarouselSettingsCarouselMode_Default" type="pagebuilder_products_carousel_mode">
238243
<data key="name">Autoplay</data>

app/code/Magento/PageBuilder/Test/Mftf/Test/AdminPageBuilderProductsCarouselTests.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3399,7 +3399,7 @@
33993399
<!-- Stage: Drag to Second Slide and Validate -->
34003400
<actionGroup ref="dragProductToSlideProductCarousel" stepKey="dragProductToSlideProductCarouselStage">
34013401
<argument name="targetProduct" value="$$createProduct5.name$$"/>
3402-
<argument name="destinationProduct" value="$$createProduct1.name$$"/>
3402+
<argument name="destinationProduct" value="$$createProduct2.name$$"/>
34033403
</actionGroup>
34043404
<actionGroup ref="validateCanSeeProductInDefaultProductCarousel" stepKey="validateCanSeeProduct6Stage2">
34053405
<argument name="product" value="$$createProduct6$$"/>

0 commit comments

Comments
 (0)