diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 1a46c38d04319..9f33061199d54 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -1180,9 +1180,33 @@ protected function _getSelectCountSql(?Select $select = null, $resetLeftJoins =
if ($resetLeftJoins) {
$countSelect->resetJoinLeft();
}
+
+ $this->removeEntityIdentifierFromGroupBy($countSelect);
+
return $countSelect;
}
+ /**
+ * Using `entity_id` for `GROUP BY` causes COUNT() return {n} rows of value = 1 instead of 1 row of value {n}
+ *
+ * @param Select $select
+ * @throws \Zend_Db_Select_Exception
+ */
+ private function removeEntityIdentifierFromGroupBy(Select $select): void
+ {
+ $originalGroupBy = $select->getPart(Select::GROUP);
+
+ if (!is_array($originalGroupBy)) {
+ return;
+ }
+
+ $groupBy = array_filter($originalGroupBy, function ($field) {
+ return false === strpos($field, $this->getIdFieldName());
+ });
+
+ $select->setPart(Select::GROUP, $groupBy);
+ }
+
/**
* Prepare statistics data
*
@@ -1765,30 +1789,19 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC)
*/
protected function _prepareProductLimitationFilters()
{
- if (isset(
- $this->_productLimitationFilters['visibility']
- ) && !isset(
- $this->_productLimitationFilters['store_id']
- )
- ) {
+ if (isset($this->_productLimitationFilters['visibility'])
+ && !isset($this->_productLimitationFilters['store_id'])) {
$this->_productLimitationFilters['store_id'] = $this->getStoreId();
}
- if (isset(
- $this->_productLimitationFilters['category_id']
- ) && !isset(
- $this->_productLimitationFilters['store_id']
- )
- ) {
+
+ if (isset($this->_productLimitationFilters['category_id'])
+ && !isset($this->_productLimitationFilters['store_id'])) {
$this->_productLimitationFilters['store_id'] = $this->getStoreId();
}
- if (isset(
- $this->_productLimitationFilters['store_id']
- ) && isset(
- $this->_productLimitationFilters['visibility']
- ) && !isset(
- $this->_productLimitationFilters['category_id']
- )
- ) {
+
+ if (isset($this->_productLimitationFilters['store_id'])
+ && isset($this->_productLimitationFilters['visibility'])
+ && !isset($this->_productLimitationFilters['category_id'])) {
$this->_productLimitationFilters['category_id'] = $this->_storeManager->getStore(
$this->_productLimitationFilters['store_id']
)->getRootCategoryId();
@@ -1819,14 +1832,8 @@ protected function _productLimitationJoinWebsite()
$filters['website_ids'],
'int'
);
- } elseif (isset(
- $filters['store_id']
- ) && (!isset(
- $filters['visibility']
- ) && !isset(
- $filters['category_id']
- )) && !$this->isEnabledFlat()
- ) {
+ } elseif (isset($filters['store_id']) && !$this->isEnabledFlat()
+ && (!isset($filters['visibility']) && !isset($filters['category_id']))) {
$joinWebsite = true;
$websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId();
$conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int');
@@ -1901,9 +1908,9 @@ protected function _productLimitationJoinPrice()
/**
* Join Product Price Table with left-join possibility
*
- * @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice()
* @param bool $joinLeft
* @return $this
+ * @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice()
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function _productLimitationPrice($joinLeft = false)
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml
new file mode 100644
index 0000000000000..f0a4faeb68e4b
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontNoProductsFoundActionGroup.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml
index be04c297cec25..f3ccc00192a43 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml
@@ -69,4 +69,12 @@
catalog/frontend/grid_per_page_values
1,2
+
+ catalog/frontend/grid_per_page
+ 12
+
+
+ catalog/frontend/grid_per_page
+ 1
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml
index d3d435b7451c3..58e3dd5c32488 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml
@@ -159,7 +159,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml
index 4a4a4a6833b33..4a93b07769fd2 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml
@@ -91,7 +91,7 @@
-
+
@@ -132,7 +132,7 @@
-
+
diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js
index 6589f7eb0ba48..dfc0b4291cd6e 100644
--- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js
+++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js
@@ -23,6 +23,7 @@ define([
direction: 'product_list_dir',
order: 'product_list_order',
limit: 'product_list_limit',
+ page: 'p',
modeDefault: 'grid',
directionDefault: 'asc',
orderDefault: 'position',
@@ -81,24 +82,63 @@ define([
},
/**
- * @param {String} paramName
- * @param {*} paramValue
- * @param {*} defaultValue
+ * @private
*/
- changeUrl: function (paramName, paramValue, defaultValue) {
+ getUrlParams: function () {
var decode = window.decodeURIComponent,
urlPaths = this.options.url.split('?'),
- baseUrl = urlPaths[0],
urlParams = urlPaths[1] ? urlPaths[1].split('&') : [],
- paramData = {},
- parameters, i, form, params, key, input, formKey;
+ params = {},
+ parameters, i;
for (i = 0; i < urlParams.length; i++) {
parameters = urlParams[i].split('=');
- paramData[decode(parameters[0])] = parameters[1] !== undefined ?
+ params[decode(parameters[0])] = parameters[1] !== undefined ?
decode(parameters[1].replace(/\+/g, '%20')) :
'';
}
+
+ return params;
+ },
+
+ /**
+ * @returns {String}
+ * @private
+ */
+ getCurrentLimit: function () {
+ return this.getUrlParams()[this.options.limit] || this.options.limitDefault;
+ },
+
+ /**
+ * @returns {String}
+ * @private
+ */
+ getCurrentPage: function () {
+ return this.getUrlParams()[this.options.page] || 1;
+ },
+
+ /**
+ * @param {String} paramName
+ * @param {*} paramValue
+ * @param {*} defaultValue
+ */
+ changeUrl: function (paramName, paramValue, defaultValue) {
+ var urlPaths = this.options.url.split('?'),
+ baseUrl = urlPaths[0],
+ paramData = this.getUrlParams(),
+ currentPage = this.getCurrentPage(),
+ form, params, key, input, formKey, newPage;
+
+ if (currentPage > 1 && paramName === this.options.limit) {
+ newPage = Math.floor(this.getCurrentLimit() * (currentPage - 1) / paramValue) + 1;
+
+ if (newPage > 1) {
+ paramData[this.options.page] = newPage;
+ } else {
+ delete paramData[this.options.page];
+ }
+ }
+
paramData[paramName] = paramValue;
if (this.options.post) {
@@ -130,6 +170,7 @@ define([
if (paramValue == defaultValue) { //eslint-disable-line eqeqeq
delete paramData[paramName];
}
+
paramData = $.param(paramData);
location.href = baseUrl + (paramData.length ? '?' + paramData : '');
}
diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php
index ce88fc290e23c..64ce76c729334 100644
--- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php
+++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php
@@ -80,6 +80,9 @@ public function resolve(): SearchCriteria
$searchCriteria->setRequestName($this->searchRequestName);
$searchCriteria->setSortOrders($this->orders);
$searchCriteria->setCurrentPage($this->currentPage - 1);
+ if ($this->size) {
+ $searchCriteria->setPageSize($this->size);
+ }
return $searchCriteria;
}
diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml
index e763df7dd3227..3cb06014234dc 100644
--- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml
+++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml
@@ -30,12 +30,16 @@
Product Simple AAA
+
+
+
+
@@ -57,6 +61,9 @@
+
+
+
diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml
index b4eb436fc1b2a..1dceb21a7c95c 100644
--- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml
+++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml
@@ -25,22 +25,11 @@
-
-
+
+
+
-
-
-
- grabNumberOfLastPage
- grabNumberOfCurrentPage
-
-
-
-
-
-
-
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
index 8c3ec20b0ede5..2512de3537f28 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
@@ -280,7 +280,7 @@ public function testCreateWithMultipleWebsites()
$websitesData = [
'website_ids' => [
1,
- (int) $website->getId(),
+ (int)$website->getId(),
]
];
$productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = $websitesData;
@@ -381,97 +381,6 @@ public function testCreate($product)
$this->deleteProduct($product[ProductInterface::SKU]);
}
- /**
- * Media gallery entries with external videos
- *
- * @return array
- */
- public function externalVideoDataProvider(): array
- {
- return [
- [
- [
- [
- 'media_type' => 'external-video',
- 'disabled' => false,
- 'label' => 'Test Video Created',
- 'types' => [],
- 'position' => 1,
- 'content' => [
- 'type' => 'image/png',
- 'name' => 'thumbnail.png',
- 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f'
- . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
- . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC',
- ],
- 'extension_attributes' => [
- 'video_content' => [
- 'media_type' => 'external-video',
- 'video_provider' => 'youtube',
- 'video_url' => 'https://www.youtube.com/',
- 'video_title' => 'Video title',
- 'video_description' => 'Video description',
- 'video_metadata' => 'Video meta',
- ],
- ],
- ]
- ]
- ],
- [
- [
- [
- 'media_type' => 'external-video',
- 'disabled' => false,
- 'label' => 'Test Video Updated',
- 'types' => [],
- 'position' => 1,
- 'content' => [
- 'type' => 'image/png',
- 'name' => 'thumbnail.png',
- 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f'
- . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
- . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC',
- ],
- 'extension_attributes' => [
- 'video_content' => [
- 'media_type' => 'external-video',
- 'video_provider' => 'vimeo',
- 'video_url' => 'https://www.vimeo.com/',
- 'video_title' => 'Video title',
- 'video_description' => 'Video description',
- 'video_metadata' => 'Video meta',
- ],
- ],
- ]
- ]
- ]
- ];
- }
-
- /**
- * Test create/ update product with external video media gallery entry
- *
- * @dataProvider externalVideoDataProvider
- * @param array $mediaGalleryData
- */
- public function testCreateWithExternalVideo(array $mediaGalleryData)
- {
- $simpleProductBaseData = $this->getSimpleProductData(
- [
- ProductInterface::NAME => 'Product With Ext. Video',
- ProductInterface::SKU => 'prod-with-ext-video'
- ]
- );
-
- $simpleProductBaseData['media_gallery_entries'] = $mediaGalleryData;
-
- $response = $this->saveProduct($simpleProductBaseData);
- $this->assertEquals(
- $simpleProductBaseData['media_gallery_entries'][0]['extension_attributes'],
- $response["media_gallery_entries"][0]["extension_attributes"]
- );
- }
-
/**
* @param array $fixtureProduct
*
@@ -1186,6 +1095,86 @@ public function testGetListWithFilteringByStoreDataProvider()
];
}
+ /**
+ * Test getList() method with pagination
+ *
+ * @param int $pageSize
+ * @param int $currentPage
+ * @param int $expectedCount
+ *
+ * @magentoAppIsolation enabled
+ * @magentoApiDataFixture Magento/Catalog/_files/products_for_search.php
+ * @dataProvider productPaginationDataProvider
+ */
+ public function testGetListPagination(int $pageSize, int $currentPage, int $expectedCount)
+ {
+ $fixtureProducts = 5;
+
+ /** @var FilterBuilder $filterBuilder */
+ $filterBuilder = Bootstrap::getObjectManager()->create(FilterBuilder::class);
+
+ $categoryFilter = $filterBuilder->setField('category_id')
+ ->setValue(333)
+ ->create();
+
+ /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+ $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class);
+
+ $searchCriteriaBuilder->addFilters([$categoryFilter]);
+ $searchCriteriaBuilder->setPageSize($pageSize);
+ $searchCriteriaBuilder->setCurrentPage($currentPage);
+
+ $searchData = $searchCriteriaBuilder->create()->__toArray();
+ $requestData = ['searchCriteria' => $searchData];
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => self::RESOURCE_PATH . '?' . http_build_query($requestData),
+ 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
+ ],
+ 'soap' => [
+ 'service' => self::SERVICE_NAME,
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => self::SERVICE_NAME . 'GetList',
+ ],
+ ];
+
+ $searchResult = $this->_webApiCall($serviceInfo, $requestData);
+
+ $this->assertEquals($fixtureProducts, $searchResult['total_count']);
+ $this->assertCount($expectedCount, $searchResult['items']);
+ }
+
+ /**
+ * Keep in mind: Fixture contains 5 products
+ *
+ * @return array
+ */
+ public function productPaginationDataProvider()
+ {
+ return [
+ 'expect-all-items' => [
+ 'pageSize' => 10,
+ 'currentPage' => 1,
+ 'expectedCount' => 5
+ ],
+ 'expect-page=size-items' => [
+ 'pageSize' => 2,
+ 'currentPage' => 1,
+ 'expectedCount' => 2
+ ],
+ 'expect-less-than-pagesize-elements' => [
+ 'pageSize' => 3,
+ 'currentPage' => 2,
+ 'expectedCount' => 2
+ ],
+ 'expect-no-items' => [
+ 'pageSize' => 100,
+ 'currentPage' => 99,
+ 'expectedCount' => 0
+ ]
+ ];
+ }
+
/**
* Test getList() method with multiple filter groups and sorting and pagination
*
@@ -1223,7 +1212,7 @@ public function testGetListWithMultipleFilterGroupsAndSortingAndPagination()
$sortOrder = $sortOrderBuilder->setField('meta_title')->setDirection(SortOrder::SORT_DESC)->create();
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
- $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class);
+ $searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class);
$searchCriteriaBuilder->addFilters([$filter1, $filter2, $filter3, $filter4]);
$searchCriteriaBuilder->addFilters([$filter5]);
@@ -1815,8 +1804,8 @@ private function assertMultiselectValue($productSku, $multiselectAttributeCode,
* Test design settings authorization
*
* @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php
- * @throws \Throwable
* @return void
+ * @throws \Throwable
*/
public function testSaveDesign(): void
{
diff --git a/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php
new file mode 100644
index 0000000000000..7c38cf15545c6
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/ProductVideo/ProductVideoExternalSourceTest.php
@@ -0,0 +1,178 @@
+ [
+ [
+ 'media_type' => 'external-video',
+ 'disabled' => false,
+ 'label' => 'Test Video Created',
+ 'types' => [],
+ 'position' => 1,
+ 'content' => $this->getVideoThumbnailStub(),
+ 'extension_attributes' => [
+ 'video_content' => [
+ 'media_type' => 'external-video',
+ 'video_provider' => 'youtube',
+ 'video_url' => 'https://www.youtube.com/',
+ 'video_title' => 'Video title',
+ 'video_description' => 'Video description',
+ 'video_metadata' => 'Video meta',
+ ],
+ ],
+ ]
+ ],
+ 'vimeo-external-video' => [
+ [
+ 'media_type' => 'external-video',
+ 'disabled' => false,
+ 'label' => 'Test Video Updated',
+ 'types' => [],
+ 'position' => 1,
+ 'content' => $this->getVideoThumbnailStub(),
+ 'extension_attributes' => [
+ 'video_content' => [
+ 'media_type' => 'external-video',
+ 'video_provider' => 'vimeo',
+ 'video_url' => 'https://www.vimeo.com/',
+ 'video_title' => 'Video title',
+ 'video_description' => 'Video description',
+ 'video_metadata' => 'Video meta',
+ ],
+ ],
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Returns the array of data for Video thumbnail
+ *
+ * @return array|string[]
+ */
+ private function getVideoThumbnailStub(): array
+ {
+ return [
+ 'type' => 'image/png',
+ 'name' => 'thumbnail.png',
+ 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f'
+ . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+ . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC',
+ ];
+ }
+
+ /**
+ * Test create/ update product with external video media gallery entry
+ *
+ * @dataProvider externalVideoDataProvider
+ * @param array $mediaGalleryData
+ */
+ public function testCreateWithExternalVideo(array $mediaGalleryData)
+ {
+ $simpleProductBaseData = $this->getSimpleProductData(
+ [
+ ProductInterface::NAME => 'Product With Ext. Video',
+ ProductInterface::SKU => 'prod-with-ext-video'
+ ]
+ );
+
+ $simpleProductBaseData['media_gallery_entries'] = [$mediaGalleryData];
+
+ $response = $this->saveProduct($simpleProductBaseData);
+ $this->assertEquals(
+ $simpleProductBaseData['media_gallery_entries'][0]['extension_attributes'],
+ $response["media_gallery_entries"][0]["extension_attributes"]
+ );
+ }
+
+ /**
+ * Get Simple Product Data
+ *
+ * @param array $productData
+ * @return array
+ */
+ protected function getSimpleProductData($productData = [])
+ {
+ return [
+ ProductInterface::SKU => isset($productData[ProductInterface::SKU])
+ ? $productData[ProductInterface::SKU] : uniqid('sku-', true),
+ ProductInterface::NAME => isset($productData[ProductInterface::NAME])
+ ? $productData[ProductInterface::NAME] : uniqid('sku-', true),
+ ProductInterface::VISIBILITY => 4,
+ ProductInterface::TYPE_ID => 'simple',
+ ProductInterface::PRICE => 3.62,
+ ProductInterface::STATUS => 1,
+ ProductInterface::ATTRIBUTE_SET_ID => 4,
+ 'custom_attributes' => [
+ ['attribute_code' => 'cost', 'value' => ''],
+ ['attribute_code' => 'description', 'value' => 'Description'],
+ ]
+ ];
+ }
+
+ /**
+ * Save Product
+ *
+ * @param $product
+ * @param string|null $storeCode
+ * @param string|null $token
+ * @return mixed
+ */
+ protected function saveProduct($product, $storeCode = null, ?string $token = null)
+ {
+ if (isset($product['custom_attributes'])) {
+ foreach ($product['custom_attributes'] as &$attribute) {
+ if ($attribute['attribute_code'] == 'category_ids'
+ && !is_array($attribute['value'])
+ ) {
+ $attribute['value'] = [""];
+ }
+ }
+ }
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => self::RESOURCE_PATH,
+ 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
+ ],
+ 'soap' => [
+ 'service' => self::SERVICE_NAME,
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => self::SERVICE_NAME . 'Save',
+ ],
+ ];
+ if ($token) {
+ $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token;
+ }
+ $requestData = ['product' => $product];
+
+ return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php
index c497c4de5a5e4..7c9cf02ac6a47 100644
--- a/lib/internal/Magento/Framework/Data/Collection.php
+++ b/lib/internal/Magento/Framework/Data/Collection.php
@@ -6,7 +6,10 @@
namespace Magento\Framework\Data;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Data\Collection\EntityFactoryInterface;
+use Magento\Framework\DataObject;
+use Magento\Framework\Exception\AlreadyExistsException;
use Magento\Framework\Option\ArrayInterface;
/**
@@ -25,7 +28,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll
/**
* Collection items
*
- * @var \Magento\Framework\DataObject[]
+ * @var DataObject[]
*/
protected $_items = [];
@@ -34,7 +37,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll
*
* @var string
*/
- protected $_itemObjectClass = \Magento\Framework\DataObject::class;
+ protected $_itemObjectClass = DataObject::class;
/**
* Order configuration
@@ -46,7 +49,7 @@ class Collection implements \IteratorAggregate, \Countable, ArrayInterface, Coll
/**
* Filters configuration
*
- * @var \Magento\Framework\DataObject[]
+ * @var DataObject[]
*/
protected $_filters = [];
@@ -117,7 +120,7 @@ public function __construct(EntityFactoryInterface $entityFactory)
*/
public function addFilter($field, $value, $type = 'and')
{
- $filter = new \Magento\Framework\DataObject();
+ $filter = new DataObject();
// implements ArrayAccess
$filter['field'] = $field;
$filter['value'] = $value;
@@ -163,9 +166,9 @@ public function addFilter($field, $value, $type = 'and')
*
* @param string|array $field
* @param string|int|array $condition
- * @throws \Magento\Framework\Exception\LocalizedException if some error in the input could be detected.
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @throws \Magento\Framework\Exception\LocalizedException if some error in the input could be detected.
*/
public function addFieldToFilter($field, $condition)
{
@@ -182,7 +185,7 @@ public function addFieldToFilter($field, $condition)
* - array() -- get all filters
*
* @param string|string[] $field
- * @return \Magento\Framework\DataObject|\Magento\Framework\DataObject[]|void
+ * @return DataObject|DataObject[]|void
*/
public function getFilter($field)
{
@@ -234,18 +237,16 @@ protected function _setIsLoaded($flag = true)
/**
* Get current collection page
*
- * @param int $displacement
+ * @param int $displacement
* @return int
*/
public function getCurPage($displacement = 0)
{
if ($this->_curPage + $displacement < 1) {
return 1;
- } elseif ($this->_curPage + $displacement > $this->getLastPageNumber()) {
- return $this->getLastPageNumber();
- } else {
- return $this->_curPage + $displacement;
}
+
+ return $this->_curPage + $displacement;
}
/**
@@ -260,9 +261,9 @@ public function getLastPageNumber()
return 1;
} elseif ($this->_pageSize) {
return (int)ceil($collectionSize / $this->_pageSize);
- } else {
- return 1;
}
+
+ return 1;
}
/**
@@ -292,7 +293,7 @@ public function getSize()
/**
* Retrieve collection first item
*
- * @return \Magento\Framework\DataObject
+ * @return DataObject
*/
public function getFirstItem()
{
@@ -309,7 +310,7 @@ public function getFirstItem()
/**
* Retrieve collection last item
*
- * @return \Magento\Framework\DataObject
+ * @return DataObject
*/
public function getLastItem()
{
@@ -325,7 +326,7 @@ public function getLastItem()
/**
* Retrieve collection items
*
- * @return \Magento\Framework\DataObject[]
+ * @return DataObject[]
*/
public function getItems()
{
@@ -336,8 +337,8 @@ public function getItems()
/**
* Retrieve field values from all items
*
- * @param string $colName
- * @return array
+ * @param string $colName
+ * @return array
*/
public function getColumnValues($colName)
{
@@ -353,9 +354,9 @@ public function getColumnValues($colName)
/**
* Search all items by field value
*
- * @param string $column
- * @param mixed $value
- * @return array
+ * @param string $column
+ * @param array $value
+ * @return array
*/
public function getItemsByColumnValue($column, $value)
{
@@ -373,9 +374,9 @@ public function getItemsByColumnValue($column, $value)
/**
* Search first item by field value
*
- * @param string $column
- * @param mixed $value
- * @return \Magento\Framework\DataObject || null
+ * @param string $column
+ * @param string|int $value
+ * @return DataObject|null
*/
public function getItemByColumnValue($column, $value)
{
@@ -392,11 +393,11 @@ public function getItemByColumnValue($column, $value)
/**
* Adding item to item array
*
- * @param \Magento\Framework\DataObject $item
+ * @param DataObject $item
* @return $this
* @throws \Exception
*/
- public function addItem(\Magento\Framework\DataObject $item)
+ public function addItem(DataObject $item)
{
$itemId = $this->_getItemId($item);
@@ -417,7 +418,7 @@ public function addItem(\Magento\Framework\DataObject $item)
/**
* Add item that has no id to collection
*
- * @param \Magento\Framework\DataObject $item
+ * @param DataObject $item
* @return $this
*/
protected function _addItem($item)
@@ -429,10 +430,10 @@ protected function _addItem($item)
/**
* Retrieve item id
*
- * @param \Magento\Framework\DataObject $item
- * @return mixed
+ * @param DataObject $item
+ * @return string|int
*/
- protected function _getItemId(\Magento\Framework\DataObject $item)
+ protected function _getItemId(DataObject $item)
{
return $item->getId();
}
@@ -454,7 +455,7 @@ public function getAllIds()
/**
* Remove item from collection by item key
*
- * @param mixed $key
+ * @param string $key
* @return $this
*/
public function removeItemByKey($key)
@@ -542,8 +543,8 @@ public function each($objMethod, $args = [])
/**
* Setting data for all collection items
*
- * @param mixed $key
- * @param mixed $value
+ * @param string $key
+ * @param string|int|null $value
* @return $this
*/
public function setDataToAll($key, $value = null)
@@ -606,7 +607,7 @@ public function setOrder($field, $direction = self::SORT_ORDER_DESC)
*/
public function setItemObjectClass($className)
{
- if (!is_a($className, \Magento\Framework\DataObject::class, true)) {
+ if (!is_a($className, DataObject::class, true)) {
throw new \InvalidArgumentException($className . ' does not extend \Magento\Framework\DataObject');
}
$this->_itemObjectClass = $className;
@@ -616,7 +617,7 @@ public function setItemObjectClass($className)
/**
* Retrieve collection empty item
*
- * @return \Magento\Framework\DataObject
+ * @return DataObject
*/
public function getNewEmptyItem()
{
@@ -748,8 +749,8 @@ public function toArray($arrRequiredFields = [])
* Return items array
* array(
* $index => array(
- * 'value' => mixed
- * 'label' => mixed
+ * 'value' => string
+ * 'label' => string
* )
* )
*
@@ -815,8 +816,8 @@ protected function _toOptionHash($valueField = 'id', $labelField = 'name')
/**
* Retrieve item by id
*
- * @param mixed $idValue
- * @return \Magento\Framework\DataObject
+ * @param string|int $idValue
+ * @return DataObject
*/
public function getItemById($idValue)
{
@@ -910,7 +911,7 @@ public function __sleep()
*/
public function __wakeup()
{
- $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+ $objectManager = ObjectManager::getInstance();
$this->_entityFactory = $objectManager->get(EntityFactoryInterface::class);
}
}