Skip to content

Commit 7e2531c

Browse files
committed
PB-107: Display total number of products matched into ProductsList
- populate total number of products
1 parent 55ab26c commit 7e2531c

File tree

6 files changed

+488
-14
lines changed

6 files changed

+488
-14
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\PageBuilder\Controller\Adminhtml\Form\Element;
8+
9+
class ProductTotals extends \Magento\Backend\App\Action
10+
{
11+
/**
12+
* Product collection factory
13+
*
14+
* @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
15+
*/
16+
private $productCollectionFactory;
17+
18+
/**
19+
* @var \Magento\Rule\Model\Condition\Sql\Builder
20+
*/
21+
private $sqlBuilder;
22+
23+
/**
24+
* Catalog product visibility
25+
*
26+
* @var \Magento\Catalog\Model\Product\Visibility
27+
*/
28+
private $catalogProductVisibility;
29+
30+
/**
31+
* @var \Magento\CatalogWidget\Model\Rule
32+
*/
33+
private $rule;
34+
35+
/**
36+
* @var \Magento\Widget\Helper\Conditions
37+
*/
38+
private $conditionsHelper;
39+
40+
/**
41+
* @var \Magento\Catalog\Api\\CategoryRepositoryInterface
42+
*/
43+
private $categoryRepository;
44+
45+
/**
46+
* @var \Magento\Framework\Controller\Result\JsonFactory
47+
*/
48+
private $jsonFactory;
49+
50+
/**
51+
* @param \Magento\Backend\App\Action\Context $context
52+
* @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
53+
* @param \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility
54+
* @param \Magento\Rule\Model\Condition\Sql\Builder $sqlBuilder
55+
* @param \Magento\CatalogWidget\Model\Rule $rule
56+
* @param \Magento\Widget\Helper\Conditions $conditionsHelper
57+
* @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository
58+
* @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory
59+
*/
60+
public function __construct(
61+
\Magento\Backend\App\Action\Context $context,
62+
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
63+
\Magento\Catalog\Model\Product\Visibility $catalogProductVisibility,
64+
\Magento\Rule\Model\Condition\Sql\Builder $sqlBuilder,
65+
\Magento\CatalogWidget\Model\Rule $rule,
66+
\Magento\Widget\Helper\Conditions $conditionsHelper,
67+
\Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
68+
\Magento\Framework\Controller\Result\JsonFactory $jsonFactory
69+
) {
70+
$this->productCollectionFactory = $productCollectionFactory;
71+
$this->catalogProductVisibility = $catalogProductVisibility;
72+
$this->sqlBuilder = $sqlBuilder;
73+
$this->rule = $rule;
74+
$this->conditionsHelper = $conditionsHelper;
75+
$this->categoryRepository = $categoryRepository;
76+
$this->jsonFactory = $jsonFactory;
77+
parent::__construct($context);
78+
}
79+
80+
/**
81+
* Get conditions
82+
*
83+
* @return \Magento\Rule\Model\Condition\Combine
84+
*/
85+
private function getConditions()
86+
{
87+
$conditions = $this->getRequest()->getParam('conditionValue');
88+
89+
if ($conditions) {
90+
$conditions = $this->conditionsHelper->decode($conditions);
91+
}
92+
93+
foreach ($conditions as $key => $condition) {
94+
if (!empty($condition['attribute'])
95+
&& in_array($condition['attribute'], ['special_from_date', 'special_to_date'])
96+
) {
97+
$conditions[$key]['value'] = date('Y-m-d H:i:s', strtotime($condition['value']));
98+
}
99+
}
100+
101+
$this->rule->loadPost(['conditions' => $conditions]);
102+
return $this->rule->getConditions();
103+
}
104+
105+
/**
106+
* Prepare and return product collection
107+
*
108+
* @return \Magento\Catalog\Model\ResourceModel\Product\Collection
109+
*/
110+
private function createCollection()
111+
{
112+
/** @var $collection \Magento\Catalog\Model\ResourceModel\Product\Collection */
113+
$collection = $this->productCollectionFactory->create();
114+
$collection = $collection->setVisibility($this->catalogProductVisibility->getVisibleInCatalogIds());
115+
116+
/** @var \Magento\Rule\Model\Condition\Combine $conditions */
117+
$conditions = $this->getConditions();
118+
$conditions->collectValidatedAttributes($collection);
119+
$this->sqlBuilder->attachConditionToCollection($collection, $conditions);
120+
121+
/**
122+
* Prevent retrieval of duplicate records. This may occur when multiselect product attribute matches
123+
* several allowed values from condition simultaneously
124+
*/
125+
$collection->distinct(true);
126+
127+
return $collection;
128+
}
129+
130+
/**
131+
* {@inheritdoc}
132+
*/
133+
public function execute()
134+
{
135+
/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
136+
$collection = $this->createCollection();
137+
return $this->jsonFactory->create()
138+
->setData([
139+
'total' => $collection->getSize()
140+
]);
141+
}
142+
}

app/code/Magento/PageBuilder/view/adminhtml/ui_component/pagebuilder_products_form.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,16 +228,17 @@
228228
<argument name="data" xsi:type="array">
229229
<item name="config" xsi:type="array">
230230
<item name="default" xsi:type="string">of ${ $.totalProductCount }</item>
231+
<item name="url" xsi:type="url" path="pagebuilder/form/element_producttotals"/>
231232
</item>
232233
</argument>
233234
<settings>
234235
<elementTmpl>ui/form/element/text</elementTmpl>
235236
<dataType>text</dataType>
236237
<imports>
237238
<link name="conditionOption">ns = ${ $.ns }, index = condition_option:value</link>
238-
<link name="conditionValues.category_ids">ns = ${ $.ns }, index = category_ids:value</link>
239-
<link name="conditionValues.sku">ns = ${ $.ns }, index = sku:value</link>
240-
<link name="conditionValues.condition">ns = ${ $.ns }, index = conditions_form:value</link>
239+
<link name="updateProductTotals">ns = ${ $.ns }, index = category_ids:value</link>
240+
<link name="updateProductTotals">ns = ${ $.ns }, index = sku:value</link>
241+
<link name="conditionValue">ns = ${ $.ns }, index = conditions_form:value</link>
241242
</imports>
242243
</settings>
243244
</field>

app/code/Magento/PageBuilder/view/adminhtml/web/js/form/element/product-totals.js

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,67 @@
33
* See COPYING.txt for license details.
44
*/
55

6-
define(['Magento_Ui/js/form/element/abstract'], function (Abstract) {
6+
define([
7+
'underscore',
8+
'jquery',
9+
'ko',
10+
'Magento_PageBuilder/js/form/provider/conditions-data-processor',
11+
'Magento_Ui/js/form/element/abstract'
12+
], function (_, $, ko, conditionsDataProcessor, Abstract) {
713
'use strict';
814

915
return Abstract.extend({
1016
defaults: {
1117
conditionOption: '',
12-
conditionValues: {},
18+
conditionValue: '',
19+
formData: {},
1320
totalProductCount: 0,
14-
disabledProductCount: 0,
1521
listens: {
16-
conditionOption: 'updateProductsCount',
17-
conditionValues: 'updateProductsCount'
22+
conditionOption: 'updateProductTotals',
23+
conditionValue: 'updateProductTotals',
24+
formData: 'updateProductTotals'
25+
},
26+
imports: {
27+
formData: '${ $.provider }:data'
1828
},
1929
links: {
2030
value: false
21-
}
31+
},
32+
url: null
2233
},
2334

2435
/** @inheritdoc */
2536
initObservable: function () {
2637
return this._super()
27-
.observe('totalProductCount disabledProductCount');
38+
.observe('value totalProductCount');
2839
},
2940

3041
/**
3142
* Update product count.
3243
*
3344
*/
34-
updateProductsCount: function () {
35-
if (!this.conditionOption || !this.conditionValues.hasOwnProperty(this.conditionOption)) {
45+
updateProductTotals: _.debounce(function () {
46+
47+
if (!this.conditionOption || _.isEmpty(this.formData) ||
48+
(this.conditionOption === 'sku' && (!this.formData['sku'] || this.formData['sku'] === ''))) {
3649
return;
3750
}
3851

39-
//perform request here;
40-
}
52+
_.extend(this.formData, this.conditionValue);
53+
conditionsDataProcessor(this.formData, this.conditionOption + '_source');
54+
55+
$.ajax({
56+
url: this.url,
57+
method: 'POST',
58+
data: {
59+
conditionValue: this.formData['conditions_encoded']
60+
}
61+
}).done(function (response) {
62+
if (response['total']) {
63+
this.totalProductCount = response['total'];
64+
this.value('of ' + this.totalProductCount);
65+
}
66+
}.bind(this));
67+
}, 10),
4168
});
4269
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
/**
12+
* @magentoAppArea adminhtml
13+
* @magentoDataFixture Magento/PageBuilder/_files/product_totals/products.php
14+
*/
15+
class ProductTotalsTest extends \Magento\TestFramework\TestCase\AbstractBackendController
16+
{
17+
/**
18+
* @var \Magento\Framework\Serialize\SerializerInterface
19+
*/
20+
private $serializer;
21+
22+
/**
23+
* @inheritdoc
24+
*/
25+
protected function setUp()
26+
{
27+
parent::setUp();
28+
$this->serializer = $this->_objectManager->get(\Magento\Framework\Serialize\SerializerInterface::class);
29+
}
30+
31+
/**
32+
* @param string condition
33+
* @param int $expectedTotal
34+
* @dataProvider productDataProvider
35+
*/
36+
public function testProductTotals($condition, $expectedTotal)
37+
{
38+
$this->getRequest()
39+
->setPostValue([
40+
'conditionValue' => $condition
41+
]);
42+
43+
$this->dispatch('backend/pagebuilder/form/element_producttotals');
44+
$decoded = $this->serializer->unserialize($this->getResponse()->getBody());
45+
46+
$this->assertEquals($expectedTotal, $decoded['total']);
47+
}
48+
49+
public function productDataProvider()
50+
{
51+
return [
52+
[
53+
// category with no products
54+
'"{"1":{"aggregator":"all","new_child":"","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Combine","value":"1"},"1--1":{"operator":"==","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Product","attribute":"category_ids","value":"4"}}"',
55+
0
56+
],
57+
[
58+
// category with 4 products
59+
'"{"1":{"aggregator":"all","new_child":"","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Combine","value":"1"},"1--1":{"operator":"==","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Product","attribute":"category_ids","value":"3"}}"',
60+
4
61+
],
62+
[
63+
// sku with no match
64+
'"{"1":{"aggregator":"all","new_child":"","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Combine","value":"1"},"1--1":{"operator":"()","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Product","attribute":"sku","value":"shoes"}}"',
65+
0
66+
],
67+
[
68+
// skus with 2 matches
69+
'"{"1":{"aggregator":"all","new_child":"","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Combine","value":"1"},"1--1":{"operator":"()","type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Product","attribute":"sku","value":"simple-3, simple-4"}}"',
70+
2
71+
],
72+
[
73+
// condition with no matches
74+
'"{"1":{"type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Combine","aggregator":"all","value":"1","new_child":""},"1--1":{"type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Product","attribute":"price","operator":">=","value":"10000"}}"',
75+
0
76+
],
77+
[
78+
// condition with 3 matches
79+
'"{"1":{"type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Combine","aggregator":"all","value":"1","new_child":""},"1--1":{"type":"Magento\\CatalogWidget\\Model\\Rule\\Condition\\Product","attribute":"price","operator":"<","value":"20"}}"',
80+
3
81+
]
82+
];
83+
84+
}
85+
86+
/**
87+
* Test that targeting a category with assigned products returns the correct count.
88+
*/
89+
public function testCategoryWithMatches()
90+
{
91+
92+
}
93+
94+
/**
95+
* Test SKU matches
96+
*/
97+
public function testSkuWithNoMatches()
98+
{
99+
100+
}
101+
102+
public function testSkusWithMatches()
103+
{
104+
105+
}
106+
107+
public function testConditionsWithNoMatches()
108+
{
109+
110+
}
111+
112+
public function testConditionsWithMatches()
113+
{
114+
115+
}
116+
}

0 commit comments

Comments
 (0)