From 583ff118ba99d95454f0ef3e866f26928993ed91 Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Thu, 7 Feb 2019 14:53:08 +0200
Subject: [PATCH 001/593] Implement exception logging #220. Created custom
error handler; Applied default error handling functionality
---
app/etc/di.xml | 1 +
.../Framework/GraphQl/Query/ErrorHandler.php | 29 +++++++++++++++++++
.../GraphQl/Query/ErrorHandlerInterface.php | 26 +++++++++++++++++
.../GraphQl/Query/QueryProcessor.php | 20 ++++++++++---
4 files changed, 72 insertions(+), 4 deletions(-)
create mode 100644 lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
create mode 100644 lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 6cf169c1d2277..dc884cb9fe29b 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -209,6 +209,7 @@
+
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
new file mode 100644
index 0000000000000..9fc3f9dc584e5
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
@@ -0,0 +1,29 @@
+exceptionFormatter = $exceptionFormatter;
$this->queryComplexityLimiter = $queryComplexityLimiter;
+ $this->errorHandler = $errorHandler;
}
/**
@@ -67,6 +77,8 @@ public function process(
$contextValue,
$variableValues,
$operationName
+ )->setErrorsHandler(
+ [$this->errorHandler, 'handle']
)->toArray(
$this->exceptionFormatter->shouldShowDetail() ?
\GraphQL\Error\Debug::INCLUDE_DEBUG_MESSAGE | \GraphQL\Error\Debug::INCLUDE_TRACE : false
From 9027c50f7fa39c6bca79c8023e2191d441a1d937 Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Thu, 7 Feb 2019 15:39:40 +0200
Subject: [PATCH 002/593] Implement exception logging #220. Implemented client
and server errors logging
---
app/etc/di.xml | 58 +++++++++++++-
.../Framework/GraphQl/Query/ErrorHandler.php | 75 ++++++++++++++++++-
.../GraphQl/Query/ErrorHandlerInterface.php | 3 +
3 files changed, 134 insertions(+), 2 deletions(-)
diff --git a/app/etc/di.xml b/app/etc/di.xml
index dc884cb9fe29b..ed9f32a1bf267 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -209,7 +209,6 @@
-
@@ -1757,4 +1756,61 @@
+
+
+
+ GraphQLClientLogger
+ GraphQLServerLogger
+ GraphQLGeneralLogger
+
+ - \GraphQL\Error\Error::CATEGORY_GRAPHQL
+ - \Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException::EXCEPTION_CATEGORY
+ - \Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException::EXCEPTION_CATEGORY
+ - \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException::EXCEPTION_CATEGORY
+ - \Magento\Framework\GraphQl\Exception\GraphQlInputException::EXCEPTION_CATEGORY
+ - \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException::EXCEPTION_CATEGORY
+ - request
+ - user
+
+
+ - \GraphQL\Error\Error::CATEGORY_INTERNAL
+
+
+
+
+
+
+ - GraphQLClientErrorHandler
+
+
+
+
+
+ \Magento\Framework\GraphQl\Query\ErrorHandlerInterface::CLIENT_LOG_FILE
+
+
+
+
+
+ - GraphQLServerErrorHandler
+
+
+
+
+
+ \Magento\Framework\GraphQl\Query\ErrorHandlerInterface::SERVER_LOG_FILE
+
+
+
+
+
+ - GraphQLGeneralErrorHandler
+
+
+
+
+
+ \Magento\Framework\GraphQl\Query\ErrorHandlerInterface::GENERAL_LOG_FILE
+
+
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
index 9fc3f9dc584e5..2f97ae238c6a7 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
@@ -14,6 +14,56 @@
*/
class ErrorHandler implements ErrorHandlerInterface
{
+ /**
+ * @var \Magento\Framework\Logger\Monolog
+ */
+ private $clientLogger;
+
+ /**
+ * @var \Magento\Framework\Logger\Monolog
+ */
+ private $serverLogger;
+
+ /**
+ * @var array
+ */
+ private $clientErrorCategories;
+
+ /**
+ * @var array
+ */
+ private $serverErrorCategories;
+
+ /**
+ * @var \Magento\Framework\Logger\Monolog
+ */
+ private $generalLogger;
+
+ /**
+ * ErrorHandler constructor.
+ *
+ * @param \Magento\Framework\Logger\Monolog $clientLogger
+ * @param \Magento\Framework\Logger\Monolog $serverLogger
+ * @param \Magento\Framework\Logger\Monolog $generalLogger
+ * @param array $clientErrorCategories
+ * @param array $serverErrorCategories
+ *
+ * @SuppressWarnings(PHPMD.LongVariable)
+ */
+ public function __construct(
+ \Magento\Framework\Logger\Monolog $clientLogger,
+ \Magento\Framework\Logger\Monolog $serverLogger,
+ \Magento\Framework\Logger\Monolog $generalLogger,
+ array $clientErrorCategories = [],
+ array $serverErrorCategories = []
+ ) {
+ $this->clientLogger = $clientLogger;
+ $this->serverLogger = $serverLogger;
+ $this->generalLogger = $generalLogger;
+ $this->clientErrorCategories = $clientErrorCategories;
+ $this->serverErrorCategories = $serverErrorCategories;
+ }
+
/**
* Handle errors
*
@@ -24,6 +74,29 @@ class ErrorHandler implements ErrorHandlerInterface
*/
public function handle(array $errors, callable $formatter):array
{
- return array_map($formatter, $errors);
+ return array_map(
+ function (\GraphQL\Error\ClientAware $error) use ($formatter) {
+ $this->logError($error);
+
+ return $formatter($error);
+ },
+ $errors
+ );
+ }
+
+ /**
+ * @param \GraphQL\Error\ClientAware $error
+ *
+ * @return boolean
+ */
+ private function logError(\GraphQL\Error\ClientAware $error):bool
+ {
+ if (in_array($error->getCategory(), $this->clientErrorCategories)) {
+ return $this->clientLogger->error($error);
+ } elseif (in_array($error->getCategory(), $this->serverErrorCategories)) {
+ return $this->serverLogger->error($error);
+ }
+
+ return $this->generalLogger->error($error);
}
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
index f1123864f4afc..f63468fa4ddb8 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
@@ -14,6 +14,9 @@
*/
interface ErrorHandlerInterface
{
+ const SERVER_LOG_FILE = 'var/log/graphql/server/exception.log';
+ const CLIENT_LOG_FILE = 'var/log/graphql/client/exception.log';
+ const GENERAL_LOG_FILE = 'var/log/graphql/exception.log';
/**
* Handle errors
*
From b1131c56305b9d6810f1d12cc0ea78f69774dc46 Mon Sep 17 00:00:00 2001
From: Yaroslav Rogoza
Date: Sun, 24 Mar 2019 18:37:42 +0100
Subject: [PATCH 003/593] Convert ConfigPageVisibilityTest to MFTF
---
.../Mftf/Section/LocaleOptionsSection.xml | 3 ++
...eAndDeveloperConfigInDeveloperModeTest.xml | 33 +++++++++++++++++++
...AndDeveloperConfigInProductionModeTest.xml | 33 +++++++++++++++++++
.../GeneralConfigurationActionGroup.xml | 5 +++
.../Config/Test/Mftf/Page/AdminConfigPage.xml | 1 +
.../TestCase/ConfigPageVisibilityTest.xml | 1 +
6 files changed, 76 insertions(+)
create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml
create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml
diff --git a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml
index a460aaebf1051..3ad8adf9e1b96 100644
--- a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml
+++ b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml
@@ -11,6 +11,9 @@
diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml
new file mode 100644
index 0000000000000..7e471f4f855a1
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml
new file mode 100644
index 0000000000000..85cc5dd047157
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml
index f05cf5be3448e..ec230ff1952ce 100644
--- a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml
+++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml
@@ -20,6 +20,11 @@
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml
index 7a62dfff8323b..507ae0594e1cd 100644
--- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml
+++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml
@@ -21,4 +21,5 @@
+
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigPageVisibilityTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigPageVisibilityTest.xml
index 2d7e609c1c389..0694966c7eaa5 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigPageVisibilityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigPageVisibilityTest.xml
@@ -9,6 +9,7 @@
severity:S1
+ mftf_migrated:yes
From 8d1ccc8c764db7bc966fa0a3c2f5b082b616a4ea Mon Sep 17 00:00:00 2001
From: Yaroslav Rogoza
Date: Mon, 25 Mar 2019 11:17:52 +0100
Subject: [PATCH 004/593] Refactoring
---
.../AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml | 2 +-
.../AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml
index 7e471f4f855a1..90fd6e451781b 100644
--- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml
+++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml
index 85cc5dd047157..9c1dbe750f013 100644
--- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml
+++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml
@@ -17,7 +17,7 @@
-
+
From d82ba2eb04a9eaa12a19dee75c8456f702515d82 Mon Sep 17 00:00:00 2001
From: Anahit Martirosyan
Date: Fri, 29 Mar 2019 10:32:46 +0400
Subject: [PATCH 005/593] MAGETWO-51891: Category with invalid data loses new
products assignment after validation
- Add automated test script
---
.../ActionGroup/AdminCategoryActionGroup.xml | 13 ++++
...ssignmentToCategoryWithInvalidDataTest.xml | 62 +++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
index 90d732c9654e1..80e71ef89da90 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
@@ -316,4 +316,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
new file mode 100644
index 0000000000000..3ef8ecfeff304
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 924b6b44f59087ff67be32f22a2c41468234488f Mon Sep 17 00:00:00 2001
From: Lusine Papyan
Date: Tue, 9 Apr 2019 15:44:58 +0400
Subject: [PATCH 006/593] MAGETWO-51891: Category with invalid data loses new
products assignment after validation
- Update automated test script
---
.../NewProductsAssignmentToCategoryWithInvalidDataTest.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
index 3ef8ecfeff304..eb02db8553f6f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
@@ -40,7 +40,7 @@
-
+
@@ -56,7 +56,7 @@
-
-
+
+
From 6e1dc006c6cee0bbb692a2d077414c0ffdab7d48 Mon Sep 17 00:00:00 2001
From: Anahit Martirosyan
Date: Tue, 30 Apr 2019 15:51:27 +0400
Subject: [PATCH 007/593] MAGETWO-51891: Category with invalid data loses new
products assignment after validation
- Add updated test script
---
.../Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
index 80e71ef89da90..57dff7fb2ae90 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
@@ -320,7 +320,7 @@
-
+
From 6dfc8184af647e0f515a94e09ebee2f3b2cbf537 Mon Sep 17 00:00:00 2001
From: Evgeny Petrov
Date: Sat, 4 May 2019 15:22:27 +0300
Subject: [PATCH 008/593] MAGETWO-51891: Category with invalid data loses new
products assignment after validation
- Add updated test script
---
...ssignmentToCategoryWithInvalidDataTest.xml | 62 -------------------
1 file changed, 62 deletions(-)
delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
deleted file mode 100644
index eb02db8553f6f..0000000000000
--- a/app/code/Magento/Catalog/Test/Mftf/Test/NewProductsAssignmentToCategoryWithInvalidDataTest.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 930ba4028399b15b1a46119d0d53e20157aafd36 Mon Sep 17 00:00:00 2001
From: Evgeny Petrov
Date: Sat, 11 May 2019 16:12:28 +0300
Subject: [PATCH 009/593] MAGETWO-51891: Category with invalid data loses new
products assignment after validation
---
.../Mftf/ActionGroup/AdminCategoryActionGroup.xml | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
index 57dff7fb2ae90..90d732c9654e1 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
@@ -316,17 +316,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
From e79e57be45d798763728e4449443616df07cda5a Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Thu, 16 May 2019 11:08:18 +0300
Subject: [PATCH 010/593] Magento2 Attribute option does not validate for
existing records before insert #16852
---
.../Eav/Setup/AddOptionToAttribute.php | 210 +++++++++++++++++
app/code/Magento/Eav/Setup/EavSetup.php | 69 +-----
.../Unit/Setup/AddAttributeOptionTest.php | 200 ++++++++++++++++
.../Eav/Setup/AddOptionToAttributeTest.php | 222 ++++++++++++++++++
.../Eav/_files/attribute_with_options.php | 46 ++++
.../attribute_with_options_rollback.php | 20 ++
6 files changed, 710 insertions(+), 57 deletions(-)
create mode 100644 app/code/Magento/Eav/Setup/AddOptionToAttribute.php
create mode 100644 app/code/Magento/Eav/Test/Unit/Setup/AddAttributeOptionTest.php
create mode 100644 dev/tests/integration/testsuite/Magento/Eav/Setup/AddOptionToAttributeTest.php
create mode 100644 dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options.php
create mode 100644 dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options_rollback.php
diff --git a/app/code/Magento/Eav/Setup/AddOptionToAttribute.php b/app/code/Magento/Eav/Setup/AddOptionToAttribute.php
new file mode 100644
index 0000000000000..c6b13f8a6e3ec
--- /dev/null
+++ b/app/code/Magento/Eav/Setup/AddOptionToAttribute.php
@@ -0,0 +1,210 @@
+setup = $setup;
+ }
+
+ /**
+ * Add Attribute Option
+ *
+ * @param array $option
+ *
+ * @return void
+ * @throws LocalizedException
+ */
+ public function execute(array $option): void
+ {
+ $optionTable = $this->setup->getTable('eav_attribute_option');
+ $optionValueTable = $this->setup->getTable('eav_attribute_option_value');
+
+ if (isset($option['value'])) {
+ $this->addValue($option, $optionTable, $optionValueTable);
+ } elseif (isset($option['values'])) {
+ $this->addValues($option, $optionTable, $optionValueTable);
+ }
+ }
+
+ /**
+ * Add option value
+ *
+ * @param array $option
+ * @param string $optionTable
+ * @param string $optionValueTable
+ *
+ * @return void
+ * @throws LocalizedException
+ */
+ private function addValue(array $option, string $optionTable, string $optionValueTable): void
+ {
+ $value = $option['value'];
+ foreach ($value as $optionId => $values) {
+ $intOptionId = (int)$optionId;
+ if (!empty($option['delete'][$optionId])) {
+ if ($intOptionId) {
+ $condition = ['option_id =?' => $intOptionId];
+ $this->setup->getConnection()->delete($optionTable, $condition);
+ }
+ continue;
+ }
+
+ if (!$intOptionId) {
+ $data = [
+ 'attribute_id' => $option['attribute_id'],
+ 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0,
+ ];
+ $this->setup->getConnection()->insert($optionTable, $data);
+ $intOptionId = $this->setup->getConnection()->lastInsertId($optionTable);
+ } else {
+ $data = [
+ 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0,
+ ];
+ $this->setup->getConnection()->update(
+ $optionTable,
+ $data,
+ ['option_id=?' => $intOptionId]
+ );
+ }
+
+ // Default value
+ if (!isset($values[0])) {
+ throw new LocalizedException(
+ __("The default option isn't defined. Set the option and try again.")
+ );
+ }
+ $condition = ['option_id =?' => $intOptionId];
+ $this->setup->getConnection()->delete($optionValueTable, $condition);
+ foreach ($values as $storeId => $value) {
+ $data = ['option_id' => $intOptionId, 'store_id' => $storeId, 'value' => $value];
+ $this->setup->getConnection()->insert($optionValueTable, $data);
+ }
+ }
+ }
+
+ /**
+ * Add option values
+ *
+ * @param array $option
+ * @param string $optionTable
+ * @param string $optionValueTable
+ *
+ * @return void
+ */
+ private function addValues(array $option, string $optionTable, string $optionValueTable): void
+ {
+ $values = $option['values'];
+ $attributeId = (int)$option['attribute_id'];
+ $existingOptions = $this->getExistingAttributeOptions($attributeId, $optionTable, $optionValueTable);
+ foreach ($values as $sortOrder => $value) {
+ // add option
+ $data = ['attribute_id' => $attributeId, 'sort_order' => $sortOrder];
+ if (!$this->isExistingOptionValue($value, $existingOptions)) {
+ $this->setup->getConnection()->insert($optionTable, $data);
+
+ //add option value
+ $intOptionId = $this->setup->getConnection()->lastInsertId($optionTable);
+ $data = ['option_id' => $intOptionId, 'store_id' => 0, 'value' => $value];
+ $this->setup->getConnection()->insert($optionValueTable, $data);
+ } elseif ($optionId = $this->getExistingOptionIdWithDiffSortOrder(
+ $sortOrder,
+ $value,
+ $existingOptions
+ )
+ ) {
+ $this->setup->getConnection()->update(
+ $optionTable,
+ ['sort_order' => $sortOrder],
+ ['option_id = ?' => $optionId]
+ );
+ }
+ }
+ }
+
+ /**
+ * Check if option value already exists
+ *
+ * @param string $value
+ * @param array $existingOptions
+ *
+ * @return bool
+ */
+ private function isExistingOptionValue(string $value, array $existingOptions): bool
+ {
+ foreach ($existingOptions as $option) {
+ if ($option['value'] == $value) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get existing attribute options
+ *
+ * @param int $attributeId
+ * @param string $optionTable
+ * @param string $optionValueTable
+ *
+ * @return array
+ */
+ private function getExistingAttributeOptions(int $attributeId, string $optionTable, string $optionValueTable): array
+ {
+ $select = $this->setup
+ ->getConnection()
+ ->select()
+ ->from(['o' => $optionTable])
+ ->reset('columns')
+ ->columns(['option_id', 'sort_order'])
+ ->join(['ov' => $optionValueTable], 'o.option_id = ov.option_id', 'value')
+ ->where(AttributeInterface::ATTRIBUTE_ID . ' = ?', $attributeId)
+ ->where('store_id = 0');
+
+ return $this->setup->getConnection()->fetchAll($select);
+ }
+
+ /**
+ * Check if option already exists, but sort_order differs
+ *
+ * @param int $sortOrder
+ * @param string $value
+ * @param array $existingOptions
+ *
+ * @return int|null
+ */
+ private function getExistingOptionIdWithDiffSortOrder(int $sortOrder, string $value, array $existingOptions): ?int
+ {
+ foreach ($existingOptions as $option) {
+ if ($option['value'] == $value && $option['sort_order'] != $sortOrder) {
+ return (int)$option['option_id'];
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php
index de285e81b1d03..c0eddf1d14f88 100644
--- a/app/code/Magento/Eav/Setup/EavSetup.php
+++ b/app/code/Magento/Eav/Setup/EavSetup.php
@@ -82,6 +82,11 @@ class EavSetup
*/
private $_defaultAttributeSetName = 'Default';
+ /**
+ * @var AddOptionToAttribute
+ */
+ private $addAttributeOption;
+
/**
* @var Code
*/
@@ -95,18 +100,23 @@ class EavSetup
* @param CacheInterface $cache
* @param CollectionFactory $attrGroupCollectionFactory
* @param Code|null $attributeCodeValidator
+ * @param AddOptionToAttribute|null $addAttributeOption
+ * @SuppressWarnings(PHPMD.LongVariable)
*/
public function __construct(
ModuleDataSetupInterface $setup,
Context $context,
CacheInterface $cache,
CollectionFactory $attrGroupCollectionFactory,
- Code $attributeCodeValidator = null
+ Code $attributeCodeValidator = null,
+ AddOptionToAttribute $addAttributeOption = null
) {
$this->cache = $cache;
$this->attrGroupCollectionFactory = $attrGroupCollectionFactory;
$this->attributeMapper = $context->getAttributeMapper();
$this->setup = $setup;
+ $this->addAttributeOption = $addAttributeOption
+ ?? ObjectManager::getInstance()->get(AddOptionToAttribute::class);
$this->attributeCodeValidator = $attributeCodeValidator ?: ObjectManager::getInstance()->get(
Code::class
);
@@ -868,62 +878,7 @@ public function addAttribute($entityTypeId, $code, array $attr)
*/
public function addAttributeOption($option)
{
- $optionTable = $this->setup->getTable('eav_attribute_option');
- $optionValueTable = $this->setup->getTable('eav_attribute_option_value');
-
- if (isset($option['value'])) {
- foreach ($option['value'] as $optionId => $values) {
- $intOptionId = (int)$optionId;
- if (!empty($option['delete'][$optionId])) {
- if ($intOptionId) {
- $condition = ['option_id =?' => $intOptionId];
- $this->setup->getConnection()->delete($optionTable, $condition);
- }
- continue;
- }
-
- if (!$intOptionId) {
- $data = [
- 'attribute_id' => $option['attribute_id'],
- 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0,
- ];
- $this->setup->getConnection()->insert($optionTable, $data);
- $intOptionId = $this->setup->getConnection()->lastInsertId($optionTable);
- } else {
- $data = [
- 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0,
- ];
- $this->setup->getConnection()->update(
- $optionTable,
- $data,
- ['option_id=?' => $intOptionId]
- );
- }
-
- // Default value
- if (!isset($values[0])) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __("The default option isn't defined. Set the option and try again.")
- );
- }
- $condition = ['option_id =?' => $intOptionId];
- $this->setup->getConnection()->delete($optionValueTable, $condition);
- foreach ($values as $storeId => $value) {
- $data = ['option_id' => $intOptionId, 'store_id' => $storeId, 'value' => $value];
- $this->setup->getConnection()->insert($optionValueTable, $data);
- }
- }
- } elseif (isset($option['values'])) {
- foreach ($option['values'] as $sortOrder => $label) {
- // add option
- $data = ['attribute_id' => $option['attribute_id'], 'sort_order' => $sortOrder];
- $this->setup->getConnection()->insert($optionTable, $data);
- $intOptionId = $this->setup->getConnection()->lastInsertId($optionTable);
-
- $data = ['option_id' => $intOptionId, 'store_id' => 0, 'value' => $label];
- $this->setup->getConnection()->insert($optionValueTable, $data);
- }
- }
+ $this->addAttributeOption->execute($option);
}
/**
diff --git a/app/code/Magento/Eav/Test/Unit/Setup/AddAttributeOptionTest.php b/app/code/Magento/Eav/Test/Unit/Setup/AddAttributeOptionTest.php
new file mode 100644
index 0000000000000..17376ceebbcb4
--- /dev/null
+++ b/app/code/Magento/Eav/Test/Unit/Setup/AddAttributeOptionTest.php
@@ -0,0 +1,200 @@
+createMock(ModuleDataSetupInterface::class);
+ $this->connectionMock = $this->createMock(Mysql::class);
+ $this->connectionMock->method('select')
+ ->willReturn($objectManager->getObject(Select::class));
+
+ $setupMock->method('getTable')->willReturn('some_table');
+ $setupMock->method('getConnection')->willReturn($this->connectionMock);
+
+ $this->operation = new AddOptionToAttribute($setupMock);
+ }
+
+ /**
+ * @throws LocalizedException
+ */
+ public function testAddNewOptions()
+ {
+ $this->connectionMock->method('fetchAll')->willReturn([]);
+ $this->connectionMock->expects($this->exactly(4))->method('insert');
+
+ $this->operation->execute(
+ [
+ 'values' => ['Black', 'White'],
+ 'attribute_id' => 4
+ ]
+ );
+ }
+
+ /**
+ * @throws LocalizedException
+ */
+ public function testAddExistingOptionsWithTheSameSortOrder()
+ {
+ $this->connectionMock->method('fetchAll')->willReturn(
+ [
+ ['option_id' => 1, 'sort_order' => 0, 'value' => 'Black'],
+ ['option_id' => 2, 'sort_order' => 1, 'value' => 'White'],
+ ]
+ );
+
+ $this->connectionMock->expects($this->never())->method('insert');
+ $this->connectionMock->expects($this->never())->method('update');
+
+ $this->operation->execute(
+ [
+ 'values' => ['Black', 'White'],
+ 'attribute_id' => 4
+ ]
+ );
+ }
+
+ /**
+ * @throws LocalizedException
+ */
+ public function testAddExistingOptionsWithDifferentSortOrder()
+ {
+ $this->connectionMock->method('fetchAll')->willReturn(
+ [
+ ['option_id' => 1, 'sort_order' => 13, 'value' => 'Black'],
+ ['option_id' => 2, 'sort_order' => 666, 'value' => 'White'],
+ ]
+ );
+
+ $this->connectionMock->expects($this->never())->method('insert');
+ $this->connectionMock->expects($this->exactly(2))->method('update');
+
+ $this->operation->execute(
+ [
+ 'values' => ['Black', 'White'],
+ 'attribute_id' => 4
+ ]
+ );
+ }
+
+ /**
+ * @throws LocalizedException
+ */
+ public function testAddMixedOptions()
+ {
+ $this->connectionMock->method('fetchAll')->willReturn(
+ [
+ ['option_id' => 1, 'sort_order' => 13, 'value' => 'Black'],
+ ]
+ );
+
+ $this->connectionMock->expects($this->exactly(2))->method('insert');
+ $this->connectionMock->expects($this->once())->method('update');
+
+ $this->operation->execute(
+ [
+ 'values' => ['Black', 'White'],
+ 'attribute_id' => 4
+ ]
+ );
+ }
+
+ /**
+ * @throws LocalizedException
+ */
+ public function testAddNewOption()
+ {
+ $this->connectionMock->expects($this->exactly(2))->method('insert');
+ $this->connectionMock->expects($this->once())->method('delete');
+
+ $this->operation->execute(
+ [
+ 'attribute_id' => 1,
+ 'order' => [0 => 13],
+ 'value' => [
+ [
+ 0 => 'zzz',
+ ],
+ ],
+ ]
+ );
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage The default option isn't defined. Set the option and try again.
+ */
+ public function testAddNewOptionWithoutDefaultValue()
+ {
+ $this->operation->execute(
+ [
+ 'attribute_id' => 1,
+ 'order' => [0 => 13],
+ 'value' => [[]],
+ ]
+ );
+ }
+
+ public function testDeleteOption()
+ {
+ $this->connectionMock->expects($this->never())->method('insert');
+ $this->connectionMock->expects($this->never())->method('update');
+ $this->connectionMock->expects($this->once())->method('delete');
+
+ $this->operation->execute(
+ [
+ 'attribute_id' => 1,
+ 'delete' => [13 => true],
+ 'value' => [
+ 13 => null,
+ ],
+ ]
+ );
+ }
+
+ public function testUpdateOption()
+ {
+ $this->connectionMock->expects($this->once())->method('insert');
+ $this->connectionMock->expects($this->once())->method('update');
+ $this->connectionMock->expects($this->once())->method('delete');
+
+ $this->operation->execute(
+ [
+ 'attribute_id' => 1,
+ 'value' => [
+ 13 => ['zzz'],
+ ],
+ ]
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Eav/Setup/AddOptionToAttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Setup/AddOptionToAttributeTest.php
new file mode 100644
index 0000000000000..c79a4e6caddba
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Eav/Setup/AddOptionToAttributeTest.php
@@ -0,0 +1,222 @@
+operation = $objectManager->get(AddOptionToAttribute::class);
+ /** @var ModuleDataSetupInterface $setup */
+ $this->setup = $objectManager->get(ModuleDataSetupInterface::class);
+ /** @var AttributeRepositoryInterface attrRepo */
+ $this->attrRepo = $objectManager->get(AttributeRepositoryInterface::class);
+ /** @var EavSetup $eavSetup */
+ $this->eavSetup = $objectManager->get(EavSetupFactory::class)
+ ->create(['setup' => $this->setup]);
+ $this->attributeId = $this->eavSetup->getAttributeId(Product::ENTITY, 'zzz');
+ }
+
+ /**
+ * @param bool $fetchPairs
+ *
+ * @return array
+ */
+ private function getAttributeOptions($fetchPairs = true): array
+ {
+ $optionTable = $this->setup->getTable('eav_attribute_option');
+ $optionValueTable = $this->setup->getTable('eav_attribute_option_value');
+
+ $select = $this->setup
+ ->getConnection()
+ ->select()
+ ->from(['o' => $optionTable])
+ ->reset('columns')
+ ->columns('sort_order')
+ ->join(['ov' => $optionValueTable], 'o.option_id = ov.option_id', 'value')
+ ->where(AttributeInterface::ATTRIBUTE_ID . ' = ?', $this->attributeId)
+ ->where('store_id = 0');
+
+ return $fetchPairs
+ ? $this->setup->getConnection()->fetchPairs($select)
+ : $this->setup->getConnection()->fetchAll($select);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php
+ */
+ public function testAddNewOptions()
+ {
+ $optionsBefore = $this->getAttributeOptions(false);
+ $this->operation->execute(
+ [
+ 'values' => ['new1', 'new2'],
+ 'attribute_id' => $this->attributeId
+ ]
+ );
+ $optionsAfter = $this->getAttributeOptions(false);
+ $this->assertEquals(count($optionsBefore) + 2, count($optionsAfter));
+ $this->assertArraySubset($optionsBefore, $optionsAfter);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php
+ */
+ public function testAddExistingOptionsWithTheSameSortOrder()
+ {
+ $optionsBefore = $this->getAttributeOptions();
+ $this->operation->execute(
+ [
+ 'values' => ['Black', 'White'],
+ 'attribute_id' => $this->attributeId
+ ]
+ );
+ $optionsAfter = $this->getAttributeOptions();
+ $this->assertEquals(count($optionsBefore), count($optionsAfter));
+ $this->assertArraySubset($optionsBefore, $optionsAfter);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php
+ */
+ public function testAddExistingOptionsWithDifferentSortOrder()
+ {
+ $optionsBefore = $this->getAttributeOptions();
+ $this->operation->execute(
+ [
+ 'values' => [666 => 'White', 777 => 'Black'],
+ 'attribute_id' => $this->attributeId
+ ]
+ );
+ $optionsAfter = $this->getAttributeOptions();
+ $this->assertSameSize($optionsBefore, array_intersect($optionsBefore, $optionsAfter));
+ $this->assertEquals($optionsAfter[777], $optionsBefore[0]);
+ $this->assertEquals($optionsAfter[666], $optionsBefore[1]);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php
+ */
+ public function testAddMixedOptions()
+ {
+ $sizeBefore = count($this->getAttributeOptions());
+ $this->operation->execute(
+ [
+ 'values' => [666 => 'Black', 'NewOption'],
+ 'attribute_id' => $this->attributeId
+ ]
+ );
+ $updatedOptions = $this->getAttributeOptions();
+ $this->assertEquals(count($updatedOptions), $sizeBefore + 1);
+ $this->assertEquals($updatedOptions[666], 'Black');
+ $this->assertEquals($updatedOptions[667], 'NewOption');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php
+ */
+ public function testAddNewOption()
+ {
+ $sizeBefore = count($this->getAttributeOptions());
+
+ $this->operation->execute(
+ [
+ 'attribute_id' => $this->attributeId,
+ 'order' => [0 => 13],
+ 'value' => [
+ [
+ 0 => 'NewOption',
+ ],
+ ],
+ ]
+ );
+ $updatedOptions = $this->getAttributeOptions();
+ $this->assertEquals(count($updatedOptions), $sizeBefore + 1);
+ $this->assertEquals($updatedOptions[13], 'NewOption');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php
+ */
+ public function testDeleteOption()
+ {
+ $optionsBefore = $this->getAttributeOptions();
+ $options = $this->attrRepo->get(Product::ENTITY, $this->attributeId)->getOptions();
+ /** @var AttributeOptionInterface $optionToDelete */
+ $optionToDelete = end($options);
+ $this->operation->execute(
+ [
+ 'attribute_id' => $this->attributeId,
+ 'delete' => [$optionToDelete->getValue() => true],
+ 'value' => [
+ $optionToDelete->getValue() => null,
+ ],
+ ]
+ );
+ $updatedOptions = $this->getAttributeOptions();
+ $this->assertArraySubset($updatedOptions, $optionsBefore);
+ $this->assertEquals(count($updatedOptions), count($optionsBefore) - 1);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php
+ */
+ public function testUpdateOption()
+ {
+ $optionsBefore = $this->getAttributeOptions();
+ $this->operation->execute(
+ [
+ 'attribute_id' => $this->attributeId,
+ 'value' => [
+ 0 => ['updatedValue'],
+ ],
+ ]
+ );
+ $optionsAfter = $this->getAttributeOptions();
+ $this->assertEquals($optionsAfter[0], 'updatedValue');
+ $this->assertSame(array_slice($optionsBefore, 1), array_slice($optionsAfter, 1));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options.php b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options.php
new file mode 100644
index 0000000000000..a53bdc68e6b1a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options.php
@@ -0,0 +1,46 @@
+get(ModuleDataSetupInterface::class);
+/** @var EavSetup $eavSetup */
+$eavSetup = $objectManager->get(EavSetupFactory::class)
+ ->create(['setup' => $setup]);
+$eavSetup->addAttribute(
+ \Magento\Catalog\Model\Product::ENTITY,
+ 'zzz',
+ [
+ 'type' => 'int',
+ 'backend' => '',
+ 'frontend' => '',
+ 'label' => 'zzz',
+ 'input' => 'select',
+ 'class' => '',
+ 'source' => '',
+ 'global' => 1,
+ 'visible' => true,
+ 'required' => true,
+ 'user_defined' => true,
+ 'default' => null,
+ 'searchable' => false,
+ 'filterable' => false,
+ 'comparable' => false,
+ 'visible_on_front' => false,
+ 'used_in_product_listing' => false,
+ 'unique' => true,
+ 'apply_to' => '',
+ 'system' => 1,
+ 'group' => 'General',
+ 'option' => ['values' => ["Black", "White", "Red", "Brown", "zzz", "Metallic"]]
+ ]
+);
diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options_rollback.php b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options_rollback.php
new file mode 100644
index 0000000000000..5b26403c797ec
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_with_options_rollback.php
@@ -0,0 +1,20 @@
+get(ModuleDataSetupInterface::class);
+/** @var EavSetup $eavSetup */
+$eavSetup = $objectManager->get(EavSetupFactory::class)
+ ->create(['setup' => $setup]);
+$eavSetup->removeAttribute(Product::ENTITY, 'zzz');
From a8ec393227a5aeca1ba9aca5c33064cd6e3d9a61 Mon Sep 17 00:00:00 2001
From: Nazarn96
Date: Wed, 22 May 2019 15:37:33 +0300
Subject: [PATCH 011/593] magento/magento#21424 static-test-fix
---
app/code/Magento/Eav/Setup/EavSetup.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php
index c0eddf1d14f88..d440a84fc8e65 100644
--- a/app/code/Magento/Eav/Setup/EavSetup.php
+++ b/app/code/Magento/Eav/Setup/EavSetup.php
@@ -577,6 +577,7 @@ public function addAttributeGroup($entityTypeId, $setId, $name, $sortOrder = nul
if (empty($data['attribute_group_code'])) {
if (empty($attributeGroupCode)) {
// in the following code md5 is not used for security purposes
+ // phpcs:disable Magento2.Security.InsecureFunction
$attributeGroupCode = md5($name);
}
$data['attribute_group_code'] = $attributeGroupCode;
From 63063bcc6cb9a2343a906d0e05e3f7490b8a6a0d Mon Sep 17 00:00:00 2001
From: Ihor Sviziev
Date: Tue, 14 May 2019 13:09:50 +0300
Subject: [PATCH 012/593] magento/magento2#22880 Fix random fails during
parallel SCD execution
No need to use locks when running in CLI mode
---
.../Magento/Framework/View/Asset/LockerProcess.php | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lib/internal/Magento/Framework/View/Asset/LockerProcess.php b/lib/internal/Magento/Framework/View/Asset/LockerProcess.php
index d320f951548b4..d74ee0490cdfb 100644
--- a/lib/internal/Magento/Framework/View/Asset/LockerProcess.php
+++ b/lib/internal/Magento/Framework/View/Asset/LockerProcess.php
@@ -62,7 +62,7 @@ public function __construct(Filesystem $filesystem)
*/
public function lockProcess($lockName)
{
- if ($this->getState()->getMode() == State::MODE_PRODUCTION) {
+ if ($this->getState()->getMode() === State::MODE_PRODUCTION || PHP_SAPI === 'cli') {
return;
}
@@ -78,11 +78,12 @@ public function lockProcess($lockName)
/**
* @inheritdoc
+ *
* @throws FileSystemException
*/
public function unlockProcess()
{
- if ($this->getState()->getMode() == State::MODE_PRODUCTION) {
+ if ($this->getState()->getMode() === State::MODE_PRODUCTION || PHP_SAPI === 'cli') {
return;
}
@@ -115,9 +116,10 @@ private function isProcessLocked()
}
/**
- * Get name of lock file
+ * Get path to lock file
*
* @param string $name
+ *
* @return string
*/
private function getFilePath($name)
@@ -126,7 +128,10 @@ private function getFilePath($name)
}
/**
+ * Get State object
+ *
* @return State
+ *
* @deprecated 100.1.1
*/
private function getState()
From 4a620bd1e6b9c21978c2c3e70d4d5338a71f676e Mon Sep 17 00:00:00 2001
From: Krissy Hiserote
Date: Fri, 24 May 2019 09:33:03 -0500
Subject: [PATCH 013/593] MC-16650: Product Attribute Type Price Not Displaying
---
.../Model/Layer/Filter/Decimal.php | 8 +++--
.../Model/Search/RequestGenerator/Decimal.php | 33 ++++++++++++++++++-
.../Search/RequestGenerator/DecimalTest.php | 22 +++++++++++--
3 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
index e9fb1070fedd5..ea5492212c1f1 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\CatalogSearch\Model\Layer\Filter;
use Magento\Catalog\Model\Layer\Filter\AbstractFilter;
@@ -74,7 +76,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request)
->getProductCollection()
->addFieldToFilter(
$this->getAttributeModel()->getAttributeCode(),
- ['from' => $from, 'to' => $to]
+ ['from' => $from, 'to' => empty($to) || $from === $to ? $to : $to - 0.001]
);
$this->getLayer()->getState()->addFilter(
@@ -111,7 +113,7 @@ protected function _getItemsData()
$from = '';
}
if ($to == '*') {
- $to = null;
+ $to = '';
}
$label = $this->renderRangeLabel(empty($from) ? 0 : $from, $to);
$value = $from . '-' . $to;
@@ -138,7 +140,7 @@ protected function _getItemsData()
protected function renderRangeLabel($fromPrice, $toPrice)
{
$formattedFromPrice = $this->priceCurrency->format($fromPrice);
- if ($toPrice === null) {
+ if ($toPrice === '') {
return __('%1 and above', $formattedFromPrice);
} else {
if ($fromPrice != $toPrice) {
diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php
index b3d39a48fe9fc..c9f738b07c175 100644
--- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php
+++ b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php
@@ -3,18 +3,36 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\CatalogSearch\Model\Search\RequestGenerator;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\Framework\Search\Request\BucketInterface;
use Magento\Framework\Search\Request\FilterInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\ObjectManager;
+use Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory;
+use Magento\Store\Model\ScopeInterface;
/**
* Catalog search range request generator.
*/
class Decimal implements GeneratorInterface
{
+ /**
+ * @var \Magento\Store\Model\ScopeInterface
+ */
+ private $scopeConfig;
+
+ /**
+ * @param \Magento\Store\Model\ScopeInterface|null $scopeConfig
+ */
+ public function __construct(ScopeConfigInterface $scopeConfig = null)
+ {
+ $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
+ }
+
/**
* @inheritdoc
*/
@@ -38,8 +56,21 @@ public function getAggregationData(Attribute $attribute, $bucketName)
'type' => BucketInterface::TYPE_DYNAMIC,
'name' => $bucketName,
'field' => $attribute->getAttributeCode(),
- 'method' => 'manual',
+ 'method' => $this->getRangeCalculation(),
'metric' => [['type' => 'count']],
];
}
+
+ /**
+ * Get range calculation by what was set in the configuration
+ *
+ * @return string
+ */
+ private function getRangeCalculation(): string
+ {
+ return $this->scopeConfig->getValue(
+ AlgorithmFactory::XML_PATH_RANGE_CALCULATION,
+ ScopeInterface::SCOPE_STORE
+ );
+ }
}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php
index 8157c1fa8fa82..f04188fbf7bdd 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\CatalogSearch\Test\Unit\Model\Search\RequestGenerator;
@@ -10,7 +11,11 @@
use Magento\CatalogSearch\Model\Search\RequestGenerator\Decimal;
use Magento\Framework\Search\Request\BucketInterface;
use Magento\Framework\Search\Request\FilterInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+/**
+ * Test catalog search range request generator.
+ */
class DecimalTest extends \PHPUnit\Framework\TestCase
{
/** @var Decimal */
@@ -19,14 +24,23 @@ class DecimalTest extends \PHPUnit\Framework\TestCase
/** @var Attribute|\PHPUnit_Framework_MockObject_MockObject */
private $attribute;
+ /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */
+ private $scopeConfigMock;
+
protected function setUp()
{
$this->attribute = $this->getMockBuilder(Attribute::class)
->disableOriginalConstructor()
->setMethods(['getAttributeCode'])
->getMockForAbstractClass();
+ $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)
+ ->setMethods(['getValue'])
+ ->getMockForAbstractClass();
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
- $this->decimal = $objectManager->getObject(Decimal::class);
+ $this->decimal = $objectManager->getObject(
+ Decimal::class,
+ ['scopeConfig' => $this->scopeConfigMock]
+ );
}
public function testGetFilterData()
@@ -51,16 +65,20 @@ public function testGetAggregationData()
{
$bucketName = 'test_bucket_name';
$attributeCode = 'test_attribute_code';
+ $method = 'manual';
$expected = [
'type' => BucketInterface::TYPE_DYNAMIC,
'name' => $bucketName,
'field' => $attributeCode,
- 'method' => 'manual',
+ 'method' => $method,
'metric' => [['type' => 'count']],
];
$this->attribute->expects($this->atLeastOnce())
->method('getAttributeCode')
->willReturn($attributeCode);
+ $this->scopeConfigMock->expects($this->once())
+ ->method('getValue')
+ ->willReturn($method);
$actual = $this->decimal->getAggregationData($this->attribute, $bucketName);
$this->assertEquals($expected, $actual);
}
From e036a53959192bfeea21a8df7cc7b07891c71138 Mon Sep 17 00:00:00 2001
From: Kieu Phan
Date: Fri, 24 May 2019 14:33:27 -0500
Subject: [PATCH 014/593] MC-16650: Product Attribute Type Price Not Displaying
Added MFTF tests
---
.../Test/Mftf/Data/ProductAttributeData.xml | 22 +++++
.../Mftf/Section/AdminProductFormSection.xml | 1 +
.../StorefrontCategoryFilterSection.xml | 1 +
.../LayerNavigationOfCatalogSearchTest.xml | 85 +++++++++++++++++++
4 files changed, 109 insertions(+)
create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml
index 817dd637f81dd..31a3745ca4417 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml
@@ -278,6 +278,28 @@
false
ProductAttributeFrontendLabel
+
+ attribute
+ price
+ global
+ false
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ ProductAttributeFrontendLabel
+
text
defaultValue
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
index f515171e835db..9ef3de033f808 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
@@ -207,5 +207,6 @@
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
index ddec4428f90e2..21a0d72dc991c 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
@@ -11,5 +11,6 @@
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
new file mode 100644
index 0000000000000..3bc086c62bb3b
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From cdc1c77a11aa1ce77cb930267e141f96e8552e4a Mon Sep 17 00:00:00 2001
From: Kieu Phan
Date: Fri, 24 May 2019 17:48:46 -0500
Subject: [PATCH 015/593] MC-16650: Product Attribute Type Price Not Displaying
Added assertion for attribute lable and set the default configuration in
Admin
---
.../Test/Mftf/Section/AdminProductFormSection.xml | 1 +
.../Mftf/Section/StorefrontCategoryFilterSection.xml | 3 ++-
.../Mftf/Test/LayerNavigationOfCatalogSearchTest.xml | 12 ++++++++----
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
index 9ef3de033f808..76659dfa1c896 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
@@ -208,5 +208,6 @@
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
index 21a0d72dc991c..31667c9b89935 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
@@ -11,6 +11,7 @@
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
index 3bc086c62bb3b..32d7ebcb1d7c5 100644
--- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
@@ -11,13 +11,14 @@
-
-
-
+
+
+
+
@@ -43,6 +44,7 @@
+
@@ -58,7 +60,9 @@
-
+
+
+
From 06926c83b5b99c115409cec4b9c19b3909c6ba4d Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Wed, 29 May 2019 11:52:34 +0300
Subject: [PATCH 016/593] MAGETWO-44170: Not pass function test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Unskip tests.
---
.../TestCase/Product/ProductTypeSwitchingOnUpdateTest.php | 1 +
.../TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml | 7 +++++++
2 files changed, 8 insertions(+)
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
index 90cd6bdb76328..2abd17fce5b45 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
@@ -36,6 +36,7 @@
class ProductTypeSwitchingOnUpdateTest extends Injectable
{
/* tags */
+ const TEST_TYPE = 'acceptance_test';
const MVP = 'yes';
/* end tags */
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
index 5fa1cfe5e5911..3d3e36754e92c 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
@@ -11,6 +11,7 @@
catalogProductSimple::default
configurableProduct::default
-
+ test_type:acceptance_test
@@ -34,6 +35,7 @@
+ test_type:acceptance_test
configurableProduct::default
catalogProductVirtual::required_fields
deleteVariations
@@ -48,6 +50,7 @@
+ test_type:acceptance_test
catalogProductVirtual::default
configurableProduct::not_virtual_for_type_switching
-
@@ -60,6 +63,7 @@
+ test_type:acceptance_test
catalogProductVirtual::default
downloadableProduct::default
-
@@ -71,6 +75,7 @@
+ test_type:acceptance_test
downloadableProduct::default
catalogProductSimple::default
-
@@ -78,6 +83,7 @@
+ test_type:acceptance_test
downloadableProduct::default
configurableProduct::not_virtual_for_type_switching
clearDownloadableData
@@ -97,6 +103,7 @@
+ test_type:acceptance_test
catalogProductSimple::default
downloadableProduct::default
-
From ea64edf1b2c274b407312adc8ce8ea3e343d0b91 Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Wed, 29 May 2019 14:51:39 +0300
Subject: [PATCH 017/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix visibility and category filters;
- Add integration tests.
---
.../ResourceModel/Product/Collection.php | 48 ++++++++++-
.../ResourceModel/Product/CollectionTest.php | 82 +++++++++++++++++--
2 files changed, 119 insertions(+), 11 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 384b6ddcefc31..cabbcb67fb5a7 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Catalog\Model\ResourceModel\Product;
@@ -22,6 +23,7 @@
use Magento\Framework\Indexer\DimensionFactory;
use Magento\Store\Model\Indexer\WebsiteDimensionProvider;
use Magento\Store\Model\Store;
+use Magento\Catalog\Api\CategoryRepositoryInterface;
/**
* Product collection
@@ -302,6 +304,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
*/
private $urlFinder;
+ /**
+ * @var CategoryRepositoryInterface
+ */
+ private $categoryRepository;
+
/**
* Collection constructor
*
@@ -330,6 +337,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
* @param TableMaintainer|null $tableMaintainer
* @param PriceTableResolver|null $priceTableResolver
* @param DimensionFactory|null $dimensionFactory
+ * @param CategoryRepositoryInterface|null $categoryRepository
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -358,7 +366,8 @@ public function __construct(
MetadataPool $metadataPool = null,
TableMaintainer $tableMaintainer = null,
PriceTableResolver $priceTableResolver = null,
- DimensionFactory $dimensionFactory = null
+ DimensionFactory $dimensionFactory = null,
+ CategoryRepositoryInterface $categoryRepository = null
) {
$this->moduleManager = $moduleManager;
$this->_catalogProductFlatState = $catalogProductFlatState;
@@ -392,6 +401,8 @@ public function __construct(
$this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get(PriceTableResolver::class);
$this->dimensionFactory = $dimensionFactory
?: ObjectManager::getInstance()->get(DimensionFactory::class);
+ $this->categoryRepository = $categoryRepository ?: ObjectManager::getInstance()
+ ->get(CategoryRepositoryInterface::class);
}
/**
@@ -1673,7 +1684,11 @@ public function addFilterByRequiredOptions()
public function setVisibility($visibility)
{
$this->_productLimitationFilters['visibility'] = $visibility;
- $this->_applyProductLimitations();
+ if ($this->getStoreId() == Store::DEFAULT_STORE_ID) {
+ $this->addAttributeToFilter('visibility', $visibility);
+ } else {
+ $this->_applyProductLimitations();
+ }
return $this;
}
@@ -2053,12 +2068,14 @@ protected function _applyProductLimitations()
protected function _applyZeroStoreProductLimitations()
{
$filters = $this->_productLimitationFilters;
+ $categories = [];
+ $categories = $this->getChildrenCategories((int)$filters['category_id'], $categories);
$conditions = [
'cat_pro.product_id=e.entity_id',
$this->getConnection()->quoteInto(
- 'cat_pro.category_id=?',
- $filters['category_id']
+ 'cat_pro.category_id IN (?)',
+ $categories
),
];
$joinCond = join(' AND ', $conditions);
@@ -2079,6 +2096,29 @@ protected function _applyZeroStoreProductLimitations()
return $this;
}
+ /**
+ * Get children categories.
+ *
+ * @param int $categoryId
+ * @param array $categories
+ * @return array
+ */
+ private function getChildrenCategories(int $categoryId, array $categories): array
+ {
+ $category = $this->categoryRepository->get($categoryId);
+ $categories[] = $category->getId();
+ if ($category->getIsAnchor()) {
+ $categoryChildren = $category->getChildren();
+ $categoryChildrenIds = explode(',', $categoryChildren);
+ foreach ($categoryChildrenIds as $categoryChildrenId) {
+ if ($categoryChildrenId) {
+ $categories = $this->getChildrenCategories((int)$categoryChildrenId, $categories);
+ }
+ }
+ }
+ return $categories;
+ }
+
/**
* Add category ids to loaded items
*
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
index 4cc6265a992fa..80d700c7e54ff 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
@@ -3,8 +3,16 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Catalog\Model\ResourceModel\Product;
+use Magento\Catalog\Model\Product\Visibility;
+use Magento\Framework\App\Area;
+use Magento\Framework\App\State;
+use Magento\Store\Model\Store;
+use Magento\TestFramework\Helper\Bootstrap;
+
/**
* Collection test
*/
@@ -31,15 +39,15 @@ class CollectionTest extends \PHPUnit\Framework\TestCase
*/
protected function setUp()
{
- $this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ $this->collection = Bootstrap::getObjectManager()->create(
\Magento\Catalog\Model\ResourceModel\Product\Collection::class
);
- $this->processor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ $this->processor = Bootstrap::getObjectManager()->create(
\Magento\Catalog\Model\Indexer\Product\Price\Processor::class
);
- $this->productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ $this->productRepository = Bootstrap::getObjectManager()->create(
\Magento\Catalog\Api\ProductRepositoryInterface::class
);
}
@@ -54,7 +62,7 @@ public function testAddPriceDataOnSchedule()
$this->processor->getIndexer()->setScheduled(true);
$this->assertTrue($this->processor->getIndexer()->isScheduled());
- $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ $productRepository = Bootstrap::getObjectManager()
->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
$product = $productRepository->get('simple');
@@ -73,7 +81,7 @@ public function testAddPriceDataOnSchedule()
//reindexing
$this->processor->getIndexer()->reindexList([1]);
- $this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ $this->collection = Bootstrap::getObjectManager()->create(
\Magento\Catalog\Model\ResourceModel\Product\Collection::class
);
$this->collection->addPriceData(0, 1);
@@ -89,6 +97,66 @@ public function testAddPriceDataOnSchedule()
$this->processor->getIndexer()->setScheduled(false);
}
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/products.php
+ * @magentoDbIsolation disabled
+ */
+ public function testSetVisibility()
+ {
+ $appState = Bootstrap::getObjectManager()
+ ->create(State::class);
+ $appState->setAreaCode(Area::AREA_CRONTAB);
+ $this->collection->setStoreId(Store::DEFAULT_STORE_ID);
+ $this->collection->setVisibility([Visibility::VISIBILITY_BOTH]);
+ $this->collection->load();
+ /** @var \Magento\Catalog\Api\Data\ProductInterface[] $product */
+ $items = $this->collection->getItems();
+ $this->assertCount(2, $items);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/category_product.php
+ * @magentoDbIsolation disabled
+ */
+ public function testSetCategoryWithStoreFilter()
+ {
+ $appState = Bootstrap::getObjectManager()
+ ->create(State::class);
+ $appState->setAreaCode(Area::AREA_CRONTAB);
+
+ $category = \Magento\Framework\App\ObjectManager::getInstance()->get(
+ \Magento\Catalog\Model\Category::class
+ )->load(333);
+ $this->collection->addCategoryFilter($category)->addStoreFilter(1);
+ $this->collection->load();
+
+ $collectionStoreFilterAfter = Bootstrap::getObjectManager()->create(
+ \Magento\Catalog\Model\ResourceModel\Product\Collection::class
+ );
+ $collectionStoreFilterAfter->addStoreFilter(1)->addCategoryFilter($category);
+ $collectionStoreFilterAfter->load();
+ $this->assertEquals($this->collection->getItems(), $collectionStoreFilterAfter->getItems());
+ $this->assertCount(1, $collectionStoreFilterAfter->getItems());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/categories.php
+ * @magentoDbIsolation disabled
+ */
+ public function testSetCategoryFilter()
+ {
+ $appState = Bootstrap::getObjectManager()
+ ->create(State::class);
+ $appState->setAreaCode(Area::AREA_CRONTAB);
+
+ $category = \Magento\Framework\App\ObjectManager::getInstance()->get(
+ \Magento\Catalog\Model\Category::class
+ )->load(3);
+ $this->collection->addCategoryFilter($category);
+ $this->collection->load();
+ $this->assertEquals($this->collection->getSize(), 3);
+ }
+
/**
* @magentoDataFixture Magento/Catalog/_files/products.php
* @magentoAppIsolation enabled
@@ -98,7 +166,7 @@ public function testAddPriceDataOnSave()
{
$this->processor->getIndexer()->setScheduled(false);
$this->assertFalse($this->processor->getIndexer()->isScheduled());
- $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ $productRepository = Bootstrap::getObjectManager()
->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
$product = $productRepository->get('simple');
@@ -184,7 +252,7 @@ public function testJoinTable()
$productTable = $this->collection->getTable('catalog_product_entity');
$urlRewriteTable = $this->collection->getTable('url_rewrite');
- // phpcs:ignore
+ // phpcs:ignore Magento2.SQL.RawQuery
$expected = 'SELECT `e`.*, `alias`.`request_path` FROM `' . $productTable . '` AS `e`'
. ' LEFT JOIN `' . $urlRewriteTable . '` AS `alias` ON (alias.entity_id =e.entity_id)'
. ' AND (alias.entity_type = \'product\')';
From 2358d1c605472e862ae05a539dd9a220f9503567 Mon Sep 17 00:00:00 2001
From: Kieu Phan
Date: Fri, 31 May 2019 15:00:47 -0500
Subject: [PATCH 018/593] MC-16650: Product Attribute Type Price Not Displaying
Changes entity name for MFTF test code
---
.../Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml | 2 +-
.../Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml
index 31a3745ca4417..ca2637e9314c7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml
@@ -278,7 +278,7 @@
false
ProductAttributeFrontendLabel
-
+
attribute
price
global
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
index 32d7ebcb1d7c5..cc414a1088ecb 100644
--- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
@@ -19,8 +19,7 @@
-
-
+
@@ -31,6 +30,7 @@
+
From 15d33b8b1c9633d0a96c244c20f10abcf50823c4 Mon Sep 17 00:00:00 2001
From: Kieu Phan
Date: Mon, 3 Jun 2019 16:44:46 -0500
Subject: [PATCH 019/593] MC-16650: Product Attribute Type Price Not Displaying
Changed selector of grabTextFrom created attribute and removed the last
assertion which is not scope of this test
---
.../Test/Mftf/Section/StorefrontCategoryFilterSection.xml | 3 +--
.../Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml | 6 +++---
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
index 31667c9b89935..fe3d3e298cbc9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryFilterSection.xml
@@ -11,7 +11,6 @@
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
index cc414a1088ecb..f1c655f4b5ab1 100644
--- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
@@ -14,6 +14,7 @@
+
@@ -44,7 +45,7 @@
-
+
@@ -61,8 +62,7 @@
-
-
+
From 14e6b3fc9c95023c00c670ae349de31e5a589994 Mon Sep 17 00:00:00 2001
From: Kieu Phan
Date: Mon, 3 Jun 2019 16:50:21 -0500
Subject: [PATCH 020/593] MC-16650: Product Attribute Type Price Not Displaying
Removed unnecessary selector after changing assertion in test
---
.../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
index 76659dfa1c896..9ef3de033f808 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
@@ -208,6 +208,5 @@
-
\ No newline at end of file
From a4f2c53f19359fd4b942e3609bfc314c07404780 Mon Sep 17 00:00:00 2001
From: Kieu Phan
Date: Mon, 3 Jun 2019 17:01:12 -0500
Subject: [PATCH 021/593] MC-16650: Product Attribute Type Price Not Displaying
---
.../Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
index f1c655f4b5ab1..e91fb9b7a55cc 100644
--- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
@@ -61,8 +61,7 @@
-
-
+
From 8f9f248cd73446c47515ad738022f08b977ad2ff Mon Sep 17 00:00:00 2001
From: Krissy Hiserote
Date: Tue, 4 Jun 2019 11:35:21 -0500
Subject: [PATCH 022/593] MC-16650: Product Attribute Type Price Not Displaying
- extract hard to read logic and add integration tests
---
.../Model/Layer/Filter/Decimal.php | 21 ++++-
.../attribute_special_price_filterable.php | 12 +++
.../_files/multiple_visible_products.php | 76 +++++++++++++++++++
.../multiple_visible_products_rollback.php | 28 +++++++
.../Model/Layer/Filter/DecimalTest.php | 74 ++++++++++++++++++
5 files changed, 210 insertions(+), 1 deletion(-)
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
index ea5492212c1f1..4c83c3f7184f1 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
@@ -76,7 +76,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request)
->getProductCollection()
->addFieldToFilter(
$this->getAttributeModel()->getAttributeCode(),
- ['from' => $from, 'to' => empty($to) || $from === $to ? $to : $to - 0.001]
+ ['from' => $from, 'to' => $this->getToRangeValue($from, $to)]
);
$this->getLayer()->getState()->addFilter(
@@ -149,4 +149,23 @@ protected function renderRangeLabel($fromPrice, $toPrice)
return __('%1 - %2', $formattedFromPrice, $this->priceCurrency->format($toPrice));
}
}
+
+ /**
+ * Get the to range value
+ *
+ * When the range is 10-20 we only need to get products that are in the 10-19.99 range.
+ * 20 should be in the next range group.
+ *
+ * @param float|string $from
+ * @param float|string $to
+ * @return float|string
+ */
+ private function getToRangeValue($from, $to)
+ {
+ if (!empty($to) && $from !== $to) {
+ $to -= 0.001;
+ }
+
+ return $to;
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
new file mode 100644
index 0000000000000..3c92db99cd4f1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
@@ -0,0 +1,12 @@
+create(
+ \Magento\Catalog\Setup\CategorySetup::class
+);
+
+$installer->updateAttribute('catalog_product', 'special_price', 'is_filterable', 1);
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
new file mode 100644
index 0000000000000..4ace41d23c872
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
@@ -0,0 +1,76 @@
+create(\Magento\Catalog\Model\Product::class);
+$product->isObjectNew(true);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setId(10)
+ ->setAttributeSetId(4)
+ ->setName('Simple Product1')
+ ->setSku('simple1')
+ ->setTaxClassId('none')
+ ->setDescription('description')
+ ->setShortDescription('short description')
+ ->setOptionsContainer('container1')
+ ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART)
+ ->setPrice(15)
+ ->setWeight(10)
+ ->setMetaTitle('meta title')
+ ->setMetaKeyword('meta keyword')
+ ->setMetaDescription('meta description')
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setWebsiteIds([1])
+ ->setCategoryIds([2])
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setSpecialPrice('10')
+ ->save();
+
+$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
+$product->isObjectNew(true);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setId(11)
+ ->setAttributeSetId(4)
+ ->setName('Simple Product2')
+ ->setSku('simple2')
+ ->setTaxClassId('none')
+ ->setDescription('description')
+ ->setShortDescription('short description')
+ ->setOptionsContainer('container1')
+ ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_ON_GESTURE)
+ ->setPrice(25)
+ ->setWeight(20)
+ ->setMetaTitle('meta title')
+ ->setMetaKeyword('meta keyword')
+ ->setMetaDescription('meta description')
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setWebsiteIds([1])
+ ->setCategoryIds([2])
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setSpecialPrice('20')
+ ->save();
+
+$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
+$product->isObjectNew(true);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setId(12)
+ ->setAttributeSetId(4)
+ ->setName('Simple Product3')
+ ->setSku('simple3')
+ ->setTaxClassId('none')
+ ->setDescription('description')
+ ->setShortDescription('short description')
+ ->setPrice(35)
+ ->setWeight(30)
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setWebsiteIds([1])
+ ->setCategoryIds([2])
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 140, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setSpecialPrice('30')
+ ->save();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
new file mode 100644
index 0000000000000..a9155d3fadf0b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
@@ -0,0 +1,28 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+
+foreach (['simple1', 'simple2', 'simple3'] as $sku) {
+ try {
+ $product = $productRepository->get($sku, false, null, true);
+ $productRepository->delete($product);
+ } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
+ //Product already removed
+ }
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
index f0c8402c51879..33dea3ea37179 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
@@ -49,6 +49,80 @@ protected function setUp()
$this->_model->setAttributeModel($attribute);
}
+ /**
+ * Test the product collection returns the correct number of items after the filter is applied.
+ *
+ * @magentoDataFixture Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
+ * @magentoDataFixture Magento/Catalog/_files/multiple_visible_products.php
+ * @magentoDbIsolation disabled
+ */
+ public function testApplyProductCollection()
+ {
+ /** @var $objectManager \Magento\TestFramework\ObjectManager */
+ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $category = $objectManager->create(\Magento\Catalog\Model\Category::class);
+ $category->load(2);
+ $this->_model->getLayer()->setCurrentCategory($category);
+
+ /** @var $attribute \Magento\Catalog\Model\Entity\Attribute */
+ $attribute = $objectManager->create(\Magento\Catalog\Model\Entity\Attribute::class);
+ $attribute->loadByCode('catalog_product', 'special_price');
+ $this->_model->setAttributeModel($attribute);
+
+ /** @var $objectManager \Magento\TestFramework\ObjectManager */
+ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ /** @var $request \Magento\TestFramework\Request */
+ $request = $objectManager->get(\Magento\TestFramework\Request::class);
+ $request->setParam('special_price', '10-20');
+ $result = $this->_model->apply($request);
+ $collection = $this->_model->getLayer()->getProductCollection();
+ $size = $collection->getSize();
+ $this->assertEquals(
+ 1,
+ $size
+ );
+ }
+
+ /**
+ * Test the filter label is correct
+ */
+ public function testApplyFilterLabel()
+ {
+ /** @var $objectManager \Magento\TestFramework\ObjectManager */
+ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ /** @var $request \Magento\TestFramework\Request */
+ $request = $objectManager->get(\Magento\TestFramework\Request::class);
+ $request->setParam('weight', '10-20');
+ $this->_model->apply($request);
+
+ $filters = $this->_model->getLayer()->getState()->getFilters();
+ $this->assertArrayHasKey(0, $filters);
+ $this->assertEquals(
+ '$10.00 - $19.99 ',
+ (string)$filters[0]->getLabel()
+ );
+ }
+
+ /**
+ * Test the filter label is correct when there is empty To value
+ */
+ public function testApplyFilterLabelWithEmptyToValue()
+ {
+ /** @var $objectManager \Magento\TestFramework\ObjectManager */
+ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ /** @var $request \Magento\TestFramework\Request */
+ $request = $objectManager->get(\Magento\TestFramework\Request::class);
+ $request->setParam('weight', '10-');
+ $this->_model->apply($request);
+
+ $filters = $this->_model->getLayer()->getState()->getFilters();
+ $this->assertArrayHasKey(0, $filters);
+ $this->assertEquals(
+ '$10.00 and above',
+ (string)$filters[0]->getLabel()
+ );
+ }
+
public function testApplyNothing()
{
$this->assertEmpty($this->_model->getData('range'));
From cf2eb43d75d7e5004f18bbf55483f2787553171b Mon Sep 17 00:00:00 2001
From: Evgeny Petrov
Date: Fri, 7 Jun 2019 14:25:05 +0300
Subject: [PATCH 023/593] MC-15341: Default product numbers to display results
in poor display on Desktop
---
app/code/Magento/Catalog/etc/config.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml
index 3a842166a3825..a9ed43fae1d16 100644
--- a/app/code/Magento/Catalog/etc/config.xml
+++ b/app/code/Magento/Catalog/etc/config.xml
@@ -23,7 +23,7 @@
grid-list
- 9,15,30
+ 12,24,36
5,10,15,20,25
9
10
From fdc9e9d9a5c92af0c50f1d4c3c4dffc6d61d7209 Mon Sep 17 00:00:00 2001
From: Evgeny Petrov
Date: Mon, 10 Jun 2019 14:13:09 +0300
Subject: [PATCH 024/593] MC-15341: Default product numbers to display results
in poor display on Desktop
---
app/code/Magento/Catalog/etc/config.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml
index a9ed43fae1d16..20511f4ff2295 100644
--- a/app/code/Magento/Catalog/etc/config.xml
+++ b/app/code/Magento/Catalog/etc/config.xml
@@ -25,7 +25,7 @@
grid-list
12,24,36
5,10,15,20,25
- 9
+ 12
10
0
position
From c240fda0d5e7dd2f6ed6ef2f8ca8bce27319c87a Mon Sep 17 00:00:00 2001
From: Aliaksei Yakimovich2
Date: Thu, 13 Jun 2019 14:41:53 +0300
Subject: [PATCH 025/593] MAGETWO-70803: [GITHUB] Inconsistent CSV file Import
error: #7495
- Fixed an issue with dublicated file validation;
---
app/code/Magento/CatalogImportExport/Model/Import/Product.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index 0b7fbaf86826b..a8395aac0f4eb 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -1508,6 +1508,7 @@ public function getImagesFromRow(array $rowData)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
* @throws LocalizedException
+ * phpcs:disable Generic.Metrics.NestingLevel
*/
protected function _saveProducts()
{
@@ -1882,6 +1883,7 @@ protected function _saveProducts()
return $this;
}
+ //phpcs:enable Generic.Metrics.NestingLevel
/**
* Prepare array with image states (visible or hidden from product page)
@@ -2726,8 +2728,6 @@ protected function _saveValidatedBunches()
try {
$rowData = $source->current();
} catch (\InvalidArgumentException $e) {
- $this->addRowError($e->getMessage(), $this->_processedRowsCount);
- $this->_processedRowsCount++;
$source->next();
continue;
}
From 1ed2728a4566dc6cda4ab7d3e99e7eeda34844df Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Thu, 13 Jun 2019 18:50:33 +0300
Subject: [PATCH 026/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix CR comment
---
.../ResourceModel/Product/Collection.php | 20 +++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 6e790abd5372a..76152250c41df 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -23,7 +23,7 @@
use Magento\Framework\Indexer\DimensionFactory;
use Magento\Store\Model\Indexer\WebsiteDimensionProvider;
use Magento\Store\Model\Store;
-use Magento\Catalog\Api\CategoryRepositoryInterface;
+use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
/**
* Product collection
@@ -305,9 +305,9 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
private $urlFinder;
/**
- * @var CategoryRepositoryInterface
+ * @var CollectionFactory
*/
- private $categoryRepository;
+ private $categoryCollectionFactory;
/**
* Collection constructor
@@ -337,7 +337,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
* @param TableMaintainer|null $tableMaintainer
* @param PriceTableResolver|null $priceTableResolver
* @param DimensionFactory|null $dimensionFactory
- * @param CategoryRepositoryInterface|null $categoryRepository
+ * @param CollectionFactory|null $categoryCollectionFactory
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -367,7 +367,7 @@ public function __construct(
TableMaintainer $tableMaintainer = null,
PriceTableResolver $priceTableResolver = null,
DimensionFactory $dimensionFactory = null,
- CategoryRepositoryInterface $categoryRepository = null
+ CollectionFactory $categoryCollectionFactory = null
) {
$this->moduleManager = $moduleManager;
$this->_catalogProductFlatState = $catalogProductFlatState;
@@ -401,8 +401,8 @@ public function __construct(
$this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get(PriceTableResolver::class);
$this->dimensionFactory = $dimensionFactory
?: ObjectManager::getInstance()->get(DimensionFactory::class);
- $this->categoryRepository = $categoryRepository ?: ObjectManager::getInstance()
- ->get(CategoryRepositoryInterface::class);
+ $this->categoryCollectionFactory = $categoryCollectionFactory ?: ObjectManager::getInstance()
+ ->get(CollectionFactory::class);
}
/**
@@ -2105,7 +2105,11 @@ protected function _applyZeroStoreProductLimitations()
*/
private function getChildrenCategories(int $categoryId, array $categories): array
{
- $category = $this->categoryRepository->get($categoryId);
+ /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */
+ $categoryCollection = $this->categoryCollectionFactory->create();
+ $category = $categoryCollection
+ ->addAttributeToSelect('is_anchor')->addIdFilter([$categoryId])
+ ->load()->getFirstItem();
$categories[] = $category->getId();
if ($category->getIsAnchor()) {
$categoryChildren = $category->getChildren();
From 3d556b2d38522d8f71aca0bba82333a9d2d179eb Mon Sep 17 00:00:00 2001
From: Lusine Papyan
Date: Wed, 19 Jun 2019 10:05:42 +0400
Subject: [PATCH 027/593] MAGETWO-44170: Not pass function test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Added automated test script.
---
...AdminProductTypeSwitchingOnEditingTest.xml | 423 ++++++++++++++++++
1 file changed, 423 insertions(+)
create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
new file mode 100644
index 0000000000000..116fbc1b6b455
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -0,0 +1,423 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From e135272787c20f1f2a5f63c9b04a1da634ee8d2a Mon Sep 17 00:00:00 2001
From: Ani Tumanyan
Date: Wed, 19 Jun 2019 10:19:23 +0400
Subject: [PATCH 028/593] MC-15341: Default product numbers to display results
in poor display on Desktop
- Added automated Test script
---
...heckDefaultNumberProductsToDisplayTest.xml | 198 ++++++++++++++++++
1 file changed, 198 insertions(+)
create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
new file mode 100644
index 0000000000000..6fb4f816d0b9e
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 3fd8742fd0d14531e1d7b7a90b8c8deb0b972f36 Mon Sep 17 00:00:00 2001
From: Evgeny Petrov
Date: Thu, 20 Jun 2019 12:41:31 +0300
Subject: [PATCH 029/593] MC-15341: Default product numbers to display results
in poor display on Desktop
- Fixes of failed tests
---
.../AdminCheckPaginationInStorefrontTest.xml | 62 +++++++++++++------
.../StorefrontOrderPagerDisplayedTest.xml | 2 +-
.../Test/StorefrontOrderPagerIsAbsentTest.xml | 2 +-
3 files changed, 44 insertions(+), 22 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
index f40a62c164ecc..384d8ddea4f17 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
@@ -41,6 +41,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -67,6 +77,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -86,11 +106,13 @@
-
+
+
+
-
-
+
+
@@ -104,73 +126,73 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml
index b8772f24a2a42..0e58bb84988a2 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml
@@ -129,7 +129,7 @@
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml
index 9909fca44fe2c..3ff8a7791d88b 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml
@@ -125,7 +125,7 @@
-
+
From fd3a8ae0e7fb437277e6e0f8fbec088582a49c2d Mon Sep 17 00:00:00 2001
From: Nikita Chubukov
Date: Thu, 20 Jun 2019 16:29:25 +0300
Subject: [PATCH 030/593] MC-15256: Exported customer without modification can
not be imported
- Changed import validation logic for attributes with empty select value
---
.../Model/Import/Customer.php | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
index ab940c9e84533..f181119f1acf5 100644
--- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
+++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\CustomerImportExport\Model\Import;
use Magento\Customer\Api\Data\CustomerInterface;
@@ -288,9 +290,12 @@ private function getCustomerEntityFieldsToUpdate(array $entitiesToUpdate): array
{
$firstCustomer = reset($entitiesToUpdate);
$columnsToUpdate = array_keys($firstCustomer);
- $customerFieldsToUpdate = array_filter($this->customerFields, function ($field) use ($columnsToUpdate) {
- return in_array($field, $columnsToUpdate);
- });
+ $customerFieldsToUpdate = array_filter(
+ $this->customerFields,
+ function ($field) use ($columnsToUpdate) {
+ return in_array($field, $columnsToUpdate);
+ }
+ );
return $customerFieldsToUpdate;
}
@@ -606,6 +611,10 @@ protected function _validateRowForUpdate(array $rowData, $rowNumber)
}
if (isset($rowData[$attributeCode]) && strlen($rowData[$attributeCode])) {
+ if ($attributeParams['type'] == 'select' && empty($rowData[$attributeCode])) {
+ continue;
+ }
+
$this->isAttributeValid(
$attributeCode,
$attributeParams,
From 62bfe249b9dd0b1b6e2517152364a94b422470fc Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Fri, 21 Jun 2019 14:08:24 +0300
Subject: [PATCH 031/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix CR comments.
---
.../Model/ResourceModel/Product/Collection.php | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 76152250c41df..7127aa2cb3e77 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -2068,8 +2068,7 @@ protected function _applyProductLimitations()
protected function _applyZeroStoreProductLimitations()
{
$filters = $this->_productLimitationFilters;
- $categories = [];
- $categories = $this->getChildrenCategories((int)$filters['category_id'], $categories);
+ $categories = $this->getChildrenCategories((int)$filters['category_id'], []);
$conditions = [
'cat_pro.product_id=e.entity_id',
@@ -2105,13 +2104,16 @@ protected function _applyZeroStoreProductLimitations()
*/
private function getChildrenCategories(int $categoryId, array $categories): array
{
+ $categories[] = $categoryId;
+
/** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */
$categoryCollection = $this->categoryCollectionFactory->create();
$category = $categoryCollection
- ->addAttributeToSelect('is_anchor')->addIdFilter([$categoryId])
- ->load()->getFirstItem();
- $categories[] = $category->getId();
- if ($category->getIsAnchor()) {
+ ->addAttributeToSelect('is_anchor')
+ ->addAttributeToFilter('is_anchor', 1)
+ ->addIdFilter([$categoryId])
+ ->getFirstItem();
+ if ($category) {
$categoryChildren = $category->getChildren();
$categoryChildrenIds = explode(',', $categoryChildren);
foreach ($categoryChildrenIds as $categoryChildrenId) {
From d40388edad3e5c812bb455628795172f412845a3 Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Sat, 22 Jun 2019 12:11:45 +0300
Subject: [PATCH 032/593] GraphQl-220: Implement exception logging #355.
Cleaned up code.
---
.../Framework/GraphQl/Query/ErrorHandler.php | 38 ++++++++++---------
.../GraphQl/Query/ErrorHandlerInterface.php | 4 +-
2 files changed, 24 insertions(+), 18 deletions(-)
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
index 2f97ae238c6a7..fb5e921e72f6e 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
@@ -7,6 +7,10 @@
namespace Magento\Framework\GraphQl\Query;
+use GraphQL\Error\ClientAware;
+use GraphQL\Error\Error;
+use Magento\Framework\Logger\Monolog;
+
/**
* Class ErrorHandler
*
@@ -15,12 +19,12 @@
class ErrorHandler implements ErrorHandlerInterface
{
/**
- * @var \Magento\Framework\Logger\Monolog
+ * @var Monolog
*/
private $clientLogger;
/**
- * @var \Magento\Framework\Logger\Monolog
+ * @var Monolog
*/
private $serverLogger;
@@ -35,25 +39,25 @@ class ErrorHandler implements ErrorHandlerInterface
private $serverErrorCategories;
/**
- * @var \Magento\Framework\Logger\Monolog
+ * @var Monolog
*/
private $generalLogger;
/**
* ErrorHandler constructor.
*
- * @param \Magento\Framework\Logger\Monolog $clientLogger
- * @param \Magento\Framework\Logger\Monolog $serverLogger
- * @param \Magento\Framework\Logger\Monolog $generalLogger
- * @param array $clientErrorCategories
- * @param array $serverErrorCategories
+ * @param Monolog $clientLogger
+ * @param Monolog $serverLogger
+ * @param Monolog $generalLogger
+ * @param array $clientErrorCategories
+ * @param array $serverErrorCategories
*
* @SuppressWarnings(PHPMD.LongVariable)
*/
public function __construct(
- \Magento\Framework\Logger\Monolog $clientLogger,
- \Magento\Framework\Logger\Monolog $serverLogger,
- \Magento\Framework\Logger\Monolog $generalLogger,
+ Monolog $clientLogger,
+ Monolog $serverLogger,
+ Monolog $generalLogger,
array $clientErrorCategories = [],
array $serverErrorCategories = []
) {
@@ -67,15 +71,15 @@ public function __construct(
/**
* Handle errors
*
- * @param \GraphQL\Error\Error[] $errors
- * @param callable $formatter
+ * @param Error[] $errors
+ * @param callable $formatter
*
* @return array
*/
- public function handle(array $errors, callable $formatter):array
+ public function handle(array $errors, callable $formatter): array
{
return array_map(
- function (\GraphQL\Error\ClientAware $error) use ($formatter) {
+ function (ClientAware $error) use ($formatter) {
$this->logError($error);
return $formatter($error);
@@ -85,11 +89,11 @@ function (\GraphQL\Error\ClientAware $error) use ($formatter) {
}
/**
- * @param \GraphQL\Error\ClientAware $error
+ * @param ClientAware $error
*
* @return boolean
*/
- private function logError(\GraphQL\Error\ClientAware $error):bool
+ private function logError(ClientAware $error): bool
{
if (in_array($error->getCategory(), $this->clientErrorCategories)) {
return $this->clientLogger->error($error);
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
index f63468fa4ddb8..dbb149a865746 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
@@ -7,6 +7,8 @@
namespace Magento\Framework\GraphQl\Query;
+use GraphQL\Error\Error;
+
/**
* Interface ErrorHandlerInterface
*
@@ -20,7 +22,7 @@ interface ErrorHandlerInterface
/**
* Handle errors
*
- * @param \GraphQL\Error\Error[] $errors
+ * @param Error[] $errors
* @param callable $formatter
*
* @return array
From 6a5c7e2f52aac7af3378270e69604ecb2da21695 Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Sat, 22 Jun 2019 13:19:54 +0300
Subject: [PATCH 033/593] GraphQl-220: Implement exception logging #355. Moved
DI to graphql area; Create ResolveLogger service; Use
ClientAvare->isClientSafe() to divide exceptions into client and server
---
app/etc/di.xml | 57 --------------
app/etc/graphql/di.xml | 41 ++++++++++
.../Framework/GraphQl/Query/ErrorHandler.php | 77 +++----------------
.../GraphQl/Query/ErrorHandlerInterface.php | 13 ++--
.../GraphQl/Query/Resolver/ResolveLogger.php | 49 ++++++++++++
.../Query/Resolver/ResolveLoggerInterface.php | 28 +++++++
6 files changed, 136 insertions(+), 129 deletions(-)
create mode 100644 app/etc/graphql/di.xml
create mode 100644 lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php
create mode 100644 lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLoggerInterface.php
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 21cdea8510638..200a56201239d 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -1768,61 +1768,4 @@
-
-
-
- GraphQLClientLogger
- GraphQLServerLogger
- GraphQLGeneralLogger
-
- - \GraphQL\Error\Error::CATEGORY_GRAPHQL
- - \Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException::EXCEPTION_CATEGORY
- - \Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException::EXCEPTION_CATEGORY
- - \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException::EXCEPTION_CATEGORY
- - \Magento\Framework\GraphQl\Exception\GraphQlInputException::EXCEPTION_CATEGORY
- - \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException::EXCEPTION_CATEGORY
- - request
- - user
-
-
- - \GraphQL\Error\Error::CATEGORY_INTERNAL
-
-
-
-
-
-
- - GraphQLClientErrorHandler
-
-
-
-
-
- \Magento\Framework\GraphQl\Query\ErrorHandlerInterface::CLIENT_LOG_FILE
-
-
-
-
-
- - GraphQLServerErrorHandler
-
-
-
-
-
- \Magento\Framework\GraphQl\Query\ErrorHandlerInterface::SERVER_LOG_FILE
-
-
-
-
-
- - GraphQLGeneralErrorHandler
-
-
-
-
-
- \Magento\Framework\GraphQl\Query\ErrorHandlerInterface::GENERAL_LOG_FILE
-
-
diff --git a/app/etc/graphql/di.xml b/app/etc/graphql/di.xml
new file mode 100644
index 0000000000000..57b5687c660cb
--- /dev/null
+++ b/app/etc/graphql/di.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ GraphQLClientLogger
+ GraphQLServerLogger
+
+
+
+
+
+ - GraphQLClientErrorHandler
+
+
+
+
+
+ Magento\Framework\GraphQl\Query\ErrorHandler::CLIENT_LOG_FILE
+
+
+
+
+
+ - GraphQLServerErrorHandler
+
+
+
+
+
+ Magento\Framework\GraphQl\Query\ErrorHandler::SERVER_LOG_FILE
+
+
+
+
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
index fb5e921e72f6e..921314a50beff 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
@@ -8,99 +8,44 @@
namespace Magento\Framework\GraphQl\Query;
use GraphQL\Error\ClientAware;
-use GraphQL\Error\Error;
-use Magento\Framework\Logger\Monolog;
+use Magento\Framework\GraphQl\Query\Resolver\ResolveLoggerInterface;
/**
- * Class ErrorHandler
+ * @inheritDoc
*
* @package Magento\Framework\GraphQl\Query
*/
class ErrorHandler implements ErrorHandlerInterface
{
- /**
- * @var Monolog
- */
- private $clientLogger;
-
- /**
- * @var Monolog
- */
- private $serverLogger;
+ const SERVER_LOG_FILE = 'var/log/graphql/server/exception.log';
+ const CLIENT_LOG_FILE = 'var/log/graphql/client/exception.log';
/**
- * @var array
+ * @var ResolveLoggerInterface
*/
- private $clientErrorCategories;
+ private $resolveLogger;
/**
- * @var array
- */
- private $serverErrorCategories;
-
- /**
- * @var Monolog
- */
- private $generalLogger;
-
- /**
- * ErrorHandler constructor.
- *
- * @param Monolog $clientLogger
- * @param Monolog $serverLogger
- * @param Monolog $generalLogger
- * @param array $clientErrorCategories
- * @param array $serverErrorCategories
- *
- * @SuppressWarnings(PHPMD.LongVariable)
+ * @param ResolveLoggerInterface $resolveLogger
*/
public function __construct(
- Monolog $clientLogger,
- Monolog $serverLogger,
- Monolog $generalLogger,
- array $clientErrorCategories = [],
- array $serverErrorCategories = []
+ ResolveLoggerInterface $resolveLogger
) {
- $this->clientLogger = $clientLogger;
- $this->serverLogger = $serverLogger;
- $this->generalLogger = $generalLogger;
- $this->clientErrorCategories = $clientErrorCategories;
- $this->serverErrorCategories = $serverErrorCategories;
+ $this->resolveLogger = $resolveLogger;
}
/**
- * Handle errors
- *
- * @param Error[] $errors
- * @param callable $formatter
- *
- * @return array
+ * @inheritDoc
*/
public function handle(array $errors, callable $formatter): array
{
return array_map(
function (ClientAware $error) use ($formatter) {
- $this->logError($error);
+ $this->resolveLogger->execute($error)->error($error);
return $formatter($error);
},
$errors
);
}
-
- /**
- * @param ClientAware $error
- *
- * @return boolean
- */
- private function logError(ClientAware $error): bool
- {
- if (in_array($error->getCategory(), $this->clientErrorCategories)) {
- return $this->clientLogger->error($error);
- } elseif (in_array($error->getCategory(), $this->serverErrorCategories)) {
- return $this->serverLogger->error($error);
- }
-
- return $this->generalLogger->error($error);
- }
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
index dbb149a865746..b89207fe3bdb1 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandlerInterface.php
@@ -12,20 +12,21 @@
/**
* Interface ErrorHandlerInterface
*
- * @package Magento\Framework\GraphQl\Query
+ * GraphQL error handler
+ *
+ * @see \Magento\Framework\GraphQl\Query\QueryProcessor
+ *
+ * @api
*/
interface ErrorHandlerInterface
{
- const SERVER_LOG_FILE = 'var/log/graphql/server/exception.log';
- const CLIENT_LOG_FILE = 'var/log/graphql/client/exception.log';
- const GENERAL_LOG_FILE = 'var/log/graphql/exception.log';
/**
* Handle errors
*
* @param Error[] $errors
- * @param callable $formatter
+ * @param callable $formatter
*
* @return array
*/
- public function handle(array $errors, callable $formatter):array;
+ public function handle(array $errors, callable $formatter): array;
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php
new file mode 100644
index 0000000000000..98e4f85b76128
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php
@@ -0,0 +1,49 @@
+clientLogger = $clientLogger;
+ $this->serverLogger = $serverLogger;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function execute(ClientAware $clientAware): LoggerInterface
+ {
+ return $clientAware->isClientSafe() ?
+ $this->clientLogger :
+ $this->serverLogger;
+ }
+}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLoggerInterface.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLoggerInterface.php
new file mode 100644
index 0000000000000..ade416a3093bd
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLoggerInterface.php
@@ -0,0 +1,28 @@
+
Date: Mon, 24 Jun 2019 10:09:07 -0500
Subject: [PATCH 034/593] MC-16650: Product Attribute Type Price Not Displaying
- use price filter for all attributes with catalog input type = 'price'
---
.../Catalog/Model/Layer/FilterList.php | 10 ++++--
.../Test/Unit/Model/Layer/FilterListTest.php | 8 +++--
.../Model/Layer/Filter/Price.php | 5 ++-
.../Model/Search/RequestGenerator/Decimal.php | 32 +------------------
.../Unit/Model/Layer/Filter/PriceTest.php | 7 ++++
.../Model/Layer/Filter/PriceTest.php | 8 +++++
6 files changed, 33 insertions(+), 37 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/Layer/FilterList.php b/app/code/Magento/Catalog/Model/Layer/FilterList.php
index 9d7b71c981c6b..7f06c97d3e8d9 100644
--- a/app/code/Magento/Catalog/Model/Layer/FilterList.php
+++ b/app/code/Magento/Catalog/Model/Layer/FilterList.php
@@ -3,9 +3,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Catalog\Model\Layer;
+use Magento\Catalog\Model\Product\Attribute\Backend\Price;
+
+/**
+ * Layer navigation filters
+ */
class FilterList
{
const CATEGORY_FILTER = 'category';
@@ -106,9 +112,9 @@ protected function getAttributeFilterClass(\Magento\Catalog\Model\ResourceModel\
{
$filterClassName = $this->filterTypes[self::ATTRIBUTE_FILTER];
- if ($attribute->getAttributeCode() == 'price') {
+ if ($attribute->getBackendModel() === Price::class) {
$filterClassName = $this->filterTypes[self::PRICE_FILTER];
- } elseif ($attribute->getBackendType() == 'decimal') {
+ } elseif ($attribute->getBackendType() === 'decimal') {
$filterClassName = $this->filterTypes[self::DECIMAL_FILTER];
}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
index 8733f305ce091..2d3c764cb6907 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
@@ -3,10 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Catalog\Test\Unit\Model\Layer;
use \Magento\Catalog\Model\Layer\FilterList;
+use Magento\Catalog\Model\Product\Attribute\Backend\Price;
class FilterListTest extends \PHPUnit\Framework\TestCase
{
@@ -95,8 +97,8 @@ public function getFiltersDataProvider()
{
return [
[
- 'method' => 'getAttributeCode',
- 'value' => FilterList::PRICE_FILTER,
+ 'method' => 'getBackendModel',
+ 'value' => Price::class,
'expectedClass' => 'PriceFilterClass',
],
[
@@ -105,7 +107,7 @@ public function getFiltersDataProvider()
'expectedClass' => 'DecimalFilterClass',
],
[
- 'method' => 'getAttributeCode',
+ 'method' => 'getBackendModel',
'value' => null,
'expectedClass' => 'AttributeFilterClass',
]
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php
index a19f53469ae01..66d9281ed38e2 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\CatalogSearch\Model\Layer\Filter;
use Magento\Catalog\Model\Layer\Filter\AbstractFilter;
@@ -11,6 +13,7 @@
* Layer price filter based on Search API
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class Price extends AbstractFilter
{
@@ -138,7 +141,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request)
list($from, $to) = $filter;
$this->getLayer()->getProductCollection()->addFieldToFilter(
- 'price',
+ $this->getAttributeModel()->getAttributeCode(),
['from' => $from, 'to' => empty($to) || $from == $to ? $to : $to - self::PRICE_DELTA]
);
diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php
index c9f738b07c175..73d011cc532db 100644
--- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php
+++ b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php
@@ -10,29 +10,12 @@
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\Framework\Search\Request\BucketInterface;
use Magento\Framework\Search\Request\FilterInterface;
-use Magento\Framework\App\Config\ScopeConfigInterface;
-use Magento\Framework\App\ObjectManager;
-use Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory;
-use Magento\Store\Model\ScopeInterface;
/**
* Catalog search range request generator.
*/
class Decimal implements GeneratorInterface
{
- /**
- * @var \Magento\Store\Model\ScopeInterface
- */
- private $scopeConfig;
-
- /**
- * @param \Magento\Store\Model\ScopeInterface|null $scopeConfig
- */
- public function __construct(ScopeConfigInterface $scopeConfig = null)
- {
- $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
- }
-
/**
* @inheritdoc
*/
@@ -56,21 +39,8 @@ public function getAggregationData(Attribute $attribute, $bucketName)
'type' => BucketInterface::TYPE_DYNAMIC,
'name' => $bucketName,
'field' => $attribute->getAttributeCode(),
- 'method' => $this->getRangeCalculation(),
+ 'method' => 'manual',
'metric' => [['type' => 'count']],
];
}
-
- /**
- * Get range calculation by what was set in the configuration
- *
- * @return string
- */
- private function getRangeCalculation(): string
- {
- return $this->scopeConfig->getValue(
- AlgorithmFactory::XML_PATH_RANGE_CALCULATION,
- ScopeInterface::SCOPE_STORE
- );
- }
}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php
index abad58a6876d3..f783f75a170e3 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/PriceTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\CatalogSearch\Test\Unit\Model\Layer\Filter;
@@ -208,6 +209,12 @@ public function testApply()
$priceId = '15-50';
$requestVar = 'test_request_var';
+ $this->target->setAttributeModel($this->attribute);
+ $attributeCode = 'price';
+ $this->attribute->expects($this->any())
+ ->method('getAttributeCode')
+ ->will($this->returnValue($attributeCode));
+
$this->target->setRequestVar($requestVar);
$this->request->expects($this->exactly(1))
->method('getParam')
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php
index 451553113af2c..a7944566eb8e0 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/PriceTest.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\CatalogSearch\Model\Layer\Filter;
use Magento\TestFramework\Helper\Bootstrap;
@@ -35,10 +37,16 @@ protected function setUp()
$category->load(4);
$layer = $this->objectManager->get(\Magento\Catalog\Model\Layer\Category::class);
$layer->setCurrentCategory($category);
+ /** @var $attribute \Magento\Catalog\Model\Entity\Attribute */
+ $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ \Magento\Catalog\Model\Entity\Attribute::class
+ );
+ $attribute->loadByCode('catalog_product', 'price');
$this->_model = $this->objectManager->create(
\Magento\CatalogSearch\Model\Layer\Filter\Price::class,
['layer' => $layer]
);
+ $this->_model->setAttributeModel($attribute);
}
public function testApplyNothing()
From 9fe43844e09517ca6d4e58c786605af871463d59 Mon Sep 17 00:00:00 2001
From: Lusine Papyan
Date: Tue, 25 Jun 2019 17:33:36 +0400
Subject: [PATCH 035/593] MAGETWO-70803: [GITHUB] Inconsistent CSV file Import
error: #7495
- Added automated test script
---
.../AdminImportProductsActionGroup.xml | 7 ++
.../Mftf/Section/AdminImportMainSection.xml | 2 +
...ImportCSVFileCorrectDifferentFilesTest.xml | 47 +++++++
.../acceptance/tests/_data/BB-Products.csv | 118 ++++++++++++++++++
.../tests/_data/BB-ProductsWorking.csv | 29 +++++
5 files changed, 203 insertions(+)
create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml
create mode 100644 dev/tests/acceptance/tests/_data/BB-Products.csv
create mode 100644 dev/tests/acceptance/tests/_data/BB-ProductsWorking.csv
diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml
index a9100b4730b8c..13843dc804655 100644
--- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml
+++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml
@@ -27,4 +27,11 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml
index 2ce6b1e35777f..6f6d6e4e6b8ac 100644
--- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml
+++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml
@@ -13,5 +13,7 @@
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml
new file mode 100644
index 0000000000000..349d3415af06e
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/_data/BB-Products.csv b/dev/tests/acceptance/tests/_data/BB-Products.csv
new file mode 100644
index 0000000000000..7ab03fd5eaeda
--- /dev/null
+++ b/dev/tests/acceptance/tests/_data/BB-Products.csv
@@ -0,0 +1,118 @@
+sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,configurable_variations,configurable_variation_labels,associated_skus
+BB-D2010129,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Sistemi di Climatizzazione,Default Category/Casa Giardino/Sistemi di Climatizzazione/Aria condizionata e ventilatori",base,"Ventilatore Portatile Spray FunFan Nero","Se
sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan . Si tratta di una soluzione pratica per mantenersi al fresco in una moltitudine di situazioni, come escursioni, gite in spiaggia, mentre si fa sport, ecc. Inoltre, grazie alle sue dimensioni ridotte (dimensioni: circa 9 x 26 x 6,5 cm) e peso ridotto (circa 130 g), lo puoi portare ovunque!www.myfunfan.com Questo ventilatore portatile originale ha un pulsante per attivare le eliche in PVC malleabili e una leva che spruzza l'acqua. Cosa c'è di più, puoi aggiungere il ghiaccio per aumentare la sensazione di freddo! Include 1 cacciavite a croce per inserire le batterie. Realizzato in PVC. Funzionamento a batterie (2 x AA, non incluse).
Dimenzioni per Ventilatore Portatile Spray FunFan :
Altezza: 10 Cm Larghezza: 28 Cm Profondita': 7.5 Cm Peso: 0.185 Kg Codice Prodotto (EAN): 4899888101772
","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan. Maggiori Informazioni ",0.185,1,"Taxable Goods","Catalog, Search",19.9,,,,Ventilatore-Portatile-Spray-FunFan-Nero,"Ventilatore Portatile Spray FunFan Nero","Casa Giardino,Casa,Giardino,Sistemi di Climatizzazione,Sistemi,Climatizzazione,Aria condizionata e ventilatori,Aria,condizionata,ventilatori,Colore Nero,Nero,","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan",http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,,http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,,,,,,"2016-01-12 09:56:53",,,,,,,,,,,,,,,,,"GTIN=4899888101772",41,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78889.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78888.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78886.jpg","GTIN=4899888101772",,,,,,,,,,
+BB-D2010130,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Sistemi di Climatizzazione,Default Category/Casa Giardino/Sistemi di Climatizzazione/Aria condizionata e ventilatori",base,"Ventilatore Portatile Spray FunFan Bianco","Se
sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan . Si tratta di una soluzione pratica per mantenersi al fresco in una moltitudine di situazioni, come escursioni, gite in spiaggia, mentre si fa sport, ecc. Inoltre, grazie alle sue dimensioni ridotte (dimensioni: circa 9 x 26 x 6,5 cm) e peso ridotto (circa 130 g), lo puoi portare ovunque!www.myfunfan.com Questo ventilatore portatile originale ha un pulsante per attivare le eliche in PVC malleabili e una leva che spruzza l'acqua. Cosa c'è di più, puoi aggiungere il ghiaccio per aumentare la sensazione di freddo! Include 1 cacciavite a croce per inserire le batterie. Realizzato in PVC. Funzionamento a batterie (2 x AA, non incluse). Dimenzioni per Ventilatore Portatile Spray FunFan :
Altezza: 10 Cm Larghezza: 28 Cm Profondita': 7.5 Cm Peso: 0.185 Kg Codice Prodotto (EAN): 4899888107965
","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan. Maggiori Informazioni ",0.185,1,"Taxable Goods","Catalog, Search",19.9,,,,Ventilatore-Portatile-Spray-FunFan-Bianco,"Ventilatore Portatile Spray FunFan Bianco","Casa Giardino,Casa,Giardino,Sistemi di Climatizzazione,Sistemi,Climatizzazione,Aria condizionata e ventilatori,Aria,condizionata,ventilatori,Colore Bianco,Bianco,","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan",http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,,http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,,,,,,"2016-01-12 09:56:53",,,,,,,,,,,,,,,,,"GTIN=4899888107965",741,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78889.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78888.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78886.jpg","GTIN=4899888107965",,,,,,,,,,
+BB-D2010131,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Sistemi di Climatizzazione,Default Category/Casa Giardino/Sistemi di Climatizzazione/Aria condizionata e ventilatori",base,"Ventilatore Portatile Spray FunFan Rosso","Se
sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan . Si tratta di una soluzione pratica per mantenersi al fresco in una moltitudine di situazioni, come escursioni, gite in spiaggia, mentre si fa sport, ecc. Inoltre, grazie alle sue dimensioni ridotte (dimensioni: circa 9 x 26 x 6,5 cm) e peso ridotto (circa 130 g), lo puoi portare ovunque!www.myfunfan.com Questo ventilatore portatile originale ha un pulsante per attivare le eliche in PVC malleabili e una leva che spruzza l'acqua. Cosa c'è di più, puoi aggiungere il ghiaccio per aumentare la sensazione di freddo! Include 1 cacciavite a croce per inserire le batterie. Realizzato in PVC. Funzionamento a batterie (2 x AA, non incluse). Dimenzioni per Ventilatore Portatile Spray FunFan :
Altezza: 10 Cm Larghezza: 28 Cm Profondita': 7.5 Cm Peso: 0.185 Kg Codice Prodotto (EAN): 4899888107972
","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan. Maggiori Informazioni ",0.185,1,"Taxable Goods","Catalog, Search",19.9,,,,Ventilatore-Portatile-Spray-FunFan-Rosso,"Ventilatore Portatile Spray FunFan Rosso","Casa Giardino,Casa,Giardino,Sistemi di Climatizzazione,Sistemi,Climatizzazione,Aria condizionata e ventilatori,Aria,condizionata,ventilatori,Colore Rosso,Rosso,","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan",http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,,http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,,,,,,"2016-01-12 09:56:53",,,,,,,,,,,,,,,,,"GTIN=4899888107972",570,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78889.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78888.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78886.jpg","GTIN=4899888107972",,,,,,,,,,
+BB-H1000163,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Sedia Pieghevole Campart Travel CH0592 Blu Marino","Se
stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel ! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Ideale per il riposo e il relax. Può portare fino a 120 kg. Dimensioni: 66 x 70 / 120 x 87 / 115 cm circa. Semplice da trasportare ovunque, grazie al suo design funzionale ed elegante (dimensioni quando piegato: circa 66 x 110 x 10 cm). 7 posizioni regolabili e un poggiatesta incorporato. Struttura in alluminio e stoffa imbottita in poliestere. Altezza sedia: circa 50 cm. Dimenzioni per Sedia Pieghevole Campart Travel:
Altezza: 10 Cm Larghezza: 66 Cm Profondita': 110 Cm Peso: 5.3 Kg Codice Prodotto (EAN): 8713016005922
","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Maggiori Informazioni ",5.3,1,"Taxable Goods","Catalog, Search",129,,,,Sedia-Pieghevole-Campart-Travel-CH0592 Blu Marino,"Sedia Pieghevole Campart Travel CH0592 Blu Marino","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0592 Blu Marino,CH0592 Blu Marino,","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc",http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_00.jpg,,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_00.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005922",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_02.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_04.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_03.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_01.jpg","GTIN=8713016005922",,,,,,,,,,
+BB-H1000162,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Sedia Pieghevole Campart Travel CH0596 Grigio","Se
stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel ! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Ideale per il riposo e il relax. Può portare fino a 120 kg. Dimensioni: 66 x 70 / 120 x 87 / 115 cm circa. Semplice da trasportare ovunque, grazie al suo design funzionale ed elegante (dimensioni quando piegato: circa 66 x 110 x 10 cm). 7 posizioni regolabili e un poggiatesta incorporato. Struttura in alluminio e stoffa imbottita in poliestere. Altezza sedia: circa 50 cm. Dimenzioni per Sedia Pieghevole Campart Travel:
Altezza: 10 Cm Larghezza: 66 Cm Profondita': 110 Cm Peso: 5.3 Kg Codice Prodotto (EAN): 8713016005960
","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Maggiori Informazioni ",5.3,1,"Taxable Goods","Catalog, Search",129,,,,Sedia-Pieghevole-Campart-Travel-CH0596 Grigio,"Sedia Pieghevole Campart Travel CH0596 Grigio","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0596 Grigio,CH0596 Grigio,","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc",http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_02.jpg,,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_02.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005960",2,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_00.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_04.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_03.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_01.jpg","GTIN=8713016005960",,,,,,,,,,
+BB-F1520329,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Poggiapiedi Pieghevole Campart Travel CH0593 Blu Marino","Approfitta
di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel ! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Possiede 2 ganci di circa 3 cm di diametro che possono essere facilmente attaccate alla barra inferiore delle sedie (utilizzabile solo per sedie con una barra inferiore di circa 2 cm di diametro). Struttura in alluminio. Tessuto: poliestere. Dimensioni: circa 51 x 47 x 96 cm (dimensioni quando ripiegato: circa 51 x 12 x 96 cm). Ideale per le sedie pieghevoli Campart Travel CH0592 e CH0596. Dimenzioni per Poggiapiedi Pieghevole Campart Travel:
Altezza: 13 Cm Larghezza: 53 Cm Profondita': 97 Cm Peso: 1.377 Kg Codice Prodotto (EAN): 8713016005939
","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Maggiori Informazioni ",1.377,1,"Taxable Goods","Catalog, Search",46.6,,,,Poggiapiedi-Pieghevole-Campart-Travel-CH0593 Blu Marino,"Poggiapiedi Pieghevole Campart Travel CH0593 Blu Marino","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0593 Blu Marino,CH0593 Blu Marino,","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc",http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_00.jpg,,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_00.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005939",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_01.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_02.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_08.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_07.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_06.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_05.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_04.jpg","GTIN=8713016005939",,,,,,,,,,
+BB-F1520328,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Poggiapiedi Pieghevole Campart Travel CH0597 Grigio","Approfitta
di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel ! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Possiede 2 ganci di circa 3 cm di diametro che possono essere facilmente attaccate alla barra inferiore delle sedie (utilizzabile solo per sedie con una barra inferiore di circa 2 cm di diametro). Struttura in alluminio. Tessuto: poliestere. Dimensioni: circa 51 x 47 x 96 cm (dimensioni quando ripiegato: circa 51 x 12 x 96 cm). Ideale per le sedie pieghevoli Campart Travel CH0592 e CH0596. Dimenzioni per Poggiapiedi Pieghevole Campart Travel:
Altezza: 13 Cm Larghezza: 53 Cm Profondita': 97 Cm Peso: 1.377 Kg Codice Prodotto (EAN): 8713016005977
","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Maggiori Informazioni ",1.377,1,"Taxable Goods","Catalog, Search",46.6,,,,Poggiapiedi-Pieghevole-Campart-Travel-CH0597 Grigio,"Poggiapiedi Pieghevole Campart Travel CH0597 Grigio","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0597 Grigio,CH0597 Grigio,","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc",http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_01.jpg,,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_01.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005977",7,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_00.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_02.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_08.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_07.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_06.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_05.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_04.jpg","GTIN=8713016005977",,,,,,,,,,
+BB-H4502058,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Orologi da parete e da tavolo",base,"Orologio da Parete Star Wars","I
fan di Star Wars non potranno fare a meno di appendere l'orologio da parete Star Wars in casa loro! Realizzato in plastica. Funziona a batterie (1 x AA, non incluse). Diametro circa: 25,5 cm. Spessore circa: 3,5 cm. Dimenzioni per Orologio da Parete Star Wars:
Altezza: 25.5 Cm Larghezza: 26 Cm Profondita': 3.8 Cm Peso: 0.287 Kg Codice Prodotto (EAN): 6950687214204
","I fan di Star Wars non potranno fare a meno di appendere l'orologio da parete Star Wars in casa loro! Realizzato in plastica. Maggiori Informazioni ",0.287,1,"Taxable Goods","Catalog, Search",22.5,,,,Orologio-da-Parete-Star-Wars,"Orologio da Parete Star Wars","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Orologi da parete e da tavolo,Orologi,parete,tavolo,","I fan di Star Wars non potranno fare a meno di appendere l'orologio da parete Star Wars in casa loro! Realizzato in plastica",http://dropshipping.bigbuy.eu/imgs/H4502058_84712.jpg,,http://dropshipping.bigbuy.eu/imgs/H4502058_84712.jpg,,,,,,"2016-08-08 21:09:24",,,,,,,,,,,,,,,,,"GTIN=6950687214204",130,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/H4502058_84713.jpg,http://dropshipping.bigbuy.eu/imgs/H4502058_84711.jpg,http://dropshipping.bigbuy.eu/imgs/H4502058_84710.jpg","GTIN=6950687214204",,,,,,,,,,
+BB-G0500195,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Illuminazione LED",base,"Braccialetto Sportivo a LED MegaLed Rosso","Se
ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Con questo braccialetto di sicurezza sarai visibile ai motorini e alle auto nell'oscurità, così da poter stare molto più sicuro e tranquillo. È dotato di 2 luci a LED con 2 possibili soluzioni (luce fissa ed intermittente). La lunghezza massima è di circa 55 cm e quella minima è di circa 42 cm. Molto leggero (circa 50 g). Autonomia circa: 24-40 ore. Funziona a batterie (2 x CR2023, incluse).
Dimenzioni per Braccialetto Sportivo a LED MegaLed:
Altezza: 3 Cm Larghezza: 20 Cm Profondita': 4 Cm Peso: 0.049 Kg Codice Prodotto (EAN): 8436545443507
","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Maggiori Informazioni ",0.049,1,"Taxable Goods","Catalog, Search",22,,,,Braccialetto-Sportivo-a-LED-MegaLed-Rosso,"Braccialetto Sportivo a LED MegaLed Rosso","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Illuminazione LED,Illuminazione,LED,Colore Rosso,Rosso,","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed",http://dropshipping.bigbuy.eu/imgs/G0500194_87774.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500194_87774.jpg,,,,,,"2016-01-08 12:34:41",,,,,,,,,,,,,,,,,"GTIN=8436545443507",22,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500194_87775.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87773.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87772.jpg","GTIN=8436545443507",,,,,,,,,,
+BB-G0500196,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Illuminazione LED",base,"Braccialetto Sportivo a LED MegaLed Verde","Se
ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Con questo braccialetto di sicurezza sarai visibile ai motorini e alle auto nell'oscurità, così da poter stare molto più sicuro e tranquillo. È dotato di 2 luci a LED con 2 possibili soluzioni (luce fissa ed intermittente). La lunghezza massima è di circa 55 cm e quella minima è di circa 42 cm. Molto leggero (circa 50 g). Autonomia circa: 24-40 ore. Funziona a batterie (2 x CR2023, incluse).
Dimenzioni per Braccialetto Sportivo a LED MegaLed:
Altezza: 3 Cm Larghezza: 20 Cm Profondita': 4 Cm Peso: 0.049 Kg Codice Prodotto (EAN): 8436545443507
","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Maggiori Informazioni ",0.049,1,"Taxable Goods","Catalog, Search",22,,,,Braccialetto-Sportivo-a-LED-MegaLed-Verde,"Braccialetto Sportivo a LED MegaLed Verde","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Illuminazione LED,Illuminazione,LED,Colore Verde,Verde,","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed",http://dropshipping.bigbuy.eu/imgs/G0500194_87775.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500194_87775.jpg,,,,,,"2016-01-08 12:34:41",,,,,,,,,,,,,,,,,"GTIN=8436545443507",37,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500194_87774.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87773.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87772.jpg","GTIN=8436545443507",,,,,,,,,,
+BB-I2500333,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Orologi da parete e da tavolo",base,"Orologio da Parete Mom's Diner","Decora
la tua cucina con l'originale orologio da parete Mom's Diner in stile vintage! È realizzato in legno. Diametro: 58 cm circa. Spessore: 0,8 cm circa. Funziona a pile (1 x AA, non inclusa). Dimenzioni per Orologio da Parete Mom's Diner:
Altezza: 59 Cm Larghezza: 59 Cm Profondita': 6 Cm Peso: 2.1 Kg Codice Prodotto (EAN): 4029811345052
","Decora la tua cucina con l'originale orologio da parete Mom's Diner in stile vintage! È realizzato in legno. Maggiori Informazioni ",2.1,1,"Taxable Goods","Catalog, Search",42.5,,,,Orologio-da-Parete-Mom's-Diner,"Orologio da Parete Mom's Diner","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Orologi da parete e da tavolo,Orologi,parete,tavolo,","Decora la tua cucina con l'originale orologio da parete Mom's Diner in stile vintage! È realizzato in legno",http://dropshipping.bigbuy.eu/imgs/I2500333_88061.jpg,,http://dropshipping.bigbuy.eu/imgs/I2500333_88061.jpg,,,,,,"2016-07-21 13:05:12",,,,,,,,,,,,,,,,,"GTIN=4029811345052",2,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I2500333_88060.jpg,http://dropshipping.bigbuy.eu/imgs/I2500333_88059.jpg,http://dropshipping.bigbuy.eu/imgs/I2500333_88058.jpg","GTIN=4029811345052",,,,,,,,,,
+BB-I2500334,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Orologi da parete e da tavolo",base,"Orologio da Parete Coffee Endless Cup","Se
sei un appassionato di caffè, non puoi rimanere senza l'orologio da parete Coffee Endless Cup ! Un orologio vintage in legno con un design in perfetto stile caffettoso! Diametro: 58 cm circa. Spessore: 0,8 cm circa. Funziona a pile (1 x AA, non inclusa). Dimenzioni per Orologio da Parete Coffee Endless Cup:
Altezza: 59 Cm Larghezza: 59 Cm Profondita': 6 Cm Peso: 2.1 Kg Codice Prodotto (EAN): 4029811345069
","Se sei un appassionato di caffè, non puoi rimanere senza l'orologio da parete Coffee Endless Cup! Un orologio vintage in legno con un design in perfetto stile caffettoso! Diametro: 58 cm circa. Maggiori Informazioni ",2.1,1,"Taxable Goods","Catalog, Search",42.5,,,,Orologio-da-Parete-Coffee-Endless-Cup,"Orologio da Parete Coffee Endless Cup","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Orologi da parete e da tavolo,Orologi,parete,tavolo,","Se sei un appassionato di caffè, non puoi rimanere senza l'orologio da parete Coffee Endless Cup! Un orologio vintage in legno con un design in perfetto stile caffettoso! Diametro: 58 cm circa",http://dropshipping.bigbuy.eu/imgs/I2500334_88065.jpg,,http://dropshipping.bigbuy.eu/imgs/I2500334_88065.jpg,,,,,,"2016-08-30 13:41:52",,,,,,,,,,,,,,,,,"GTIN=4029811345069",4,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I2500334_88064.jpg,http://dropshipping.bigbuy.eu/imgs/I2500334_88063.jpg,http://dropshipping.bigbuy.eu/imgs/I2500334_88062.jpg","GTIN=4029811345069",,,,,,,,,,
+BB-V0000252,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Insegna Dito Vintage Look Stop!","Se
sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Dimensioni: 69 x 17 x 1 cm circa. Dimenzioni per Insegna Dito Vintage Look:
Altezza: 17 Cm Larghezza: 69 Cm Profondita': 1 Cm Peso: 0.63 Kg Codice Prodotto (EAN): 4029811346196
","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Maggiori Informazioni ",0.63,1,"Taxable Goods","Catalog, Search",15.99,,,,Insegna-Dito-Vintage-Look-Stop!,"Insegna Dito Vintage Look Stop!","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Stop!,Stop!,","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno",http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,,,,,,"2016-02-29 09:49:10",,,,,,,,,,,,,,,,,"GTIN=4029811346196",21,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89743.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89742.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89741.jpg","GTIN=4029811346196",,,,,,,,,,
+BB-V0000253,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Insegna Dito Vintage Look Adults Only","Se
sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Dimensioni: 69 x 17 x 1 cm circa. Dimenzioni per Insegna Dito Vintage Look:
Altezza: 17 Cm Larghezza: 69 Cm Profondita': 1 Cm Peso: 0.63 Kg Codice Prodotto (EAN): 4029811346202
","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Maggiori Informazioni ",0.63,1,"Taxable Goods","Catalog, Search",15.99,,,,Insegna-Dito-Vintage-Look-Adults Only,"Insegna Dito Vintage Look Adults Only","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Adults Only,Adults Only,","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno",http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,,,,,,"2016-02-29 09:49:10",,,,,,,,,,,,,,,,,"GTIN=4029811346202",18,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89743.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89742.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89741.jpg","GTIN=4029811346202",,,,,,,,,,
+BB-V0000254,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Insegna Dito Vintage Look Talk","Se
sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Dimensioni: 69 x 17 x 1 cm circa. Dimenzioni per Insegna Dito Vintage Look:
Altezza: 17 Cm Larghezza: 69 Cm Profondita': 1 Cm Peso: 0.63 Kg Codice Prodotto (EAN): 4029811346318
","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Maggiori Informazioni ",0.63,1,"Taxable Goods","Catalog, Search",15.99,,,,Insegna-Dito-Vintage-Look-Talk,"Insegna Dito Vintage Look Talk","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Talk,Talk,","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno",http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,,,,,,"2016-02-29 09:49:10",,,,,,,,,,,,,,,,,"GTIN=4029811346318",8,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89743.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89742.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89741.jpg","GTIN=4029811346318",,,,,,,,,,
+BB-V0000256,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Freccia Decorativa Vintage Look Go Left","Stupisci
tutti con la divertente ed originale freccia decorativa Vintage Look ! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Misure appross.: 25 x 40 x 1 cm. Dimenzioni per Freccia Decorativa Vintage Look:
Altezza: 25.5 Cm Larghezza: 0.8 Cm Profondita': 40 Cm Peso: 0.376 Kg Codice Prodotto (EAN): 4029811346325
","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Maggiori Informazioni ",0.376,1,"Taxable Goods","Catalog, Search",13.9,,,,Freccia-Decorativa-Vintage-Look-Go Left,"Freccia Decorativa Vintage Look Go Left","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Go Left,Go Left,","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno",http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,,,,,,"2016-02-29 10:39:59",,,,,,,,,,,,,,,,,"GTIN=4029811346325",4,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89759.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89758.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89757.jpg","GTIN=4029811346325",,,,,,,,,,
+BB-V0000257,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Freccia Decorativa Vintage Look Exit","Stupisci
tutti con la divertente ed originale freccia decorativa Vintage Look ! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Misure appross.: 25 x 40 x 1 cm. Dimenzioni per Freccia Decorativa Vintage Look:
Altezza: 25.5 Cm Larghezza: 0.8 Cm Profondita': 40 Cm Peso: 0.376 Kg Codice Prodotto (EAN): 4029811346332
","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Maggiori Informazioni ",0.376,1,"Taxable Goods","Catalog, Search",13.9,,,,Freccia-Decorativa-Vintage-Look-Exit,"Freccia Decorativa Vintage Look Exit","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Exit,Exit,","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno",http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,,,,,,"2016-02-29 10:39:59",,,,,,,,,,,,,,,,,"GTIN=4029811346332",19,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89759.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89758.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89757.jpg","GTIN=4029811346332",,,,,,,,,,
+BB-V0000258,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Freccia Decorativa Vintage Look Cold beer here","Stupisci
tutti con la divertente ed originale freccia decorativa Vintage Look ! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Misure appross.: 25 x 40 x 1 cm. Dimenzioni per Freccia Decorativa Vintage Look:
Altezza: 25.5 Cm Larghezza: 0.8 Cm Profondita': 40 Cm Peso: 0.376 Kg Codice Prodotto (EAN): 4029811346349
","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Maggiori Informazioni ",0.376,1,"Taxable Goods","Catalog, Search",13.9,,,,Freccia-Decorativa-Vintage-Look-Cold beer here,"Freccia Decorativa Vintage Look Cold beer here","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Cold beer here,Cold beer here,","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno",http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,,,,,,"2016-02-29 10:39:59",,,,,,,,,,,,,,,,,"GTIN=4029811346349",20,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89759.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89758.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89757.jpg","GTIN=4029811346349",,,,,,,,,,
+BB-V0200190,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Centrotavola e Vasi",base,"Ciotola in Bambù TakeTokio Bianco","Arricchisci
la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio , una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera , portafrutta, ecc. Realizzata in pregiato legno di bambù. Dimensioni (diametro x altezza): 25 x 15 cm circa. Diametro della base: 9 cm circa.www.taketokio.com
Dimenzioni per Ciotola in Bambù TakeTokio:
Altezza: 25 Cm Larghezza: 25 Cm Profondita': 15 Cm Peso: 0.39 Kg Codice Prodotto (EAN): 8718158904577
","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc. Maggiori Informazioni ",0.39,1,"Taxable Goods","Catalog, Search",19.8,,,,Ciotola-in-Bambù-TakeTokio-Bianco,"Ciotola in Bambù TakeTokio Bianco","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Centrotavola e Vasi,Centrotavola,Vasi,Colore Bianco,Bianco,","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc",http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8718158904577",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90744.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90743.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90742.jpg","GTIN=8718158904577",,,,,,,,,,
+BB-V0200192,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Centrotavola e Vasi",base,"Ciotola in Bambù TakeTokio Grigio","Arricchisci
la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio , una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera , portafrutta, ecc. Realizzata in pregiato legno di bambù. Dimensioni (diametro x altezza): 25 x 15 cm circa. Diametro della base: 9 cm circa.www.taketokio.com
Dimenzioni per Ciotola in Bambù TakeTokio:
Altezza: 25 Cm Larghezza: 25 Cm Profondita': 15 Cm Peso: 0.39 Kg Codice Prodotto (EAN): 8718158904577
","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc. Maggiori Informazioni ",0.39,1,"Taxable Goods","Catalog, Search",19.8,,,,Ciotola-in-Bambù-TakeTokio-Grigio,"Ciotola in Bambù TakeTokio Grigio","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Centrotavola e Vasi,Centrotavola,Vasi,Colore Grigio,Grigio,","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc",http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8718158904577",26,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90744.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90743.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90742.jpg","GTIN=8718158904577",,,,,,,,,,
+BB-V0200191,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Centrotavola e Vasi",base,"Ciotola in Bambù TakeTokio Nero","Arricchisci
la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio , una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera , portafrutta, ecc. Realizzata in pregiato legno di bambù. Dimensioni (diametro x altezza): 25 x 15 cm circa. Diametro della base: 9 cm circa.www.taketokio.com
Dimenzioni per Ciotola in Bambù TakeTokio:
Altezza: 25 Cm Larghezza: 25 Cm Profondita': 15 Cm Peso: 0.39 Kg Codice Prodotto (EAN): 8718158904577
","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc. Maggiori Informazioni ",0.39,1,"Taxable Goods","Catalog, Search",19.8,,,,Ciotola-in-Bambù-TakeTokio-Nero,"Ciotola in Bambù TakeTokio Nero","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Centrotavola e Vasi,Centrotavola,Vasi,Colore Nero,Nero,","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc",http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8718158904577",22,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90744.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90743.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90742.jpg","GTIN=8718158904577",,,,,,,,,,
+BB-V0200360,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Scatola porta Tè Flower Vintage Coconut Rosa","Gli
amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut ! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Dispone di un coperchio in cristallo e vari scompartimenti. Dimensioni: circa 23 x 7 x 23 cm.www.vintagecoconut.com
Dimenzioni per Scatola porta Tè Flower Vintage Coconut:
Altezza: 23 Cm Larghezza: 7.1 Cm Profondita': 23 Cm Peso: 0.795 Kg Codice Prodotto (EAN): 8711295889547
","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Maggiori Informazioni ",0.795,1,"Taxable Goods","Catalog, Search",25.9,,,,Scatola-porta-Tè-Flower-Vintage-Coconut-Rosa,"Scatola porta Tè Flower Vintage Coconut Rosa","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,Colore Rosa,Rosa,","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi",http://dropshipping.bigbuy.eu/imgs/V0200328_92649.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200328_92649.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8711295889547",13,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200328_92648.jpg,http://dropshipping.bigbuy.eu/imgs/V0200328_92647.jpg","GTIN=8711295889547",,,,,,,,,,
+BB-V0200361,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Scatola porta Tè Flower Vintage Coconut Azzurro","Gli
amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut ! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Dispone di un coperchio in cristallo e vari scompartimenti. Dimensioni: circa 23 x 7 x 23 cm.www.vintagecoconut.com
Dimenzioni per Scatola porta Tè Flower Vintage Coconut:
Altezza: 23 Cm Larghezza: 7.1 Cm Profondita': 23 Cm Peso: 0.795 Kg Codice Prodotto (EAN): 8711295889547
","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Maggiori Informazioni ",0.795,1,"Taxable Goods","Catalog, Search",25.9,,,,Scatola-porta-Tè-Flower-Vintage-Coconut-Azzurro,"Scatola porta Tè Flower Vintage Coconut Azzurro","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,Colore Azzurro,Azzurro,","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi",http://dropshipping.bigbuy.eu/imgs/V0200328_92648.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200328_92648.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8711295889547",21,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200328_92649.jpg,http://dropshipping.bigbuy.eu/imgs/V0200328_92647.jpg","GTIN=8711295889547",,,,,,,,,,
+BB-V0200353,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Barbecue",base,"Ventilatore a Pistola classico per Barbecue BBQ Classics","Utilizza
i migliori barbecue alimentando il fuoco con il ventilatore a pistola classico per babecue BBQ Classics ! Basterà solo fare una leggera pressione sul pulsante per far uscire l'aria.www.bbqclassics.com
Realizzato in plastica e metallo Dimensioni: 25 x 18 x 4 cm circa Dimenzioni per Ventilatore a Pistola classico per Barbecue BBQ Classics:
Altezza: 5.5 Cm Larghezza: 11 Cm Profondita': 22 Cm Peso: 0.167 Kg Codice Prodotto (EAN): 8718158032706
","Utilizza i migliori barbecue alimentando il fuoco con il ventilatore a pistola classico per babecue BBQ Classics! Basterà solo fare una leggera pressione sul pulsante per far uscire l'aria. Maggiori Informazioni ",0.167,1,"Taxable Goods","Catalog, Search",9.3,,,,Ventilatore-a-Pistola-classico-per-Barbecue-BBQ-Classics,"Ventilatore a Pistola classico per Barbecue BBQ Classics","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Barbecue,","Utilizza i migliori barbecue alimentando il fuoco con il ventilatore a pistola classico per babecue BBQ Classics! Basterà solo fare una leggera pressione sul pulsante per far uscire l'aria",http://dropshipping.bigbuy.eu/imgs/V0200353_92695.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200353_92695.jpg,,,,,,"2016-09-13 09:56:07",,,,,,,,,,,,,,,,,"GTIN=8718158032706",60,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200353_92696.jpg,http://dropshipping.bigbuy.eu/imgs/V0200353_92694.jpg","GTIN=8718158032706",,,,,,,,,,
+BB-V1600123,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Frozen (32 x 23 cm)","Non
c'è nulla di meglio che tenere le camere dei più piccini in ordine in modo originale e divertente. Con il contenitore portagiochi Frozen (32 x 23 cm) sarà semplicissimo!Realizzato in polipropilene Dimensioni aprossimative: 32 x 15 x 23 cm Età consigliata: +3 anni
Dimenzioni per Contenitore Portagiochi Frozen (32 x 23 cm):
Altezza: 15 Cm Larghezza: 34 Cm Profondita': 23 Cm Peso: 0.331 Kg Codice Prodotto (EAN): 8412842766006
","Non c'è nulla di meglio che tenere le camere dei più piccini in ordine in modo originale e divertente. Maggiori Informazioni ",0.331,1,"Taxable Goods","Catalog, Search",21.9,,,,Contenitore-Portagiochi-Frozen-(32-x-23-cm),"Contenitore Portagiochi Frozen (32 x 23 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","Non c'è nulla di meglio che tenere le camere dei più piccini in ordine in modo originale e divertente",http://dropshipping.bigbuy.eu/imgs/V1600123_93002.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600123_93002.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8412842766006",48,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600123_93005.jpg,http://dropshipping.bigbuy.eu/imgs/V1600123_93004.jpg,http://dropshipping.bigbuy.eu/imgs/V1600123_93003.jpg","GTIN=8412842766006",,,,,,,,,,
+BB-V1600124,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Spiderman (32 x 23 cm)","Desideri
sorprendere i più piccini con un regalo molto originale? Il contenitore portagiochi Spiderman (32 x 23 cm) decorerà e metterà in ordine le loro camerette.Realizzato in polipropilene Dimensioni approssimative: 32 x 15 x 23 cm Età consigliata: +3 anni Dimenzioni per Contenitore Portagiochi Spiderman (32 x 23 cm):
Altezza: 15 Cm Larghezza: 34 Cm Profondita': 23 Cm Peso: 0.331 Kg Codice Prodotto (EAN): 8412842766037
","Desideri sorprendere i più piccini con un regalo molto originale? Il contenitore portagiochi Spiderman (32 x 23 cm) decorerà e metterà in ordine le loro camerette. Maggiori Informazioni ",0.331,1,"Taxable Goods","Catalog, Search",21.9,,,,Contenitore-Portagiochi-Spiderman--(32-x-23-cm),"Contenitore Portagiochi Spiderman (32 x 23 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","Desideri sorprendere i più piccini con un regalo molto originale? Il contenitore portagiochi Spiderman (32 x 23 cm) decorerà e metterà in ordine le loro camerette",http://dropshipping.bigbuy.eu/imgs/V1600124_93006.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600124_93006.jpg,,,,,,"2016-08-08 21:09:24",,,,,,,,,,,,,,,,,"GTIN=8412842766037",52,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600124_93008.jpg,http://dropshipping.bigbuy.eu/imgs/V1600124_93007.jpg","GTIN=8412842766037",,,,,,,,,,
+BB-V1600125,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Frozen (45 x 32 cm)","Insegna
ai tuoi bambini a tenere i giocattoli conservati ben in ordine con l'aiuto del contenitore portagiochi Frozen (45 x 32 cm) . Il portagiocattoli che tutte le bambine sognano!Realizzato in polipropilene Dimensioni: circa 45 x 22 x 32 cm Età consigliata: +3 anni Dimenzioni per Contenitore Portagiochi Frozen (45 x 32 cm):
Altezza: 22 Cm Larghezza: 37 Cm Profondita': 45 Cm Peso: 0.775 Kg Codice Prodotto (EAN): 8412842766129
","Insegna ai tuoi bambini a tenere i giocattoli conservati ben in ordine con l'aiuto del contenitore portagiochi Frozen (45 x 32 cm). Maggiori Informazioni ",0.775,1,"Taxable Goods","Catalog, Search",39.6,,,,Contenitore-Portagiochi-Frozen-(45-x-32-cm),"Contenitore Portagiochi Frozen (45 x 32 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","Insegna ai tuoi bambini a tenere i giocattoli conservati ben in ordine con l'aiuto del contenitore portagiochi Frozen (45 x 32 cm)",http://dropshipping.bigbuy.eu/imgs/V1600125_93010.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600125_93010.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8412842766129",17,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600125_93011.jpg,http://dropshipping.bigbuy.eu/imgs/V1600125_93009.jpg","GTIN=8412842766129",,,,,,,,,,
+BB-V1600126,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Spiderman (45 x 32 cm)","I
piccoli di casa ora possono ordinare e riporre i loro giocattoli facilmente grazie al contenitore portagiochi Spiderman (45 x 32 cm) . Il contenitore portagiochi preferito dai bambini!Fabbricato in polipropilene Dimensioni: circa 45 x 22 x 32 cm Età raccomandata: +3 anni Dimenzioni per Contenitore Portagiochi Spiderman (45 x 32 cm):
Altezza: 22 Cm Larghezza: 37 Cm Profondita': 45 Cm Peso: 0.775 Kg Codice Prodotto (EAN): 8412842766150
","I piccoli di casa ora possono ordinare e riporre i loro giocattoli facilmente grazie al contenitore portagiochi Spiderman (45 x 32 cm). Maggiori Informazioni ",0.775,1,"Taxable Goods","Catalog, Search",39.6,,,,Contenitore-Portagiochi-Spiderman-(45-x-32-cm),"Contenitore Portagiochi Spiderman (45 x 32 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","I piccoli di casa ora possono ordinare e riporre i loro giocattoli facilmente grazie al contenitore portagiochi Spiderman (45 x 32 cm)",http://dropshipping.bigbuy.eu/imgs/V1600126_93012.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600126_93012.jpg,,,,,,"2016-08-08 21:09:24",,,,,,,,,,,,,,,,,"GTIN=8412842766150",18,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600126_93014.jpg,http://dropshipping.bigbuy.eu/imgs/V1600126_93013.jpg","GTIN=8412842766150",,,,,,,,,,
+BB-V1300154,,Default,simple,"Default Category/Relax Tempo Libero,Default Category/Relax Tempo Libero/Mare e Piscina",base,"Zaino per Piscina Spiderman (4 pezzi)","Vorresti
sorprendere i più piccoli della casa con un regalo originale ? Se adorano il mare o la piscina, lo zaino per piscina Spiderman (4 pezzi) li farà impazzire.Dispone di una cerniera, una rete posteriore e uno scompartimento per il nome Dispone di manico regolabile 1 asciugamano 42,5 x 90,5 cm (80 % poliestere e 20 % poliammide) dall'asciugatura rapida 1 cuffia da nuoto taglia unica (85 % poliestere e 15 % elastam) 1 paio di occhialini da nuoto (norme 89/686/CEE e ISO 12312-1:2013) anti appannamento Dimensioni dello zaino: 24 x 31 x 6 cm circa Dimenzioni per Zaino per Piscina Spiderman (4 pezzi):
Altezza: 3 Cm Larghezza: 25 Cm Profondita': 30.5 Cm Peso: 0.283 Kg Codice Prodotto (EAN): 7569000752232
","Vorresti sorprendere i più piccoli della casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Spiderman (4 pezzi) li farà impazzire. Maggiori Informazioni ",0.283,1,"Taxable Goods","Catalog, Search",37.9,,,,Zaino-per-Piscina-Spiderman-(4-pezzi),"Zaino per Piscina Spiderman (4 pezzi)","Relax Tempo Libero,Relax,Tempo,Libero,Mare e Piscina,Mare,Piscina,","Vorresti sorprendere i più piccoli della casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Spiderman (4 pezzi) li farà impazzire",http://dropshipping.bigbuy.eu/imgs/V1300154_93570.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300154_93570.jpg,,,,,,"2016-08-16 13:42:17",,,,,,,,,,,,,,,,,"GTIN=7569000752232",129,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300154_93576.jpg,http://dropshipping.bigbuy.eu/imgs/V1300154_93575.jpg,http://dropshipping.bigbuy.eu/imgs/V1300154_93574.jpg,http://dropshipping.bigbuy.eu/imgs/V1300154_93573.jpg,http://dropshipping.bigbuy.eu/imgs/V1300154_93572.jpg,http://dropshipping.bigbuy.eu/imgs/V1300154_93571.jpg","GTIN=7569000752232",,,,,,,,,,
+BB-V1300156,,Default,simple,"Default Category/Relax Tempo Libero,Default Category/Relax Tempo Libero/Mare e Piscina",base,"Zaino per Piscina Frozen (4 pezzi)","Vuoi
fare un regalo originale ai piccoli di casa? Se adorano il mare o la piscina, lo zaino per piscina Frozen (4 pezzi) li farà impazzire.Presenta una cerniera, una rete posteriore e uno scompartimento per il nome Dispone di manico regolabile 1 asciugamano 42,5 x 90,5 cm (80 % poliestere e 20 % poliammide) dall'asciugatura rapida 1 cuffia da nuoto taglia unica (85 % poliestere e 15 % elastam) 1 paio di occhialini da nuoto (norme 89/686/CEE e ISO 12312-1:2013) anti appannamento Dimensioni dello zaino: circa 24 x 31 x 6 cm Dimenzioni per Zaino per Piscina Frozen (4 pezzi):
Altezza: 2 Cm Larghezza: 23 Cm Profondita': 30.5 Cm Peso: 0.277 Kg Codice Prodotto (EAN): 7569000752225
","Vuoi fare un regalo originale ai piccoli di casa? Se adorano il mare o la piscina, lo zaino per piscina Frozen (4 pezzi) li farà impazzire. Maggiori Informazioni ",0.277,1,"Taxable Goods","Catalog, Search",37.9,,,,Zaino-per-Piscina-Frozen-(4-pezzi),"Zaino per Piscina Frozen (4 pezzi)","Relax Tempo Libero,Relax,Tempo,Libero,Mare e Piscina,Mare,Piscina,","Vuoi fare un regalo originale ai piccoli di casa? Se adorano il mare o la piscina, lo zaino per piscina Frozen (4 pezzi) li farà impazzire",http://dropshipping.bigbuy.eu/imgs/V1300156_93580.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300156_93580.jpg,,,,,,"2016-08-26 13:31:11",,,,,,,,,,,,,,,,,"GTIN=7569000752225",104,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300156_93594.jpg,http://dropshipping.bigbuy.eu/imgs/V1300156_93585.jpg,http://dropshipping.bigbuy.eu/imgs/V1300156_93584.jpg,http://dropshipping.bigbuy.eu/imgs/V1300156_93583.jpg,http://dropshipping.bigbuy.eu/imgs/V1300156_93582.jpg,http://dropshipping.bigbuy.eu/imgs/V1300156_93581.jpg","GTIN=7569000752225",,,,,,,,,,
+BB-V1300157,,Default,simple,"Default Category/Relax Tempo Libero,Default Category/Relax Tempo Libero/Mare e Piscina",base,"Zaino per Piscina Minnie (4 pezzi)","Ti
piacerebbe sorprendere le bimbe della casa con un regalo originale ? Se adorano il mare o la piscina, lo zaino per piscina Minnie (4 pezzi) le farà impazzire.Dispone di una cerniera, una rete posteriore e uno scompartimento per il nome Dispone di manico regolabile 1 asciugamano 42,5 x 90,5 cm (80 % poliestere e 20 % poliammide) dall'asciugatura rapida 1 cuffia da nuoto taglia unica (85 % poliestere e 15 % elastam) 1 paio di occhialini da nuoto (norme 89/686/CEE e ISO 12312-1:2013) anti appannamento Dimensioni dello zaino: 24 x 31 x 6 cm circa Dimenzioni per Zaino per Piscina Minnie (4 pezzi):
Altezza: 4 Cm Larghezza: 24 Cm Profondita': 30 Cm Peso: 0.277 Kg Codice Prodotto (EAN): 8427934823796
","Ti piacerebbe sorprendere le bimbe della casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Minnie (4 pezzi) le farà impazzire. Maggiori Informazioni ",0.277,1,"Taxable Goods","Catalog, Search",37.9,,,,Zaino-per-Piscina-Minnie-(4-pezzi),"Zaino per Piscina Minnie (4 pezzi)","Relax Tempo Libero,Relax,Tempo,Libero,Mare e Piscina,Mare,Piscina,","Ti piacerebbe sorprendere le bimbe della casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Minnie (4 pezzi) le farà impazzire",http://dropshipping.bigbuy.eu/imgs/V1300157_93587.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300157_93587.jpg,,,,,,"2016-08-26 13:30:29",,,,,,,,,,,,,,,,,"GTIN=8427934823796",137,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300157_93593.jpg,http://dropshipping.bigbuy.eu/imgs/V1300157_93592.jpg,http://dropshipping.bigbuy.eu/imgs/V1300157_93591.jpg,http://dropshipping.bigbuy.eu/imgs/V1300157_93590.jpg,http://dropshipping.bigbuy.eu/imgs/V1300157_93589.jpg,http://dropshipping.bigbuy.eu/imgs/V1300157_93586.jpg","GTIN=8427934823796",,,,,,,,,,
+BB-V1300158,,Default,simple,"Default Category/Relax Tempo Libero,Default Category/Relax Tempo Libero/Mare e Piscina",base,"Zaino per Piscina Avengers (4 pezzi)","Ti
piacerebbe sorprendere i più piccoli della casa con un regalo originale ? Se adorano il mare o la piscina, lo zaino per piscina Avengers (4 pezzi) li farà impazzire.Dispone di una cerniera, una rete posteriore e uno scompartimento per il nome Dispone di manico regolabile 1 asciugamano 42,5 x 90,5 cm (80 % poliestere e 20 % poliammide) dall'asciugatura rapida 1 cuffia da nuoto taglia unica (85 % poliestere e 15 % elastam) 1 paio di occhialini da nuoto (norme 89/686/CEE e ISO 12312-1:2013) anti appannamento Dimensioni dello zaino: 24 x 31 x 6 cm circa Dimenzioni per Zaino per Piscina Avengers (4 pezzi):
Altezza: 3 Cm Larghezza: 23 Cm Profondita': 31 Cm Peso: 0.279 Kg Codice Prodotto (EAN): 7569000752249
","Ti piacerebbe sorprendere i più piccoli della casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Avengers (4 pezzi) li farà impazzire. Maggiori Informazioni ",0.279,1,"Taxable Goods","Catalog, Search",37.9,,,,Zaino-per-Piscina-Avengers-(4-pezzi),"Zaino per Piscina Avengers (4 pezzi)","Relax Tempo Libero,Relax,Tempo,Libero,Mare e Piscina,Mare,Piscina,","Ti piacerebbe sorprendere i più piccoli della casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Avengers (4 pezzi) li farà impazzire",http://dropshipping.bigbuy.eu/imgs/V1300158_93596.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300158_93596.jpg,,,,,,"2016-08-26 11:29:45",,,,,,,,,,,,,,,,,"GTIN=7569000752249",139,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300158_93601.jpg,http://dropshipping.bigbuy.eu/imgs/V1300158_93600.jpg,http://dropshipping.bigbuy.eu/imgs/V1300158_93599.jpg,http://dropshipping.bigbuy.eu/imgs/V1300158_93598.jpg,http://dropshipping.bigbuy.eu/imgs/V1300158_93597.jpg,http://dropshipping.bigbuy.eu/imgs/V1300158_93595.jpg","GTIN=7569000752249",,,,,,,,,,
+BB-V1300159,,Default,simple,"Default Category/Relax Tempo Libero,Default Category/Relax Tempo Libero/Mare e Piscina",base,"Zaino per Piscina Minions (4 pezzi)","Ti
piacerebbe sorprendere i più piccoli di casa con un regalo originale ? Se adorano il mare o la piscina, lo zaino per piscina Minions (4 pezzi) li farà impazzire.Dispone di una cerniera, una rete posteriore e uno scompartimento per il nome Dispone di manico regolabile 1 asciugamano 42,5 x 90,5 cm (80 % poliestere e 20 % poliammide) dall'asciugatura rapida 1 cuffia da nuoto taglia unica (85 % poliestere e 15 % elastam) 1 paio di occhialini da nuoto (norme 89/686/CEE e ISO 12312-1:2013) anti appannamento Dimensioni dello zaino: 24 x 31 x 6 cm circa Dimenzioni per Zaino per Piscina Minions (4 pezzi):
Altezza: 3 Cm Larghezza: 23 Cm Profondita': 31 Cm Peso: 0.279 Kg Codice Prodotto (EAN): 8427934823833
","Ti piacerebbe sorprendere i più piccoli di casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Minions (4 pezzi) li farà impazzire. Maggiori Informazioni ",0.279,1,"Taxable Goods","Catalog, Search",37.9,,,,Zaino-per-Piscina-Minions-(4-pezzi),"Zaino per Piscina Minions (4 pezzi)","Relax Tempo Libero,Relax,Tempo,Libero,Mare e Piscina,Mare,Piscina,","Ti piacerebbe sorprendere i più piccoli di casa con un regalo originale? Se adorano il mare o la piscina, lo zaino per piscina Minions (4 pezzi) li farà impazzire",http://dropshipping.bigbuy.eu/imgs/V1300159_93602.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300159_93602.jpg,,,,,,"2016-08-26 11:29:10",,,,,,,,,,,,,,,,,"GTIN=8427934823833",132,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300159_93608.jpg,http://dropshipping.bigbuy.eu/imgs/V1300159_93607.jpg,http://dropshipping.bigbuy.eu/imgs/V1300159_93606.jpg,http://dropshipping.bigbuy.eu/imgs/V1300159_93605.jpg,http://dropshipping.bigbuy.eu/imgs/V1300159_93604.jpg,http://dropshipping.bigbuy.eu/imgs/V1300159_93603.jpg","GTIN=8427934823833",,,,,,,,,,
+BB-G0500179,,Default,simple,"Default Category/Sport Fitness,Default Category/Sport Fitness/Abbigliamento, Accessori e Dispositivi Indossabili",base,"Clip di Sicurezza a LED per Scarpe da Corsa GoFit","Ti
piace allenarti la sera all'aria aperta? Allora, non dimenticare di indossare la tua clip di sicurezza a LED per scarpe da corsa GoFit ! Grazie a questo utile accessorio da corsa , sarai visibile al buio mentre corri o ti alleni. Include 2 tipi di luce (fissa o intermittente) e può essere facilmente applicata sulla parte posteriore della scarpa o indossata intorno al polso. Possiede un bottone on/off che ti permette inoltre di cambiare il tipo di luce. Progettato in Europa con materiali di alta qualità. 1 pezzo incluso.Caratteristiche:
LED verde per alta visibilità Circa 100 ore di luce intermittente Circa 70 ore di luce fissa Adattabile a scrarpe da 6 a 8,5 cm di larghezza Funziona a batterie (2 x CR2032, incluse) Dimenzioni per Clip di Sicurezza a LED per Scarpe da Corsa GoFit :
Altezza: 18 Cm Larghezza: 9 Cm Profondita': 3.7 Cm Peso: 0.095 Kg Codice Prodotto (EAN): 8018417209116
","Ti piace allenarti la sera all'aria aperta? Allora, non dimenticare di indossare la tua clip di sicurezza a LED per scarpe da corsa GoFit! Grazie a questo utile accessorio da corsa, sarai visibile al buio mentre corri o ti alleni. Maggiori Informazioni ",0.095,1,"Taxable Goods","Catalog, Search",34.95,,,,Clip-di-Sicurezza-a-LED-per-Scarpe-da-Corsa-GoFit,"Clip di Sicurezza a LED per Scarpe da Corsa GoFit","Sport Fitness,Sport,Fitness,Abbigliamento, Accessori e Dispositivi Indossabili,Abbigliamento,,Accessori,Dispositivi,Indossabili,","Ti piace allenarti la sera all'aria aperta? Allora, non dimenticare di indossare la tua clip di sicurezza a LED per scarpe da corsa GoFit! Grazie a questo utile accessorio da corsa, sarai visibile al buio mentre corri o ti alleni",http://dropshipping.bigbuy.eu/imgs/clip_led_running_go_fit.jpg,,http://dropshipping.bigbuy.eu/imgs/clip_led_running_go_fit.jpg,,,,,,"2016-08-22 08:23:08",,,,,,,,,,,,,,,,,"GTIN=8018417209116",207,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/clip_led_running_go_fit_04.jpg,http://dropshipping.bigbuy.eu/imgs/clip_led_running_go_fit_02.jpg,http://dropshipping.bigbuy.eu/imgs/clip_led_running_go_fit_002.jpg","GTIN=8018417209116",,,,,,,,,,
+BB-G0500187,,Default,simple,"Default Category/Sport Fitness,Default Category/Sport Fitness/Abbigliamento, Accessori e Dispositivi Indossabili",base,"Sensore di Velocità Bluetooth GoFit","Se
sei un amante del ciclismo e vuoi tener traccia della velocità, ritmo e distanza mentre pedali? Allora non perdere l'occasione, acquista il sensore di velocità Bluetooth GoFit , in modo da essere in grado di monitorare tutti i suoi dati in ogni momento, grazie al suo ingegnoso dispositivo! Devi solo installare il sensore e scaricare l'apposita applicazione sul tuo telefono cellulare. Questo sensore di velocità e ritmo è stato progettato in Europa ed è costituito di materiali resistenti all'acqua, in polimeri termoplastici. Molto semplice da installare. Compatibile con iOS (7.0 e successivi) e Android (4.3 e successivi). Funziona a batterie (1 x CR2032, incluse).Include:
1 sensore di velocità e ritmo (dimensioni: circa 8,5 x 7 x 1,5 cm) 1 magnete per ritmo pedale (dimensioni: circa 1,5 x 3,5 x 2 cm) 1 magnete per ruote 1 cacciavite 2 fascette 1 banda elastica Dimenzioni per Sensore di Velocità Bluetooth GoFit:
Altezza: 20.3 Cm Larghezza: 9 Cm Profondita': 3.5 Cm Peso: 0.143 Kg Codice Prodotto (EAN): 8018417209109
","Se sei un amante del ciclismo e vuoi tener traccia della velocità, ritmo e distanza mentre pedali? Allora non perdere l'occasione, acquista il sensore di velocità Bluetooth GoFit, in modo da essere in grado di monitorare tutti i suoi dati in ogni momento, grazie al suo ingegnoso dispositivo! Devi solo installare il sensore e scaricare l'apposita applicazione sul tuo telefono cellulare. Maggiori Informazioni ",0.143,1,"Taxable Goods","Catalog, Search",179.9,,,,Sensore-di-Velocità-Bluetooth-GoFit,"Sensore di Velocità Bluetooth GoFit","Sport Fitness,Sport,Fitness,Abbigliamento, Accessori e Dispositivi Indossabili,Abbigliamento,,Accessori,Dispositivi,Indossabili,","Se sei un amante del ciclismo e vuoi tener traccia della velocità, ritmo e distanza mentre pedali? Allora non perdere l'occasione, acquista il sensore di velocità Bluetooth GoFit, in modo da essere in grado di monitorare tutti i suoi dati in ogni mo",http://dropshipping.bigbuy.eu/imgs/G0500187_81130.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500187_81130.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8018417209109",17,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500187_81089.jpg,http://dropshipping.bigbuy.eu/imgs/G0500187_81088.jpg,http://dropshipping.bigbuy.eu/imgs/G0500187_81087.jpg,http://dropshipping.bigbuy.eu/imgs/G0500187_81086.jpg","GTIN=8018417209109",,,,,,,,,,
+BB-G0500188,,Default,simple,"Default Category/Sport Fitness,Default Category/Sport Fitness/Abbigliamento, Accessori e Dispositivi Indossabili",base,"Luce a Led di Sicurezza per Lacci GoFit (pacco da 2)","Da
ora in poi potrai fare jogging serenamente, sapendo che puoi essere visto dai veicoli intorno a te! Basta inserire la luce a led di sicurezza GoFit (pacco da 2) dentro ogni scarpa da corsa per aumentare la tua visibilità. Le loro 2 potenti luci a LED verdi si attivano ad ogni passo che fai. È davvero semplice! Ogni luce a LED funziona a pile ( 2 x CR1220 , 6 V , incluse). Durata delle batterie: 150,000 lampeggi di luce. Queste luci a LED sono costituite di materiali di alta qualità e sono adatte all'utilizzo con i lacci con uno spessore massimo di 9 mm. Include 2 unità. Dimensioni: circa 4 x 1,5 x 0,8 cm. Dimenzioni per Luce a Led di Sicurezza per Lacci GoFit (pacco da 2):
Altezza: 9 Cm Larghezza: 3.7 Cm Profondita': 20 Cm Peso: 0.061 Kg Codice Prodotto (EAN): 8018417209482
","Da ora in poi potrai fare jogging serenamente, sapendo che puoi essere visto dai veicoli intorno a te! Basta inserire la luce a led di sicurezza GoFit (pacco da 2) dentro ogni scarpa da corsa per aumentare la tua visibilità. Maggiori Informazioni ",0.061,1,"Taxable Goods","Catalog, Search",33.95,,,,Luce-a-Led-di-Sicurezza-per-Lacci-GoFit-(pacco-da-2),"Luce a Led di Sicurezza per Lacci GoFit (pacco da 2)","Sport Fitness,Sport,Fitness,Abbigliamento, Accessori e Dispositivi Indossabili,Abbigliamento,,Accessori,Dispositivi,Indossabili,","Da ora in poi potrai fare jogging serenamente, sapendo che puoi essere visto dai veicoli intorno a te! Basta inserire la luce a led di sicurezza GoFit (pacco da 2) dentro ogni scarpa da corsa per aumentare la tua visibilità",http://dropshipping.bigbuy.eu/imgs/G0500188_81129.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500188_81129.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8018417209482",238,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500188_81096.jpg,http://dropshipping.bigbuy.eu/imgs/G0500188_81095.jpg","GTIN=8018417209482",,,,,,,,,,
+BB-G0500189,,Default,simple,"Default Category/Sport Fitness,Default Category/Sport Fitness/Abbigliamento, Accessori e Dispositivi Indossabili",base,"Bracciale di Sicurezza LED GoFit","Non
possiedi ancora il bracciale di sicurezza LED GoFit ? Non perdertelo e pratica i tuoi sport preferiti con questo leggero e comodo bracciale progettato in Europa. Qualunque auto o moto ti vedrà nel buio! Include 2 luci a LED e 2 modalità di illuminazione (fissa e lampeggiante) che puoi scegliere premendo il pulsante del bracciale. La cinghia di velcro lo fissa al braccio ed è flessibile e regolabile. La lunghezza massima è di circa 38,5 cm e la minima è di 31 cm. Funziona a batterie (2 x CR2023, incluse). Dimenzioni per Bracciale di Sicurezza LED GoFit:
Altezza: 9 Cm Larghezza: 4 Cm Profondita': 20 Cm Peso: 0.087 Kg Codice Prodotto (EAN): 8018417209475
","Non possiedi ancora il bracciale di sicurezza LED GoFit? Non perdertelo e pratica i tuoi sport preferiti con questo leggero e comodo bracciale progettato in Europa. Maggiori Informazioni ",0.087,1,"Taxable Goods","Catalog, Search",44.95,,,,Bracciale-di-Sicurezza-LED-GoFit,"Bracciale di Sicurezza LED GoFit","Sport Fitness,Sport,Fitness,Abbigliamento, Accessori e Dispositivi Indossabili,Abbigliamento,,Accessori,Dispositivi,Indossabili,","Non possiedi ancora il bracciale di sicurezza LED GoFit? Non perdertelo e pratica i tuoi sport preferiti con questo leggero e comodo bracciale progettato in Europa",http://dropshipping.bigbuy.eu/imgs/G0500189_81128.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500189_81128.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8018417209475",93,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500189_81093.jpg,http://dropshipping.bigbuy.eu/imgs/G0500189_81092.jpg,http://dropshipping.bigbuy.eu/imgs/G0500189_81091.jpg","GTIN=8018417209475",,,,,,,,,,
+BB-F1510306,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Abbigliamento e Scarpe,Default Category/Moda Accessori/Abbigliamento e Scarpe/Pigiami e vestaglie",base,"Coperta con Maniche Snug Snug Big Tribu Leopardato","Affronta
il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu ! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. La coperta con maniche Big Tribu è dotata di tasca centrale per avere tutto a portata di mano, il telecomando della TV, il tuo libro preferito, ecc. Realizzata in morbido pile 100 % poliestere. Misure: 170 x 130 cm circa.www.snugsnug.com
Dimenzioni per Coperta con Maniche Snug Snug Big Tribu:
Altezza: 25 Cm Larghezza: 8.5 Cm Profondita': 26.5 Cm Peso: 0.602 Kg Codice Prodotto (EAN): 4899888103530
","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. Maggiori Informazioni ",0.602,1,"Taxable Goods","Catalog, Search",39.9,,,,Coperta-con-Maniche-Snug-Snug-Big-Tribu-Leopardato,"Coperta con Maniche Snug Snug Big Tribu Leopardato","Moda Accessori,Moda,Accessori,Abbigliamento e Scarpe,Abbigliamento,Scarpe,Pigiami e vestaglie,Pigiami,vestaglie,Colore Leopardato,Leopardato,","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle",http://dropshipping.bigbuy.eu/imgs/F1510300_81361.jpg,,http://dropshipping.bigbuy.eu/imgs/F1510300_81361.jpg,,,,,,"2015-12-30 17:30:06",,,,,,,,,,,,,,,,,"GTIN=4899888103530",46,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/F1510300_81365.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81364.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81363.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81362.jpg","GTIN=4899888103530",,,,,,,,,,
+BB-F1510307,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Abbigliamento e Scarpe,Default Category/Moda Accessori/Abbigliamento e Scarpe/Pigiami e vestaglie",base,"Coperta con Maniche Snug Snug Big Tribu Zebra","Affronta
il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu ! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. La coperta con maniche Big Tribu è dotata di tasca centrale per avere tutto a portata di mano, il telecomando della TV, il tuo libro preferito, ecc. Realizzata in morbido pile 100 % poliestere. Misure: 170 x 130 cm circa.www.snugsnug.com
Dimenzioni per Coperta con Maniche Snug Snug Big Tribu:
Altezza: 25 Cm Larghezza: 8.5 Cm Profondita': 26.5 Cm Peso: 0.602 Kg Codice Prodotto (EAN): 4899888103530
","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. Maggiori Informazioni ",0.602,1,"Taxable Goods","Catalog, Search",39.9,,,,Coperta-con-Maniche-Snug-Snug-Big-Tribu-Zebra,"Coperta con Maniche Snug Snug Big Tribu Zebra","Moda Accessori,Moda,Accessori,Abbigliamento e Scarpe,Abbigliamento,Scarpe,Pigiami e vestaglie,Pigiami,vestaglie,Colore Zebra,Zebra,","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle",http://dropshipping.bigbuy.eu/imgs/F1510300_81365.jpg,,http://dropshipping.bigbuy.eu/imgs/F1510300_81365.jpg,,,,,,"2016-01-21 08:26:10",,,,,,,,,,,,,,,,,"GTIN=4899888103530",486,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/F1510300_81361.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81364.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81363.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81362.jpg","GTIN=4899888103530",,,,,,,,,,
+BB-F1510308,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Abbigliamento e Scarpe,Default Category/Moda Accessori/Abbigliamento e Scarpe/Pigiami e vestaglie",base,"Coperta con Maniche Snug Snug Big Tribu Dalmata","Affronta
il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu ! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. La coperta con maniche Big Tribu è dotata di tasca centrale per avere tutto a portata di mano, il telecomando della TV, il tuo libro preferito, ecc. Realizzata in morbido pile 100 % poliestere. Misure: 170 x 130 cm circa.www.snugsnug.com
Dimenzioni per Coperta con Maniche Snug Snug Big Tribu:
Altezza: 25 Cm Larghezza: 8.5 Cm Profondita': 26.5 Cm Peso: 0.602 Kg Codice Prodotto (EAN): 4899888103530
","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. Maggiori Informazioni ",0.602,1,"Taxable Goods","Catalog, Search",39.9,,,,Coperta-con-Maniche-Snug-Snug-Big-Tribu-Dalmata,"Coperta con Maniche Snug Snug Big Tribu Dalmata","Moda Accessori,Moda,Accessori,Abbigliamento e Scarpe,Abbigliamento,Scarpe,Pigiami e vestaglie,Pigiami,vestaglie,Colore Dalmata,Dalmata,","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug Big Tribu! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle",http://dropshipping.bigbuy.eu/imgs/F1510300_81364.jpg,,http://dropshipping.bigbuy.eu/imgs/F1510300_81364.jpg,,,,,,"2016-02-22 08:16:26",,,,,,,,,,,,,,,,,"GTIN=4899888103530",307,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/F1510300_81361.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81365.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81363.jpg,http://dropshipping.bigbuy.eu/imgs/F1510300_81362.jpg","GTIN=4899888103530",,,,,,,,,,
+BB-F1510302,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Abbigliamento e Scarpe,Default Category/Moda Accessori/Abbigliamento e Scarpe/Pigiami e vestaglie",base,"Coperta con Maniche Snug Snug One Big Azzurro","Affronta
il freddo invernale con l'originale coperta con maniche Snug Snug One Big ! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. La coperta con maniche One Big è dotata di tasca centrale per avere tutto a portata di mano, il telecomando della TV, il tuo libro preferito, ecc. Realizzata in morbido pile 100 % poliestere. Misure: 170 x 130 cm circa.www.snugsnug.com Dimenzioni per Coperta con Maniche Snug Snug One Big:
Altezza: 26.5 Cm Larghezza: 25 Cm Profondita': 23.7 Cm Peso: 0.538 Kg Codice Prodotto (EAN): 4899888102977
","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. Maggiori Informazioni ",0.538,1,"Taxable Goods","Catalog, Search",29.9,,,,Coperta-con-Maniche-Snug-Snug-One-Big-Azzurro,"Coperta con Maniche Snug Snug One Big Azzurro","Moda Accessori,Moda,Accessori,Abbigliamento e Scarpe,Abbigliamento,Scarpe,Pigiami e vestaglie,Pigiami,vestaglie,Colore Azzurro,Azzurro,","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle m",http://dropshipping.bigbuy.eu/imgs/F1510301_81356.jpg,,http://dropshipping.bigbuy.eu/imgs/F1510301_81356.jpg,,,,,,"2015-12-28 17:26:13",,,,,,,,,,,,,,,,,"GTIN=4899888102977",538,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/F1510301_81360.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81359.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81358.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81357.jpg","GTIN=4899888102977",,,,,,,,,,
+BB-F1510303,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Abbigliamento e Scarpe,Default Category/Moda Accessori/Abbigliamento e Scarpe/Pigiami e vestaglie",base,"Coperta con Maniche Snug Snug One Big Rosso","Affronta
il freddo invernale con l'originale coperta con maniche Snug Snug One Big ! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. La coperta con maniche One Big è dotata di tasca centrale per avere tutto a portata di mano, il telecomando della TV, il tuo libro preferito, ecc. Realizzata in morbido pile 100 % poliestere. Misure: 170 x 130 cm circa.www.snugsnug.com Dimenzioni per Coperta con Maniche Snug Snug One Big:
Altezza: 26.5 Cm Larghezza: 25 Cm Profondita': 23.7 Cm Peso: 0.538 Kg Codice Prodotto (EAN): 4899888102977
","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. Maggiori Informazioni ",0.538,1,"Taxable Goods","Catalog, Search",29.9,,,,Coperta-con-Maniche-Snug-Snug-One-Big-Rosso,"Coperta con Maniche Snug Snug One Big Rosso","Moda Accessori,Moda,Accessori,Abbigliamento e Scarpe,Abbigliamento,Scarpe,Pigiami e vestaglie,Pigiami,vestaglie,Colore Rosso,Rosso,","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle m",http://dropshipping.bigbuy.eu/imgs/F1510301_81360.jpg,,http://dropshipping.bigbuy.eu/imgs/F1510301_81360.jpg,,,,,,"2016-01-20 10:35:30",,,,,,,,,,,,,,,,,"GTIN=4899888102977",600,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/F1510301_81356.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81359.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81358.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81357.jpg","GTIN=4899888102977",,,,,,,,,,
+BB-F1510304,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Abbigliamento e Scarpe,Default Category/Moda Accessori/Abbigliamento e Scarpe/Pigiami e vestaglie",base,"Coperta con Maniche Snug Snug One Big Verde","Affronta
il freddo invernale con l'originale coperta con maniche Snug Snug One Big ! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. La coperta con maniche One Big è dotata di tasca centrale per avere tutto a portata di mano, il telecomando della TV, il tuo libro preferito, ecc. Realizzata in morbido pile 100 % poliestere. Misure: 170 x 130 cm circa.www.snugsnug.com Dimenzioni per Coperta con Maniche Snug Snug One Big:
Altezza: 26.5 Cm Larghezza: 25 Cm Profondita': 23.7 Cm Peso: 0.538 Kg Codice Prodotto (EAN): 4899888102977
","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. Maggiori Informazioni ",0.538,1,"Taxable Goods","Catalog, Search",29.9,,,,Coperta-con-Maniche-Snug-Snug-One-Big-Verde,"Coperta con Maniche Snug Snug One Big Verde","Moda Accessori,Moda,Accessori,Abbigliamento e Scarpe,Abbigliamento,Scarpe,Pigiami e vestaglie,Pigiami,vestaglie,Colore Verde,Verde,","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle m",http://dropshipping.bigbuy.eu/imgs/F1510301_81359.jpg,,http://dropshipping.bigbuy.eu/imgs/F1510301_81359.jpg,,,,,,"2015-10-06 11:20:02",,,,,,,,,,,,,,,,,"GTIN=4899888102977",764,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/F1510301_81356.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81360.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81358.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81357.jpg","GTIN=4899888102977",,,,,,,,,,
+BB-F1510305,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Abbigliamento e Scarpe,Default Category/Moda Accessori/Abbigliamento e Scarpe/Pigiami e vestaglie",base,"Coperta con Maniche Snug Snug One Big Rosa","Affronta
il freddo invernale con l'originale coperta con maniche Snug Snug One Big ! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. La coperta con maniche One Big è dotata di tasca centrale per avere tutto a portata di mano, il telecomando della TV, il tuo libro preferito, ecc. Realizzata in morbido pile 100 % poliestere. Misure: 170 x 130 cm circa.www.snugsnug.com Dimenzioni per Coperta con Maniche Snug Snug One Big:
Altezza: 26.5 Cm Larghezza: 25 Cm Profondita': 23.7 Cm Peso: 0.538 Kg Codice Prodotto (EAN): 4899888102977
","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle maniche che ti permettono una totale libertà di movimenti. Maggiori Informazioni ",0.538,1,"Taxable Goods","Catalog, Search",29.9,,,,Coperta-con-Maniche-Snug-Snug-One-Big-Rosa,"Coperta con Maniche Snug Snug One Big Rosa","Moda Accessori,Moda,Accessori,Abbigliamento e Scarpe,Abbigliamento,Scarpe,Pigiami e vestaglie,Pigiami,vestaglie,Colore Rosa,Rosa,","Affronta il freddo invernale con l'originale coperta con maniche Snug Snug One Big! Un fantastico prodotto firmato Snug Snug che ti farà sentire comodo e al caldo, mentre sei sdraiato sul divano o stai facendo qualsiasi lavoro in casa, grazie alle m",http://dropshipping.bigbuy.eu/imgs/F1510301_81358.jpg,,http://dropshipping.bigbuy.eu/imgs/F1510301_81358.jpg,,,,,,"2015-12-29 06:48:13",,,,,,,,,,,,,,,,,"GTIN=4899888102977",968,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/F1510301_81356.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81360.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81359.jpg,http://dropshipping.bigbuy.eu/imgs/F1510301_81357.jpg","GTIN=4899888102977",,,,,,,,,,
+BB-V1300145,,Default,simple,"Default Category/Moda Accessori,Default Category/Moda Accessori/Accessori,Default Category/Relax Tempo Libero/Accessori/Ombrelli",base,"Ombrello Star Wars con LED","I
fan di Guerre Stellari impazziranno con l'ombrello Star Wars con LED !Interruttore on/off sul manico LED di vari colori sul manico centrale Funziona a batterie (3 x AA, incluse) Struttura: metallo, plastica e fibra di vetro Cupola: poliestere (pongee) Lunghezza approssimativa: 79,5 cm Diametro approssimativo: 95 cm Dimenzioni per Ombrello Star Wars con LED:
Altezza: 4 Cm Larghezza: 79.5 Cm Profondita': 5 Cm Peso: 0.458 Kg Codice Prodotto (EAN): 7569000752317
","I fan di Guerre Stellari impazziranno con l'ombrello Star Wars con LED!Interruttore on/off sul manicoLED di vari colori sul manico centraleFunziona a batterie (3 x AA, incluse)Struttura: metallo, plastica e fibra di vetroCupola: poliestere (pongee)Lunghezza approssimativa: 79,5 cmDiametro approssimativo: 95 cm. Maggiori Informazioni ",0.458,1,"Taxable Goods","Catalog, Search",53.9,,,,Ombrello-Star-Wars-con-LED,"Ombrello Star Wars con LED","Moda Accessori,Moda,Accessori,Accessori,Ombrelli,","I fan di Guerre Stellari impazziranno con l'ombrello Star Wars con LED!Interruttore on/off sul manicoLED di vari colori sul manico centraleFunziona a batterie (3 x AA, incluse)Struttura: metallo, plastica e fibra di vetroCupola: poliestere (pongee)L",http://dropshipping.bigbuy.eu/imgs/V1300145_93662.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300145_93662.jpg,,,,,,"2016-09-12 07:48:53",,,,,,,,,,,,,,,,,"GTIN=7569000752317",23,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300145_93665.jpg,http://dropshipping.bigbuy.eu/imgs/V1300145_93664.jpg,http://dropshipping.bigbuy.eu/imgs/V1300145_93663.jpg,http://dropshipping.bigbuy.eu/imgs/V1300145_93661.jpg","GTIN=7569000752317",,,,,,,,,,
+BB-H4530316,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Giocattoli e Giochi,Default Category/Giochi Bambini/Giocattoli e Giochi/Giochi educativi",base,"Elastici per fare bracciali con Perline di Frozen","Se
cerchi un gioco che intrattenga i tuoi figli e che sia l'ideale per essere alla moda, non perdere gli elastici per fare bracciali con le perline di Frozen . Contiene 130 elastici di diversi colori, perline di differenti forme con i protagonisti di Frozen, 1 gancino metallico, una chiusura a S, perline di diversi colori, 1 strumento per tenere gli elastici. Età consigliata: +5 anni.
Dimenzioni per Elastici per fare bracciali con Perline di Frozen:
Altezza: 18 Cm Larghezza: 3.5 Cm Profondita': 15 Cm Peso: 0.102 Kg Codice Prodotto (EAN): 8714274680036
","Se cerchi un gioco che intrattenga i tuoi figli e che sia l'ideale per essere alla moda, non perdere gli elastici per fare bracciali con le perline di Frozen. Maggiori Informazioni ",0.102,1,"Taxable Goods","Catalog, Search",35,,,,Elastici-per-fare-bracciali-con-Perline-di-Frozen,"Elastici per fare bracciali con Perline di Frozen","Giochi Bambini,Giochi,Bambini,Giocattoli e Giochi,Giocattoli,Giochi,Giochi educativi,Giochi,educativi,","Se cerchi un gioco che intrattenga i tuoi figli e che sia l'ideale per essere alla moda, non perdere gli elastici per fare bracciali con le perline di Frozen",http://dropshipping.bigbuy.eu/imgs/H4530316_93063.jpg,,http://dropshipping.bigbuy.eu/imgs/H4530316_93063.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8714274680036",78,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/H4530316_93067.jpg,http://dropshipping.bigbuy.eu/imgs/H4530316_93066.jpg,http://dropshipping.bigbuy.eu/imgs/H4530316_93065.jpg,http://dropshipping.bigbuy.eu/imgs/H4530316_93064.jpg","GTIN=8714274680036",,,,,,,,,,
+BB-V1300134,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Ombrelli e cappellini per bambini",base,"Berretto per Bambini Cars Rosso","Vuoi
sorprendere i piccoli della tua casa? Lightning McQueen proteggerà i più piccoli della casa dal sole di questa estate! Non perderti il berretto per bambini Cars . Dimensioni 54-56 cm. Misure della visiera: 14 x 6,5 cm circa. Composizione: 65% cotone e 35% poliestere Dimenzioni per Berretto per Bambini Cars:
Altezza: 10 Cm Larghezza: 17 Cm Profondita': 21 Cm Peso: 0.048 Kg Codice Prodotto (EAN): 8427934797318
","Vuoi sorprendere i piccoli della tua casa? Lightning McQueen proteggerà i più piccoli della casa dal sole di questa estate! Non perderti il berretto per bambini Cars. Maggiori Informazioni ",0.048,1,"Taxable Goods","Catalog, Search",7.9,,,,Berretto-per-Bambini-Cars-Rosso,"Berretto per Bambini Cars Rosso","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Ombrelli e cappellini per bambini,Ombrelli,cappellini,bambini,Colore Rosso,Rosso,","Vuoi sorprendere i piccoli della tua casa? Lightning McQueen proteggerà i più piccoli della casa dal sole di questa estate! Non perderti il berretto per bambini Cars",http://dropshipping.bigbuy.eu/imgs/V1300133_91571.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300133_91571.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934797318",87,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300133_91229.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91197.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91196.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91195.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91194.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91193.jpg","GTIN=8427934797318",,,,,,,,,,
+BB-V1300135,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Ombrelli e cappellini per bambini",base,"Berretto per Bambini Cars Nero","Vuoi
sorprendere i piccoli della tua casa? Lightning McQueen proteggerà i più piccoli della casa dal sole di questa estate! Non perderti il berretto per bambini Cars . Dimensioni 54-56 cm. Misure della visiera: 14 x 6,5 cm circa. Composizione: 65% cotone e 35% poliestere Dimenzioni per Berretto per Bambini Cars:
Altezza: 10 Cm Larghezza: 17 Cm Profondita': 21 Cm Peso: 0.048 Kg Codice Prodotto (EAN): 8427934797301
","Vuoi sorprendere i piccoli della tua casa? Lightning McQueen proteggerà i più piccoli della casa dal sole di questa estate! Non perderti il berretto per bambini Cars. Maggiori Informazioni ",0.048,1,"Taxable Goods","Catalog, Search",7.9,,,,Berretto-per-Bambini-Cars-Nero,"Berretto per Bambini Cars Nero","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Ombrelli e cappellini per bambini,Ombrelli,cappellini,bambini,Colore Nero,Nero,","Vuoi sorprendere i piccoli della tua casa? Lightning McQueen proteggerà i più piccoli della casa dal sole di questa estate! Non perderti il berretto per bambini Cars",http://dropshipping.bigbuy.eu/imgs/V1300133_91229.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300133_91229.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934797301",123,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300133_91571.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91197.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91196.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91195.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91194.jpg,http://dropshipping.bigbuy.eu/imgs/V1300133_91193.jpg","GTIN=8427934797301",,,,,,,,,,
+BB-V1300138,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Ombrelli e cappellini per bambini",base,"Cappello per Bambini Batman vs Superman Blu Marino","A
quale bambino non piace vantarsi dei suoi accessori moda ? Sorprendi i più piccoli con il cappello per bambini Batman vs Superman e quest'estate fai sì che siano ben protetti dai raggi solari. Taglia 52-54 cm. Dimensioni della visiera: 15 x 6,5 cm circa. Composizione: 65 % cotone e 35 % poliestere. Dimenzioni per Cappello per Bambini Batman vs Superman:
Altezza: 10 Cm Larghezza: 19 Cm Profondita': 20 Cm Peso: 0.048 Kg Codice Prodotto (EAN): 8427934824359
","A quale bambino non piace vantarsi dei suoi accessori moda? Sorprendi i più piccoli con il cappello per bambini Batman vs Superman e quest'estate fai sì che siano ben protetti dai raggi solari. Maggiori Informazioni ",0.048,1,"Taxable Goods","Catalog, Search",12.5,,,,Cappello-per-Bambini-Batman-vs-Superman-Blu Marino,"Cappello per Bambini Batman vs Superman Blu Marino","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Ombrelli e cappellini per bambini,Ombrelli,cappellini,bambini,Colore Blu Marino,Blu Marino,","A quale bambino non piace vantarsi dei suoi accessori moda? Sorprendi i più piccoli con il cappello per bambini Batman vs Superman e quest'estate fai sì che siano ben protetti dai raggi solari",http://dropshipping.bigbuy.eu/imgs/V1300136_91230.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300136_91230.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934824359",98,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300136_91231.jpg,http://dropshipping.bigbuy.eu/imgs/V1300136_91201.jpg,http://dropshipping.bigbuy.eu/imgs/V1300136_91200.jpg,http://dropshipping.bigbuy.eu/imgs/V1300136_91199.jpg","GTIN=8427934824359",,,,,,,,,,
+BB-V1300137,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Ombrelli e cappellini per bambini",base,"Cappello per Bambini Batman vs Superman Grigio","A
quale bambino non piace vantarsi dei suoi accessori moda ? Sorprendi i più piccoli con il cappello per bambini Batman vs Superman e quest'estate fai sì che siano ben protetti dai raggi solari. Taglia 52-54 cm. Dimensioni della visiera: 15 x 6,5 cm circa. Composizione: 65 % cotone e 35 % poliestere. Dimenzioni per Cappello per Bambini Batman vs Superman:
Altezza: 10 Cm Larghezza: 19 Cm Profondita': 20 Cm Peso: 0.048 Kg Codice Prodotto (EAN): 8427934824335
","A quale bambino non piace vantarsi dei suoi accessori moda? Sorprendi i più piccoli con il cappello per bambini Batman vs Superman e quest'estate fai sì che siano ben protetti dai raggi solari. Maggiori Informazioni ",0.048,1,"Taxable Goods","Catalog, Search",12.5,,,,Cappello-per-Bambini-Batman-vs-Superman-Grigio,"Cappello per Bambini Batman vs Superman Grigio","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Ombrelli e cappellini per bambini,Ombrelli,cappellini,bambini,Colore Grigio,Grigio,","A quale bambino non piace vantarsi dei suoi accessori moda? Sorprendi i più piccoli con il cappello per bambini Batman vs Superman e quest'estate fai sì che siano ben protetti dai raggi solari",http://dropshipping.bigbuy.eu/imgs/V1300136_91231.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300136_91231.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934824335",102,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300136_91230.jpg,http://dropshipping.bigbuy.eu/imgs/V1300136_91201.jpg,http://dropshipping.bigbuy.eu/imgs/V1300136_91200.jpg,http://dropshipping.bigbuy.eu/imgs/V1300136_91199.jpg","GTIN=8427934824335",,,,,,,,,,
+BB-V1300125,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Ombrelli e cappellini per bambini",base,"Berretto per Bambini Avengers Rosso","A
quale bambino non piace mostrare gli accessori di moda ? Sorprendili con il berretto per bambini Avengers e proteggili dai raggi del sole di questa estate. Taglia 52-54 cm. Dimensioni della visiera: 15 x 6,5 cm circa. Composizione: 65% cotone e 35% poliestere. Dimenzioni per Berretto per Bambini Avengers:
Altezza: 10 Cm Larghezza: 20 Cm Profondita': 18 Cm Peso: 0.048 Kg Codice Prodotto (EAN): 8427934792252
","A quale bambino non piace mostrare gli accessori di moda? Sorprendili con il berretto per bambini Avengers e proteggili dai raggi del sole di questa estate. Maggiori Informazioni ",0.048,1,"Taxable Goods","Catalog, Search",10.9,,,,Berretto-per-Bambini-Avengers-Rosso,"Berretto per Bambini Avengers Rosso","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Ombrelli e cappellini per bambini,Ombrelli,cappellini,bambini,Colore Rosso,Rosso,","A quale bambino non piace mostrare gli accessori di moda? Sorprendili con il berretto per bambini Avengers e proteggili dai raggi del sole di questa estate",http://dropshipping.bigbuy.eu/imgs/V1300124_91179.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300124_91179.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934792252",101,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300124_91180.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91178.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91177.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91176.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91175.jpg","GTIN=8427934792252",,,,,,,,,,
+BB-V1300126,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Ombrelli e cappellini per bambini",base,"Berretto per Bambini Avengers Nero","A
quale bambino non piace mostrare gli accessori di moda ? Sorprendili con il berretto per bambini Avengers e proteggili dai raggi del sole di questa estate. Taglia 52-54 cm. Dimensioni della visiera: 15 x 6,5 cm circa. Composizione: 65% cotone e 35% poliestere. Dimenzioni per Berretto per Bambini Avengers:
Altezza: 10 Cm Larghezza: 20 Cm Profondita': 18 Cm Peso: 0.048 Kg Codice Prodotto (EAN): 8427934792269
","A quale bambino non piace mostrare gli accessori di moda? Sorprendili con il berretto per bambini Avengers e proteggili dai raggi del sole di questa estate. Maggiori Informazioni ",0.048,1,"Taxable Goods","Catalog, Search",10.9,,,,Berretto-per-Bambini-Avengers-Nero,"Berretto per Bambini Avengers Nero","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Ombrelli e cappellini per bambini,Ombrelli,cappellini,bambini,Colore Nero,Nero,","A quale bambino non piace mostrare gli accessori di moda? Sorprendili con il berretto per bambini Avengers e proteggili dai raggi del sole di questa estate",http://dropshipping.bigbuy.eu/imgs/V1300124_91180.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300124_91180.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934792269",92,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300124_91179.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91178.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91177.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91176.jpg,http://dropshipping.bigbuy.eu/imgs/V1300124_91175.jpg","GTIN=8427934792269",,,,,,,,,,
+BB-V0500179,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Stoviglie per bambini",base,"Posate Bambini Disney (5 pezzi) Minnie","Quando
i piccoli di casa hanno già familiarizzato con il fatto di mangiare da soli, bisogna fare il passo successivo e comprare loro il set per mangiare da grandi, come le posate per bambini Disney (5 pezzi). Include: 1 piatto fondo, 1 piatto piano, 1 cucchiaio, 1 forchetta e 1 tazza.Età raccomandata: +12 mesi Realizzato in polipropilene (senza BPA) Adatto a lavastoviglie e microonde Dimensioni del piatto fondo (diametro x altura): 15,5 x 3 cm circa Dimensioni del piatto piano (diametro x altura): 22 x 1,5 cm circa Lunghezza del cucchiaio: 15,5 cm circa Lunghezza della forchetta: 15,5 cm circa Dimensioni della tazza: 11 x 8,5 x 8,5 cm circa Capacità della tazza: circa 250 ml Conforme alla normativa UNE-EN 14372 (requisiti di sicurezza per gli articoli di puericultura per l'alimentazione: servizio di posate e utensili) Conforme alla normativa UNE-EN 14350 (requisiti di sicurezza per gli articoli di puericultura per l'alimentazione liquida) Dimenzioni per Posate Bambini Disney (5 pezzi):
Altezza: 27.5 Cm Larghezza: 9.7 Cm Profondita': 9 Cm Peso: 0.487 Kg Codice Prodotto (EAN): 3662332013348
","Quando i piccoli di casa hanno già familiarizzato con il fatto di mangiare da soli, bisogna fare il passo successivo e comprare loro il set per mangiare da grandi, come le posate per bambini Disney (5 pezzi). Maggiori Informazioni ",0.487,1,"Taxable Goods","Catalog, Search",41.5,,,,Posate-Bambini-Disney-(5-pezzi)-Minnie,"Posate Bambini Disney (5 pezzi) Minnie","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Stoviglie per bambini,Stoviglie,bambini,Modello Minnie,Minnie,","Quando i piccoli di casa hanno già familiarizzato con il fatto di mangiare da soli, bisogna fare il passo successivo e comprare loro il set per mangiare da grandi, come le posate per bambini Disney (5 pezzi)",http://dropshipping.bigbuy.eu/imgs/V0500178_92050.jpg,,http://dropshipping.bigbuy.eu/imgs/V0500178_92050.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3662332013348",34,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0500178_92051.jpg,http://dropshipping.bigbuy.eu/imgs/V0500178_92049.jpg","GTIN=3662332013348",,,,,,,,,,
+BB-V0500180,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Stoviglie per bambini",base,"Posate Bambini Disney (5 pezzi) Mickey","Quando
i piccoli di casa hanno già familiarizzato con il fatto di mangiare da soli, bisogna fare il passo successivo e comprare loro il set per mangiare da grandi, come le posate per bambini Disney (5 pezzi). Include: 1 piatto fondo, 1 piatto piano, 1 cucchiaio, 1 forchetta e 1 tazza.Età raccomandata: +12 mesi Realizzato in polipropilene (senza BPA) Adatto a lavastoviglie e microonde Dimensioni del piatto fondo (diametro x altura): 15,5 x 3 cm circa Dimensioni del piatto piano (diametro x altura): 22 x 1,5 cm circa Lunghezza del cucchiaio: 15,5 cm circa Lunghezza della forchetta: 15,5 cm circa Dimensioni della tazza: 11 x 8,5 x 8,5 cm circa Capacità della tazza: circa 250 ml Conforme alla normativa UNE-EN 14372 (requisiti di sicurezza per gli articoli di puericultura per l'alimentazione: servizio di posate e utensili) Conforme alla normativa UNE-EN 14350 (requisiti di sicurezza per gli articoli di puericultura per l'alimentazione liquida) Dimenzioni per Posate Bambini Disney (5 pezzi):
Altezza: 27.5 Cm Larghezza: 9.7 Cm Profondita': 9 Cm Peso: 0.487 Kg Codice Prodotto (EAN): 3662332013355
","Quando i piccoli di casa hanno già familiarizzato con il fatto di mangiare da soli, bisogna fare il passo successivo e comprare loro il set per mangiare da grandi, come le posate per bambini Disney (5 pezzi). Maggiori Informazioni ",0.487,1,"Taxable Goods","Catalog, Search",41.5,,,,Posate-Bambini-Disney-(5-pezzi)-Mickey,"Posate Bambini Disney (5 pezzi) Mickey","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Stoviglie per bambini,Stoviglie,bambini,Modello Mickey,Mickey,","Quando i piccoli di casa hanno già familiarizzato con il fatto di mangiare da soli, bisogna fare il passo successivo e comprare loro il set per mangiare da grandi, come le posate per bambini Disney (5 pezzi)",http://dropshipping.bigbuy.eu/imgs/V0500178_92051.jpg,,http://dropshipping.bigbuy.eu/imgs/V0500178_92051.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3662332013355",37,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0500178_92050.jpg,http://dropshipping.bigbuy.eu/imgs/V0500178_92049.jpg","GTIN=3662332013355",,,,,,,,,,
+BB-V1300179,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Ombrelli e cappellini per bambini",base,"Ombrello per Bambini Pieghevole Star Wars","Ti
presentiamo l'ombrello più galattico del pianeta, l'ombrello per bambini pieghevole Star Wars ! Perfetto come regalo per bambini. Struttura: 75 % metallo, 25 % plastica Cupola: 100 % poliestere Lunghezza: circa 23-52 cm Diametro: circa 85 cm Custodia inclusa
Dimenzioni per Ombrello per Bambini Pieghevole Star Wars:
Altezza: 4 Cm Larghezza: 6.5 Cm Profondita': 23 Cm Peso: 0.255 Kg Codice Prodotto (EAN): 7569000732739
","Ti presentiamo l'ombrello più galattico del pianeta, l'ombrello per bambini pieghevole Star Wars! Perfetto come regalo per bambini. Maggiori Informazioni ",0.255,1,"Taxable Goods","Catalog, Search",24.5,,,,Ombrello-per-Bambini-Pieghevole-Star-Wars,"Ombrello per Bambini Pieghevole Star Wars","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Ombrelli e cappellini per bambini,Ombrelli,cappellini,bambini,","Ti presentiamo l'ombrello più galattico del pianeta, l'ombrello per bambini pieghevole Star Wars! Perfetto come regalo per bambini",http://dropshipping.bigbuy.eu/imgs/V1300179_101280.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300179_101280.jpg,,,,,,"2016-08-29 12:58:30",,,,,,,,,,,,,,,,,"GTIN=7569000732739",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300179_101281.jpg,http://dropshipping.bigbuy.eu/imgs/V1300179_101279.jpg,http://dropshipping.bigbuy.eu/imgs/V1300179_101278.jpg,http://dropshipping.bigbuy.eu/imgs/V1300179_101277.jpg","GTIN=7569000732739",,,,,,,,,,
+BB-V1300195,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Passeggiate e viaggi",base,"Borsa Termica Porta Merenda Rubble (PAW Patrol)","Ti
presentiamo la borsa termica porta merende Rubble (PAW Patrol) ! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla. Ideale per portare con sé il pranzo e la merenda.Dimensioni: circa 23 x 19 x 8 cm Composizione: poliestere, schiuma di poliuretano e PEVA (polietilene di acetato di vinilo) Dimenzioni per Borsa Termica Porta Merenda Rubble (PAW Patrol):
Altezza: 4 Cm Larghezza: 22 Cm Profondita': 26 Cm Peso: 0.196 Kg Codice Prodotto (EAN): 7569000732890
","Ti presentiamo la borsa termica porta merende Rubble (PAW Patrol)! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla. Maggiori Informazioni ",0.196,1,"Taxable Goods","Catalog, Search",26.9,,,,Borsa-Termica-Porta-Merenda-Rubble-(PAW-Patrol),"Borsa Termica Porta Merenda Rubble (PAW Patrol)","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Passeggiate e viaggi,Passeggiate,viaggi,","Ti presentiamo la borsa termica porta merende Rubble (PAW Patrol)! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla",http://dropshipping.bigbuy.eu/imgs/V1300195_101259.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300195_101259.jpg,,,,,,"2016-08-26 13:36:55",,,,,,,,,,,,,,,,,"GTIN=7569000732890",59,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300195_101258.jpg,http://dropshipping.bigbuy.eu/imgs/V1300195_101257.jpg,http://dropshipping.bigbuy.eu/imgs/V1300195_101256.jpg","GTIN=7569000732890",,,,,,,,,,
+BB-V1300196,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Passeggiate e viaggi",base,"Borsa Termica Porta Merenda Everest (PAW Patrol)","Scopri
la borsa termica porta merenda Everest (PAW Patrol) che sta facendo furore tra i bambini! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla. Perfetta per portare con sé il pranzo e la merenda.Dimensioni: circa 23 x 19 x 8 cm Composizione: poliestere, schiuma di poliuretano e PEVA (polietilene di acetato di vinilo) Dimenzioni per Borsa Termica Porta Merenda Everest (PAW Patrol):
Altezza: 4 Cm Larghezza: 22 Cm Profondita': 26 Cm Peso: 0.196 Kg Codice Prodotto (EAN): 7569000732906
","Scopri la borsa termica porta merenda Everest (PAW Patrol) che sta facendo furore tra i bambini! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla. Maggiori Informazioni ",0.196,1,"Taxable Goods","Catalog, Search",26.9,,,,Borsa-Termica-Porta-Merenda-Everest-(PAW-Patrol),"Borsa Termica Porta Merenda Everest (PAW Patrol)","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Passeggiate e viaggi,Passeggiate,viaggi,","Scopri la borsa termica porta merenda Everest (PAW Patrol) che sta facendo furore tra i bambini! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla",http://dropshipping.bigbuy.eu/imgs/V1300196_101260.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300196_101260.jpg,,,,,,"2016-08-26 13:36:36",,,,,,,,,,,,,,,,,"GTIN=7569000732906",51,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300196_101263.jpg,http://dropshipping.bigbuy.eu/imgs/V1300196_101262.jpg,http://dropshipping.bigbuy.eu/imgs/V1300196_101261.jpg","GTIN=7569000732906",,,,,,,,,,
+BB-V1300198,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Neonati e Bambini,Default Category/Giochi Bambini/Neonati e Bambini/Passeggiate e viaggi",base,"Borsa Termica Porta Merenda Frozen","Tutte
le bambine vogliono subito la borsa termica porta merenda Frozen ! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla. Perfetta per portare con sé il pranzo e la merenda.Dimensioni: circa 23 x 19 x 8 cm Composizione: poliestere, schiuma di poliuretano e PEVA (polietilene di acetato di vinilo) Dimenzioni per Borsa Termica Porta Merenda Frozen:
Altezza: 4 Cm Larghezza: 22 Cm Profondita': 26 Cm Peso: 0.196 Kg Codice Prodotto (EAN): 7569000732920
","Tutte le bambine vogliono subito la borsa termica porta merenda Frozen! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla. Maggiori Informazioni ",0.196,1,"Taxable Goods","Catalog, Search",26.9,,,,Borsa-Termica-Porta-Merenda-Frozen,"Borsa Termica Porta Merenda Frozen","Giochi Bambini,Giochi,Bambini,Neonati e Bambini,Neonati,Bambini,Passeggiate e viaggi,Passeggiate,viaggi,","Tutte le bambine vogliono subito la borsa termica porta merenda Frozen! Dispone di un disegno in rilievo nella parte frontale (gomma EVA), cerniera, manico e due cinte regolabili per appenderla",http://dropshipping.bigbuy.eu/imgs/V1300198_101268.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300198_101268.jpg,,,,,,"2016-08-30 07:32:17",,,,,,,,,,,,,,,,,"GTIN=7569000732920",52,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300198_101271.jpg,http://dropshipping.bigbuy.eu/imgs/V1300198_101270.jpg,http://dropshipping.bigbuy.eu/imgs/V1300198_101269.jpg","GTIN=7569000732920",,,,,,,,,,
+BB-V1300204,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Materiale Scolastico,Default Category/Giochi Bambini/Materiale Scolastico/Astucci e portapenne",base,"Astuccio Scuola 3D Frozen","Le
piccola fan delle principesse Anna ed Elsa non possono tornare a scuola senza l'astuccio scuola 3D Frozen !Cinque scompartimenti separati Due cerniere Dimensioni: 21,5 x 12 x 10 cm circa Composizione: poliestere Dimenzioni per Astuccio Scuola 3D Frozen:
Altezza: 2 Cm Larghezza: 24 Cm Profondita': 14 Cm Peso: 0.099 Kg Codice Prodotto (EAN): 7569000733248
","Le piccola fan delle principesse Anna ed Elsa non possono tornare a scuola senza l'astuccio scuola 3D Frozen!Cinque scompartimenti separatiDue cerniereDimensioni: 21,5 x 12 x 10 cm circaComposizione: poliestere. Maggiori Informazioni ",0.099,1,"Taxable Goods","Catalog, Search",19.9,,,,Astuccio-Scuola-3D-Frozen,"Astuccio Scuola 3D Frozen","Giochi Bambini,Giochi,Bambini,Materiale Scolastico,Materiale,Scolastico,Astucci e portapenne,Astucci,portapenne,","Le piccola fan delle principesse Anna ed Elsa non possono tornare a scuola senza l'astuccio scuola 3D Frozen!Cinque scompartimenti separatiDue cerniereDimensioni: 21,5 x 12 x 10 cm circaComposizione: poliestere",http://dropshipping.bigbuy.eu/imgs/V1300204_102661.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300204_102661.jpg,,,,,,"2016-09-14 07:51:11",,,,,,,,,,,,,,,,,"GTIN=7569000733248",138,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300204_102663.jpg,http://dropshipping.bigbuy.eu/imgs/V1300204_102662.jpg","GTIN=7569000733248",,,,,,,,,,
+BB-V1300208,,Default,simple,"Default Category/Giochi Bambini,Default Category/Giochi Bambini/Materiale Scolastico,Default Category/Giochi Bambini/Materiale Scolastico/Zaini scuola",base,"Zaino-Sacca Frozen","Lo
zaino-sacca Frozen è lo zaino che sta facendo furore tra le bambine!Realizzato in poliestere Manico superiore e cinghie con velcro Tasca frontale con cerniera Dimensioni: circa 33 x 44 cm
Dimenzioni per Zaino-Sacca Frozen:
Altezza: 0.5 Cm Larghezza: 37 Cm Profondita': 46 Cm Peso: 0.138 Kg Codice Prodotto (EAN): 7569000733286
","Lo zaino-sacca Frozen è lo zaino che sta facendo furore tra le bambine!Realizzato in poliestereManico superiore e cinghie con velcroTasca frontale con cernieraDimensioni: circa 33 x 44 cm . Maggiori Informazioni ",0.138,1,"Taxable Goods","Catalog, Search",24.9,,,,Zaino-Sacca-Frozen,"Zaino-Sacca Frozen","Giochi Bambini,Giochi,Bambini,Materiale Scolastico,Materiale,Scolastico,Zaini scuola,Zaini,scuola,","Lo zaino-sacca Frozen è lo zaino che sta facendo furore tra le bambine!Realizzato in poliestereManico superiore e cinghie con velcroTasca frontale con cernieraDimensioni: circa 33 x 44 cm ",http://dropshipping.bigbuy.eu/imgs/V1300208_102667.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300208_102667.jpg,,,,,,"2016-09-14 07:50:58",,,,,,,,,,,,,,,,,"GTIN=7569000733286",141,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300208_102669.jpg,http://dropshipping.bigbuy.eu/imgs/V1300208_102668.jpg","GTIN=7569000733286",,,,,,,,,,
+BB-I4115041,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cover e custodie",base,"Custodia Impermeabile per Cellulare WpShield Azzurro","Sei
una di quelle persone che portano il loro smartphone ovunque? Se desideri che il tuo telefono venga protetto dalla sporcizia, sabbia, graffi e anche dall'acqua, non perderti la custodia impermeabile per cellulare WpShield . Con questa custodia impermeabile potrai portare il telefono ovunque ti piaccia, anche per un tuffo in piscina o sul mare.www.waterproofshield.com Questa custodia impermeabile dispone sia di un cinturino con chiusura a velcro (lunghezza massima: circa 37 cm) e un cavo con chiusura di sicurezza da indossare al collo (lunghezza massima: circa 60 cm). Composizione: PVC (Spessore: circa 3 mm). Dimensioni: circa 10,5 x 15,5 cm. Dimenzioni per Custodia Impermeabile per Cellulare WpShield :
Altezza: 20 Cm Larghezza: 11.1 Cm Profondita': 1.9 Cm Peso: 0.06 Kg Codice Prodotto (EAN): 4899888106722
","Sei una di quelle persone che portano il loro smartphone ovunque? Se desideri che il tuo telefono venga protetto dalla sporcizia, sabbia, graffi e anche dall'acqua, non perderti la custodia impermeabile per cellulare WpShield. Maggiori Informazioni ",0.06,1,"Taxable Goods","Catalog, Search",14.5,,,,Custodia-Impermeabile-per-Cellulare-WpShield-Azzurro,"Custodia Impermeabile per Cellulare WpShield Azzurro","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cover e custodie,Cover,custodie,Colore Azzurro,Azzurro,","Sei una di quelle persone che portano il loro smartphone ovunque? Se desideri che il tuo telefono venga protetto dalla sporcizia, sabbia, graffi e anche dall'acqua, non perderti la custodia impermeabile per cellulare WpShield",http://dropshipping.bigbuy.eu/imgs/I4115040_78768.jpg,,http://dropshipping.bigbuy.eu/imgs/I4115040_78768.jpg,,,,,,"2015-12-21 12:09:49",,,,,,,,,,,,,,,,,"GTIN=4899888106722",4125,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I4115040_78770.jpg,http://dropshipping.bigbuy.eu/imgs/I4115040_78769.jpg,http://dropshipping.bigbuy.eu/imgs/I4115040_78767.jpg","GTIN=4899888106722",,,,,,,,,,
+BB-I4115042,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cover e custodie",base,"Custodia Impermeabile per Cellulare WpShield Bianco","Sei
una di quelle persone che portano il loro smartphone ovunque? Se desideri che il tuo telefono venga protetto dalla sporcizia, sabbia, graffi e anche dall'acqua, non perderti la custodia impermeabile per cellulare WpShield . Con questa custodia impermeabile potrai portare il telefono ovunque ti piaccia, anche per un tuffo in piscina o sul mare.www.waterproofshield.com Questa custodia impermeabile dispone sia di un cinturino con chiusura a velcro (lunghezza massima: circa 37 cm) e un cavo con chiusura di sicurezza da indossare al collo (lunghezza massima: circa 60 cm). Composizione: PVC (Spessore: circa 3 mm). Dimensioni: circa 10,5 x 15,5 cm. Dimenzioni per Custodia Impermeabile per Cellulare WpShield :
Altezza: 20 Cm Larghezza: 11.1 Cm Profondita': 1.9 Cm Peso: 0.06 Kg Codice Prodotto (EAN): 4899888106739
","Sei una di quelle persone che portano il loro smartphone ovunque? Se desideri che il tuo telefono venga protetto dalla sporcizia, sabbia, graffi e anche dall'acqua, non perderti la custodia impermeabile per cellulare WpShield. Maggiori Informazioni ",0.06,1,"Taxable Goods","Catalog, Search",14.5,,,,Custodia-Impermeabile-per-Cellulare-WpShield-Bianco,"Custodia Impermeabile per Cellulare WpShield Bianco","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cover e custodie,Cover,custodie,Colore Bianco,Bianco,","Sei una di quelle persone che portano il loro smartphone ovunque? Se desideri che il tuo telefono venga protetto dalla sporcizia, sabbia, graffi e anche dall'acqua, non perderti la custodia impermeabile per cellulare WpShield",http://dropshipping.bigbuy.eu/imgs/I4115040_78770.jpg,,http://dropshipping.bigbuy.eu/imgs/I4115040_78770.jpg,,,,,,"2015-12-21 12:10:05",,,,,,,,,,,,,,,,,"GTIN=4899888106739",4321,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I4115040_78768.jpg,http://dropshipping.bigbuy.eu/imgs/I4115040_78769.jpg,http://dropshipping.bigbuy.eu/imgs/I4115040_78767.jpg","GTIN=4899888106739",,,,,,,,,,
+BB-I4115044,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Batterie, Caricatori, Adattatori",base,"Doppia Porta USB con Presa Elettrica e Caricabatteria da Auto Pocken Bianco","Non
uscire di casa senza la doppia porta USB con presa elettrica e caricabatteria da auto Pocken per auto! Puoi connetterla in auto o alla rete elettrica per ricaricare i dispositivi mobili, poichè include sia un adattatore per auto che una spina. Questo caricatore con doppio ingresso USB è molto pratico e semplice da usare. Dimensioni approssimative: 6 x 6 x 3,5 cm.www.pockenrg.com
Caratteristiche tecniche:
Ingresso AC: 100-240 V / 0.15 A / 50-60 Hz Ingresso DC: 12-24 V / 0.35 A Uscita DC: +5 V / 1 A Dimenzioni per Doppia Porta USB con Presa Elettrica e Caricabatteria da Auto Pocken :
Altezza: 4 Cm Larghezza: 6.5 Cm Profondita': 6.5 Cm Peso: 0.077 Kg Codice Prodotto (EAN): 4899888106944
","Non uscire di casa senza la doppia porta USB con presa elettrica e caricabatteria da auto Pocken per auto! Puoi connetterla in auto o alla rete elettrica per ricaricare i dispositivi mobili, poichè include sia un adattatore per auto che una spina. Maggiori Informazioni ",0.077,1,"Taxable Goods","Catalog, Search",29.99,,,,Doppia-Porta-USB-con-Presa-Elettrica-e-Caricabatteria-da-Auto-Pocken-Bianco,"Doppia Porta USB con Presa Elettrica e Caricabatteria da Auto Pocken Bianco","Informatica Elettronica,Informatica,Elettronica,Batterie, Caricatori, Adattatori,Batterie,,Caricatori,,Adattatori,Colore Bianco,Bianco,","Non uscire di casa senza la doppia porta USB con presa elettrica e caricabatteria da auto Pocken per auto! Puoi connetterla in auto o alla rete elettrica per ricaricare i dispositivi mobili, poichè include sia un adattatore per auto che una spina",http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_0002.jpg,,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_0002.jpg,,,,,,"2015-05-14 07:58:09",,,,,,,,,,,,,,,,,"GTIN=4899888106944",161,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_00.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_08.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_04.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_004.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_0004.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_03.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_0003.jpg","GTIN=4899888106944",,,,,,,,,,
+BB-I4115045,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Batterie, Caricatori, Adattatori",base,"Doppia Porta USB con Presa Elettrica e Caricabatteria da Auto Pocken Nero","Non
uscire di casa senza la doppia porta USB con presa elettrica e caricabatteria da auto Pocken per auto! Puoi connetterla in auto o alla rete elettrica per ricaricare i dispositivi mobili, poichè include sia un adattatore per auto che una spina. Questo caricatore con doppio ingresso USB è molto pratico e semplice da usare. Dimensioni approssimative: 6 x 6 x 3,5 cm.www.pockenrg.com
Caratteristiche tecniche:
Ingresso AC: 100-240 V / 0.15 A / 50-60 Hz Ingresso DC: 12-24 V / 0.35 A Uscita DC: +5 V / 1 A Dimenzioni per Doppia Porta USB con Presa Elettrica e Caricabatteria da Auto Pocken :
Altezza: 4 Cm Larghezza: 6.5 Cm Profondita': 6.5 Cm Peso: 0.077 Kg Codice Prodotto (EAN): 4899888106678
","Non uscire di casa senza la doppia porta USB con presa elettrica e caricabatteria da auto Pocken per auto! Puoi connetterla in auto o alla rete elettrica per ricaricare i dispositivi mobili, poichè include sia un adattatore per auto che una spina. Maggiori Informazioni ",0.077,1,"Taxable Goods","Catalog, Search",29.99,,,,Doppia-Porta-USB-con-Presa-Elettrica-e-Caricabatteria-da-Auto-Pocken-Nero,"Doppia Porta USB con Presa Elettrica e Caricabatteria da Auto Pocken Nero","Informatica Elettronica,Informatica,Elettronica,Batterie, Caricatori, Adattatori,Batterie,,Caricatori,,Adattatori,Colore Nero,Nero,","Non uscire di casa senza la doppia porta USB con presa elettrica e caricabatteria da auto Pocken per auto! Puoi connetterla in auto o alla rete elettrica per ricaricare i dispositivi mobili, poichè include sia un adattatore per auto che una spina",http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_00.jpg,,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_00.jpg,,,,,,"2015-08-18 12:37:09",,,,,,,,,,,,,,,,,"GTIN=4899888106678",265,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_0002.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_08.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_04.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_004.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_0004.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_03.jpg,http://dropshipping.bigbuy.eu/imgs/cargador_usb_doble_coche_0003.jpg","GTIN=4899888106678",,,,,,,,,,
+BB-I3505259,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Cuffie",base,"Auricolari da Corsa GoFit Bianco","Fai
sport mentre ascolti la musica o parli al telefono indossando questi auricolari da corsa ! Questi auricolari sportivi GoFit sono molto comodi e pratici, si adattano perfettamente al tuo orecchio per una migliore aderenza. Progettate specificamente per gli altleti. Con materiali speciali e ottima qualità sonora. Caratteristiche:Suono: stereo Connessione audio: cavo con uscita 3,5 mm Microfono integrato Pulsante di risposta alla chiamata Risposta in frequenza: 20-20000 Hz Intensità del suono: 93 dB Impedenza dello speaker: 32 Ω Adatto agli iPhone, smartphone e telefoni cellulari Dimenzioni per Auricolari da Corsa GoFit:
Altezza: 20 Cm Larghezza: 9 Cm Profondita': 2.7 Cm Peso: 0.079 Kg Codice Prodotto (EAN): 8018417204005
","Fai sport mentre ascolti la musica o parli al telefono indossando questi auricolari da corsa! Questi auricolari sportivi GoFit sono molto comodi e pratici, si adattano perfettamente al tuo orecchio per una migliore aderenza. Maggiori Informazioni ",0.079,1,"Taxable Goods","Catalog, Search",38.9,,,,Auricolari-da-Corsa-GoFit-Bianco,"Auricolari da Corsa GoFit Bianco","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Cuffie,Colore Bianco,Bianco,","Fai sport mentre ascolti la musica o parli al telefono indossando questi auricolari da corsa! Questi auricolari sportivi GoFit sono molto comodi e pratici, si adattano perfettamente al tuo orecchio per una migliore aderenza",http://dropshipping.bigbuy.eu/imgs/I3505223_80641.jpg,,http://dropshipping.bigbuy.eu/imgs/I3505223_80641.jpg,,,,,,"2015-09-23 10:52:34",,,,,,,,,,,,,,,,,"GTIN=8018417204005",21,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I3505223_80644.jpg,http://dropshipping.bigbuy.eu/imgs/I3505223_80643.jpg,http://dropshipping.bigbuy.eu/imgs/I3505223_80642.jpg,http://dropshipping.bigbuy.eu/imgs/I3505223_80640.jpg","GTIN=8018417204005",,,,,,,,,,
+BB-I3505260,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Cuffie",base,"Auricolari da Corsa GoFit Arancio","Fai
sport mentre ascolti la musica o parli al telefono indossando questi auricolari da corsa ! Questi auricolari sportivi GoFit sono molto comodi e pratici, si adattano perfettamente al tuo orecchio per una migliore aderenza. Progettate specificamente per gli altleti. Con materiali speciali e ottima qualità sonora. Caratteristiche:Suono: stereo Connessione audio: cavo con uscita 3,5 mm Microfono integrato Pulsante di risposta alla chiamata Risposta in frequenza: 20-20000 Hz Intensità del suono: 93 dB Impedenza dello speaker: 32 Ω Adatto agli iPhone, smartphone e telefoni cellulari Dimenzioni per Auricolari da Corsa GoFit:
Altezza: 20 Cm Larghezza: 9 Cm Profondita': 2.7 Cm Peso: 0.079 Kg Codice Prodotto (EAN): 8018417209512
","Fai sport mentre ascolti la musica o parli al telefono indossando questi auricolari da corsa! Questi auricolari sportivi GoFit sono molto comodi e pratici, si adattano perfettamente al tuo orecchio per una migliore aderenza. Maggiori Informazioni ",0.079,1,"Taxable Goods","Catalog, Search",38.9,,,,Auricolari-da-Corsa-GoFit-Arancio,"Auricolari da Corsa GoFit Arancio","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Cuffie,Colore Arancio,Arancio,","Fai sport mentre ascolti la musica o parli al telefono indossando questi auricolari da corsa! Questi auricolari sportivi GoFit sono molto comodi e pratici, si adattano perfettamente al tuo orecchio per una migliore aderenza",http://dropshipping.bigbuy.eu/imgs/I3505223_80644.jpg,,http://dropshipping.bigbuy.eu/imgs/I3505223_80644.jpg,,,,,,"2015-09-23 10:52:34",,,,,,,,,,,,,,,,,"GTIN=8018417209512",43,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I3505223_80641.jpg,http://dropshipping.bigbuy.eu/imgs/I3505223_80643.jpg,http://dropshipping.bigbuy.eu/imgs/I3505223_80642.jpg,http://dropshipping.bigbuy.eu/imgs/I3505223_80640.jpg","GTIN=8018417209512",,,,,,,,,,
+BB-I3505248,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Casse",base,"Altoparlante Bluetooth Portatile AudioSonic SK1511 Azzurro","Se
adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smartphone? Con questo prodotto versatile è possibile! Lunghezza della corda in silicone: circa 40 cm. Dimensioni: circa 5,5 x 7 x 1 cm. Raggio del bluetooth: circa 10 m.Caratteristiche:
Batteria ricaricabile Li-ion Vita della batteria: 4 ore Batteria 300 mAh Microfono incorporato Controllo a mani libere Pulsanti di controllo Porta micro USB porta: 5 V Porta input Aux Potenza: 3 W Include:
Cavo USB + micro USB Cavo dual jack da 3,5 mm Dimenzioni per Altoparlante Bluetooth Portatile AudioSonic:
Altezza: 13 Cm Larghezza: 8 Cm Profondita': 4.6 Cm Peso: 0.143 Kg Codice Prodotto (EAN): 8713016015112
","Se adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smartphone? Con questo prodotto versatile è possibile! Lunghezza della corda in silicone: circa 40 cm. Maggiori Informazioni ",0.143,1,"Taxable Goods","Catalog, Search",33.35,,,,Altoparlante-Bluetooth-Portatile-AudioSonic-SK1511 Azzurro,"Altoparlante Bluetooth Portatile AudioSonic SK1511 Azzurro","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Casse,Referenza e Colore SK1511 Azzurro,SK1511 Azzurro,","Se adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smart",http://dropshipping.bigbuy.eu/imgs/altavoz_audiosonic_SK-1539_00.jpg,,http://dropshipping.bigbuy.eu/imgs/altavoz_audiosonic_SK-1539_00.jpg,,,,,,"2015-12-21 11:15:29",,,,,,,,,,,,,,,,,"GTIN=8713016015112",8,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_01.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz-cordón-SK-1511.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_004.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_02.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_0004.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_00.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_04.jpg","GTIN=8713016015112",,,,,,,,,,
+BB-I3505249,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Casse",base,"Altoparlante Bluetooth Portatile AudioSonic SK1513 Rosa","Se
adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smartphone? Con questo prodotto versatile è possibile! Lunghezza della corda in silicone: circa 40 cm. Dimensioni: circa 5,5 x 7 x 1 cm. Raggio del bluetooth: circa 10 m.Caratteristiche:
Batteria ricaricabile Li-ion Vita della batteria: 4 ore Batteria 300 mAh Microfono incorporato Controllo a mani libere Pulsanti di controllo Porta micro USB porta: 5 V Porta input Aux Potenza: 3 W Include:
Cavo USB + micro USB Cavo dual jack da 3,5 mm Dimenzioni per Altoparlante Bluetooth Portatile AudioSonic:
Altezza: 13 Cm Larghezza: 8 Cm Profondita': 4.6 Cm Peso: 0.143 Kg Codice Prodotto (EAN): 8713016000996
","Se adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smartphone? Con questo prodotto versatile è possibile! Lunghezza della corda in silicone: circa 40 cm. Maggiori Informazioni ",0.143,1,"Taxable Goods","Catalog, Search",33.35,,,,Altoparlante-Bluetooth-Portatile-AudioSonic-SK1513 Rosa,"Altoparlante Bluetooth Portatile AudioSonic SK1513 Rosa","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Casse,Referenza e Colore SK1513 Rosa,SK1513 Rosa,","Se adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smart",http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_01.jpg,,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_01.jpg,,,,,,"2015-06-01 09:01:55",,,,,,,,,,,,,,,,,"GTIN=8713016000996",16,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/altavoz_audiosonic_SK-1539_00.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz-cordón-SK-1511.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_004.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_02.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_0004.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_00.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_04.jpg","GTIN=8713016000996",,,,,,,,,,
+BB-I3505250,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Casse",base,"Altoparlante Bluetooth Portatile AudioSonic SK1512 Verde","Se
adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smartphone? Con questo prodotto versatile è possibile! Lunghezza della corda in silicone: circa 40 cm. Dimensioni: circa 5,5 x 7 x 1 cm. Raggio del bluetooth: circa 10 m.Caratteristiche:
Batteria ricaricabile Li-ion Vita della batteria: 4 ore Batteria 300 mAh Microfono incorporato Controllo a mani libere Pulsanti di controllo Porta micro USB porta: 5 V Porta input Aux Potenza: 3 W Include:
Cavo USB + micro USB Cavo dual jack da 3,5 mm Dimenzioni per Altoparlante Bluetooth Portatile AudioSonic:
Altezza: 13 Cm Larghezza: 8 Cm Profondita': 4.6 Cm Peso: 0.143 Kg Codice Prodotto (EAN): 8713016000972
","Se adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smartphone? Con questo prodotto versatile è possibile! Lunghezza della corda in silicone: circa 40 cm. Maggiori Informazioni ",0.143,1,"Taxable Goods","Catalog, Search",33.35,,,,Altoparlante-Bluetooth-Portatile-AudioSonic-SK1512 Verde,"Altoparlante Bluetooth Portatile AudioSonic SK1512 Verde","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Casse,Referenza e Colore SK1512 Verde,SK1512 Verde,","Se adori la musica e la tecnologia, l'altoparlante Bluetooth portatile AudioSonic è l'ideale per te! Riesci ad immaginare di indossare questo altoparlante intorno al collo mentre ascolti la tua musica preferita e rispondi alle chiamate del tuo smart",http://dropshipping.bigbuy.eu/imgs/altavoz-cordón-SK-1511.jpg,,http://dropshipping.bigbuy.eu/imgs/altavoz-cordón-SK-1511.jpg,,,,,,"2016-02-01 10:38:01",,,,,,,,,,,,,,,,,"GTIN=8713016000972",22,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/altavoz_audiosonic_SK-1539_00.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_01.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_004.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_02.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_0004.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_00.jpg,http://dropshipping.bigbuy.eu/imgs/altavoz_SK-1511_04.jpg","GTIN=8713016000972",,,,,,,,,,
+BB-I3505262,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Cuffie",base,"Auricolari da Corsa GoFit","Ora,
ascoltare la musica o fare una chiamata non saranno più scuse valide per non allenarsi, grazie agli auricolari da corsa GoFit ! Sono davvero comodi e pratici auricolari che si adattano completamente all'orecchio, Speciale design Europeo per l'utilizzo in allenamento. Materiali e suono di alta qualità. Include una piccola custodia in tessuto per conservare gli auricolari.Caratteristiche:
Flessibili e resistenti all'acqua Suono: stereo Connessione audio: cavo jack 3,5 mm Microfono incorporato Pulsante di risposta e termine chiamata Risposta di frequenza: 20-20000 Hz Livello sonoro: 93 dB Impedenza altoparlante: 32 Ω Adatti all'uso con iPhone, smartphone e altri cellulari Dimenzioni per Auricolari da Corsa GoFit:
Altezza: 20.5 Cm Larghezza: 9 Cm Profondita': 4 Cm Peso: 0.102 Kg Codice Prodotto (EAN): 8018417208119
","Ora, ascoltare la musica o fare una chiamata non saranno più scuse valide per non allenarsi, grazie agli auricolari da corsa GoFit! Sono davvero comodi e pratici auricolari che si adattano completamente all'orecchio, Speciale design Europeo per l'utilizzo in allenamento. Maggiori Informazioni ",0.102,1,"Taxable Goods","Catalog, Search",49.9,,,,Auricolari-da-Corsa-GoFit,"Auricolari da Corsa GoFit","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Cuffie,","Ora, ascoltare la musica o fare una chiamata non saranno più scuse valide per non allenarsi, grazie agli auricolari da corsa GoFit! Sono davvero comodi e pratici auricolari che si adattano completamente all'orecchio, Speciale design Europeo per l'ut",http://dropshipping.bigbuy.eu/imgs/I3505262_80647.jpg,,http://dropshipping.bigbuy.eu/imgs/I3505262_80647.jpg,,,,,,"2016-09-07 15:19:36",,,,,,,,,,,,,,,,,"GTIN=8018417208119",46,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I3505262_80646.jpg,http://dropshipping.bigbuy.eu/imgs/I3505262_80645.jpg","GTIN=8018417208119",,,,,,,,,,
+BB-G0500185,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Cuffie",base,"Fascia Sportiva con Auricolari GoFit Verde","Se
adori gli sport e ti piace tenerti informato con gli ultimi accessori sportivi , non puoi perderti questa ottima fascia sportiva con auricolari GoFit . Con questa fascia per la testa, potrai ascoltare i tuoi brani musicali preferiti mentre fai jogging, inoltre potrai rispondere alle chiamate, semplicemente premendo il pulsante di risposta e chiusura chiamate incorporato nella doppia connessione, con cavo audio jack da 3,5 mm (lunghezza: circa 1 m). Altoparlanti rimovibili per permetterti di lavare la fascia. Ampiezza: circa 9,5 cm. Diametro: circa 27 cm. Esterno 100% poliestere. Interno in microfibra polare. Caratteristiche:Sensibilità: 5 dB Impedenza altoparlante: 32 Ω Frequenza: 20 Hz-20 kHz Adatto a smartphone e altri telefoni mobili Dimenzioni per Fascia Sportiva con Auricolari GoFit:
Altezza: 20 Cm Larghezza: 9 Cm Profondita': 4.5 Cm Peso: 0.102 Kg Codice Prodotto (EAN): 8018417209635
","Se adori gli sport e ti piace tenerti informato con gli ultimi accessori sportivi, non puoi perderti questa ottima fascia sportiva con auricolari GoFit. Maggiori Informazioni ",0.102,1,"Taxable Goods","Catalog, Search",49.99,,,,Fascia-Sportiva-con-Auricolari-GoFit-Verde,"Fascia Sportiva con Auricolari GoFit Verde","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Cuffie,Colore Verde,Verde,","Se adori gli sport e ti piace tenerti informato con gli ultimi accessori sportivi, non puoi perderti questa ottima fascia sportiva con auricolari GoFit",http://dropshipping.bigbuy.eu/imgs/G0500184_81112.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500184_81112.jpg,,,,,,"2015-09-28 07:07:26",,,,,,,,,,,,,,,,,"GTIN=8018417209635",11,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500184_81118.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81117.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81116.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81115.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81114.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81113.jpg","GTIN=8018417209635",,,,,,,,,,
+BB-G0500186,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Cuffie",base,"Fascia Sportiva con Auricolari GoFit Arancio","Se
adori gli sport e ti piace tenerti informato con gli ultimi accessori sportivi , non puoi perderti questa ottima fascia sportiva con auricolari GoFit . Con questa fascia per la testa, potrai ascoltare i tuoi brani musicali preferiti mentre fai jogging, inoltre potrai rispondere alle chiamate, semplicemente premendo il pulsante di risposta e chiusura chiamate incorporato nella doppia connessione, con cavo audio jack da 3,5 mm (lunghezza: circa 1 m). Altoparlanti rimovibili per permetterti di lavare la fascia. Ampiezza: circa 9,5 cm. Diametro: circa 27 cm. Esterno 100% poliestere. Interno in microfibra polare. Caratteristiche:Sensibilità: 5 dB Impedenza altoparlante: 32 Ω Frequenza: 20 Hz-20 kHz Adatto a smartphone e altri telefoni mobili Dimenzioni per Fascia Sportiva con Auricolari GoFit:
Altezza: 20 Cm Larghezza: 9 Cm Profondita': 4.5 Cm Peso: 0.102 Kg Codice Prodotto (EAN): 8018417209505
","Se adori gli sport e ti piace tenerti informato con gli ultimi accessori sportivi, non puoi perderti questa ottima fascia sportiva con auricolari GoFit. Maggiori Informazioni ",0.102,1,"Taxable Goods","Catalog, Search",49.99,,,,Fascia-Sportiva-con-Auricolari-GoFit-Arancio,"Fascia Sportiva con Auricolari GoFit Arancio","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Cuffie,Colore Arancio,Arancio,","Se adori gli sport e ti piace tenerti informato con gli ultimi accessori sportivi, non puoi perderti questa ottima fascia sportiva con auricolari GoFit",http://dropshipping.bigbuy.eu/imgs/G0500184_81118.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500184_81118.jpg,,,,,,"2015-09-28 07:07:51",,,,,,,,,,,,,,,,,"GTIN=8018417209505",32,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500184_81112.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81117.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81116.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81115.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81114.jpg,http://dropshipping.bigbuy.eu/imgs/G0500184_81113.jpg","GTIN=8018417209505",,,,,,,,,,
+BB-I3505265,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Casse",base,"Altoparlante Sportivo Bluetooth GoFit","Sei
un amante dello sport e adori fare escursioni in montagna? Ti piace la musica? Allora questo prodotto ti starà a pennello! Questo favoloso altoparlante sportivo Bluetooth GoFit ti accompagnerà dove vuoi, permettendoti di ascoltare la tua musica preferita durante le tue uscite, così come rispondere alle chiamate attraverso la funzione Bluetooth. Resistente all'acqua. Include un cavo di ricarica USB. L'altoparlante ha una clip per inserirlo su una cintura e una banda elastica per indossarlo al polso, nello zaino, ecc. Misure (diametro x altezza) circa: 8,5 cm x 3 cm. Peso: circa 159 g.Caratteristiche:
Protezione impermeabile: IP4 Bluetooth 2.0 + EDR: distanza fino a circa 10 m Funzione mani libere Permette la ricezione di chiamate e terminarle Pulsanti di pausa, avanzamento e indietro Tempo di riproduzione: circa 2,5 ore Frequenza: 90 Hz-20 KHz Uscita altoparlante: 5 W Connessione per audio jack 3,5 mm
Dimenzioni per Altoparlante Sportivo Bluetooth GoFit:
Altezza: 20 Cm Larghezza: 6 Cm Profondita': 9.5 Cm Peso: 0.278 Kg Codice Prodotto (EAN): 8018417207747
","Sei un amante dello sport e adori fare escursioni in montagna? Ti piace la musica? Allora questo prodotto ti starà a pennello! Questo favoloso altoparlante sportivo Bluetooth GoFit ti accompagnerà dove vuoi, permettendoti di ascoltare la tua musica preferita durante le tue uscite, così come rispondere alle chiamate attraverso la funzione Bluetooth. Maggiori Informazioni ",0.278,1,"Taxable Goods","Catalog, Search",89.9,,,,Altoparlante-Sportivo-Bluetooth-GoFit,"Altoparlante Sportivo Bluetooth GoFit","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Casse,","Sei un amante dello sport e adori fare escursioni in montagna? Ti piace la musica? Allora questo prodotto ti starà a pennello! Questo favoloso altoparlante sportivo Bluetooth GoFit ti accompagnerà dove vuoi, permettendoti di ascoltare la tua musica ",http://dropshipping.bigbuy.eu/imgs/I3505265_81327.jpg,,http://dropshipping.bigbuy.eu/imgs/I3505265_81327.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8018417207747",10,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I3505265_81169.jpg,http://dropshipping.bigbuy.eu/imgs/I3505265_81168.jpg,http://dropshipping.bigbuy.eu/imgs/I3505265_81167.jpg","GTIN=8018417207747",,,,,,,,,,
+BB-H1000173,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Scooter Elettrici",base,"Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid Azzurro","Dimentica
i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spostarsi che chiunque noterà. Metti alla prova il tuo equilibrio e divertiti!www.roverdroid.com
Caratteristiche:
Batteria a litio: 160 Wh Pulsante on/off Indicatore luminoso del livello di carica sull'asse centrale Illuminazione di segnaletica anteriore LED Zona d'appoggio in gomma resistente e antiscivolo Velocità massima: 10 km/h circa Autonomia con carica completa: da 17 a 20 km circa Peso: 10 kg circa Tempo di carica: 3 ore circa Peso massimo supportato: 100 kg Dimensioni: 58 x 18 x 18 cm circa Diametro delle ruote: 7"" Borsa di trasporto e caricabatterie inclusi (CA: 100-240 V, 50-60 Hz, 1,2 A / DC: 36 V, 2 A) Dimenzioni per Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid:
Altezza: 22.5 Cm Larghezza: 64 Cm Profondita': 24 Cm Peso: 11.85 Kg Codice Prodotto (EAN): 4899888109204
","Dimentica i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spostarsi che chiunque noterà. Maggiori Informazioni ",11.85,1,"Taxable Goods","Catalog, Search",599,,,,Mini-Scooter-Elettrico-di-Auto-Equilibrio-(2-ruote)-Rover-Droid-Azzurro,"Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid Azzurro","Informatica Elettronica,Informatica,Elettronica,Scooter Elettrici,Scooter,Elettrici,Colore Azzurro,Azzurro,","Dimentica i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spos",http://dropshipping.bigbuy.eu/imgs/H1000172_90268.jpg,,http://dropshipping.bigbuy.eu/imgs/H1000172_90268.jpg,,,,,,"2016-02-25 15:50:21",,,,,,,,,,,,,,,,,"GTIN=4899888109204",60,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/H1000172_90282.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_90270.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87995.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87979.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87978.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87976.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87975.jpg","GTIN=4899888109204",,,,,,,,,,
+BB-H1000174,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Scooter Elettrici",base,"Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid Nero","Dimentica
i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spostarsi che chiunque noterà. Metti alla prova il tuo equilibrio e divertiti!www.roverdroid.com
Caratteristiche:
Batteria a litio: 160 Wh Pulsante on/off Indicatore luminoso del livello di carica sull'asse centrale Illuminazione di segnaletica anteriore LED Zona d'appoggio in gomma resistente e antiscivolo Velocità massima: 10 km/h circa Autonomia con carica completa: da 17 a 20 km circa Peso: 10 kg circa Tempo di carica: 3 ore circa Peso massimo supportato: 100 kg Dimensioni: 58 x 18 x 18 cm circa Diametro delle ruote: 7"" Borsa di trasporto e caricabatterie inclusi (CA: 100-240 V, 50-60 Hz, 1,2 A / DC: 36 V, 2 A) Dimenzioni per Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid:
Altezza: 22.5 Cm Larghezza: 64 Cm Profondita': 24 Cm Peso: 11.85 Kg Codice Prodotto (EAN): 4899888109273
","Dimentica i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spostarsi che chiunque noterà. Maggiori Informazioni ",11.85,1,"Taxable Goods","Catalog, Search",599,,,,Mini-Scooter-Elettrico-di-Auto-Equilibrio-(2-ruote)-Rover-Droid-Nero,"Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid Nero","Informatica Elettronica,Informatica,Elettronica,Scooter Elettrici,Scooter,Elettrici,Colore Nero,Nero,","Dimentica i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spos",http://dropshipping.bigbuy.eu/imgs/H1000172_90282.jpg,,http://dropshipping.bigbuy.eu/imgs/H1000172_90282.jpg,,,,,,"2016-02-25 15:50:21",,,,,,,,,,,,,,,,,"GTIN=4899888109273",131,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/H1000172_90268.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_90270.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87995.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87979.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87978.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87976.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87975.jpg","GTIN=4899888109273",,,,,,,,,,
+BB-H1000182,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Scooter Elettrici",base,"Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid Grafitti","Dimentica
i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spostarsi che chiunque noterà. Metti alla prova il tuo equilibrio e divertiti!www.roverdroid.com
Caratteristiche:
Batteria a litio: 160 Wh Pulsante on/off Indicatore luminoso del livello di carica sull'asse centrale Illuminazione di segnaletica anteriore LED Zona d'appoggio in gomma resistente e antiscivolo Velocità massima: 10 km/h circa Autonomia con carica completa: da 17 a 20 km circa Peso: 10 kg circa Tempo di carica: 3 ore circa Peso massimo supportato: 100 kg Dimensioni: 58 x 18 x 18 cm circa Diametro delle ruote: 7"" Borsa di trasporto e caricabatterie inclusi (CA: 100-240 V, 50-60 Hz, 1,2 A / DC: 36 V, 2 A) Dimenzioni per Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid:
Altezza: 22.5 Cm Larghezza: 64 Cm Profondita': 24 Cm Peso: 11.85 Kg Codice Prodotto (EAN): 4899888109747
","Dimentica i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spostarsi che chiunque noterà. Maggiori Informazioni ",11.85,1,"Taxable Goods","Catalog, Search",599,,,,Mini-Scooter-Elettrico-di-Auto-Equilibrio-(2-ruote)-Rover-Droid-Grafitti,"Mini Scooter Elettrico di Auto Equilibrio (2 ruote) Rover Droid Grafitti","Informatica Elettronica,Informatica,Elettronica,Scooter Elettrici,Scooter,Elettrici,Colore Grafitti,Grafitti,","Dimentica i noiosi trasporti convenzionali e diventa il re delle strade con il mini scooter elettrico di auto equilibrio (2 ruote) Rover Droid! Questo pratico, intelligente ed originale veicolo motorizzato costituisce un modo rapido e comodo di spos",http://dropshipping.bigbuy.eu/imgs/H1000172_90270.jpg,,http://dropshipping.bigbuy.eu/imgs/H1000172_90270.jpg,,,,,,"2016-02-25 16:33:57",,,,,,,,,,,,,,,,,"GTIN=4899888109747",92,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/H1000172_90268.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_90282.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87995.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87979.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87978.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87976.jpg,http://dropshipping.bigbuy.eu/imgs/H1000172_87975.jpg","GTIN=4899888109747",,,,,,,,,,
+BB-I2500322,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Orologi e Sveglie,Default Category/Informatica Elettronica/Orologi e Sveglie/Smartwatch",base,"Orologio Intelligente Smartwatch BT110 con Audio Nero","Sfoggia
il tuo orologio intelligente Smartwatch BT110 con audio ! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usarlo come cronometro, barometro, altimetro e podometro. Inoltre, questo orologio dispone di varie funzioni autonome: allarme, calendario, calcolatrice, controllo del sonno...www.bitblin.com Caratteristiche:
Schermo touch Risoluzione: 128 x 128 pixel Batteria a litio: 3,7 V / 230 mA Durata appross. in attesa: 150 h Durata appross. in conversazione: 3 h Connessione micro USB Bluetooth 3.0 Vibrazione Dimensioni appross. della sfera: 4 x 4,5 x 1 cm Cavo USB da micro USB incluso Il menu è disponibile in spagnolo, inglese, francese, danese, polacco, portoghese, italiano, tedesco, turco, russo e svedese. Compatibile con smartphones Android
Con Smartphone Android da 4.0 a 5.0 è possibile ricevere notifiche di SMS, e-mails, WhatsApp e social network (scaricando l'applicazione indicata nella pagina web del prodotto).
Dimenzioni per Orologio Intelligente Smartwatch BT110 con Audio:
Altezza: 11 Cm Larghezza: 8 Cm Profondita': 5 Cm Peso: 0.145 Kg Codice Prodotto (EAN): 4899888109242
","Sfoggia il tuo orologio intelligente Smartwatch BT110 con audio! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usarlo come cronometro, barometro, altimetro e podometro. Maggiori Informazioni ",0.145,1,"Taxable Goods","Catalog, Search",94.9,,,,Orologio-Intelligente-Smartwatch-BT110-con-Audio-Nero,"Orologio Intelligente Smartwatch BT110 con Audio Nero","Informatica Elettronica,Informatica,Elettronica,Orologi e Sveglie,Orologi,Sveglie,Smartwatch,Colore Nero,Nero,","Sfoggia il tuo orologio intelligente Smartwatch BT110 con audio! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usa",http://dropshipping.bigbuy.eu/imgs/I2500321_84125.jpg,,http://dropshipping.bigbuy.eu/imgs/I2500321_84125.jpg,,,,,,"2016-02-11 08:36:39",,,,,,,,,,,,,,,,,"GTIN=4899888109242",2,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I2500321_101196.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101195.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101194.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101193.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84129.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84128.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84127.jpg","GTIN=4899888109242",,,,,,,,,,
+BB-I2500323,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Orologi e Sveglie,Default Category/Informatica Elettronica/Orologi e Sveglie/Smartwatch",base,"Orologio Intelligente Smartwatch BT110 con Audio Bianco","Sfoggia
il tuo orologio intelligente Smartwatch BT110 con audio ! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usarlo come cronometro, barometro, altimetro e podometro. Inoltre, questo orologio dispone di varie funzioni autonome: allarme, calendario, calcolatrice, controllo del sonno...www.bitblin.com Caratteristiche:
Schermo touch Risoluzione: 128 x 128 pixel Batteria a litio: 3,7 V / 230 mA Durata appross. in attesa: 150 h Durata appross. in conversazione: 3 h Connessione micro USB Bluetooth 3.0 Vibrazione Dimensioni appross. della sfera: 4 x 4,5 x 1 cm Cavo USB da micro USB incluso Il menu è disponibile in spagnolo, inglese, francese, danese, polacco, portoghese, italiano, tedesco, turco, russo e svedese. Compatibile con smartphones Android
Con Smartphone Android da 4.0 a 5.0 è possibile ricevere notifiche di SMS, e-mails, WhatsApp e social network (scaricando l'applicazione indicata nella pagina web del prodotto).
Dimenzioni per Orologio Intelligente Smartwatch BT110 con Audio:
Altezza: 11 Cm Larghezza: 8 Cm Profondita': 5 Cm Peso: 0.145 Kg Codice Prodotto (EAN): 4899888109259
","Sfoggia il tuo orologio intelligente Smartwatch BT110 con audio! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usarlo come cronometro, barometro, altimetro e podometro. Maggiori Informazioni ",0.145,1,"Taxable Goods","Catalog, Search",94.9,,,,Orologio-Intelligente-Smartwatch-BT110-con-Audio-Bianco,"Orologio Intelligente Smartwatch BT110 con Audio Bianco","Informatica Elettronica,Informatica,Elettronica,Orologi e Sveglie,Orologi,Sveglie,Smartwatch,Colore Bianco,Bianco,","Sfoggia il tuo orologio intelligente Smartwatch BT110 con audio! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usa",http://dropshipping.bigbuy.eu/imgs/I2500321_101196.jpg,,http://dropshipping.bigbuy.eu/imgs/I2500321_101196.jpg,,,,,,"2016-02-11 08:36:39",,,,,,,,,,,,,,,,,"GTIN=4899888109259",2,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I2500321_84125.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101195.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101194.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101193.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84129.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84128.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84127.jpg","GTIN=4899888109259",,,,,,,,,,
+BB-I2500324,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Orologi e Sveglie,Default Category/Informatica Elettronica/Orologi e Sveglie/Smartwatch",base,"Orologio Intelligente Smartwatch BT110 con Audio Rosso","Sfoggia
il tuo orologio intelligente Smartwatch BT110 con audio ! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usarlo come cronometro, barometro, altimetro e podometro. Inoltre, questo orologio dispone di varie funzioni autonome: allarme, calendario, calcolatrice, controllo del sonno...www.bitblin.com Caratteristiche:
Schermo touch Risoluzione: 128 x 128 pixel Batteria a litio: 3,7 V / 230 mA Durata appross. in attesa: 150 h Durata appross. in conversazione: 3 h Connessione micro USB Bluetooth 3.0 Vibrazione Dimensioni appross. della sfera: 4 x 4,5 x 1 cm Cavo USB da micro USB incluso Il menu è disponibile in spagnolo, inglese, francese, danese, polacco, portoghese, italiano, tedesco, turco, russo e svedese. Compatibile con smartphones Android
Con Smartphone Android da 4.0 a 5.0 è possibile ricevere notifiche di SMS, e-mails, WhatsApp e social network (scaricando l'applicazione indicata nella pagina web del prodotto).
Dimenzioni per Orologio Intelligente Smartwatch BT110 con Audio:
Altezza: 11 Cm Larghezza: 8 Cm Profondita': 5 Cm Peso: 0.145 Kg Codice Prodotto (EAN): 4899888109266
","Sfoggia il tuo orologio intelligente Smartwatch BT110 con audio! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usarlo come cronometro, barometro, altimetro e podometro. Maggiori Informazioni ",0.145,1,"Taxable Goods","Catalog, Search",94.9,,,,Orologio-Intelligente-Smartwatch-BT110-con-Audio-Rosso,"Orologio Intelligente Smartwatch BT110 con Audio Rosso","Informatica Elettronica,Informatica,Elettronica,Orologi e Sveglie,Orologi,Sveglie,Smartwatch,Colore Rosso,Rosso,","Sfoggia il tuo orologio intelligente Smartwatch BT110 con audio! Sincronizzalo tramite Bluetooth al tuo smartphone e ti permette di realizzare e rispondere alle telefonate, accedere all'agenda e alla cronologia delle chiamate, ascoltare musica e usa",http://dropshipping.bigbuy.eu/imgs/I2500321_101195.jpg,,http://dropshipping.bigbuy.eu/imgs/I2500321_101195.jpg,,,,,,"2016-02-11 08:36:39",,,,,,,,,,,,,,,,,"GTIN=4899888109266",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I2500321_84125.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101196.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101194.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_101193.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84129.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84128.jpg,http://dropshipping.bigbuy.eu/imgs/I2500321_84127.jpg","GTIN=4899888109266",,,,,,,,,,
+BB-I4110022,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Smartphone MyWigo UNO 5'' Bianco","Acquista
uno dei migliori telefoni cellulari sbloccati sul mercato in questo momento, lo smartphone MyWigo UNO 5'' ! Include caricabatterie e cavetto USB al cavetto micro USB.Caratteristiche:
Schermo Vetro Ricurvo 5'' HD IPS 2.5D Batteria a polimeri di litio: 2350 mAh Fotocamera anteriore: 5 Mpx Telecamera posteriore: 13 Mpx Sony, IMX 214 sensore con autofocus e flash LED Processore: MTK6753 Octa Core a 1.3 GHz di 64 Bit RAM: 2 GB DDR Memoria interna: 32 GB (16 GB + 16 GB micro SD) Dual SIM Sistema operativo: Android Lollipop 5.1 Wi-Fi Bluetooth 4.0 + HS GPS 2G: GSM a 850/900/1800/1900 MHz 3G: WCDMA 900/2100 MHz Tecnologia 4G (LTE) FDD 800/1800/2100/2600 MHz Caricabatterie: AC 110-240 V, DC 5 V, 1000 mA Dimensioni: 7 x 14 x 0,8 cm circa Dimensioni dello schermo: 6 x 11 cm circa Peso: 138 gr circa Dimenzioni per Smartphone MyWigo UNO 5'' :
Altezza: 10.5 Cm Larghezza: 18 Cm Profondita': 5 Cm Peso: 0.347 Kg Codice Prodotto (EAN): 8436533839565
","Acquista uno dei migliori telefoni cellulari sbloccati sul mercato in questo momento, lo smartphone MyWigo UNO 5''! Include caricabatterie e cavetto USB al cavetto micro USB. Maggiori Informazioni ",0.347,1,"Taxable Goods","Catalog, Search",412.5,,,,Smartphone-MyWigo-UNO-5''-Bianco,"Smartphone MyWigo UNO 5'' Bianco","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Bianco,Bianco,","Acquista uno dei migliori telefoni cellulari sbloccati sul mercato in questo momento, lo smartphone MyWigo UNO 5''! Include caricabatterie e cavetto USB al cavetto micro USB",http://dropshipping.bigbuy.eu/imgs/I4110021_87809.jpg,,http://dropshipping.bigbuy.eu/imgs/I4110021_87809.jpg,,,,,,"2015-12-16 14:44:27",,,,,,,,,,,,,,,,,"GTIN=8436533839565",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I4110021_87813.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87812.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87811.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87810.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87808.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87807.jpg","GTIN=8436533839565",,,,,,,,,,
+BB-I4110023,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Smartphone MyWigo UNO 5'' Nero","Acquista
uno dei migliori telefoni cellulari sbloccati sul mercato in questo momento, lo smartphone MyWigo UNO 5'' ! Include caricabatterie e cavetto USB al cavetto micro USB.Caratteristiche:
Schermo Vetro Ricurvo 5'' HD IPS 2.5D Batteria a polimeri di litio: 2350 mAh Fotocamera anteriore: 5 Mpx Telecamera posteriore: 13 Mpx Sony, IMX 214 sensore con autofocus e flash LED Processore: MTK6753 Octa Core a 1.3 GHz di 64 Bit RAM: 2 GB DDR Memoria interna: 32 GB (16 GB + 16 GB micro SD) Dual SIM Sistema operativo: Android Lollipop 5.1 Wi-Fi Bluetooth 4.0 + HS GPS 2G: GSM a 850/900/1800/1900 MHz 3G: WCDMA 900/2100 MHz Tecnologia 4G (LTE) FDD 800/1800/2100/2600 MHz Caricabatterie: AC 110-240 V, DC 5 V, 1000 mA Dimensioni: 7 x 14 x 0,8 cm circa Dimensioni dello schermo: 6 x 11 cm circa Peso: 138 gr circa Dimenzioni per Smartphone MyWigo UNO 5'' :
Altezza: 10.5 Cm Larghezza: 18 Cm Profondita': 5 Cm Peso: 0.347 Kg Codice Prodotto (EAN): 8436533839558
","Acquista uno dei migliori telefoni cellulari sbloccati sul mercato in questo momento, lo smartphone MyWigo UNO 5''! Include caricabatterie e cavetto USB al cavetto micro USB. Maggiori Informazioni ",0.347,1,"Taxable Goods","Catalog, Search",412.5,,,,Smartphone-MyWigo-UNO-5''-Nero,"Smartphone MyWigo UNO 5'' Nero","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Nero,Nero,","Acquista uno dei migliori telefoni cellulari sbloccati sul mercato in questo momento, lo smartphone MyWigo UNO 5''! Include caricabatterie e cavetto USB al cavetto micro USB",http://dropshipping.bigbuy.eu/imgs/I4110021_87813.jpg,,http://dropshipping.bigbuy.eu/imgs/I4110021_87813.jpg,,,,,,"2015-12-16 14:44:27",,,,,,,,,,,,,,,,,"GTIN=8436533839558",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I4110021_87809.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87812.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87811.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87810.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87808.jpg,http://dropshipping.bigbuy.eu/imgs/I4110021_87807.jpg","GTIN=8436533839558",,,,,,,,,,
+BB-V1400101,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Telefono Cellulare Thomson Tlink11 Bianco","Se
sei alla ricerca di un telefono cellulare user-friendly per le persone anziane o per chi è agli inizi nel mondo della telefonia mobile, il telefono cellulare Thomson Tlink11 è ciò che stai cercando!Caratteristiche:
Telefono cellulare sbloccato Schermo: 1.77"" 128 x 160, colori 65 K Reti: GSM-EDGE 850/900/1800/1900 MHz Dual SIM Bluetooth MP3 Radio FM Micro USB Micro SD (scheda non inclusa) Connessione per auricolari (non inclusi) Lista contatti: 200 nomi Funzione SMS Mani-libere Batteria Li-ion 600 mAh Include: batteria e caricabatterie Dimensioni: 4.5 x 11 x 1 cm circa Dimenzioni per Telefono Cellulare Thomson Tlink11:
Altezza: 17 Cm Larghezza: 11 Cm Profondita': 5 Cm Peso: 0.167 Kg Codice Prodotto (EAN): 3527570047688
","Se sei alla ricerca di un telefono cellulare user-friendly per le persone anziane o per chi è agli inizi nel mondo della telefonia mobile, il telefono cellulare Thomson Tlink11 è ciò che stai cercando!Caratteristiche:Telefono cellulare sbloccatoSchermo: 1. Maggiori Informazioni ",0.167,1,"Taxable Goods","Catalog, Search",43.5,,,,Telefono-Cellulare-Thomson-Tlink11-Bianco,"Telefono Cellulare Thomson Tlink11 Bianco","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Bianco,Bianco,","Se sei alla ricerca di un telefono cellulare user-friendly per le persone anziane o per chi è agli inizi nel mondo della telefonia mobile, il telefono cellulare Thomson Tlink11 è ciò che stai cercando!Caratteristiche:Telefono cellulare sbloccatoSche",http://dropshipping.bigbuy.eu/imgs/V1400100_91205.jpg,,http://dropshipping.bigbuy.eu/imgs/V1400100_91205.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3527570047688",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1400100_91204.jpg,http://dropshipping.bigbuy.eu/imgs/V1400100_91203.jpg","GTIN=3527570047688",,,,,,,,,,
+BB-V1400102,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Telefono Cellulare Thomson Tlink11 Nero","Se
sei alla ricerca di un telefono cellulare user-friendly per le persone anziane o per chi è agli inizi nel mondo della telefonia mobile, il telefono cellulare Thomson Tlink11 è ciò che stai cercando!Caratteristiche:
Telefono cellulare sbloccato Schermo: 1.77"" 128 x 160, colori 65 K Reti: GSM-EDGE 850/900/1800/1900 MHz Dual SIM Bluetooth MP3 Radio FM Micro USB Micro SD (scheda non inclusa) Connessione per auricolari (non inclusi) Lista contatti: 200 nomi Funzione SMS Mani-libere Batteria Li-ion 600 mAh Include: batteria e caricabatterie Dimensioni: 4.5 x 11 x 1 cm circa Dimenzioni per Telefono Cellulare Thomson Tlink11:
Altezza: 17 Cm Larghezza: 11 Cm Profondita': 5 Cm Peso: 0.167 Kg Codice Prodotto (EAN): 3527570047596
","Se sei alla ricerca di un telefono cellulare user-friendly per le persone anziane o per chi è agli inizi nel mondo della telefonia mobile, il telefono cellulare Thomson Tlink11 è ciò che stai cercando!Caratteristiche:Telefono cellulare sbloccatoSchermo: 1. Maggiori Informazioni ",0.167,1,"Taxable Goods","Catalog, Search",43.5,,,,Telefono-Cellulare-Thomson-Tlink11-Nero,"Telefono Cellulare Thomson Tlink11 Nero","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Nero,Nero,","Se sei alla ricerca di un telefono cellulare user-friendly per le persone anziane o per chi è agli inizi nel mondo della telefonia mobile, il telefono cellulare Thomson Tlink11 è ciò che stai cercando!Caratteristiche:Telefono cellulare sbloccatoSche",http://dropshipping.bigbuy.eu/imgs/V1400100_91204.jpg,,http://dropshipping.bigbuy.eu/imgs/V1400100_91204.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3527570047596",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1400100_91205.jpg,http://dropshipping.bigbuy.eu/imgs/V1400100_91203.jpg","GTIN=3527570047596",,,,,,,,,,
+BB-V1400104,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Telefono Cellulare Thomson Serea51 Bianco","In
arrivo il telefono cellulare Thomson Serea51 per gli amanti dei cellulari semplici e funzionali, per le persone anziane o per coloro che si avviciano per la prima volta al mondo della telefonia mobile!Caratteristiche:
Cellulare sbloccato Schermo: 1,77"" 160 x 128, 65 K colori Reti: GSM-EDGE 850/900/1800/1900 MHz Tasto per le chiamate d'emergenza Tasti di grandi dimensioni Luce LED Fotocamera VGA Bluetooth MP3 Radio FM Micro USB Micro SD (scheda non inclusa) Agenda: 250 voci Funzione SMS, MMS kit auricolare mani libere Batteria Li-ion 800 mAh Durata della batteria: 220 h in standby / 5,5 h in conversazione Include: batteria, base di ricarica, adattatore di CA, cavo dati USB e auricolari Dimensioni (senza base): 5 x 11 x 1,4 cm circa Dimenzioni per Telefono Cellulare Thomson Serea51:
Altezza: 18 Cm Larghezza: 11.5 Cm Profondita': 7.5 Cm Peso: 0.288 Kg Codice Prodotto (EAN): 3527570046964
","In arrivo il telefono cellulare Thomson Serea51 per gli amanti dei cellulari semplici e funzionali, per le persone anziane o per coloro che si avviciano per la prima volta al mondo della telefonia mobile!Caratteristiche:Cellulare sbloccatoSchermo: 1,77" 160 x 128, 65 K coloriReti: GSM-EDGE 850/900/1800/1900 MHzTasto per le chiamate d'emergenzaTasti di grandi dimensioniLuce LEDFotocamera VGABluetoothMP3Radio FMMicro USBMicro SD (scheda non inclusa)Agenda: 250 vociFunzione SMS, MMSkit auricolare mani libereBatteria Li-ion 800 mAhDurata della batteria: 220 h in standby / 5,5 h in conversazioneInclude: batteria, base di ricarica, adattatore di CA, cavo dati USB e auricolariDimensioni (senza base): 5 x 11 x 1,4 cm circa. Maggiori Informazioni ",0.288,1,"Taxable Goods","Catalog, Search",85.9,,,,Telefono-Cellulare-Thomson-Serea51-Bianco,"Telefono Cellulare Thomson Serea51 Bianco","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Bianco,Bianco,","In arrivo il telefono cellulare Thomson Serea51 per gli amanti dei cellulari semplici e funzionali, per le persone anziane o per coloro che si avviciano per la prima volta al mondo della telefonia mobile!Caratteristiche:Cellulare sbloccatoSchermo: 1",http://dropshipping.bigbuy.eu/imgs/V1400103_91207.jpg,,http://dropshipping.bigbuy.eu/imgs/V1400103_91207.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3527570046964",42,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1400103_91208.jpg,http://dropshipping.bigbuy.eu/imgs/V1400103_91206.jpg","GTIN=3527570046964",,,,,,,,,,
+BB-V1400105,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Telefono Cellulare Thomson Serea51 Nero","In
arrivo il telefono cellulare Thomson Serea51 per gli amanti dei cellulari semplici e funzionali, per le persone anziane o per coloro che si avviciano per la prima volta al mondo della telefonia mobile!Caratteristiche:
Cellulare sbloccato Schermo: 1,77"" 160 x 128, 65 K colori Reti: GSM-EDGE 850/900/1800/1900 MHz Tasto per le chiamate d'emergenza Tasti di grandi dimensioni Luce LED Fotocamera VGA Bluetooth MP3 Radio FM Micro USB Micro SD (scheda non inclusa) Agenda: 250 voci Funzione SMS, MMS kit auricolare mani libere Batteria Li-ion 800 mAh Durata della batteria: 220 h in standby / 5,5 h in conversazione Include: batteria, base di ricarica, adattatore di CA, cavo dati USB e auricolari Dimensioni (senza base): 5 x 11 x 1,4 cm circa Dimenzioni per Telefono Cellulare Thomson Serea51:
Altezza: 18 Cm Larghezza: 11.5 Cm Profondita': 7.5 Cm Peso: 0.288 Kg Codice Prodotto (EAN): 3527570046186
","In arrivo il telefono cellulare Thomson Serea51 per gli amanti dei cellulari semplici e funzionali, per le persone anziane o per coloro che si avviciano per la prima volta al mondo della telefonia mobile!Caratteristiche:Cellulare sbloccatoSchermo: 1,77" 160 x 128, 65 K coloriReti: GSM-EDGE 850/900/1800/1900 MHzTasto per le chiamate d'emergenzaTasti di grandi dimensioniLuce LEDFotocamera VGABluetoothMP3Radio FMMicro USBMicro SD (scheda non inclusa)Agenda: 250 vociFunzione SMS, MMSkit auricolare mani libereBatteria Li-ion 800 mAhDurata della batteria: 220 h in standby / 5,5 h in conversazioneInclude: batteria, base di ricarica, adattatore di CA, cavo dati USB e auricolariDimensioni (senza base): 5 x 11 x 1,4 cm circa. Maggiori Informazioni ",0.288,1,"Taxable Goods","Catalog, Search",85.9,,,,Telefono-Cellulare-Thomson-Serea51-Nero,"Telefono Cellulare Thomson Serea51 Nero","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Nero,Nero,","In arrivo il telefono cellulare Thomson Serea51 per gli amanti dei cellulari semplici e funzionali, per le persone anziane o per coloro che si avviciano per la prima volta al mondo della telefonia mobile!Caratteristiche:Cellulare sbloccatoSchermo: 1",http://dropshipping.bigbuy.eu/imgs/V1400103_91208.jpg,,http://dropshipping.bigbuy.eu/imgs/V1400103_91208.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3527570046186",33,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1400103_91207.jpg,http://dropshipping.bigbuy.eu/imgs/V1400103_91206.jpg","GTIN=3527570046186",,,,,,,,,,
+BB-V1400107,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Telefono Cellularel Thomson Serea62 Nero","Se
sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62 . È studiato anche per le persone anziane che vogliono avvicinarsi al mondo della telefonia mobile . Caratteristiche:
Schermo: 2,4"" 240 x 320, 262 K a colori Reti: GSM-EDGE 850/900/1800/1900 MHz Tasto per le chiamate d'emergenza Menù semplificato e facile da usare Tasti di grandi dimensioni Fotocamera VGA Bluetooth MP3 Radio FM Luce LED Micro USB Micro SD (carta non inclusa) Agenda: 250 voci Funzione SMS, MMS kit auricolare mani libere Batteria Li-ion 800 mAh Durata della batteria: 480 h in standby / 5,5 h in conversazione Include: batteria, base di ricarica, adattatore di CA, cavo dati USB e auricolari Dimensioni (senza base): 5,5 x 10,5 x 2 cm circa Dimenzioni per Telefono Cellularel Thomson Serea62:
Altezza: 5.2 Cm Larghezza: 11.3 Cm Profondita': 18.6 Cm Peso: 0.284 Kg Codice Prodotto (EAN): 3527570046094
","Se sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62. Maggiori Informazioni ",0.284,1,"Taxable Goods","Catalog, Search",99.5,,,,Telefono-Cellularel-Thomson-Serea62-Nero,"Telefono Cellularel Thomson Serea62 Nero","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Nero,Nero,","Se sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62",http://dropshipping.bigbuy.eu/imgs/V1400106_91213.jpg,,http://dropshipping.bigbuy.eu/imgs/V1400106_91213.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3527570046094",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1400106_91215.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91214.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91212.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91211.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91210.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91209.jpg","GTIN=3527570046094",,,,,,,,,,
+BB-V1400108,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Telefono Cellularel Thomson Serea62 Bianco","Se
sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62 . È studiato anche per le persone anziane che vogliono avvicinarsi al mondo della telefonia mobile . Caratteristiche:
Schermo: 2,4"" 240 x 320, 262 K a colori Reti: GSM-EDGE 850/900/1800/1900 MHz Tasto per le chiamate d'emergenza Menù semplificato e facile da usare Tasti di grandi dimensioni Fotocamera VGA Bluetooth MP3 Radio FM Luce LED Micro USB Micro SD (carta non inclusa) Agenda: 250 voci Funzione SMS, MMS kit auricolare mani libere Batteria Li-ion 800 mAh Durata della batteria: 480 h in standby / 5,5 h in conversazione Include: batteria, base di ricarica, adattatore di CA, cavo dati USB e auricolari Dimensioni (senza base): 5,5 x 10,5 x 2 cm circa Dimenzioni per Telefono Cellularel Thomson Serea62:
Altezza: 5.2 Cm Larghezza: 11.3 Cm Profondita': 18.6 Cm Peso: 0.284 Kg Codice Prodotto (EAN): 3527570046100
","Se sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62. Maggiori Informazioni ",0.284,1,"Taxable Goods","Catalog, Search",99.5,,,,Telefono-Cellularel-Thomson-Serea62-Bianco,"Telefono Cellularel Thomson Serea62 Bianco","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Bianco,Bianco,","Se sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62",http://dropshipping.bigbuy.eu/imgs/V1400106_91215.jpg,,http://dropshipping.bigbuy.eu/imgs/V1400106_91215.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3527570046100",13,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1400106_91213.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91214.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91212.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91211.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91210.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91209.jpg","GTIN=3527570046100",,,,,,,,,,
+BB-V1400109,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Telefonia e Accessori,Default Category/Informatica Elettronica/Telefonia e Accessori/Cellulari",base,"Telefono Cellularel Thomson Serea62 Rosso","Se
sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62 . È studiato anche per le persone anziane che vogliono avvicinarsi al mondo della telefonia mobile . Caratteristiche:
Schermo: 2,4"" 240 x 320, 262 K a colori Reti: GSM-EDGE 850/900/1800/1900 MHz Tasto per le chiamate d'emergenza Menù semplificato e facile da usare Tasti di grandi dimensioni Fotocamera VGA Bluetooth MP3 Radio FM Luce LED Micro USB Micro SD (carta non inclusa) Agenda: 250 voci Funzione SMS, MMS kit auricolare mani libere Batteria Li-ion 800 mAh Durata della batteria: 480 h in standby / 5,5 h in conversazione Include: batteria, base di ricarica, adattatore di CA, cavo dati USB e auricolari Dimensioni (senza base): 5,5 x 10,5 x 2 cm circa Dimenzioni per Telefono Cellularel Thomson Serea62:
Altezza: 5.2 Cm Larghezza: 11.3 Cm Profondita': 18.6 Cm Peso: 0.284 Kg Codice Prodotto (EAN): 3527570046117
","Se sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62. Maggiori Informazioni ",0.284,1,"Taxable Goods","Catalog, Search",99.5,,,,Telefono-Cellularel-Thomson-Serea62-Rosso,"Telefono Cellularel Thomson Serea62 Rosso","Informatica Elettronica,Informatica,Elettronica,Telefonia e Accessori,Telefonia,Accessori,Cellulari,Colore Rosso,Rosso,","Se sei alla ricerca di un cellulare semplice ma dal design impeccabile, non puoi lasciarti sfuggire il telefono cellulare Thomson Serea62",http://dropshipping.bigbuy.eu/imgs/V1400106_91214.jpg,,http://dropshipping.bigbuy.eu/imgs/V1400106_91214.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=3527570046117",17,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1400106_91213.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91215.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91212.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91211.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91210.jpg,http://dropshipping.bigbuy.eu/imgs/V1400106_91209.jpg","GTIN=3527570046117",,,,,,,,,,
+BB-V0100186,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Cuffie",base,"Cuffie Fatina maginca Playz Kidz","Le
cuffie Fatina Magica Playz Kidz son perfetti per i piccoli di casa! Queste cuffie per bambini sono ideali come regalo per i re della casa!www.playzkidz.com
Auricolari stereo Cuffie imbottite Compatibili con MP3, MP4, CD, radio e PC Età raccomandata: +4 anni Dimenzioni per Cuffie Fatina maginca Playz Kidz:
Altezza: 22.2 Cm Larghezza: 9.5 Cm Profondita': 26.7 Cm Peso: 0.243 Kg Codice Prodotto (EAN): 4899888111122
","Le cuffie Fatina Magica Playz Kidz son perfetti per i piccoli di casa! Queste cuffie per bambini sono ideali come regalo per i re della casa!www. Maggiori Informazioni ",0.243,1,"Taxable Goods","Catalog, Search",18.9,,,,Cuffie-Fatina-maginca-Playz-Kidz,"Cuffie Fatina maginca Playz Kidz","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Cuffie,","Le cuffie Fatina Magica Playz Kidz son perfetti per i piccoli di casa! Queste cuffie per bambini sono ideali come regalo per i re della casa!www",http://dropshipping.bigbuy.eu/imgs/V0100186_93443.jpg,,http://dropshipping.bigbuy.eu/imgs/V0100186_93443.jpg,,,,,,"2016-08-16 05:37:23",,,,,,,,,,,,,,,,,"GTIN=4899888111122",2436,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0100186_93379.jpg,http://dropshipping.bigbuy.eu/imgs/V0100186_93377.jpg,http://dropshipping.bigbuy.eu/imgs/V0100186_93376.jpg,http://dropshipping.bigbuy.eu/imgs/V0100186_93375.jpg","GTIN=4899888111122",,,,,,,,,,
+BB-V0100187,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Audio e Hi-Fi,Default Category/Informatica Elettronica/Audio e Hi-Fi/Cuffie",base,"Cuffie Mostriciattoli Playz Kidz","I
piccoli di casa impazziranno per le cuffie Mostriciattoli Playz Kidz ! Grazie al loro design originale e divertente, queste cuffie per bambini sono il regalo perfetto!www.playzkidz.com
Auricolari stereo Cuffie imbottite Compatibili con MP3, MP4, CD, radio e PC Età raccomandata: +4 anni Dimenzioni per Cuffie Mostriciattoli Playz Kidz:
Altezza: 22.2 Cm Larghezza: 9.5 Cm Profondita': 26.7 Cm Peso: 0.245 Kg Codice Prodotto (EAN): 4899888111139
","I piccoli di casa impazziranno per le cuffie Mostriciattoli Playz Kidz! Grazie al loro design originale e divertente, queste cuffie per bambini sono il regalo perfetto!www. Maggiori Informazioni ",0.245,1,"Taxable Goods","Catalog, Search",18.9,,,,Cuffie-Mostriciattoli-Playz-Kidz,"Cuffie Mostriciattoli Playz Kidz","Informatica Elettronica,Informatica,Elettronica,Audio e Hi-Fi,Audio,Hi-Fi,Cuffie,","I piccoli di casa impazziranno per le cuffie Mostriciattoli Playz Kidz! Grazie al loro design originale e divertente, queste cuffie per bambini sono il regalo perfetto!www",http://dropshipping.bigbuy.eu/imgs/V0100187_93370.jpg,,http://dropshipping.bigbuy.eu/imgs/V0100187_93370.jpg,,,,,,"2016-08-30 08:26:19",,,,,,,,,,,,,,,,,"GTIN=4899888111139",2438,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0100187_93374.jpg,http://dropshipping.bigbuy.eu/imgs/V0100187_93373.jpg,http://dropshipping.bigbuy.eu/imgs/V0100187_93372.jpg,http://dropshipping.bigbuy.eu/imgs/V0100187_93371.jpg","GTIN=4899888111139",,,,,,,,,,
+BB-V1300174,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Orologi e Sveglie,Default Category/Informatica Elettronica/Orologi e Sveglie/Sveglie",base,"Orologio Sveglia con Contasecondi Star Wars R2-D2","Sorprendi
i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'o rologio sveglia con contasecondi Star Wars ! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi.Dispone di segnale acustico d'allarme e di pulsante per spegnerlo Funziona a batterie (1 x AA, non inclusa) Dimensioni: circa 10,5 x 13,5 x 6 cm Età consigliata: +3 anni Dimenzioni per Orologio Sveglia con Contasecondi Star Wars:
Altezza: 0.13 Cm Larghezza: 0.1 Cm Profondita': 0.5 Cm Peso: 0.15 Kg Codice Prodotto (EAN): 8427934787128
","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi. Maggiori Informazioni ",0.15,1,"Taxable Goods","Catalog, Search",15.8,,,,Orologio-Sveglia-con-Contasecondi-Star-Wars-R2-D2,"Orologio Sveglia con Contasecondi Star Wars R2-D2","Informatica Elettronica,Informatica,Elettronica,Orologi e Sveglie,Orologi,Sveglie,Sveglie,Design R2-D2,R2-D2,","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi",http://dropshipping.bigbuy.eu/imgs/V1300173_102640.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300173_102640.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934787128",69,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300173_102644.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102643.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102642.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102641.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102639.jpg","GTIN=8427934787128",,,,,,,,,,
+BB-V1300175,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Orologi e Sveglie,Default Category/Informatica Elettronica/Orologi e Sveglie/Sveglie",base,"Orologio Sveglia con Contasecondi Star Wars Stormtrooper","Sorprendi
i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'o rologio sveglia con contasecondi Star Wars ! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi.Dispone di segnale acustico d'allarme e di pulsante per spegnerlo Funziona a batterie (1 x AA, non inclusa) Dimensioni: circa 10,5 x 13,5 x 6 cm Età consigliata: +3 anni Dimenzioni per Orologio Sveglia con Contasecondi Star Wars:
Altezza: 0.13 Cm Larghezza: 0.1 Cm Profondita': 0.5 Cm Peso: 0.15 Kg Codice Prodotto (EAN): 8427934787135
","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi. Maggiori Informazioni ",0.15,1,"Taxable Goods","Catalog, Search",15.8,,,,Orologio-Sveglia-con-Contasecondi-Star-Wars-Stormtrooper,"Orologio Sveglia con Contasecondi Star Wars Stormtrooper","Informatica Elettronica,Informatica,Elettronica,Orologi e Sveglie,Orologi,Sveglie,Sveglie,Design Stormtrooper,Stormtrooper,","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi",http://dropshipping.bigbuy.eu/imgs/V1300173_102644.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300173_102644.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934787135",69,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300173_102640.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102643.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102642.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102641.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102639.jpg","GTIN=8427934787135",,,,,,,,,,
+BB-V1300176,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Orologi e Sveglie,Default Category/Informatica Elettronica/Orologi e Sveglie/Sveglie",base,"Orologio Sveglia con Contasecondi Star Wars Yoda","Sorprendi
i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'o rologio sveglia con contasecondi Star Wars ! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi.Dispone di segnale acustico d'allarme e di pulsante per spegnerlo Funziona a batterie (1 x AA, non inclusa) Dimensioni: circa 10,5 x 13,5 x 6 cm Età consigliata: +3 anni Dimenzioni per Orologio Sveglia con Contasecondi Star Wars:
Altezza: 0.13 Cm Larghezza: 0.1 Cm Profondita': 0.5 Cm Peso: 0.15 Kg Codice Prodotto (EAN): 8427934787142
","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi. Maggiori Informazioni ",0.15,1,"Taxable Goods","Catalog, Search",15.8,,,,Orologio-Sveglia-con-Contasecondi-Star-Wars-Yoda,"Orologio Sveglia con Contasecondi Star Wars Yoda","Informatica Elettronica,Informatica,Elettronica,Orologi e Sveglie,Orologi,Sveglie,Sveglie,Design Yoda,Yoda,","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi",http://dropshipping.bigbuy.eu/imgs/V1300173_102643.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300173_102643.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934787142",68,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300173_102640.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102644.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102642.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102641.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102639.jpg","GTIN=8427934787142",,,,,,,,,,
+BB-V1300177,,Default,simple,"Default Category/Informatica Elettronica,Default Category/Informatica Elettronica/Orologi e Sveglie,Default Category/Informatica Elettronica/Orologi e Sveglie/Sveglie",base,"Orologio Sveglia con Contasecondi Star Wars Chewbacca","Sorprendi
i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'o rologio sveglia con contasecondi Star Wars ! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi.Dispone di segnale acustico d'allarme e di pulsante per spegnerlo Funziona a batterie (1 x AA, non inclusa) Dimensioni: circa 10,5 x 13,5 x 6 cm Età consigliata: +3 anni Dimenzioni per Orologio Sveglia con Contasecondi Star Wars:
Altezza: 0.13 Cm Larghezza: 0.1 Cm Profondita': 0.5 Cm Peso: 0.15 Kg Codice Prodotto (EAN): 8427934787159
","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi. Maggiori Informazioni ",0.15,1,"Taxable Goods","Catalog, Search",15.8,,,,Orologio-Sveglia-con-Contasecondi-Star-Wars-Chewbacca,"Orologio Sveglia con Contasecondi Star Wars Chewbacca","Informatica Elettronica,Informatica,Elettronica,Orologi e Sveglie,Orologi,Sveglie,Sveglie,Design Chewbacca,Chewbacca,","Sorprendi i tuoi familiari ed amici con un regalo originale che li lascerà a bocca aperta, l'orologio sveglia con contasecondi Star Wars! Una sveglia ideale per gli appassionati della famosa saga e dei suoi personaggi",http://dropshipping.bigbuy.eu/imgs/V1300173_102642.jpg,,http://dropshipping.bigbuy.eu/imgs/V1300173_102642.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8427934787159",69,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1300173_102640.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102644.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102643.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102641.jpg,http://dropshipping.bigbuy.eu/imgs/V1300173_102639.jpg","GTIN=8427934787159",,,,,,,,,,
+BB-G1000110,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Offerte",base,"Attrezzo da Ginnastica Body Rocker","Non
perdere tempo e denaro andandoin palestra e procurarti ora l'attrezzo da ginnastica Body Rocker! Una forma facile ed efficace di rimettresi in forma facendo esercizio in casa. Grazie al suo sistema di oscillazione, è perfetto per lavorare e rafforzare, spalle, braccia, schiena, petto, glutei, ecc. Fatto in acciaio con impugnature in gomma. Include manuale d'istruzioni e DVD dimostrativo. (Questo prodotto può può presentare lievi danni che non impediscono il funzionamento del prodotto: impugnature in gomma piuma scollate). Dimenzioni per Attrezzo da Ginnastica Body Rocker :
Altezza: 21 Cm Larghezza: 20 Cm Profondita': 78 Cm Peso: 2.13 Kg Codice Prodotto (EAN): 4899888101314
","Non perdere tempo e denaro andandoin palestra e procurarti ora l'attrezzo da ginnastica Body Rocker! Una forma facile ed efficace di rimettresi in forma facendo esercizio in casa. Maggiori Informazioni ",2.13,1,"Taxable Goods","Catalog, Search",79,,,,Attrezzo-da-Ginnastica-Body-Rocker,"Attrezzo da Ginnastica Body Rocker","Outlet Offerte,Outlet,Offerte,Offerte,","Non perdere tempo e denaro andandoin palestra e procurarti ora l'attrezzo da ginnastica Body Rocker! Una forma facile ed efficace di rimettresi in forma facendo esercizio in casa",http://dropshipping.bigbuy.eu/imgs/bodirocker-00.jpg,,http://dropshipping.bigbuy.eu/imgs/bodirocker-00.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=4899888101314",121,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/bodirocker-03.jpg,http://dropshipping.bigbuy.eu/imgs/bodirocker-06.jpg,http://dropshipping.bigbuy.eu/imgs/bodirocker-05.jpg,http://dropshipping.bigbuy.eu/imgs/bodirocker-07.jpg,http://dropshipping.bigbuy.eu/imgs/bodirocker-02.jpg,http://dropshipping.bigbuy.eu/imgs/bodirocker-01.jpg,http://dropshipping.bigbuy.eu/imgs/bodirocker-04.jpg","GTIN=4899888101314",,,,,,,,,,
+BB-J2000066,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Lovely Arancio","Acquista
la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. La coperta Snug Snug con le maniche ti riscalderà senza aver bisogno del riscaldamento per tutto il giorno. Risparmierai i soldi e starai comodo grazie alla coperta extra morbida Kangoo Snug Snug con le maniche , poiché potrai fare tutto ciò che vuoi mentre sei coperto.La coperta Snug Snug con le maniche è il regalo perfetto per qualsiasi occasione. Nessuno si aspetta un regalo così straordinario! Questa coperta con le maniche è l'ideale per quei momenti in cui fa freddo fuori e vuoi solo stare a casa a leggere, usare il computer o semplicemente a rilassarti sul divano. Fino ad ora, non è era facile raggomitolarsi sul divano e voler fare qualcosa, perché dovevi tirare fuori le braccia, ma faceva freddo. Tutto questo è finito grazie alla coperta Kangoo Snug Snug con le maniche!
Caratteristiche:
Fatto di pile ultra soffice Lavabile in lavatrice Dimensioni: lunghezza 185 cm, larghezza 160 cm | Dimensioni manica: 60cm www.snugsnug.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio):
Altezza: 28 Cm Larghezza: 26 Cm Profondita': 12 Cm Peso: 0.8 Kg Codice Prodotto (EAN): 4899888102731
","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. Maggiori Informazioni ",0.8,1,"Taxable Goods","Catalog, Search",22.9,,,,OUTLET-Coperta-super-soffice-Kangoo-Snug-Snug-con-maniche-per-adulti-|-Decorazioni-originali-(Senza-imballaggio)-Lovely Arancio,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Lovely Arancio","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Lovely Arancio,Lovely Arancio,","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali",http://dropshipping.bigbuy.eu/imgs/J2000065_91715.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000065_91715.jpg,,,,,,"2016-02-11 15:49:04",,,,,,,,,,,,,,,,,"GTIN=4899888102731",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000065_91719.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91718.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91717.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91716.jpg","GTIN=4899888102731",,,,,,,,,,
+BB-J2000239,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Commando","Acquista
la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. La coperta Snug Snug con le maniche ti riscalderà senza aver bisogno del riscaldamento per tutto il giorno. Risparmierai i soldi e starai comodo grazie alla coperta extra morbida Kangoo Snug Snug con le maniche , poiché potrai fare tutto ciò che vuoi mentre sei coperto.La coperta Snug Snug con le maniche è il regalo perfetto per qualsiasi occasione. Nessuno si aspetta un regalo così straordinario! Questa coperta con le maniche è l'ideale per quei momenti in cui fa freddo fuori e vuoi solo stare a casa a leggere, usare il computer o semplicemente a rilassarti sul divano. Fino ad ora, non è era facile raggomitolarsi sul divano e voler fare qualcosa, perché dovevi tirare fuori le braccia, ma faceva freddo. Tutto questo è finito grazie alla coperta Kangoo Snug Snug con le maniche!
Caratteristiche:
Fatto di pile ultra soffice Lavabile in lavatrice Dimensioni: lunghezza 185 cm, larghezza 160 cm | Dimensioni manica: 60cm www.snugsnug.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio):
Altezza: 28 Cm Larghezza: 26 Cm Profondita': 12 Cm Peso: 0.8 Kg Codice Prodotto (EAN): 4899888102731
","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. Maggiori Informazioni ",0.8,1,"Taxable Goods","Catalog, Search",22.9,,,,OUTLET-Coperta-super-soffice-Kangoo-Snug-Snug-con-maniche-per-adulti-|-Decorazioni-originali-(Senza-imballaggio)-Commando,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Commando","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Commando,Commando,","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali",http://dropshipping.bigbuy.eu/imgs/J2000065_91719.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000065_91719.jpg,,,,,,"2016-02-11 15:49:04",,,,,,,,,,,,,,,,,"GTIN=4899888102731",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000065_91715.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91718.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91717.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91716.jpg","GTIN=4899888102731",,,,,,,,,,
+BB-J2000240,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Galaktic","Acquista
la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. La coperta Snug Snug con le maniche ti riscalderà senza aver bisogno del riscaldamento per tutto il giorno. Risparmierai i soldi e starai comodo grazie alla coperta extra morbida Kangoo Snug Snug con le maniche , poiché potrai fare tutto ciò che vuoi mentre sei coperto.La coperta Snug Snug con le maniche è il regalo perfetto per qualsiasi occasione. Nessuno si aspetta un regalo così straordinario! Questa coperta con le maniche è l'ideale per quei momenti in cui fa freddo fuori e vuoi solo stare a casa a leggere, usare il computer o semplicemente a rilassarti sul divano. Fino ad ora, non è era facile raggomitolarsi sul divano e voler fare qualcosa, perché dovevi tirare fuori le braccia, ma faceva freddo. Tutto questo è finito grazie alla coperta Kangoo Snug Snug con le maniche!
Caratteristiche:
Fatto di pile ultra soffice Lavabile in lavatrice Dimensioni: lunghezza 185 cm, larghezza 160 cm | Dimensioni manica: 60cm www.snugsnug.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio):
Altezza: 28 Cm Larghezza: 26 Cm Profondita': 12 Cm Peso: 0.8 Kg Codice Prodotto (EAN): 4899888102731
","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. Maggiori Informazioni ",0.8,1,"Taxable Goods","Catalog, Search",22.9,,,,OUTLET-Coperta-super-soffice-Kangoo-Snug-Snug-con-maniche-per-adulti-|-Decorazioni-originali-(Senza-imballaggio)-Galaktic,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Galaktic","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Galaktic,Galaktic,","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali",http://dropshipping.bigbuy.eu/imgs/J2000065_91718.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000065_91718.jpg,,,,,,"2016-02-11 15:49:04",,,,,,,,,,,,,,,,,"GTIN=4899888102731",14,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000065_91715.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91719.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91717.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91716.jpg","GTIN=4899888102731",,,,,,,,,,
+BB-J2000329,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Lovely Blu","Acquista
la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. La coperta Snug Snug con le maniche ti riscalderà senza aver bisogno del riscaldamento per tutto il giorno. Risparmierai i soldi e starai comodo grazie alla coperta extra morbida Kangoo Snug Snug con le maniche , poiché potrai fare tutto ciò che vuoi mentre sei coperto.La coperta Snug Snug con le maniche è il regalo perfetto per qualsiasi occasione. Nessuno si aspetta un regalo così straordinario! Questa coperta con le maniche è l'ideale per quei momenti in cui fa freddo fuori e vuoi solo stare a casa a leggere, usare il computer o semplicemente a rilassarti sul divano. Fino ad ora, non è era facile raggomitolarsi sul divano e voler fare qualcosa, perché dovevi tirare fuori le braccia, ma faceva freddo. Tutto questo è finito grazie alla coperta Kangoo Snug Snug con le maniche!
Caratteristiche:
Fatto di pile ultra soffice Lavabile in lavatrice Dimensioni: lunghezza 185 cm, larghezza 160 cm | Dimensioni manica: 60cm www.snugsnug.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio):
Altezza: 28 Cm Larghezza: 26 Cm Profondita': 12 Cm Peso: 0.8 Kg Codice Prodotto (EAN): 4899888102731
","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali. Maggiori Informazioni ",0.8,1,"Taxable Goods","Catalog, Search",22.9,,,,OUTLET-Coperta-super-soffice-Kangoo-Snug-Snug-con-maniche-per-adulti-|-Decorazioni-originali-(Senza-imballaggio)-Lovely Blu,"OUTLET Coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali (Senza imballaggio) Lovely Blu","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Lovely Blu,Lovely Blu,","Acquista la coperta super soffice Kangoo Snug Snug con maniche per adulti | Decorazioni originali",http://dropshipping.bigbuy.eu/imgs/J2000065_91717.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000065_91717.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=4899888102731",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000065_91715.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91719.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91718.jpg,http://dropshipping.bigbuy.eu/imgs/J2000065_91716.jpg","GTIN=4899888102731",,,,,,,,,,
+BB-J2000085,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio) Rosa S","Acquista
Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo. Scopri il modo più facile per mantenerti caldo a casa quando c'è freddo! Devi solo riscaldare questi stivali rilassanti nel microonde per godere immediatamente del calore e del rilassamento ai tuoi piedi. Sono fatti di morbido tessuto polare e di un rivestimento in semi di lavanda che emana un profumo straordinario quando riscaldato. Goditi semplicemente il profumo rilassante! I Warm Hug Feet Stivali Riscaldabili al Microonde mantengono il calore per lungo tempo grazie ai semi di lavanda contenuti all'interno. Per pulire questi stivali riscaldabili, passaci sopra un panno umido. Non riscaldarmi mai per più di 2 minuti e non usarli se sono troppo caldi. Disponibili in rosa e blu.Taglie (equivalenze approssimative)
S (35 a 38) M (38 a 41) L (41 a 43) Rispettare sempre i limiti nel microonde
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio):
Altezza: 27 Cm Larghezza: 10.5 Cm Profondita': 29 Cm Peso: 0.67 Kg Codice Prodotto (EAN): 4899881028786
","Acquista Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo. Maggiori Informazioni ",0.67,1,"Taxable Goods","Catalog, Search",33.5,,,,OUTLET-Warm-Hug-Feet-Stivali-Riscaldabili-al-Microonde-(Senza-imballaggio)-Rosa-S,"OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio) Rosa S","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Rosa,Rosa,Taglia S,S,","Acquista Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-00.jpg,,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-00.jpg,,,,,,"2016-02-02 10:45:18",,,,,,,,,,,,,,,,,"GTIN=4899881028786",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-03.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-01.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-02.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-04.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-05.jpg","GTIN=4899881028786",,,,,,,,,,
+BB-J2000086,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio) Viola M","Acquista
Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo. Scopri il modo più facile per mantenerti caldo a casa quando c'è freddo! Devi solo riscaldare questi stivali rilassanti nel microonde per godere immediatamente del calore e del rilassamento ai tuoi piedi. Sono fatti di morbido tessuto polare e di un rivestimento in semi di lavanda che emana un profumo straordinario quando riscaldato. Goditi semplicemente il profumo rilassante! I Warm Hug Feet Stivali Riscaldabili al Microonde mantengono il calore per lungo tempo grazie ai semi di lavanda contenuti all'interno. Per pulire questi stivali riscaldabili, passaci sopra un panno umido. Non riscaldarmi mai per più di 2 minuti e non usarli se sono troppo caldi. Disponibili in rosa e blu.Taglie (equivalenze approssimative)
S (35 a 38) M (38 a 41) L (41 a 43) Rispettare sempre i limiti nel microonde
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio):
Altezza: 27 Cm Larghezza: 10.5 Cm Profondita': 29 Cm Peso: 0.67 Kg Codice Prodotto (EAN): 4899881028786
","Acquista Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo. Maggiori Informazioni ",0.67,1,"Taxable Goods","Catalog, Search",33.5,,,,OUTLET-Warm-Hug-Feet-Stivali-Riscaldabili-al-Microonde-(Senza-imballaggio)-Viola-M,"OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio) Viola M","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Viola,Viola,Taglia M,M,","Acquista Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-03.jpg,,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-03.jpg,,,,,,"2016-02-02 10:45:18",,,,,,,,,,,,,,,,,"GTIN=4899881028786",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-00.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-01.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-02.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-04.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-05.jpg","GTIN=4899881028786",,,,,,,,,,
+BB-J2000188,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio) Viola L","Acquista
Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo. Scopri il modo più facile per mantenerti caldo a casa quando c'è freddo! Devi solo riscaldare questi stivali rilassanti nel microonde per godere immediatamente del calore e del rilassamento ai tuoi piedi. Sono fatti di morbido tessuto polare e di un rivestimento in semi di lavanda che emana un profumo straordinario quando riscaldato. Goditi semplicemente il profumo rilassante! I Warm Hug Feet Stivali Riscaldabili al Microonde mantengono il calore per lungo tempo grazie ai semi di lavanda contenuti all'interno. Per pulire questi stivali riscaldabili, passaci sopra un panno umido. Non riscaldarmi mai per più di 2 minuti e non usarli se sono troppo caldi. Disponibili in rosa e blu.Taglie (equivalenze approssimative)
S (35 a 38) M (38 a 41) L (41 a 43) Rispettare sempre i limiti nel microonde
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio):
Altezza: 27 Cm Larghezza: 10.5 Cm Profondita': 29 Cm Peso: 0.67 Kg Codice Prodotto (EAN): 4899881028786
","Acquista Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo. Maggiori Informazioni ",0.67,1,"Taxable Goods","Catalog, Search",33.5,,,,OUTLET-Warm-Hug-Feet-Stivali-Riscaldabili-al-Microonde-(Senza-imballaggio)-Viola-L,"OUTLET Warm Hug Feet Stivali Riscaldabili al Microonde (Senza imballaggio) Viola L","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Viola,Viola,Taglia L,L,","Acquista Warm Hug Feet Stivali Riscaldabili al Microonde al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-01.jpg,,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-01.jpg,,,,,,"2016-02-02 10:45:18",,,,,,,,,,,,,,,,,"GTIN=4899881028786",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-00.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-03.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-02.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-04.jpg,http://dropshipping.bigbuy.eu/imgs/warm-hugh-feet-boots-05.jpg","GTIN=4899881028786",,,,,,,,,,
+BB-J2000123,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Macchina per Gelato Princess 282602 (Senza imballaggio)","Avviso
per gli amanti del gelato: ecco la nuovissima macchina per gelato Princess 282602 . Una gelatiera diversa dalle altre, che ti permetterà di preparare in un batter d'occhio gelati dolci o salati per i grandi e i più piccini! Al naturale o con salsa di frutta, pepite o qualsiasi altra guarnizione...le tue papille ti ringrazieranno! Indispensabile per un'estate al fresco!Basta mettere in freezer, per 12 ore circa, il recipiente rimovibile a forma di secchiello con manico integrato di 17,5 x 14,5 cm (diametro x altezza) ed è fatto! Il gelato 100% naturale è servito!
Caratteristiche:
Potenza: 5 W Frequenza: 50 Hz Tensione: 220-240 V Pulsante On/Off Gommini antiscivolo Scomparto per cavo di alimentazione Mescolatore rimovibile Facile da pulire Apertura di riempimento (dimensioni approssimative: 5 x 5,5 cm) Dimensioni approssimative: 22 x 25 x 22 cm NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Macchina per Gelato Princess 282602 (Senza imballaggio):
Altezza: 31 Cm Larghezza: 23.4 Cm Profondita': 23.5 Cm Peso: 3.26 Kg Codice Prodotto (EAN): 8712836304895
","Avviso per gli amanti del gelato: ecco la nuovissima macchina per gelato Princess 282602. Maggiori Informazioni ",3.26,1,"Taxable Goods","Catalog, Search",110.88,,,,OUTLET-Macchina-per-Gelato-Princess-282602-(Senza-imballaggio),"OUTLET Macchina per Gelato Princess 282602 (Senza imballaggio)","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,","Avviso per gli amanti del gelato: ecco la nuovissima macchina per gelato Princess 282602",http://dropshipping.bigbuy.eu/imgs/J2000123_88610.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000123_88610.jpg,,,,,,"2016-08-09 01:54:36",,,,,,,,,,,,,,,,,"GTIN=8712836304895",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000123_88612.jpg,http://dropshipping.bigbuy.eu/imgs/J2000123_88611.jpg","GTIN=8712836304895",,,,,,,,,,
+BB-J2000176,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Celeste","Acquistare
Coperta Eden Deluxe 160 x 240 al miglior prezzo. Questa fantastica coperta Eden Deluxe è perfetta per il tuo letto. La coperta Eden Dexuxe misura 160 x 240 cm. Questa coperta è veramente soffice e confortevole. La coperta Eden Deluxe è l'ideale per godere di un caldo inverno. 100% poliestere. Con comoda maniglia da trasporto.NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio):
Altezza: 43 Cm Larghezza: 14 Cm Profondita': 51.5 Cm Peso: 2.183 Kg Codice Prodotto (EAN): 8436045510426
","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo. Maggiori Informazioni ",2.183,1,"Taxable Goods","Catalog, Search",57.9,,,,OUTLET-Coperta-Eden-Deluxe-160-x-240-(Senza-imballaggio)-Celeste,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Celeste","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Design Celeste,Celeste,","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,,,,,,"2016-02-17 15:45:14",,,,,,,,,,,,,,,,,"GTIN=8436045510426",5,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg","GTIN=8436045510426",,,,,,,,,,
+BB-J2000178,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Rosso","Acquistare
Coperta Eden Deluxe 160 x 240 al miglior prezzo. Questa fantastica coperta Eden Deluxe è perfetta per il tuo letto. La coperta Eden Dexuxe misura 160 x 240 cm. Questa coperta è veramente soffice e confortevole. La coperta Eden Deluxe è l'ideale per godere di un caldo inverno. 100% poliestere. Con comoda maniglia da trasporto.NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio):
Altezza: 43 Cm Larghezza: 14 Cm Profondita': 51.5 Cm Peso: 2.183 Kg Codice Prodotto (EAN): 8436045510426
","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo. Maggiori Informazioni ",2.183,1,"Taxable Goods","Catalog, Search",57.9,,,,OUTLET-Coperta-Eden-Deluxe-160-x-240-(Senza-imballaggio)-Rosso,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Rosso","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Design Rosso,Rosso,","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,,,,,,"2016-02-17 15:45:14",,,,,,,,,,,,,,,,,"GTIN=8436045510426",3,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg","GTIN=8436045510426",,,,,,,,,,
+BB-J2000179,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Cioccolato","Acquistare
Coperta Eden Deluxe 160 x 240 al miglior prezzo. Questa fantastica coperta Eden Deluxe è perfetta per il tuo letto. La coperta Eden Dexuxe misura 160 x 240 cm. Questa coperta è veramente soffice e confortevole. La coperta Eden Deluxe è l'ideale per godere di un caldo inverno. 100% poliestere. Con comoda maniglia da trasporto.NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio):
Altezza: 43 Cm Larghezza: 14 Cm Profondita': 51.5 Cm Peso: 2.183 Kg Codice Prodotto (EAN): 8436045510426
","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo. Maggiori Informazioni ",2.183,1,"Taxable Goods","Catalog, Search",57.9,,,,OUTLET-Coperta-Eden-Deluxe-160-x-240-(Senza-imballaggio)-Cioccolato,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Cioccolato","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Design Cioccolato,Cioccolato,","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,,,,,,"2016-02-17 15:45:14",,,,,,,,,,,,,,,,,"GTIN=8436045510426",5,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg","GTIN=8436045510426",,,,,,,,,,
+BB-J2000180,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Crudo","Acquistare
Coperta Eden Deluxe 160 x 240 al miglior prezzo. Questa fantastica coperta Eden Deluxe è perfetta per il tuo letto. La coperta Eden Dexuxe misura 160 x 240 cm. Questa coperta è veramente soffice e confortevole. La coperta Eden Deluxe è l'ideale per godere di un caldo inverno. 100% poliestere. Con comoda maniglia da trasporto.NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio):
Altezza: 43 Cm Larghezza: 14 Cm Profondita': 51.5 Cm Peso: 2.183 Kg Codice Prodotto (EAN): 8436045510426
","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo. Maggiori Informazioni ",2.183,1,"Taxable Goods","Catalog, Search",57.9,,,,OUTLET-Coperta-Eden-Deluxe-160-x-240-(Senza-imballaggio)-Crudo,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Crudo","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Design Crudo,Crudo,","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,,,,,,"2016-02-17 15:45:14",,,,,,,,,,,,,,,,,"GTIN=8436045510426",9,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg","GTIN=8436045510426",,,,,,,,,,
+BB-J2000181,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Fragola","Acquistare
Coperta Eden Deluxe 160 x 240 al miglior prezzo. Questa fantastica coperta Eden Deluxe è perfetta per il tuo letto. La coperta Eden Dexuxe misura 160 x 240 cm. Questa coperta è veramente soffice e confortevole. La coperta Eden Deluxe è l'ideale per godere di un caldo inverno. 100% poliestere. Con comoda maniglia da trasporto.NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio):
Altezza: 43 Cm Larghezza: 14 Cm Profondita': 51.5 Cm Peso: 2.183 Kg Codice Prodotto (EAN): 8436045510426
","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo. Maggiori Informazioni ",2.183,1,"Taxable Goods","Catalog, Search",57.9,,,,OUTLET-Coperta-Eden-Deluxe-160-x-240-(Senza-imballaggio)-Fragola,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Fragola","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Design Fragola,Fragola,","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,,,,,,"2016-02-17 15:45:14",,,,,,,,,,,,,,,,,"GTIN=8436045510426",8,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg","GTIN=8436045510426",,,,,,,,,,
+BB-J2000182,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Cardinale","Acquistare
Coperta Eden Deluxe 160 x 240 al miglior prezzo. Questa fantastica coperta Eden Deluxe è perfetta per il tuo letto. La coperta Eden Dexuxe misura 160 x 240 cm. Questa coperta è veramente soffice e confortevole. La coperta Eden Deluxe è l'ideale per godere di un caldo inverno. 100% poliestere. Con comoda maniglia da trasporto.NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio):
Altezza: 43 Cm Larghezza: 14 Cm Profondita': 51.5 Cm Peso: 2.183 Kg Codice Prodotto (EAN): 8436045510426
","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo. Maggiori Informazioni ",2.183,1,"Taxable Goods","Catalog, Search",57.9,,,,OUTLET-Coperta-Eden-Deluxe-160-x-240-(Senza-imballaggio)-Cardinale,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Cardinale","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Design Cardinale,Cardinale,","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg,,,,,,"2016-02-17 15:45:14",,,,,,,,,,,,,,,,,"GTIN=8436045510426",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg","GTIN=8436045510426",,,,,,,,,,
+BB-J2000247,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Turchese","Acquistare
Coperta Eden Deluxe 160 x 240 al miglior prezzo. Questa fantastica coperta Eden Deluxe è perfetta per il tuo letto. La coperta Eden Dexuxe misura 160 x 240 cm. Questa coperta è veramente soffice e confortevole. La coperta Eden Deluxe è l'ideale per godere di un caldo inverno. 100% poliestere. Con comoda maniglia da trasporto.NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio):
Altezza: 43 Cm Larghezza: 14 Cm Profondita': 51.5 Cm Peso: 2.183 Kg Codice Prodotto (EAN): 8436045510426
","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo. Maggiori Informazioni ",2.183,1,"Taxable Goods","Catalog, Search",57.9,,,,OUTLET-Coperta-Eden-Deluxe-160-x-240-(Senza-imballaggio)-Turchese,"OUTLET Coperta Eden Deluxe 160 x 240 (Senza imballaggio) Turchese","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Design Turchese,Turchese,","Acquistare Coperta Eden Deluxe 160 x 240 al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000175_88982.jpg,,,,,,"2016-02-17 15:45:14",,,,,,,,,,,,,,,,,"GTIN=8436045510426",7,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000175_88981.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88987.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88986.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88985.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88984.jpg,http://dropshipping.bigbuy.eu/imgs/J2000175_88983.jpg","GTIN=8436045510426",,,,,,,,,,
+BB-J2000177,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Macchina RC Ferrari 599 GTO (Senza imballaggio)","Hai
visto la macchina RC Ferrari 599 GTO ? Puoi facilmente usare questo divertente giocattolo autorizzato Ferrari grazie al suo telecomando. La macchina funziona a batterie 5 x AA (non incluse) e il telecomando a batteria 1 x 6F22 9V (non inclusa). Giocattolo adatto per bambini di età superiore ai 6 anni. Funzioni della macchina radiocontrollata Ferrari:Avanti e indietro Gira a sinistra e a destra Fari e Fanali posteriori NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Macchina RC Ferrari 599 GTO (Senza imballaggio):
Altezza: 17.5 Cm Larghezza: 43.5 Cm Profondita': 22.7 Cm Peso: 1.244 Kg Codice Prodotto (EAN): 8718158011299
","Hai visto la macchina RC Ferrari 599 GTO? Puoi facilmente usare questo divertente giocattolo autorizzato Ferrari grazie al suo telecomando. Maggiori Informazioni ",1.244,1,"Taxable Goods","Catalog, Search",119,,,,OUTLET-Macchina-RC-Ferrari-599-GTO--(Senza-imballaggio),"OUTLET Macchina RC Ferrari 599 GTO (Senza imballaggio)","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,","Hai visto la macchina RC Ferrari 599 GTO? Puoi facilmente usare questo divertente giocattolo autorizzato Ferrari grazie al suo telecomando",http://dropshipping.bigbuy.eu/imgs/J2000177_89020.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000177_89020.jpg,,,,,,"2016-07-22 06:19:54",,,,,,,,,,,,,,,,,"GTIN=8718158011299",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000177_89022.jpg,http://dropshipping.bigbuy.eu/imgs/J2000177_89021.jpg","GTIN=8718158011299",,,,,,,,,,
+BB-J2000255,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio) Beige S","Metti
in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod. Sostiene il seno e offre una grande capacità di sostegno. Prodotta il un tessuto delicato, flessibile e traspirante che si adatta perfettamente al tuo corpo e offre una copertura completa (seno, fianchi e glutei). Equivalenza di taglie: S: 36-38, M: 38-40, L: 40-42.www.boobyandtummy.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio):
Altezza: 17 Cm Larghezza: 13.5 Cm Profondita': 7 Cm Peso: 0.14 Kg Codice Prodotto (EAN): 4899888101352
","Metti in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod. Maggiori Informazioni ",0.14,1,"Taxable Goods","Catalog, Search",23.1,,,,OUTLET-Canottiera-Modellante-con-Reggiseno-Booby-&-Tummy--(Senza-imballaggio)-Beige-S,"OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio) Beige S","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Beige,Beige,Taglia S,S,","Metti in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod",http://dropshipping.bigbuy.eu/imgs/J2000254_89903.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000254_89903.jpg,,,,,,"2016-02-12 08:36:52",,,,,,,,,,,,,,,,,"GTIN=4899888101352",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000254_89906.jpg,http://dropshipping.bigbuy.eu/imgs/J2000254_89905.jpg,http://dropshipping.bigbuy.eu/imgs/J2000254_89904.jpg","GTIN=4899888101352",,,,,,,,,,
+BB-J2000285,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio) Beige L","Metti
in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod. Sostiene il seno e offre una grande capacità di sostegno. Prodotta il un tessuto delicato, flessibile e traspirante che si adatta perfettamente al tuo corpo e offre una copertura completa (seno, fianchi e glutei). Equivalenza di taglie: S: 36-38, M: 38-40, L: 40-42.www.boobyandtummy.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio):
Altezza: 17 Cm Larghezza: 13.5 Cm Profondita': 7 Cm Peso: 0.14 Kg Codice Prodotto (EAN): 4899888101352
","Metti in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod. Maggiori Informazioni ",0.14,1,"Taxable Goods","Catalog, Search",23.1,,,,OUTLET-Canottiera-Modellante-con-Reggiseno-Booby-&-Tummy--(Senza-imballaggio)-Beige-L,"OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio) Beige L","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Beige,Beige,Taglia L,L,","Metti in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod",http://dropshipping.bigbuy.eu/imgs/J2000254_89906.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000254_89906.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=4899888101352",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000254_89903.jpg,http://dropshipping.bigbuy.eu/imgs/J2000254_89905.jpg,http://dropshipping.bigbuy.eu/imgs/J2000254_89904.jpg","GTIN=4899888101352",,,,,,,,,,
+BB-J2000279,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio) Beige M","Metti
in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod. Sostiene il seno e offre una grande capacità di sostegno. Prodotta il un tessuto delicato, flessibile e traspirante che si adatta perfettamente al tuo corpo e offre una copertura completa (seno, fianchi e glutei). Equivalenza di taglie: S: 36-38, M: 38-40, L: 40-42.www.boobyandtummy.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio):
Altezza: 17 Cm Larghezza: 13.5 Cm Profondita': 7 Cm Peso: 0.14 Kg Codice Prodotto (EAN): 4899888101352
","Metti in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod. Maggiori Informazioni ",0.14,1,"Taxable Goods","Catalog, Search",23.1,,,,OUTLET-Canottiera-Modellante-con-Reggiseno-Booby-&-Tummy--(Senza-imballaggio)-Beige-M,"OUTLET Canottiera Modellante con Reggiseno Booby & Tummy (Senza imballaggio) Beige M","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Colore Beige,Beige,Taglia M,M,","Metti in luce un corpo scultoreo con la Canottiera Modellante con Reggiseno Booby & Tummy! Discreta, comod",http://dropshipping.bigbuy.eu/imgs/J2000254_89905.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000254_89905.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=4899888101352",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000254_89903.jpg,http://dropshipping.bigbuy.eu/imgs/J2000254_89906.jpg,http://dropshipping.bigbuy.eu/imgs/J2000254_89904.jpg","GTIN=4899888101352",,,,,,,,,,
+BB-J2000264,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Scopa elettrica triangolare senza fili 360 Sweep (Senza imballaggio)","La scopa
elettrica triangolare 360 Sweep è perfetta per pulire in modo facile, rapido ed efficace. Grazie alla sua tecnologia innovativa, le setole ruotano automaticamente per rimuovere a fondo tutto lo sporco. Inoltre, questa scopa elettrica è leggera e facile da usare, il che la rende molto comoda e pratica. La scopa elettrica Sweep ruota di 360º e raggiunge facilmente qualsiasi angolo di casa.Prova la nuova scopa elettrica triangolare 360 Sweep e scopri un modo migliore per pulire!
Caratteristiche della Scopa elettrica triangolare 360 Sweep:
Scopa elettrica senza fili Setole rotanti Bastone in alluminio removibile (c.ca 115cm) Base piatta triangolare (3 lati identici: circa 33cm di lunghezza x 3cm di altezza) Batteria ricaricabile 7.2V Caricabatteria (230V, 50Hz) Durata batteria: Approx. 30 min Scopartimento interno raccogli-polvere Leggera, facile da usare, svuotare e pulire Estremamente silenziosa www.360sweep.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Scopa elettrica triangolare senza fili 360 Sweep (Senza imballaggio):
Altezza: 32 Cm Larghezza: 39.5 Cm Profondita': 9.5 Cm Peso: 1.625 Kg Codice Prodotto (EAN): 4899888102458
","La scopa elettrica triangolare 360 Sweep è perfetta per pulire in modo facile, rapido ed efficace. Maggiori Informazioni ",1.625,1,"Taxable Goods","Catalog, Search",89.9,,,,OUTLET-Scopa-elettrica-triangolare-senza-fili-360-Sweep-(Senza-imballaggio),"OUTLET Scopa elettrica triangolare senza fili 360 Sweep (Senza imballaggio)","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,","La scopa elettrica triangolare 360 Sweep è perfetta per pulire in modo facile, rapido ed efficace",http://dropshipping.bigbuy.eu/imgs/J2000264_89527.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000264_89527.jpg,,,,,,"2016-09-01 06:15:11",,,,,,,,,,,,,,,,,"GTIN=4899888102458",2,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000264_89533.jpg,http://dropshipping.bigbuy.eu/imgs/J2000264_89532.jpg,http://dropshipping.bigbuy.eu/imgs/J2000264_89531.jpg,http://dropshipping.bigbuy.eu/imgs/J2000264_89530.jpg,http://dropshipping.bigbuy.eu/imgs/J2000264_89529.jpg,http://dropshipping.bigbuy.eu/imgs/J2000264_89528.jpg","GTIN=4899888102458",,,,,,,,,,
+BB-J2000291,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Sabbia Kinetic per Bambini Playz Kidz (Senza imballaggio)","Porta
la spiaggia a casa con la sabbia kinetic per bambini Playz Kidz ! Il regalo perfetto per sorprendere i tuoi piccoli, che si divertiranno a costruire ogni tipo di castello e forma di sabbia. Questo divertente ed originale gioco sviluppa i talenti artistici e la creatività dei bambini. Comprende circa 0,5 kg di sabbia kinetic e 3 accessori in plastica: un rullo, una forcella e una spatola. Non tossico. Non macchia i vestiti o si attacca alle mani. Età consigliata: 3+ anniwww.playzkidz.com
NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Sabbia Kinetic per Bambini Playz Kidz (Senza imballaggio):
Altezza: 17 Cm Larghezza: 14 Cm Profondita': 14 Cm Peso: 0.65 Kg Codice Prodotto (EAN): 4899888108085
","Porta la spiaggia a casa con la sabbia kinetic per bambini Playz Kidz! Il regalo perfetto per sorprendere i tuoi piccoli, che si divertiranno a costruire ogni tipo di castello e forma di sabbia. Maggiori Informazioni ",0.65,1,"Taxable Goods","Catalog, Search",11.9,,,,OUTLET-Sabbia-Kinetic-per-Bambini-Playz-Kidz--(Senza-imballaggio),"OUTLET Sabbia Kinetic per Bambini Playz Kidz (Senza imballaggio)","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,","Porta la spiaggia a casa con la sabbia kinetic per bambini Playz Kidz! Il regalo perfetto per sorprendere i tuoi piccoli, che si divertiranno a costruire ogni tipo di castello e forma di sabbia",http://dropshipping.bigbuy.eu/imgs/J2000291_90163.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000291_90163.jpg,,,,,,"2016-08-09 01:48:11",,,,,,,,,,,,,,,,,"GTIN=4899888108085",778,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000291_90165.jpg,http://dropshipping.bigbuy.eu/imgs/J2000291_90164.jpg","GTIN=4899888108085",,,,,,,,,,
+BB-J2000350,,Default,simple,"Default Category/Outlet Offerte,Default Category/Outlet Offerte/Senza imballaggio",base,"OUTLET Reggiseno Crochet Bra (3 Pezzi) (Senza imballaggio) S","Acquista
il Reggiseno Crochet (3 pezzi) al miglior prezzo. Sentiti fantastica e sexy per tutto il giorno! Dimentica i fili, fascette o spalline. Il Crochet Bra utilizza la tecnologia Woven Everlast per un comfort massimo. Questi reggiseni sono stati elaborati senza tutti quegli elementi in modo da essere usato comodamente dimenticando che lo stai indossando. Questo reggiseno si adatta al tuo seno, sollevandolo e sostenendolo. Il Crochet Bra si adatta perfettamente al tuo corpo e forma , senza lasciare segni o pieghe. In più è morbido, flessibile e molto comodo, e si adatta ad ogni coppa, sollevando il tio seno in modo significativo.www.crochetbra.com Caratteristiche:
Lavabile in lavatrice Bretelle comode Fatto al 96% di nylon e 4% di spandex Adattabile alla forma del tuo seno Solleva il tuo seno La confezione include 3 reggiseni (1 beige, 1 nero and 1 bianco) Equivalenza taglie appross.: S: 80/85 - M: 90/95 - L: 100/105 NOTA: Questo prodotto presenta lievi danni estetici, quali graffi o sfregature, che non influiscono sul suo funzionamento. Spedito in confezione standard.
Dimenzioni per OUTLET Reggiseno Crochet Bra (3 Pezzi) (Senza imballaggio):
Altezza: 4.5 Cm Larghezza: 15.5 Cm Profondita': 27 Cm Peso: 0.269 Kg Codice Prodotto (EAN): 4899888101345
","Acquista il Reggiseno Crochet (3 pezzi) al miglior prezzo. Maggiori Informazioni ",0.269,1,"Taxable Goods","Catalog, Search",34.9,,,,OUTLET-Reggiseno-Crochet-Bra-(3-Pezzi)-(Senza-imballaggio)-S,"OUTLET Reggiseno Crochet Bra (3 Pezzi) (Senza imballaggio) S","Outlet Offerte,Outlet,Offerte,Senza imballaggio,Senza,imballaggio,Taglia S,S,","Acquista il Reggiseno Crochet (3 pezzi) al miglior prezzo",http://dropshipping.bigbuy.eu/imgs/J2000349_93435.jpg,,http://dropshipping.bigbuy.eu/imgs/J2000349_93435.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=4899888101345",8,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/J2000349_93438.jpg,http://dropshipping.bigbuy.eu/imgs/J2000349_93437.jpg,http://dropshipping.bigbuy.eu/imgs/J2000349_93436.jpg","GTIN=4899888101345",,,,,,,,,,
diff --git a/dev/tests/acceptance/tests/_data/BB-ProductsWorking.csv b/dev/tests/acceptance/tests/_data/BB-ProductsWorking.csv
new file mode 100644
index 0000000000000..0ea052a043526
--- /dev/null
+++ b/dev/tests/acceptance/tests/_data/BB-ProductsWorking.csv
@@ -0,0 +1,29 @@
+sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,configurable_variations,configurable_variation_labels,associated_skus
+BB-D2010129,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Sistemi di Climatizzazione,Default Category/Casa Giardino/Sistemi di Climatizzazione/Aria condizionata e ventilatori",base,"Ventilatore Portatile Spray FunFan Nero","Se
sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan . Si tratta di una soluzione pratica per mantenersi al fresco in una moltitudine di situazioni, come escursioni, gite in spiaggia, mentre si fa sport, ecc. Inoltre, grazie alle sue dimensioni ridotte (dimensioni: circa 9 x 26 x 6,5 cm) e peso ridotto (circa 130 g), lo puoi portare ovunque!www.myfunfan.com Questo ventilatore portatile originale ha un pulsante per attivare le eliche in PVC malleabili e una leva che spruzza l'acqua. Cosa c'è di più, puoi aggiungere il ghiaccio per aumentare la sensazione di freddo! Include 1 cacciavite a croce per inserire le batterie. Realizzato in PVC. Funzionamento a batterie (2 x AA, non incluse). Dimenzioni per Ventilatore Portatile Spray FunFan :
Altezza: 10 Cm Larghezza: 28 Cm Profondita': 7.5 Cm Peso: 0.185 Kg Codice Prodotto (EAN): 4899888101772
","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan. Maggiori Informazioni ",0.185,1,"Taxable Goods","Catalog, Search",19.9,,,,Ventilatore-Portatile-Spray-FunFan-Nero,"Ventilatore Portatile Spray FunFan Nero","Casa Giardino,Casa,Giardino,Sistemi di Climatizzazione,Sistemi,Climatizzazione,Aria condizionata e ventilatori,Aria,condizionata,ventilatori,Colore Nero,Nero,","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan",http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,,http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,,,,,,"2016-01-12 09:56:53",,,,,,,,,,,,,,,,,"GTIN=4899888101772",41,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78889.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78888.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78886.jpg","GTIN=4899888101772",,,,,,,,,,
+BB-D2010130,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Sistemi di Climatizzazione,Default Category/Casa Giardino/Sistemi di Climatizzazione/Aria condizionata e ventilatori",base,"Ventilatore Portatile Spray FunFan Bianco","Se
sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan . Si tratta di una soluzione pratica per mantenersi al fresco in una moltitudine di situazioni, come escursioni, gite in spiaggia, mentre si fa sport, ecc. Inoltre, grazie alle sue dimensioni ridotte (dimensioni: circa 9 x 26 x 6,5 cm) e peso ridotto (circa 130 g), lo puoi portare ovunque!www.myfunfan.com Questo ventilatore portatile originale ha un pulsante per attivare le eliche in PVC malleabili e una leva che spruzza l'acqua. Cosa c'è di più, puoi aggiungere il ghiaccio per aumentare la sensazione di freddo! Include 1 cacciavite a croce per inserire le batterie. Realizzato in PVC. Funzionamento a batterie (2 x AA, non incluse). Dimenzioni per Ventilatore Portatile Spray FunFan :
Altezza: 10 Cm Larghezza: 28 Cm Profondita': 7.5 Cm Peso: 0.185 Kg Codice Prodotto (EAN): 4899888107965
","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan. Maggiori Informazioni ",0.185,1,"Taxable Goods","Catalog, Search",19.9,,,,Ventilatore-Portatile-Spray-FunFan-Bianco,"Ventilatore Portatile Spray FunFan Bianco","Casa Giardino,Casa,Giardino,Sistemi di Climatizzazione,Sistemi,Climatizzazione,Aria condizionata e ventilatori,Aria,condizionata,ventilatori,Colore Bianco,Bianco,","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan",http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,,http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,,,,,,"2016-01-12 09:56:53",,,,,,,,,,,,,,,,,"GTIN=4899888107965",741,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78889.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78888.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78886.jpg","GTIN=4899888107965",,,,,,,,,,
+BB-D2010131,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Sistemi di Climatizzazione,Default Category/Casa Giardino/Sistemi di Climatizzazione/Aria condizionata e ventilatori",base,"Ventilatore Portatile Spray FunFan Rosso","Se
sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan . Si tratta di una soluzione pratica per mantenersi al fresco in una moltitudine di situazioni, come escursioni, gite in spiaggia, mentre si fa sport, ecc. Inoltre, grazie alle sue dimensioni ridotte (dimensioni: circa 9 x 26 x 6,5 cm) e peso ridotto (circa 130 g), lo puoi portare ovunque!www.myfunfan.com Questo ventilatore portatile originale ha un pulsante per attivare le eliche in PVC malleabili e una leva che spruzza l'acqua. Cosa c'è di più, puoi aggiungere il ghiaccio per aumentare la sensazione di freddo! Include 1 cacciavite a croce per inserire le batterie. Realizzato in PVC. Funzionamento a batterie (2 x AA, non incluse). Dimenzioni per Ventilatore Portatile Spray FunFan :
Altezza: 10 Cm Larghezza: 28 Cm Profondita': 7.5 Cm Peso: 0.185 Kg Codice Prodotto (EAN): 4899888107972
","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan. Maggiori Informazioni ",0.185,1,"Taxable Goods","Catalog, Search",19.9,,,,Ventilatore-Portatile-Spray-FunFan-Rosso,"Ventilatore Portatile Spray FunFan Rosso","Casa Giardino,Casa,Giardino,Sistemi di Climatizzazione,Sistemi,Climatizzazione,Aria condizionata e ventilatori,Aria,condizionata,ventilatori,Colore Rosso,Rosso,","Se sei il tipo di persona che è sempre alla ricerca di prodotti innovativi per combattere il caldo estivo, non perderti il ventilatore portatile spray FunFan",http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,,http://dropshipping.bigbuy.eu/imgs/D2010128_78890.jpg,,,,,,"2016-01-12 09:56:53",,,,,,,,,,,,,,,,,"GTIN=4899888107972",570,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/D2010128_78887.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78898.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78889.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78888.jpg,http://dropshipping.bigbuy.eu/imgs/D2010128_78886.jpg","GTIN=4899888107972",,,,,,,,,,
+BB-H1000163,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Sedia Pieghevole Campart Travel CH0592 Blu Marino","Se
stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel ! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Ideale per il riposo e il relax. Può portare fino a 120 kg. Dimensioni: 66 x 70 / 120 x 87 / 115 cm circa. Semplice da trasportare ovunque, grazie al suo design funzionale ed elegante (dimensioni quando piegato: circa 66 x 110 x 10 cm). 7 posizioni regolabili e un poggiatesta incorporato. Struttura in alluminio e stoffa imbottita in poliestere. Altezza sedia: circa 50 cm. Dimenzioni per Sedia Pieghevole Campart Travel:
Altezza: 10 Cm Larghezza: 66 Cm Profondita': 110 Cm Peso: 5.3 Kg Codice Prodotto (EAN): 8713016005922
","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Maggiori Informazioni ",5.3,1,"Taxable Goods","Catalog, Search",129,,,,Sedia-Pieghevole-Campart-Travel-CH0592 Blu Marino,"Sedia Pieghevole Campart Travel CH0592 Blu Marino","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0592 Blu Marino,CH0592 Blu Marino,","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc",http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_00.jpg,,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_00.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005922",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_02.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_04.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_03.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_01.jpg","GTIN=8713016005922",,,,,,,,,,
+BB-H1000162,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Sedia Pieghevole Campart Travel CH0596 Grigio","Se
stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel ! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Ideale per il riposo e il relax. Può portare fino a 120 kg. Dimensioni: 66 x 70 / 120 x 87 / 115 cm circa. Semplice da trasportare ovunque, grazie al suo design funzionale ed elegante (dimensioni quando piegato: circa 66 x 110 x 10 cm). 7 posizioni regolabili e un poggiatesta incorporato. Struttura in alluminio e stoffa imbottita in poliestere. Altezza sedia: circa 50 cm. Dimenzioni per Sedia Pieghevole Campart Travel:
Altezza: 10 Cm Larghezza: 66 Cm Profondita': 110 Cm Peso: 5.3 Kg Codice Prodotto (EAN): 8713016005960
","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc. Maggiori Informazioni ",5.3,1,"Taxable Goods","Catalog, Search",129,,,,Sedia-Pieghevole-Campart-Travel-CH0596 Grigio,"Sedia Pieghevole Campart Travel CH0596 Grigio","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0596 Grigio,CH0596 Grigio,","Se stai cercando comfort e convenienza allo stesso tempo, probabilmente adorerai la sedia pieghevole Campart Travel! Questa sedia da campeggio imbottita è perfetta per i luoghi di campeggio, cortili, giardini, ecc",http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_02.jpg,,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_02.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005960",2,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_00.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_04.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_03.jpg,http://dropshipping.bigbuy.eu/imgs/silla_plegable_camping_01.jpg","GTIN=8713016005960",,,,,,,,,,
+BB-F1520329,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Poggiapiedi Pieghevole Campart Travel CH0593 Blu Marino","Approfitta
di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel ! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Possiede 2 ganci di circa 3 cm di diametro che possono essere facilmente attaccate alla barra inferiore delle sedie (utilizzabile solo per sedie con una barra inferiore di circa 2 cm di diametro). Struttura in alluminio. Tessuto: poliestere. Dimensioni: circa 51 x 47 x 96 cm (dimensioni quando ripiegato: circa 51 x 12 x 96 cm). Ideale per le sedie pieghevoli Campart Travel CH0592 e CH0596. Dimenzioni per Poggiapiedi Pieghevole Campart Travel:
Altezza: 13 Cm Larghezza: 53 Cm Profondita': 97 Cm Peso: 1.377 Kg Codice Prodotto (EAN): 8713016005939
","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Maggiori Informazioni ",1.377,1,"Taxable Goods","Catalog, Search",46.6,,,,Poggiapiedi-Pieghevole-Campart-Travel-CH0593 Blu Marino,"Poggiapiedi Pieghevole Campart Travel CH0593 Blu Marino","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0593 Blu Marino,CH0593 Blu Marino,","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc",http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_00.jpg,,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_00.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005939",1,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_01.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_02.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_08.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_07.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_06.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_05.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_04.jpg","GTIN=8713016005939",,,,,,,,,,
+BB-F1520328,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Decorazione, illuminazione e mobili",base,"Poggiapiedi Pieghevole Campart Travel CH0597 Grigio","Approfitta
di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel ! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Possiede 2 ganci di circa 3 cm di diametro che possono essere facilmente attaccate alla barra inferiore delle sedie (utilizzabile solo per sedie con una barra inferiore di circa 2 cm di diametro). Struttura in alluminio. Tessuto: poliestere. Dimensioni: circa 51 x 47 x 96 cm (dimensioni quando ripiegato: circa 51 x 12 x 96 cm). Ideale per le sedie pieghevoli Campart Travel CH0592 e CH0596. Dimenzioni per Poggiapiedi Pieghevole Campart Travel:
Altezza: 13 Cm Larghezza: 53 Cm Profondita': 97 Cm Peso: 1.377 Kg Codice Prodotto (EAN): 8713016005977
","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc. Maggiori Informazioni ",1.377,1,"Taxable Goods","Catalog, Search",46.6,,,,Poggiapiedi-Pieghevole-Campart-Travel-CH0597 Grigio,"Poggiapiedi Pieghevole Campart Travel CH0597 Grigio","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Decorazione, illuminazione e mobili,Decorazione,,illuminazione,mobili,Referenza e Colore CH0597 Grigio,CH0597 Grigio,","Approfitta di un'esperienza rilassante con l'aiuto del poggiapiedi pieghevole Campart Travel! Questo poggiapiedi imbottito è l'accessorio ideale per sedie da cortile, terrazzo, giardini, luoghi da campeggio, ecc",http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_01.jpg,,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_01.jpg,,,,,,"2015-09-21 15:58:54",,,,,,,,,,,,,,,,,"GTIN=8713016005977",7,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_00.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_02.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_08.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_07.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_06.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_05.jpg,http://dropshipping.bigbuy.eu/imgs/reposapies_CH-0609_04.jpg","GTIN=8713016005977",,,,,,,,,,
+BB-H4502058,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Orologi da parete e da tavolo",base,"Orologio da Parete Star Wars","I
fan di Star Wars non potranno fare a meno di appendere l'orologio da parete Star Wars in casa loro! Realizzato in plastica. Funziona a batterie (1 x AA, non incluse). Diametro circa: 25,5 cm. Spessore circa: 3,5 cm. Dimenzioni per Orologio da Parete Star Wars:
Altezza: 25.5 Cm Larghezza: 26 Cm Profondita': 3.8 Cm Peso: 0.287 Kg Codice Prodotto (EAN): 6950687214204
","I fan di Star Wars non potranno fare a meno di appendere l'orologio da parete Star Wars in casa loro! Realizzato in plastica. Maggiori Informazioni ",0.287,1,"Taxable Goods","Catalog, Search",22.5,,,,Orologio-da-Parete-Star-Wars,"Orologio da Parete Star Wars","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Orologi da parete e da tavolo,Orologi,parete,tavolo,","I fan di Star Wars non potranno fare a meno di appendere l'orologio da parete Star Wars in casa loro! Realizzato in plastica",http://dropshipping.bigbuy.eu/imgs/H4502058_84712.jpg,,http://dropshipping.bigbuy.eu/imgs/H4502058_84712.jpg,,,,,,"2016-08-08 21:09:24",,,,,,,,,,,,,,,,,"GTIN=6950687214204",130,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/H4502058_84713.jpg,http://dropshipping.bigbuy.eu/imgs/H4502058_84711.jpg,http://dropshipping.bigbuy.eu/imgs/H4502058_84710.jpg","GTIN=6950687214204",,,,,,,,,,
+BB-G0500195,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Illuminazione LED",base,"Braccialetto Sportivo a LED MegaLed Rosso","Se
ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Con questo braccialetto di sicurezza sarai visibile ai motorini e alle auto nell'oscurità, così da poter stare molto più sicuro e tranquillo. È dotato di 2 luci a LED con 2 possibili soluzioni (luce fissa ed intermittente). La lunghezza massima è di circa 55 cm e quella minima è di circa 42 cm. Molto leggero (circa 50 g). Autonomia circa: 24-40 ore. Funziona a batterie (2 x CR2023, incluse).
Dimenzioni per Braccialetto Sportivo a LED MegaLed:
Altezza: 3 Cm Larghezza: 20 Cm Profondita': 4 Cm Peso: 0.049 Kg Codice Prodotto (EAN): 8436545443507
","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Maggiori Informazioni ",0.049,1,"Taxable Goods","Catalog, Search",22,,,,Braccialetto-Sportivo-a-LED-MegaLed-Rosso,"Braccialetto Sportivo a LED MegaLed Rosso","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Illuminazione LED,Illuminazione,LED,Colore Rosso,Rosso,","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed",http://dropshipping.bigbuy.eu/imgs/G0500194_87774.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500194_87774.jpg,,,,,,"2016-01-08 12:34:41",,,,,,,,,,,,,,,,,"GTIN=8436545443507",22,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500194_87775.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87773.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87772.jpg","GTIN=8436545443507",,,,,,,,,,
+BB-G0500196,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Illuminazione LED",base,"Braccialetto Sportivo a LED MegaLed Verde","Se
ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Con questo braccialetto di sicurezza sarai visibile ai motorini e alle auto nell'oscurità, così da poter stare molto più sicuro e tranquillo. È dotato di 2 luci a LED con 2 possibili soluzioni (luce fissa ed intermittente). La lunghezza massima è di circa 55 cm e quella minima è di circa 42 cm. Molto leggero (circa 50 g). Autonomia circa: 24-40 ore. Funziona a batterie (2 x CR2023, incluse).
Dimenzioni per Braccialetto Sportivo a LED MegaLed:
Altezza: 3 Cm Larghezza: 20 Cm Profondita': 4 Cm Peso: 0.049 Kg Codice Prodotto (EAN): 8436545443507
","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed. Maggiori Informazioni ",0.049,1,"Taxable Goods","Catalog, Search",22,,,,Braccialetto-Sportivo-a-LED-MegaLed-Verde,"Braccialetto Sportivo a LED MegaLed Verde","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Illuminazione LED,Illuminazione,LED,Colore Verde,Verde,","Se ti piace praticare la corsa, il ciclismo, o qualunque sport all'aria aperta, non può mancare tra i tuoi accessori il braccialetto per lo sport a LED MegaLed",http://dropshipping.bigbuy.eu/imgs/G0500194_87775.jpg,,http://dropshipping.bigbuy.eu/imgs/G0500194_87775.jpg,,,,,,"2016-01-08 12:34:41",,,,,,,,,,,,,,,,,"GTIN=8436545443507",37,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/G0500194_87774.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87773.jpg,http://dropshipping.bigbuy.eu/imgs/G0500194_87772.jpg","GTIN=8436545443507",,,,,,,,,,
+BB-I2500333,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Orologi da parete e da tavolo",base,"Orologio da Parete Mom's Diner","Decora
la tua cucina con l'originale orologio da parete Mom's Diner in stile vintage! È realizzato in legno. Diametro: 58 cm circa. Spessore: 0,8 cm circa. Funziona a pile (1 x AA, non inclusa). Dimenzioni per Orologio da Parete Mom's Diner:
Altezza: 59 Cm Larghezza: 59 Cm Profondita': 6 Cm Peso: 2.1 Kg Codice Prodotto (EAN): 4029811345052
","Decora la tua cucina con l'originale orologio da parete Mom's Diner in stile vintage! È realizzato in legno. Maggiori Informazioni ",2.1,1,"Taxable Goods","Catalog, Search",42.5,,,,Orologio-da-Parete-Mom's-Diner,"Orologio da Parete Mom's Diner","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Orologi da parete e da tavolo,Orologi,parete,tavolo,","Decora la tua cucina con l'originale orologio da parete Mom's Diner in stile vintage! È realizzato in legno",http://dropshipping.bigbuy.eu/imgs/I2500333_88061.jpg,,http://dropshipping.bigbuy.eu/imgs/I2500333_88061.jpg,,,,,,"2016-07-21 13:05:12",,,,,,,,,,,,,,,,,"GTIN=4029811345052",2,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I2500333_88060.jpg,http://dropshipping.bigbuy.eu/imgs/I2500333_88059.jpg,http://dropshipping.bigbuy.eu/imgs/I2500333_88058.jpg","GTIN=4029811345052",,,,,,,,,,
+BB-I2500334,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Orologi da parete e da tavolo",base,"Orologio da Parete Coffee Endless Cup","Se
sei un appassionato di caffè, non puoi rimanere senza l'orologio da parete Coffee Endless Cup ! Un orologio vintage in legno con un design in perfetto stile caffettoso! Diametro: 58 cm circa. Spessore: 0,8 cm circa. Funziona a pile (1 x AA, non inclusa). Dimenzioni per Orologio da Parete Coffee Endless Cup:
Altezza: 59 Cm Larghezza: 59 Cm Profondita': 6 Cm Peso: 2.1 Kg Codice Prodotto (EAN): 4029811345069
","Se sei un appassionato di caffè, non puoi rimanere senza l'orologio da parete Coffee Endless Cup! Un orologio vintage in legno con un design in perfetto stile caffettoso! Diametro: 58 cm circa. Maggiori Informazioni ",2.1,1,"Taxable Goods","Catalog, Search",42.5,,,,Orologio-da-Parete-Coffee-Endless-Cup,"Orologio da Parete Coffee Endless Cup","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Orologi da parete e da tavolo,Orologi,parete,tavolo,","Se sei un appassionato di caffè, non puoi rimanere senza l'orologio da parete Coffee Endless Cup! Un orologio vintage in legno con un design in perfetto stile caffettoso! Diametro: 58 cm circa",http://dropshipping.bigbuy.eu/imgs/I2500334_88065.jpg,,http://dropshipping.bigbuy.eu/imgs/I2500334_88065.jpg,,,,,,"2016-08-30 13:41:52",,,,,,,,,,,,,,,,,"GTIN=4029811345069",4,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/I2500334_88064.jpg,http://dropshipping.bigbuy.eu/imgs/I2500334_88063.jpg,http://dropshipping.bigbuy.eu/imgs/I2500334_88062.jpg","GTIN=4029811345069",,,,,,,,,,
+BB-V0000252,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Insegna Dito Vintage Look Stop!","Se
sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Dimensioni: 69 x 17 x 1 cm circa. Dimenzioni per Insegna Dito Vintage Look:
Altezza: 17 Cm Larghezza: 69 Cm Profondita': 1 Cm Peso: 0.63 Kg Codice Prodotto (EAN): 4029811346196
","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Maggiori Informazioni ",0.63,1,"Taxable Goods","Catalog, Search",15.99,,,,Insegna-Dito-Vintage-Look-Stop!,"Insegna Dito Vintage Look Stop!","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Stop!,Stop!,","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno",http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,,,,,,"2016-02-29 09:49:10",,,,,,,,,,,,,,,,,"GTIN=4029811346196",21,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89743.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89742.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89741.jpg","GTIN=4029811346196",,,,,,,,,,
+BB-V0000253,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Insegna Dito Vintage Look Adults Only","Se
sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Dimensioni: 69 x 17 x 1 cm circa. Dimenzioni per Insegna Dito Vintage Look:
Altezza: 17 Cm Larghezza: 69 Cm Profondita': 1 Cm Peso: 0.63 Kg Codice Prodotto (EAN): 4029811346202
","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Maggiori Informazioni ",0.63,1,"Taxable Goods","Catalog, Search",15.99,,,,Insegna-Dito-Vintage-Look-Adults Only,"Insegna Dito Vintage Look Adults Only","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Adults Only,Adults Only,","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno",http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,,,,,,"2016-02-29 09:49:10",,,,,,,,,,,,,,,,,"GTIN=4029811346202",18,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89743.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89742.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89741.jpg","GTIN=4029811346202",,,,,,,,,,
+BB-V0000254,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Insegna Dito Vintage Look Talk","Se
sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Dimensioni: 69 x 17 x 1 cm circa. Dimenzioni per Insegna Dito Vintage Look:
Altezza: 17 Cm Larghezza: 69 Cm Profondita': 1 Cm Peso: 0.63 Kg Codice Prodotto (EAN): 4029811346318
","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno. Maggiori Informazioni ",0.63,1,"Taxable Goods","Catalog, Search",15.99,,,,Insegna-Dito-Vintage-Look-Talk,"Insegna Dito Vintage Look Talk","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Talk,Talk,","Se sei alla ricerca di una decorazione vintage originale e divertente per la tua casa, l'insegna dito Vintage Look ti conquisterà con il suo stile e i suoi simpatici messaggi! Realizzata in legno",http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000251_89744.jpg,,,,,,"2016-02-29 09:49:10",,,,,,,,,,,,,,,,,"GTIN=4029811346318",8,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000251_89745.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89746.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89743.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89742.jpg,http://dropshipping.bigbuy.eu/imgs/V0000251_89741.jpg","GTIN=4029811346318",,,,,,,,,,
+BB-V0000256,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Freccia Decorativa Vintage Look Go Left","Stupisci
tutti con la divertente ed originale freccia decorativa Vintage Look ! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Misure appross.: 25 x 40 x 1 cm. Dimenzioni per Freccia Decorativa Vintage Look:
Altezza: 25.5 Cm Larghezza: 0.8 Cm Profondita': 40 Cm Peso: 0.376 Kg Codice Prodotto (EAN): 4029811346325
","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Maggiori Informazioni ",0.376,1,"Taxable Goods","Catalog, Search",13.9,,,,Freccia-Decorativa-Vintage-Look-Go Left,"Freccia Decorativa Vintage Look Go Left","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Go Left,Go Left,","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno",http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,,,,,,"2016-02-29 10:39:59",,,,,,,,,,,,,,,,,"GTIN=4029811346325",4,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89759.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89758.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89757.jpg","GTIN=4029811346325",,,,,,,,,,
+BB-V0000257,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Freccia Decorativa Vintage Look Exit","Stupisci
tutti con la divertente ed originale freccia decorativa Vintage Look ! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Misure appross.: 25 x 40 x 1 cm. Dimenzioni per Freccia Decorativa Vintage Look:
Altezza: 25.5 Cm Larghezza: 0.8 Cm Profondita': 40 Cm Peso: 0.376 Kg Codice Prodotto (EAN): 4029811346332
","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Maggiori Informazioni ",0.376,1,"Taxable Goods","Catalog, Search",13.9,,,,Freccia-Decorativa-Vintage-Look-Exit,"Freccia Decorativa Vintage Look Exit","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Exit,Exit,","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno",http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,,,,,,"2016-02-29 10:39:59",,,,,,,,,,,,,,,,,"GTIN=4029811346332",19,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89759.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89758.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89757.jpg","GTIN=4029811346332",,,,,,,,,,
+BB-V0000258,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Altri articoli decorativi",base,"Freccia Decorativa Vintage Look Cold beer here","Stupisci
tutti con la divertente ed originale freccia decorativa Vintage Look ! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Misure appross.: 25 x 40 x 1 cm. Dimenzioni per Freccia Decorativa Vintage Look:
Altezza: 25.5 Cm Larghezza: 0.8 Cm Profondita': 40 Cm Peso: 0.376 Kg Codice Prodotto (EAN): 4029811346349
","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno. Maggiori Informazioni ",0.376,1,"Taxable Goods","Catalog, Search",13.9,,,,Freccia-Decorativa-Vintage-Look-Cold beer here,"Freccia Decorativa Vintage Look Cold beer here","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Altri articoli decorativi,Altri,articoli,decorativi,Design Cold beer here,Cold beer here,","Stupisci tutti con la divertente ed originale freccia decorativa Vintage Look! Le pareti di casa tua non resteranno indifferenti a nessuno! Fabbricata in legno",http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,,http://dropshipping.bigbuy.eu/imgs/V0000255_89760.jpg,,,,,,"2016-02-29 10:39:59",,,,,,,,,,,,,,,,,"GTIN=4029811346349",20,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0000255_89756.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89761.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89759.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89758.jpg,http://dropshipping.bigbuy.eu/imgs/V0000255_89757.jpg","GTIN=4029811346349",,,,,,,,,,
+BB-V0200190,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Centrotavola e Vasi",base,"Ciotola in Bambù TakeTokio Bianco","Arricchisci
la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio , una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera , portafrutta, ecc. Realizzata in pregiato legno di bambù. Dimensioni (diametro x altezza): 25 x 15 cm circa. Diametro della base: 9 cm circa.www.taketokio.com
Dimenzioni per Ciotola in Bambù TakeTokio:
Altezza: 25 Cm Larghezza: 25 Cm Profondita': 15 Cm Peso: 0.39 Kg Codice Prodotto (EAN): 8718158904577
","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc. Maggiori Informazioni ",0.39,1,"Taxable Goods","Catalog, Search",19.8,,,,Ciotola-in-Bambù-TakeTokio-Bianco,"Ciotola in Bambù TakeTokio Bianco","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Centrotavola e Vasi,Centrotavola,Vasi,Colore Bianco,Bianco,","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc",http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8718158904577",0,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90744.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90743.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90742.jpg","GTIN=8718158904577",,,,,,,,,,
+BB-V0200192,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Centrotavola e Vasi",base,"Ciotola in Bambù TakeTokio Grigio","Arricchisci
la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio , una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera , portafrutta, ecc. Realizzata in pregiato legno di bambù. Dimensioni (diametro x altezza): 25 x 15 cm circa. Diametro della base: 9 cm circa.www.taketokio.com
Dimenzioni per Ciotola in Bambù TakeTokio:
Altezza: 25 Cm Larghezza: 25 Cm Profondita': 15 Cm Peso: 0.39 Kg Codice Prodotto (EAN): 8718158904577
","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc. Maggiori Informazioni ",0.39,1,"Taxable Goods","Catalog, Search",19.8,,,,Ciotola-in-Bambù-TakeTokio-Grigio,"Ciotola in Bambù TakeTokio Grigio","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Centrotavola e Vasi,Centrotavola,Vasi,Colore Grigio,Grigio,","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc",http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8718158904577",26,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90744.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90743.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90742.jpg","GTIN=8718158904577",,,,,,,,,,
+BB-V0200191,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Decorazione e Illuminazione,Default Category/Casa Giardino/Decorazione e Illuminazione/Centrotavola e Vasi",base,"Ciotola in Bambù TakeTokio Nero","Arricchisci
la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio , una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera , portafrutta, ecc. Realizzata in pregiato legno di bambù. Dimensioni (diametro x altezza): 25 x 15 cm circa. Diametro della base: 9 cm circa.www.taketokio.com
Dimenzioni per Ciotola in Bambù TakeTokio:
Altezza: 25 Cm Larghezza: 25 Cm Profondita': 15 Cm Peso: 0.39 Kg Codice Prodotto (EAN): 8718158904577
","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc. Maggiori Informazioni ",0.39,1,"Taxable Goods","Catalog, Search",19.8,,,,Ciotola-in-Bambù-TakeTokio-Nero,"Ciotola in Bambù TakeTokio Nero","Casa Giardino,Casa,Giardino,Decorazione e Illuminazione,Decorazione,Illuminazione,Centrotavola e Vasi,Centrotavola,Vasi,Colore Nero,Nero,","Arricchisci la selezione dei tuoi utensili da cucina con la ciotola in bambù TakeTokio, una ciotola da cucina funzionale e dal design moderno è perfetta come insalatiera, portafrutta, ecc",http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200189_90745.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8718158904577",22,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200189_90746.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90747.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90744.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90743.jpg,http://dropshipping.bigbuy.eu/imgs/V0200189_90742.jpg","GTIN=8718158904577",,,,,,,,,,
+BB-V0200360,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Scatola porta Tè Flower Vintage Coconut Rosa","Gli
amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut ! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Dispone di un coperchio in cristallo e vari scompartimenti. Dimensioni: circa 23 x 7 x 23 cm.www.vintagecoconut.com
Dimenzioni per Scatola porta Tè Flower Vintage Coconut:
Altezza: 23 Cm Larghezza: 7.1 Cm Profondita': 23 Cm Peso: 0.795 Kg Codice Prodotto (EAN): 8711295889547
","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Maggiori Informazioni ",0.795,1,"Taxable Goods","Catalog, Search",25.9,,,,Scatola-porta-Tè-Flower-Vintage-Coconut-Rosa,"Scatola porta Tè Flower Vintage Coconut Rosa","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,Colore Rosa,Rosa,","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi",http://dropshipping.bigbuy.eu/imgs/V0200328_92649.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200328_92649.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8711295889547",13,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200328_92648.jpg,http://dropshipping.bigbuy.eu/imgs/V0200328_92647.jpg","GTIN=8711295889547",,,,,,,,,,
+BB-V0200361,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Scatola porta Tè Flower Vintage Coconut Azzurro","Gli
amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut ! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Dispone di un coperchio in cristallo e vari scompartimenti. Dimensioni: circa 23 x 7 x 23 cm.www.vintagecoconut.com
Dimenzioni per Scatola porta Tè Flower Vintage Coconut:
Altezza: 23 Cm Larghezza: 7.1 Cm Profondita': 23 Cm Peso: 0.795 Kg Codice Prodotto (EAN): 8711295889547
","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi. Maggiori Informazioni ",0.795,1,"Taxable Goods","Catalog, Search",25.9,,,,Scatola-porta-Tè-Flower-Vintage-Coconut-Azzurro,"Scatola porta Tè Flower Vintage Coconut Azzurro","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,Colore Azzurro,Azzurro,","Gli amanti della moda vintage non potranno resistere di fronte all'adorabile scatola porta tè Flower Vintage Coconut! Una scatola vintage in legno ottima come decorazione e utilissima per sistemare le bustine di tè o di altri infusi",http://dropshipping.bigbuy.eu/imgs/V0200328_92648.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200328_92648.jpg,,,,,,"-0001-11-30 00:00:00",,,,,,,,,,,,,,,,,"GTIN=8711295889547",21,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200328_92649.jpg,http://dropshipping.bigbuy.eu/imgs/V0200328_92647.jpg","GTIN=8711295889547",,,,,,,,,,
+BB-V0200353,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Giardino e Terrazza,Default Category/Casa Giardino/Giardino e Terrazza/Barbecue",base,"Ventilatore a Pistola classico per Barbecue BBQ Classics","Utilizza
i migliori barbecue alimentando il fuoco con il ventilatore a pistola classico per babecue BBQ Classics ! Basterà solo fare una leggera pressione sul pulsante per far uscire l'aria.www.bbqclassics.com
Realizzato in plastica e metallo Dimensioni: 25 x 18 x 4 cm circa Dimenzioni per Ventilatore a Pistola classico per Barbecue BBQ Classics:
Altezza: 5.5 Cm Larghezza: 11 Cm Profondita': 22 Cm Peso: 0.167 Kg Codice Prodotto (EAN): 8718158032706
","Utilizza i migliori barbecue alimentando il fuoco con il ventilatore a pistola classico per babecue BBQ Classics! Basterà solo fare una leggera pressione sul pulsante per far uscire l'aria. Maggiori Informazioni ",0.167,1,"Taxable Goods","Catalog, Search",9.3,,,,Ventilatore-a-Pistola-classico-per-Barbecue-BBQ-Classics,"Ventilatore a Pistola classico per Barbecue BBQ Classics","Casa Giardino,Casa,Giardino,Giardino e Terrazza,Giardino,Terrazza,Barbecue,","Utilizza i migliori barbecue alimentando il fuoco con il ventilatore a pistola classico per babecue BBQ Classics! Basterà solo fare una leggera pressione sul pulsante per far uscire l'aria",http://dropshipping.bigbuy.eu/imgs/V0200353_92695.jpg,,http://dropshipping.bigbuy.eu/imgs/V0200353_92695.jpg,,,,,,"2016-09-13 09:56:07",,,,,,,,,,,,,,,,,"GTIN=8718158032706",60,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V0200353_92696.jpg,http://dropshipping.bigbuy.eu/imgs/V0200353_92694.jpg","GTIN=8718158032706",,,,,,,,,,
+BB-V1600123,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Frozen (32 x 23 cm)","Non
c'è nulla di meglio che tenere le camere dei più piccini in ordine in modo originale e divertente. Con il contenitore portagiochi Frozen (32 x 23 cm) sarà semplicissimo!Realizzato in polipropilene Dimensioni aprossimative: 32 x 15 x 23 cm Età consigliata: +3 anni
Dimenzioni per Contenitore Portagiochi Frozen (32 x 23 cm):
Altezza: 15 Cm Larghezza: 34 Cm Profondita': 23 Cm Peso: 0.331 Kg Codice Prodotto (EAN): 8412842766006
","Non c'è nulla di meglio che tenere le camere dei più piccini in ordine in modo originale e divertente. Maggiori Informazioni ",0.331,1,"Taxable Goods","Catalog, Search",21.9,,,,Contenitore-Portagiochi-Frozen-(32-x-23-cm),"Contenitore Portagiochi Frozen (32 x 23 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","Non c'è nulla di meglio che tenere le camere dei più piccini in ordine in modo originale e divertente",http://dropshipping.bigbuy.eu/imgs/V1600123_93002.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600123_93002.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8412842766006",48,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600123_93005.jpg,http://dropshipping.bigbuy.eu/imgs/V1600123_93004.jpg,http://dropshipping.bigbuy.eu/imgs/V1600123_93003.jpg","GTIN=8412842766006",,,,,,,,,,
+BB-V1600124,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Spiderman (32 x 23 cm)","Desideri
sorprendere i più piccini con un regalo molto originale? Il contenitore portagiochi Spiderman (32 x 23 cm) decorerà e metterà in ordine le loro camerette.Realizzato in polipropilene Dimensioni approssimative: 32 x 15 x 23 cm Età consigliata: +3 anni Dimenzioni per Contenitore Portagiochi Spiderman (32 x 23 cm):
Altezza: 15 Cm Larghezza: 34 Cm Profondita': 23 Cm Peso: 0.331 Kg Codice Prodotto (EAN): 8412842766037
","Desideri sorprendere i più piccini con un regalo molto originale? Il contenitore portagiochi Spiderman (32 x 23 cm) decorerà e metterà in ordine le loro camerette. Maggiori Informazioni ",0.331,1,"Taxable Goods","Catalog, Search",21.9,,,,Contenitore-Portagiochi-Spiderman--(32-x-23-cm),"Contenitore Portagiochi Spiderman (32 x 23 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","Desideri sorprendere i più piccini con un regalo molto originale? Il contenitore portagiochi Spiderman (32 x 23 cm) decorerà e metterà in ordine le loro camerette",http://dropshipping.bigbuy.eu/imgs/V1600124_93006.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600124_93006.jpg,,,,,,"2016-08-08 21:09:24",,,,,,,,,,,,,,,,,"GTIN=8412842766037",52,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600124_93008.jpg,http://dropshipping.bigbuy.eu/imgs/V1600124_93007.jpg","GTIN=8412842766037",,,,,,,,,,
+BB-V1600125,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Frozen (45 x 32 cm)","Insegna
ai tuoi bambini a tenere i giocattoli conservati ben in ordine con l'aiuto del contenitore portagiochi Frozen (45 x 32 cm) . Il portagiocattoli che tutte le bambine sognano!Realizzato in polipropilene Dimensioni: circa 45 x 22 x 32 cm Età consigliata: +3 anni Dimenzioni per Contenitore Portagiochi Frozen (45 x 32 cm):
Altezza: 22 Cm Larghezza: 37 Cm Profondita': 45 Cm Peso: 0.775 Kg Codice Prodotto (EAN): 8412842766129
","Insegna ai tuoi bambini a tenere i giocattoli conservati ben in ordine con l'aiuto del contenitore portagiochi Frozen (45 x 32 cm). Maggiori Informazioni ",0.775,1,"Taxable Goods","Catalog, Search",39.6,,,,Contenitore-Portagiochi-Frozen-(45-x-32-cm),"Contenitore Portagiochi Frozen (45 x 32 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","Insegna ai tuoi bambini a tenere i giocattoli conservati ben in ordine con l'aiuto del contenitore portagiochi Frozen (45 x 32 cm)",http://dropshipping.bigbuy.eu/imgs/V1600125_93010.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600125_93010.jpg,,,,,,"2016-08-08 21:04:27",,,,,,,,,,,,,,,,,"GTIN=8412842766129",17,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600125_93011.jpg,http://dropshipping.bigbuy.eu/imgs/V1600125_93009.jpg","GTIN=8412842766129",,,,,,,,,,
+BB-V1600126,,Default,simple,"Default Category/Casa Giardino,Default Category/Casa Giardino/Arredamento,Default Category/Casa Giardino/Arredamento/Soluzioni per Organizzare",base,"Contenitore Portagiochi Spiderman (45 x 32 cm)","I
piccoli di casa ora possono ordinare e riporre i loro giocattoli facilmente grazie al contenitore portagiochi Spiderman (45 x 32 cm) . Il contenitore portagiochi preferito dai bambini!Fabbricato in polipropilene Dimensioni: circa 45 x 22 x 32 cm Età raccomandata: +3 anni Dimenzioni per Contenitore Portagiochi Spiderman (45 x 32 cm):
Altezza: 22 Cm Larghezza: 37 Cm Profondita': 45 Cm Peso: 0.775 Kg Codice Prodotto (EAN): 8412842766150
","I piccoli di casa ora possono ordinare e riporre i loro giocattoli facilmente grazie al contenitore portagiochi Spiderman (45 x 32 cm). Maggiori Informazioni ",0.775,1,"Taxable Goods","Catalog, Search",39.6,,,,Contenitore-Portagiochi-Spiderman-(45-x-32-cm),"Contenitore Portagiochi Spiderman (45 x 32 cm)","Casa Giardino,Casa,Giardino,Arredamento,Soluzioni per Organizzare,Soluzioni,Organizzare,","I piccoli di casa ora possono ordinare e riporre i loro giocattoli facilmente grazie al contenitore portagiochi Spiderman (45 x 32 cm)",http://dropshipping.bigbuy.eu/imgs/V1600126_93012.jpg,,http://dropshipping.bigbuy.eu/imgs/V1600126_93012.jpg,,,,,,"2016-08-08 21:09:24",,,,,,,,,,,,,,,,,"GTIN=8412842766150",18,0,1,0,1,1,1,1,10000,1,1,,1,0,1,1,1,1,0,0,0,,,,,,,"http://dropshipping.bigbuy.eu/imgs/V1600126_93014.jpg,http://dropshipping.bigbuy.eu/imgs/V1600126_93013.jpg","GTIN=8412842766150",,,,,,,,,,
From 9b4b44f0fe094da2ccf20a90a29f561fab50a107 Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Wed, 26 Jun 2019 16:11:53 +0300
Subject: [PATCH 036/593] MAGETWO-44170: Not pass functional test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Add a tag about migration tests.
---
.../Test/AdminProductTypeSwitchingOnEditingTest.xml | 4 ++--
.../Product/ProductTypeSwitchingOnUpdateTest.php | 1 -
.../Product/ProductTypeSwitchingOnUpdateTest.xml | 12 ++++++------
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
index 116fbc1b6b455..2154a89a5fa73 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -94,7 +94,7 @@
-
+
@@ -221,7 +221,7 @@
-
+
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
index 2abd17fce5b45..90cd6bdb76328 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
@@ -36,7 +36,6 @@
class ProductTypeSwitchingOnUpdateTest extends Injectable
{
/* tags */
- const TEST_TYPE = 'acceptance_test';
const MVP = 'yes';
/* end tags */
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
index 3d3e36754e92c..732dac98e0779 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
@@ -11,7 +11,7 @@
catalogProductSimple::default
configurableProduct::default
-
- test_type:acceptance_test
+ mftf_migrated:yes
@@ -35,7 +35,7 @@
- test_type:acceptance_test
+ mftf_migrated:yes
configurableProduct::default
catalogProductVirtual::required_fields
deleteVariations
@@ -50,7 +50,7 @@
- test_type:acceptance_test
+ mftf_migrated:yes
catalogProductVirtual::default
configurableProduct::not_virtual_for_type_switching
-
@@ -63,7 +63,7 @@
- test_type:acceptance_test
+ mftf_migrated:yes
catalogProductVirtual::default
downloadableProduct::default
-
@@ -75,7 +75,7 @@
- test_type:acceptance_test
+ mftf_migrated:yes
downloadableProduct::default
catalogProductSimple::default
-
@@ -103,7 +103,7 @@
- test_type:acceptance_test
+ mftf_migrated:yes
catalogProductSimple::default
downloadableProduct::default
-
From af956595235f7739eba3119c447b1ccce711aece Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Thu, 27 Jun 2019 10:54:21 +0300
Subject: [PATCH 037/593] MAGETWO-44170: Not pass functional test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Fix a typo in the test.
---
.../Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
index 2154a89a5fa73..22eaa9c8b34bb 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -385,7 +385,7 @@
-
+
From 26816700ad5fd7e9330b8f3dce118d672f7567c6 Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Thu, 4 Jul 2019 12:11:38 +0300
Subject: [PATCH 038/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix CR comments.
---
.../ResourceModel/Product/Collection.php | 24 +++++++------------
1 file changed, 8 insertions(+), 16 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 7127aa2cb3e77..b4673412d25b2 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -2068,7 +2068,7 @@ protected function _applyProductLimitations()
protected function _applyZeroStoreProductLimitations()
{
$filters = $this->_productLimitationFilters;
- $categories = $this->getChildrenCategories((int)$filters['category_id'], []);
+ $categories = $this->getChildrenCategories((int)$filters['category_id']);
$conditions = [
'cat_pro.product_id=e.entity_id',
@@ -2099,30 +2099,22 @@ protected function _applyZeroStoreProductLimitations()
* Get children categories.
*
* @param int $categoryId
- * @param array $categories
* @return array
*/
- private function getChildrenCategories(int $categoryId, array $categories): array
+ private function getChildrenCategories(int $categoryId): array
{
- $categories[] = $categoryId;
+ $categoryIds[] = $categoryId;
/** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */
$categoryCollection = $this->categoryCollectionFactory->create();
- $category = $categoryCollection
- ->addAttributeToSelect('is_anchor')
+ $categories = $categoryCollection
->addAttributeToFilter('is_anchor', 1)
- ->addIdFilter([$categoryId])
- ->getFirstItem();
- if ($category) {
+ ->addAttributeToFilter('path', ['like' => $categoryId . '/%'])->getItems();
+ foreach ($categories as $category) {
$categoryChildren = $category->getChildren();
- $categoryChildrenIds = explode(',', $categoryChildren);
- foreach ($categoryChildrenIds as $categoryChildrenId) {
- if ($categoryChildrenId) {
- $categories = $this->getChildrenCategories((int)$categoryChildrenId, $categories);
- }
- }
+ $categoryIds = array_merge($categoryIds, explode(',', $categoryChildren));
}
- return $categories;
+ return $categoryIds;
}
/**
From 2d8d7047ca764dec901585376618ee658a0107ba Mon Sep 17 00:00:00 2001
From: Lusine Papyan
Date: Thu, 4 Jul 2019 18:00:28 +0400
Subject: [PATCH 039/593] MC-15341: Default product numbers to display results
in poor display on Desktop
- Updated automated test script
---
.../Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
index 384d8ddea4f17..de84d00cabab9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
@@ -20,6 +20,10 @@
+
+
+
+
From 6491b27f785b75a8e71cbd37b3e77886b5e12a5e Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Fri, 5 Jul 2019 12:24:54 +0300
Subject: [PATCH 040/593] MAGETWO-44170: Not pass functional test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Change id for test cases.
---
.../Test/AdminProductTypeSwitchingOnEditingTest.xml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
index 22eaa9c8b34bb..b1af17fb25838 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -85,7 +85,7 @@
-
+
@@ -129,7 +129,7 @@
-
+
@@ -202,7 +202,7 @@
-
+
@@ -258,7 +258,7 @@
-
+
@@ -297,7 +297,7 @@
-
+
@@ -353,7 +353,7 @@
-
+
From b31f618d87e3b7d4b70dc91bfe6248a4534ee26e Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Fri, 5 Jul 2019 12:55:53 +0000
Subject: [PATCH 041/593] Added createCustomerGraphQl plugin
---
.../Plugin/Customer/CreateCustomerAccount.php | 60 +++++++++++++++++++
.../CustomerGraphQl/etc/graphql/di.xml | 3 +
2 files changed, 63 insertions(+)
create mode 100644 app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php
diff --git a/app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php
new file mode 100644
index 0000000000000..d2aa003125771
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php
@@ -0,0 +1,60 @@
+scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Before Executing method.
+ *
+ * @param \Magento\CustomerGraphQl\Model\Customer\CreateCustomerAccount $subject
+ * @param $data
+ * @return array
+ */
+ public function beforeExecute (
+ \Magento\CustomerGraphQl\Model\Customer\CreateCustomerAccount $subject, $data
+ ) {
+ if (!$this->scopeConfig->getValue(
+ self::XML_PATH_NEWSLETTER_ACTIVE,
+ ScopeInterface::SCOPE_STORE
+ )
+ ) {
+ $data['is_subscribed'] = false;
+ }
+
+ return [$data];
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
index cb4ab0b7b27f4..cafe1820b9279 100644
--- a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
@@ -6,6 +6,9 @@
*/
-->
+
+
+
From 1259496618d876b24fe4f241dee872bc7c73d891 Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Sun, 7 Jul 2019 18:31:10 +0300
Subject: [PATCH 042/593] GraphQl-220: Implement exception logging. Stopped
using arguments with the same type
---
app/etc/graphql/di.xml | 8 +--
.../Framework/GraphQl/Query/ErrorHandler.php | 14 +++---
.../GraphQl/Query/Resolver/LoggerFactory.php | 39 +++++++++++++++
...terface.php => LoggerFactoryInterface.php} | 4 +-
.../GraphQl/Query/Resolver/ResolveLogger.php | 49 -------------------
5 files changed, 49 insertions(+), 65 deletions(-)
create mode 100644 lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php
rename lib/internal/Magento/Framework/GraphQl/Query/Resolver/{ResolveLoggerInterface.php => LoggerFactoryInterface.php} (82%)
delete mode 100644 lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php
diff --git a/app/etc/graphql/di.xml b/app/etc/graphql/di.xml
index 57b5687c660cb..474266bb9eff0 100644
--- a/app/etc/graphql/di.xml
+++ b/app/etc/graphql/di.xml
@@ -7,12 +7,7 @@
-->
-
-
- GraphQLClientLogger
- GraphQLServerLogger
-
-
+
@@ -37,5 +32,4 @@
Magento\Framework\GraphQl\Query\ErrorHandler::SERVER_LOG_FILE
-
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
index 921314a50beff..874f714284936 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
@@ -8,7 +8,7 @@
namespace Magento\Framework\GraphQl\Query;
use GraphQL\Error\ClientAware;
-use Magento\Framework\GraphQl\Query\Resolver\ResolveLoggerInterface;
+use Magento\Framework\GraphQl\Query\Resolver\LoggerFactoryInterface;
/**
* @inheritDoc
@@ -21,17 +21,17 @@ class ErrorHandler implements ErrorHandlerInterface
const CLIENT_LOG_FILE = 'var/log/graphql/client/exception.log';
/**
- * @var ResolveLoggerInterface
+ * @var LoggerFactoryInterface
*/
- private $resolveLogger;
+ private $loggerFactory;
/**
- * @param ResolveLoggerInterface $resolveLogger
+ * @param LoggerFactoryInterface $loggerFactory
*/
public function __construct(
- ResolveLoggerInterface $resolveLogger
+ LoggerFactoryInterface $loggerFactory
) {
- $this->resolveLogger = $resolveLogger;
+ $this->loggerFactory = $loggerFactory;
}
/**
@@ -41,7 +41,7 @@ public function handle(array $errors, callable $formatter): array
{
return array_map(
function (ClientAware $error) use ($formatter) {
- $this->resolveLogger->execute($error)->error($error);
+ $this->loggerFactory->getLogger($error)->error($error);
return $formatter($error);
},
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php
new file mode 100644
index 0000000000000..2b35b53981100
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php
@@ -0,0 +1,39 @@
+objectManager = $objectManager;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLogger(ClientAware $clientAware): LoggerInterface
+ {
+ return $clientAware->isClientSafe() ?
+ $this->objectManager->get('GraphQLClientLogger') :
+ $this->objectManager->get('GraphQLServerLogger');
+ }
+}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLoggerInterface.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactoryInterface.php
similarity index 82%
rename from lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLoggerInterface.php
rename to lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactoryInterface.php
index ade416a3093bd..778fb61db994d 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLoggerInterface.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactoryInterface.php
@@ -15,7 +15,7 @@
*
* @api
*/
-interface ResolveLoggerInterface
+interface LoggerFactoryInterface
{
/**
* Get logger to use for certain ClientAware exception
@@ -24,5 +24,5 @@ interface ResolveLoggerInterface
*
* @return LoggerInterface
*/
- public function execute(ClientAware $clientAware): LoggerInterface;
+ public function getLogger(ClientAware $clientAware): LoggerInterface;
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php
deleted file mode 100644
index 98e4f85b76128..0000000000000
--- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/ResolveLogger.php
+++ /dev/null
@@ -1,49 +0,0 @@
-clientLogger = $clientLogger;
- $this->serverLogger = $serverLogger;
- }
-
- /**
- * @inheritDoc
- */
- public function execute(ClientAware $clientAware): LoggerInterface
- {
- return $clientAware->isClientSafe() ?
- $this->clientLogger :
- $this->serverLogger;
- }
-}
From 723bd8d4f3386c6e0372ebac27a62b2c546cd0b4 Mon Sep 17 00:00:00 2001
From: Nikita Chubukov
Date: Mon, 8 Jul 2019 13:06:06 +0300
Subject: [PATCH 043/593] MC-15256: Exported customer without modification can
not be imported
- Integration test
---
.../CustomerImportExport/Model/Import/CustomerTest.php | 5 +++++
.../Model/Import/_files/customers_to_import.csv | 4 ++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
index 05d9c5d3acb1e..54890f7db3a1d 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
@@ -133,6 +133,11 @@ public function testImportData()
$updatedCustomer->getCreatedAt(),
'Creation date must be changed'
);
+ $this->assertNotEquals(
+ $existingCustomer->getGender(),
+ $updatedCustomer->getGender(),
+ 'Gender must be changed'
+ );
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_import.csv b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_import.csv
index 30a283ce0502f..96c14c67607aa 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_import.csv
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_import.csv
@@ -1,7 +1,7 @@
email,_website,_store,confirmation,created_at,created_in,default_billing,default_shipping,disable_auto_group_change,dob,firstname,gender,group_id,lastname,middlename,password_hash,prefix,rp_token,rp_token_created_at,store_id,suffix,taxvat,website_id,password
-AnthonyANealy@magento.com,base,admin,,5/6/2012 15:53,Admin,1,1,0,5/6/2010,Anthony,Male,1,Nealy,A.,6a9c9bfb2ba88a6ad2a64e7402df44a763e0c48cd21d7af9e7e796cd4677ee28:RF,,,,0,,,1,
+AnthonyANealy@magento.com,base,admin,,5/6/2012 15:53,Admin,1,1,0,5/6/2010,Anthony,Female,1,Nealy,A.,6a9c9bfb2ba88a6ad2a64e7402df44a763e0c48cd21d7af9e7e796cd4677ee28:RF,,,,0,,,1,
LoriBBanks@magento.com,admin,admin,,5/6/2012 15:59,Admin,3,3,0,5/6/2010,Lori,Female,1,Banks,B.,7ad6dbdc83d3e9f598825dc58b84678c7351e4281f6bc2b277a32dcd88b9756b:pz,,,,0,,,0,
CharlesTAlston@teleworm.us,base,admin,,5/6/2012 16:13,Admin,4,4,0,,Jhon,Female,1,Doe,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
-customer@example.com,base,admin,,5/6/2012 16:15,Admin,4,4,0,,Firstname,Male,1,Lastname,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
+customer@example.com,base,admin,,5/6/2012 16:15,Admin,4,4,0,,Firstname,Female,1,Lastname,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
julie.worrell@example.com,base,admin,,5/6/2012 16:19,Admin,4,4,0,,Julie,Female,1,Worrell,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
david.lamar@example.com,base,admin,,5/6/2012 16:25,Admin,4,4,0,,David,,1,Lamar,T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,2,
From fc328d923d1e69d46501eef17a831954adcc01a5 Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Mon, 8 Jul 2019 18:00:03 +0300
Subject: [PATCH 044/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix CR comments.
---
.../Catalog/Model/ResourceModel/Product/Collection.php | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index b4673412d25b2..dffa49d97838c 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -2108,8 +2108,10 @@ private function getChildrenCategories(int $categoryId): array
/** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */
$categoryCollection = $this->categoryCollectionFactory->create();
$categories = $categoryCollection
- ->addAttributeToFilter('is_anchor', 1)
- ->addAttributeToFilter('path', ['like' => $categoryId . '/%'])->getItems();
+ ->addAttributeToFilter(
+ ['is_anchor', 'path'],
+ [1, ['like' => $categoryId . '/%']]
+ )->getItems();
foreach ($categories as $category) {
$categoryChildren = $category->getChildren();
$categoryIds = array_merge($categoryIds, explode(',', $categoryChildren));
From 51b433c519ad304225a8c952fbcc30c9c32a9720 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Thu, 11 Jul 2019 21:31:59 +0000
Subject: [PATCH 045/593] Moved logic from plugin to CreateCustomer
---
.../Plugin/Customer/CreateCustomerAccount.php | 60 -------------------
.../Model/Resolver/CreateCustomer.php | 27 ++++++++-
.../CustomerGraphQl/etc/graphql/di.xml | 3 -
3 files changed, 26 insertions(+), 64 deletions(-)
delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php
diff --git a/app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php
deleted file mode 100644
index d2aa003125771..0000000000000
--- a/app/code/Magento/CustomerGraphQl/Model/Plugin/Customer/CreateCustomerAccount.php
+++ /dev/null
@@ -1,60 +0,0 @@
-scopeConfig = $scopeConfig;
- }
-
- /**
- * Before Executing method.
- *
- * @param \Magento\CustomerGraphQl\Model\Customer\CreateCustomerAccount $subject
- * @param $data
- * @return array
- */
- public function beforeExecute (
- \Magento\CustomerGraphQl\Model\Customer\CreateCustomerAccount $subject, $data
- ) {
- if (!$this->scopeConfig->getValue(
- self::XML_PATH_NEWSLETTER_ACTIVE,
- ScopeInterface::SCOPE_STORE
- )
- ) {
- $data['is_subscribed'] = false;
- }
-
- return [$data];
- }
-}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index e12c636f0edf6..848f418e85917 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -13,12 +13,24 @@
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+use Magento\Store\Model\ScopeInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
/**
* Create customer account resolver
*/
class CreateCustomer implements ResolverInterface
{
+ /**
+ * Configuration path to newsletter active setting
+ */
+ const XML_PATH_NEWSLETTER_ACTIVE = 'newsletter/general/active';
+
+ /**
+ * @var ScopeConfigInterface
+ */
+ private $scopeConfig;
+
/**
* @var ExtractCustomerData
*/
@@ -30,13 +42,18 @@ class CreateCustomer implements ResolverInterface
private $createCustomerAccount;
/**
+ * CreateCustomer constructor.
+ *
* @param ExtractCustomerData $extractCustomerData
* @param CreateCustomerAccount $createCustomerAccount
+ * @param ScopeConfigInterface $scopeConfig
*/
public function __construct(
ExtractCustomerData $extractCustomerData,
- CreateCustomerAccount $createCustomerAccount
+ CreateCustomerAccount $createCustomerAccount,
+ ScopeConfigInterface $scopeConfig
) {
+ $this->scopeConfig = $scopeConfig;
$this->extractCustomerData = $extractCustomerData;
$this->createCustomerAccount = $createCustomerAccount;
}
@@ -55,6 +72,14 @@ public function resolve(
throw new GraphQlInputException(__('"input" value should be specified'));
}
+ if (!$this->scopeConfig->getValue(
+ self::XML_PATH_NEWSLETTER_ACTIVE,
+ ScopeInterface::SCOPE_STORE
+ )
+ ) {
+ $args['input']['is_subscribed'] = false;
+ }
+
$customer = $this->createCustomerAccount->execute($args['input']);
$data = $this->extractCustomerData->execute($customer);
diff --git a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
index cafe1820b9279..cb4ab0b7b27f4 100644
--- a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
@@ -6,9 +6,6 @@
*/
-->
-
-
-
From dfa329ff5d9b2ac7f39f23e81e7be7e6b76d882a Mon Sep 17 00:00:00 2001
From: sdovbenko
Date: Fri, 12 Jul 2019 01:19:29 +0300
Subject: [PATCH 046/593] Added test method testCreateCustomerSubscribed
---
.../GraphQl/Customer/CreateCustomerTest.php | 33 +++++++++++++++++++
.../Customer/_files/customer_subscribe.php | 16 +++++++++
2 files changed, 49 insertions(+)
create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
index fc51f57a83a76..28f4030787613 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
@@ -241,6 +241,39 @@ public function testCreateCustomerIfPassedAttributeDosNotExistsInCustomerInput()
$this->graphQlMutation($query);
}
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer_subscribe.php
+ * @throws \Exception
+ */
+ public function testCreateCustomerSubscribed()
+ {
+ $newFirstname = 'Richard';
+ $newLastname = 'Rowe';
+ $newEmail = 'new_customer@example.com';
+
+ $query = <<graphQlMutation($query);
+
+ $this->assertEquals(false, $response['createCustomer']['customer']['is_subscribed']);
+ }
+
public function tearDown()
{
$newEmail = 'new_customer@example.com';
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
new file mode 100644
index 0000000000000..9585ad8b02740
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
@@ -0,0 +1,16 @@
+create(\Magento\Config\Model\ResourceModel\Config::class);
+
+$resourceConfig->saveConfig(
+ 'newsletter/general/active',
+ false,
+ 'default',
+ 0
+);
\ No newline at end of file
From cec3ca0baab08929afc3593b31f5379ce30ff052 Mon Sep 17 00:00:00 2001
From: Lusine Papyan
Date: Fri, 12 Jul 2019 12:32:40 +0400
Subject: [PATCH 047/593] MC-15341: Default product numbers to display results
in poor display on Desktop
- Updated automated test script
---
.../AdminCatalogStorefrontConfigSection.xml | 16 ++++++++
.../AdminCheckPaginationInStorefrontTest.xml | 41 ++++++++++---------
2 files changed, 38 insertions(+), 19 deletions(-)
create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml
new file mode 100644
index 0000000000000..d0200f1e0a5b0
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
index de84d00cabab9..1b72458747067 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml
@@ -20,10 +20,6 @@
-
-
-
-
@@ -93,16 +89,24 @@
-
+
+
+
+
+
+
+
+
+
-
+
@@ -110,7 +114,6 @@
-
@@ -122,35 +125,35 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -159,26 +162,26 @@
-
+
-
+
-
+
-
+
@@ -187,13 +190,13 @@
-
+
-
+
From dc8c4d35aa0f55e6ef819a1fb7087372194a4353 Mon Sep 17 00:00:00 2001
From: Nikita Chubukov
Date: Thu, 11 Jul 2019 01:10:15 +0300
Subject: [PATCH 048/593] MC-15256: Exported customer without modification can
not be imported
- Fix integration test
---
.../Magento/CustomerImportExport/Model/Import/Customer.php | 4 ++--
.../Magento/Customer/_files/import_export/customer.php | 2 +-
.../CustomerImportExport/Model/Import/CustomerTest.php | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
index 991d4f3558adc..33dc7e55228bf 100644
--- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
+++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
@@ -600,14 +600,14 @@ protected function _validateRowForUpdate(array $rowData, $rowNumber)
$isFieldNotSetAndCustomerDoesNotExist =
!isset($rowData[$attributeCode]) && !$this->_getCustomerId($email, $website);
$isFieldSetAndTrimmedValueIsEmpty
- = isset($rowData[$attributeCode]) && '' === trim($rowData[$attributeCode]);
+ = isset($rowData[$attributeCode]) && '' === trim((string)$rowData[$attributeCode]);
if ($isFieldRequired && ($isFieldNotSetAndCustomerDoesNotExist || $isFieldSetAndTrimmedValueIsEmpty)) {
$this->addRowError(self::ERROR_VALUE_IS_REQUIRED, $rowNumber, $attributeCode);
continue;
}
- if (isset($rowData[$attributeCode]) && strlen($rowData[$attributeCode])) {
+ if (isset($rowData[$attributeCode]) && strlen((string)$rowData[$attributeCode])) {
if ($attributeParams['type'] == 'select' && empty($rowData[$attributeCode])) {
continue;
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customer.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customer.php
index 215dd2a709418..3a39e62af0ccb 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customer.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customer.php
@@ -30,7 +30,7 @@
)->setLastname(
'Alston'
)->setGender(
- 2
+ '2'
);
$customer->isObjectNew(true);
diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
index 54890f7db3a1d..daab687408749 100644
--- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
+++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php
@@ -133,7 +133,7 @@ public function testImportData()
$updatedCustomer->getCreatedAt(),
'Creation date must be changed'
);
- $this->assertNotEquals(
+ $this->assertEquals(
$existingCustomer->getGender(),
$updatedCustomer->getGender(),
'Gender must be changed'
From 2ca787283282e1063de089d37c71bad28d5ba835 Mon Sep 17 00:00:00 2001
From: sdovbenko
Date: Sat, 13 Jul 2019 01:27:10 +0300
Subject: [PATCH 049/593] Added rollback for newsletter/general/active
---
.../_files/customer_subscribe_rollback.php | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
new file mode 100644
index 0000000000000..43210e4cda694
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
@@ -0,0 +1,15 @@
+create(WriterInterface::class);
+$configWriter->delete('newsletter/general/active');
From c37a7a41649fd68ca7f800082c5eff25fdec9cc0 Mon Sep 17 00:00:00 2001
From: sdovbenko
Date: Sat, 13 Jul 2019 01:34:05 +0300
Subject: [PATCH 050/593] Added todo
---
.../Magento/Customer/_files/customer_subscribe_rollback.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
index 43210e4cda694..d8773e97b5db7 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167
declare(strict_types=1);
use Magento\Framework\App\Config\Storage\WriterInterface;
From c09c17e3b7c3d9a86cf650aa8b642cb632d29013 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Tue, 16 Jul 2019 22:25:15 +0000
Subject: [PATCH 051/593] Created Config Module for Newsletter
---
.../Model/Resolver/CreateCustomer.php | 29 ++++------
app/code/Magento/Newsletter/Model/Config.php | 53 +++++++++++++++++++
2 files changed, 63 insertions(+), 19 deletions(-)
create mode 100644 app/code/Magento/Newsletter/Model/Config.php
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index 848f418e85917..daf37027f2d16 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -14,23 +14,13 @@
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Store\Model\ScopeInterface;
-use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Newsletter\Model\Config;
/**
* Create customer account resolver
*/
class CreateCustomer implements ResolverInterface
{
- /**
- * Configuration path to newsletter active setting
- */
- const XML_PATH_NEWSLETTER_ACTIVE = 'newsletter/general/active';
-
- /**
- * @var ScopeConfigInterface
- */
- private $scopeConfig;
-
/**
* @var ExtractCustomerData
*/
@@ -41,19 +31,24 @@ class CreateCustomer implements ResolverInterface
*/
private $createCustomerAccount;
+ /**
+ * @var Config
+ */
+ private $config;
+
/**
* CreateCustomer constructor.
*
* @param ExtractCustomerData $extractCustomerData
* @param CreateCustomerAccount $createCustomerAccount
- * @param ScopeConfigInterface $scopeConfig
+ * @param Config $config
*/
public function __construct(
ExtractCustomerData $extractCustomerData,
CreateCustomerAccount $createCustomerAccount,
- ScopeConfigInterface $scopeConfig
+ Config $config
) {
- $this->scopeConfig = $scopeConfig;
+ $this->config = $config;
$this->extractCustomerData = $extractCustomerData;
$this->createCustomerAccount = $createCustomerAccount;
}
@@ -72,11 +67,7 @@ public function resolve(
throw new GraphQlInputException(__('"input" value should be specified'));
}
- if (!$this->scopeConfig->getValue(
- self::XML_PATH_NEWSLETTER_ACTIVE,
- ScopeInterface::SCOPE_STORE
- )
- ) {
+ if (!$this->config->isActive()) {
$args['input']['is_subscribed'] = false;
}
diff --git a/app/code/Magento/Newsletter/Model/Config.php b/app/code/Magento/Newsletter/Model/Config.php
new file mode 100644
index 0000000000000..0da18b661399d
--- /dev/null
+++ b/app/code/Magento/Newsletter/Model/Config.php
@@ -0,0 +1,53 @@
+scopeConfig = $scopeConfig;
+ $this->resourceConfig = $resourceConfig;
+ }
+
+ /**
+ * Returns newsletter's enabled status
+ *
+ * @return bool
+ */
+ public function isActive()
+ {
+ return $this->scopeConfig->isSetFlag(self::XML_PATH_NEWSLETTER_ACTIVE);
+ }
+}
From ed7ce471751045357ec8a0cdcf5b9858625625c9 Mon Sep 17 00:00:00 2001
From: Lilit Sargsyan
Date: Wed, 17 Jul 2019 17:45:08 +0400
Subject: [PATCH 052/593] MC-15341: Default product numbers to display results
in poor display on Desktop
- Updated automated test script
---
.../CheckDefaultNumberProductsToDisplayTest.xml | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
index 6fb4f816d0b9e..5718844fb6df3 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
@@ -21,12 +21,6 @@
-
-
-
-
-
-
@@ -183,6 +177,14 @@
+
+
+
+
+
+
+
+
From 0f43b32c2d5a22bee305fa8bc211f835d4590b59 Mon Sep 17 00:00:00 2001
From: Ihor Sviziev
Date: Thu, 18 Jul 2019 09:33:52 +0300
Subject: [PATCH 053/593] magento/magento2#22880 Fix random fails during
parallel SCD execution
Remove unit tests that were failing because we not doing anything in CLI mode
---
.../Test/Unit/Asset/LockerProcessTest.php | 44 -------------------
1 file changed, 44 deletions(-)
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php
index aba3ff1099cc0..ca065bbbc4f6e 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php
@@ -64,24 +64,6 @@ protected function setUp()
);
}
- /**
- * Test for lockProcess method
- *
- * @param string $method
- *
- * @dataProvider dataProviderTestLockProcess
- */
- public function testLockProcess($method)
- {
- $this->stateMock->expects(self::once())->method('getMode')->willReturn(State::MODE_DEVELOPER);
- $this->filesystemMock->expects(self::once())
- ->method('getDirectoryWrite')
- ->with(DirectoryList::VAR_DIR)
- ->willReturn($this->$method());
-
- $this->lockerProcess->lockProcess(self::LOCK_NAME);
- }
-
public function testNotLockProcessInProductionMode()
{
$this->stateMock->expects(self::once())->method('getMode')->willReturn(State::MODE_PRODUCTION);
@@ -90,21 +72,6 @@ public function testNotLockProcessInProductionMode()
$this->lockerProcess->lockProcess(self::LOCK_NAME);
}
- /**
- * Test for unlockProcess method
- */
- public function testUnlockProcess()
- {
- $this->stateMock->expects(self::exactly(2))->method('getMode')->willReturn(State::MODE_DEVELOPER);
- $this->filesystemMock->expects(self::once())
- ->method('getDirectoryWrite')
- ->with(DirectoryList::VAR_DIR)
- ->willReturn($this->getTmpDirectoryMockFalse(1));
-
- $this->lockerProcess->lockProcess(self::LOCK_NAME);
- $this->lockerProcess->unlockProcess();
- }
-
public function testNotUnlockProcessInProductionMode()
{
$this->stateMock->expects(self::exactly(2))->method('getMode')->willReturn(State::MODE_PRODUCTION);
@@ -114,17 +81,6 @@ public function testNotUnlockProcessInProductionMode()
$this->lockerProcess->unlockProcess();
}
- /**
- * @return array
- */
- public function dataProviderTestLockProcess()
- {
- return [
- ['method' => 'getTmpDirectoryMockTrue'],
- ['method' => 'getTmpDirectoryMockFalse']
- ];
- }
-
/**
* @return WriteInterface|\PHPUnit_Framework_MockObject_MockObject
*/
From 8488c07818215f92b073a6e6b31b38162caa7aaf Mon Sep 17 00:00:00 2001
From: Valentin Hirson
Date: Thu, 18 Jul 2019 12:23:22 +0200
Subject: [PATCH 054/593] #23766 - fix pattern validation on form submit
---
lib/web/mage/validation.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js
index a7a5ade2ae9f3..8c1b68be3ba6c 100644
--- a/lib/web/mage/validation.js
+++ b/lib/web/mage/validation.js
@@ -559,7 +559,7 @@
/* eslint-enable max-len */
'pattern': [
function (value, element, param) {
- return this.optional(element) || param.test(value);
+ return this.optional(element) || new RegExp(param).test(value);
},
$.mage.__('Invalid format.')
],
From 04739de66cf39fe6f1de50cf93dd05471ec03c7d Mon Sep 17 00:00:00 2001
From: Veronika Kurochkina
Date: Fri, 26 Jul 2019 17:14:04 +0300
Subject: [PATCH 055/593] MAGETWO-44170: Not pass function test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Added automated test script.
---
.../Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
index b1af17fb25838..cac5e209af9fd 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -395,6 +395,7 @@
+
From bf54e6f78a4eef5081c14cb0bd95c1967dad4f01 Mon Sep 17 00:00:00 2001
From: Oleksandr Gorkun
Date: Fri, 26 Jul 2019 13:58:37 -0500
Subject: [PATCH 056/593] MAGETWO-96975: Remove __sleep and __wakeup from code
---
app/code/Magento/Authorization/Model/Role.php | 10 +
.../Magento/Backend/Model/Auth/Session.php | 197 ++++++++++++-
.../Backend/Model/Auth/SessionAclHydrator.php | 36 +++
.../Model/Auth/SessionUserHydrator.php | 54 ++++
.../Spi/SessionAclHydratorInterface.php | 34 +++
.../Spi/SessionUserHydratorInterface.php | 34 +++
.../Test/Unit/Model/Auth/SessionTest.php | 273 ------------------
.../Model/Authorization/RoleLocatorTest.php | 39 ---
.../Test/Unit/Model/Locale/ManagerTest.php | 130 ---------
app/code/Magento/Backend/composer.json | 1 +
app/code/Magento/Backend/etc/di.xml | 4 +
.../Model/ResourceModel/Eav/Attribute.php | 10 +
.../Config/Model/Config/Backend/Encrypted.php | 10 +
.../Product/Type/Configurable/Attribute.php | 13 +-
.../Configurable/Attribute/Collection.php | 14 +-
app/code/Magento/Customer/Model/Attribute.php | 10 +
.../Magento/Eav/Model/Entity/Attribute.php | 14 +-
.../Entity/Attribute/AbstractAttribute.php | 10 +
.../Model/ResourceModel/Entity/Attribute.php | 10 +
.../System/Config/Fieldset/GroupTest.php | 110 -------
.../Condition/CanViewNotificationTest.php | 16 +-
app/code/Magento/Store/Model/Store.php | 10 +
app/code/Magento/User/Model/User.php | 14 +
.../AdminSessionUserContextTest.php | 89 ------
.../Magento/User/Test/Unit/Model/UserTest.php | 25 --
.../Backend/Model/Auth/SessionTest.php | 39 ++-
.../Backend/Model/Locale/ResolverTest.php | 15 +-
.../Rule/Design/SerializationAware.php | 34 +++
.../resources/rulesets/design.xml | 25 ++
.../Magento/Test/Php/_files/phpmd/ruleset.xml | 1 +
.../Magento/Framework/App/AreaList/Proxy.php | 15 +-
.../Magento/Framework/App/Response/Http.php | 17 +-
.../App/Route/ConfigInterface/Proxy.php | 12 +-
.../App/Test/Unit/Response/HttpTest.php | 39 ---
lib/internal/Magento/Framework/DB/Select.php | 16 +-
.../Framework/DB/Select/RendererProxy.php | 12 +-
.../Magento/Framework/Data/Collection.php | 10 +
.../Framework/Data/Collection/AbstractDb.php | 10 +
.../DataObject/Copy/Config/Data/Proxy.php | 12 +-
.../Framework/Interception/Interceptor.php | 10 +
.../Model/AbstractExtensibleModel.php | 10 +
.../Magento/Framework/Model/AbstractModel.php | 10 +
.../Model/ResourceModel/Db/AbstractDb.php | 22 +-
.../Db/Collection/AbstractCollection.php | 10 +
.../Framework/Mview/Config/Data/Proxy.php | 12 +-
.../Framework/Translate/Inline/Proxy.php | 12 +-
.../Magento/Framework/View/Layout/Proxy.php | 12 +-
47 files changed, 773 insertions(+), 749 deletions(-)
create mode 100644 app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php
create mode 100644 app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php
create mode 100644 app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php
create mode 100644 app/code/Magento/Backend/Spi/SessionUserHydratorInterface.php
delete mode 100644 app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php
delete mode 100644 app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php
delete mode 100644 app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php
delete mode 100644 app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php
delete mode 100644 app/code/Magento/User/Test/Unit/Model/Authorization/AdminSessionUserContextTest.php
create mode 100644 dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php
diff --git a/app/code/Magento/Authorization/Model/Role.php b/app/code/Magento/Authorization/Model/Role.php
index fc32fbcaa2e98..719059c6f67c4 100644
--- a/app/code/Magento/Authorization/Model/Role.php
+++ b/app/code/Magento/Authorization/Model/Role.php
@@ -52,18 +52,28 @@ public function __construct( //phpcs:ignore Generic.CodeAnalysis.UselessOverridi
/**
* @inheritDoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = parent::__sleep();
return array_diff($properties, ['_resource', '_resourceCollection']);
}
/**
* @inheritDoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_resource = $objectManager->get(\Magento\Authorization\Model\ResourceModel\Role::class);
diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php
index 809b78b7b98bc..d34323d4bec87 100644
--- a/app/code/Magento/Backend/Model/Auth/Session.php
+++ b/app/code/Magento/Backend/Model/Auth/Session.php
@@ -5,17 +5,20 @@
*/
namespace Magento\Backend\Model\Auth;
+use Magento\Framework\Acl;
+use Magento\Framework\AclFactory;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory;
use Magento\Framework\Stdlib\CookieManagerInterface;
+use Magento\Backend\Spi\SessionUserHydratorInterface;
+use Magento\Backend\Spi\SessionAclHydratorInterface;
+use Magento\User\Model\User;
+use Magento\User\Model\UserFactory;
/**
* Backend Auth session model
*
* @api
- * @method \Magento\User\Model\User|null getUser()
- * @method \Magento\Backend\Model\Auth\Session setUser(\Magento\User\Model\User $value)
- * @method \Magento\Framework\Acl|null getAcl()
- * @method \Magento\Backend\Model\Auth\Session setAcl(\Magento\Framework\Acl $value)
* @method int getUpdatedAt()
* @method \Magento\Backend\Model\Auth\Session setUpdatedAt(int $value)
*
@@ -56,6 +59,36 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage
*/
protected $_config;
+ /**
+ * @var SessionUserHydratorInterface
+ */
+ private $userHydrator;
+
+ /**
+ * @var SessionAclHydratorInterface
+ */
+ private $aclHydrator;
+
+ /**
+ * @var UserFactory
+ */
+ private $userFactory;
+
+ /**
+ * @var AclFactory
+ */
+ private $aclFactory;
+
+ /**
+ * @var User|null
+ */
+ private $user;
+
+ /**
+ * @var Acl|null
+ */
+ private $acl;
+
/**
* @param \Magento\Framework\App\Request\Http $request
* @param \Magento\Framework\Session\SidResolverInterface $sidResolver
@@ -70,6 +103,10 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage
* @param \Magento\Backend\Model\UrlInterface $backendUrl
* @param \Magento\Backend\App\ConfigInterface $config
* @throws \Magento\Framework\Exception\SessionException
+ * @param SessionUserHydratorInterface|null $userHydrator
+ * @param SessionAclHydratorInterface|null $aclHydrator
+ * @param UserFactory|null $userFactory
+ * @param AclFactory|null $aclFactory
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -84,11 +121,19 @@ public function __construct(
\Magento\Framework\App\State $appState,
\Magento\Framework\Acl\Builder $aclBuilder,
\Magento\Backend\Model\UrlInterface $backendUrl,
- \Magento\Backend\App\ConfigInterface $config
+ \Magento\Backend\App\ConfigInterface $config,
+ ?SessionUserHydratorInterface $userHydrator = null,
+ ?SessionAclHydratorInterface $aclHydrator = null,
+ ?UserFactory $userFactory = null,
+ ?AclFactory $aclFactory = null
) {
$this->_config = $config;
$this->_aclBuilder = $aclBuilder;
$this->_backendUrl = $backendUrl;
+ $this->userHydrator = $userHydrator ?? ObjectManager::getInstance()->get(SessionUserHydratorInterface::class);
+ $this->aclHydrator = $aclHydrator ?? ObjectManager::getInstance()->get(SessionAclHydratorInterface::class);
+ $this->userFactory = $userFactory ?? ObjectManager::getInstance()->get(UserFactory::class);
+ $this->aclFactory = $aclFactory ?? ObjectManager::getInstance()->get(AclFactory::class);
parent::__construct(
$request,
$sidResolver,
@@ -232,6 +277,16 @@ public function processLogin()
return $this;
}
+ /**
+ * @inheritDoc
+ */
+ public function destroy(array $options = null)
+ {
+ $this->user = null;
+ $this->acl = null;
+ parent::destroy($options);
+ }
+
/**
* Process of configuring of current auth storage when logout was performed
*
@@ -255,4 +310,136 @@ public function isValidForPath($path)
{
return true;
}
+
+ /**
+ * Logged-in user.
+ *
+ * @return User|null
+ */
+ public function getUser()
+ {
+ if (!$this->user) {
+ $userData = $this->getUserData();
+ if ($userData) {
+ /** @var User $user */
+ $user = $this->userFactory->create();
+ $this->userHydrator->hydrate($user, $userData);
+ $this->user = $user;
+ }
+ }
+
+ return $this->user;
+ }
+
+ /**
+ * Set logged-in user instance.
+ *
+ * @param User|null $user
+ * @return Session
+ */
+ public function setUser($user)
+ {
+ $this->setUserData(null);
+ if ($user) {
+ $this->setUserData($this->userHydrator->extract($user));
+ }
+ $this->user = $user;
+
+ return $this;
+ }
+
+ /**
+ * Is user logged in?
+ *
+ * @return bool
+ */
+ public function hasUser()
+ {
+ return $this->user || $this->hasUserData();
+ }
+
+ /**
+ * Remove logged-in user.
+ *
+ * @return Session
+ */
+ public function unsUser()
+ {
+ $this->user = null;
+ return $this->unsUserData();
+ }
+
+ /**
+ * Logged-in user's ACL data.
+ *
+ * @return Acl|null
+ */
+ public function getAcl()
+ {
+ if (!$this->acl) {
+ $aclData = $this->getUserAclData();
+ if ($aclData) {
+ /** @var Acl $acl */
+ $acl = $this->aclFactory->create();
+ $this->aclHydrator->hydrate($acl, $aclData);
+ $this->acl = $acl;
+ }
+ }
+
+ return $this->acl;
+ }
+
+ /**
+ * Set logged-in user's ACL data instance.
+ *
+ * @param Acl|null $acl
+ * @return Session
+ */
+ public function setAcl($acl)
+ {
+ $this->setUserAclData(null);
+ if ($acl) {
+ $this->setUserAclData($this->aclHydrator->extract($acl));
+ }
+ $this->acl = $acl;
+
+ return $this;
+ }
+
+ /**
+ * Whether ACL data is present.
+ *
+ * @return bool
+ */
+ public function hasAcl()
+ {
+ return $this->acl || $this->hasUserAclData();
+ }
+
+ /**
+ * Remove ACL data.
+ *
+ * @return Session
+ */
+ public function unsAcl()
+ {
+ $this->acl = null;
+ return $this->unsUserAclData();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function writeClose()
+ {
+ //Updating data in session in case these objects has been changed.
+ if ($this->user) {
+ $this->setUser($this->user);
+ }
+ if ($this->acl) {
+ $this->setAcl($this->acl);
+ }
+
+ parent::writeClose();
+ }
}
diff --git a/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php b/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php
new file mode 100644
index 0000000000000..34e01be696672
--- /dev/null
+++ b/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php
@@ -0,0 +1,36 @@
+ $acl->_rules, 'resources' => $acl->_resources, 'roles' => $acl->_roleRegistry];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function hydrate(Acl $target, array $data): void
+ {
+ $target->_rules = $data['rules'];
+ $target->_resources = $data['resources'];
+ $target->_roleRegistry = $data['roles'];
+ }
+}
diff --git a/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php b/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php
new file mode 100644
index 0000000000000..6dee8b7b302c8
--- /dev/null
+++ b/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php
@@ -0,0 +1,54 @@
+roleFactory = $roleFactory;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function extract(User $user): array
+ {
+ return ['data' => $user->getData(), 'role_data' => $user->getRole()->getData()];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function hydrate(User $target, array $data): void
+ {
+ $target->setData($data['data']);
+ /** @var Role $role */
+ $role = $this->roleFactory->create();
+ $role->setData($data['role_data']);
+ $target->setData('extracted_role', $role);
+ $target->getRole();
+ }
+}
diff --git a/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php b/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php
new file mode 100644
index 0000000000000..7227cc92fcc8e
--- /dev/null
+++ b/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php
@@ -0,0 +1,34 @@
+cookieMetadataFactory = $this->createPartialMock(
- \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory::class,
- ['createPublicCookieMetadata']
- );
-
- $this->config = $this->createPartialMock(\Magento\Backend\App\Config::class, ['getValue']);
- $this->cookieManager = $this->createPartialMock(
- \Magento\Framework\Stdlib\Cookie\PhpCookieManager::class,
- ['getCookie', 'setPublicCookie']
- );
- $this->storage = $this->createPartialMock(
- \Magento\Framework\Session\Storage::class,
- ['getUser', 'getAcl', 'setAcl']
- );
- $this->sessionConfig = $this->createPartialMock(
- \Magento\Framework\Session\Config::class,
- ['getCookiePath', 'getCookieDomain', 'getCookieSecure', 'getCookieHttpOnly']
- );
- $this->aclBuilder = $this->getMockBuilder(\Magento\Framework\Acl\Builder::class)
- ->disableOriginalConstructor()
- ->getMock();
- $objectManager = new ObjectManager($this);
- $this->session = $objectManager->getObject(
- \Magento\Backend\Model\Auth\Session::class,
- [
- 'config' => $this->config,
- 'sessionConfig' => $this->sessionConfig,
- 'cookieManager' => $this->cookieManager,
- 'cookieMetadataFactory' => $this->cookieMetadataFactory,
- 'storage' => $this->storage,
- 'aclBuilder' => $this->aclBuilder
- ]
- );
- }
-
- protected function tearDown()
- {
- $this->config = null;
- $this->sessionConfig = null;
- $this->session = null;
- }
-
- /**
- * @dataProvider refreshAclDataProvider
- * @param $isUserPassedViaParams
- */
- public function testRefreshAcl($isUserPassedViaParams)
- {
- $aclMock = $this->getMockBuilder(\Magento\Framework\Acl::class)->disableOriginalConstructor()->getMock();
- $this->aclBuilder->expects($this->any())->method('getAcl')->willReturn($aclMock);
- $userMock = $this->getMockBuilder(\Magento\User\Model\User::class)
- ->setMethods(['getReloadAclFlag', 'setReloadAclFlag', 'unsetData', 'save'])
- ->disableOriginalConstructor()
- ->getMock();
- $userMock->expects($this->any())->method('getReloadAclFlag')->willReturn(true);
- $userMock->expects($this->once())->method('setReloadAclFlag')->with('0')->willReturnSelf();
- $userMock->expects($this->once())->method('save');
- $this->storage->expects($this->once())->method('setAcl')->with($aclMock);
- $this->storage->expects($this->any())->method('getAcl')->willReturn($aclMock);
- if ($isUserPassedViaParams) {
- $this->session->refreshAcl($userMock);
- } else {
- $this->storage->expects($this->once())->method('getUser')->willReturn($userMock);
- $this->session->refreshAcl();
- }
- $this->assertSame($aclMock, $this->session->getAcl());
- }
-
- /**
- * @return array
- */
- public function refreshAclDataProvider()
- {
- return [
- 'User set via params' => [true],
- 'User set to session object' => [false]
- ];
- }
-
- public function testIsLoggedInPositive()
- {
- $user = $this->createPartialMock(\Magento\User\Model\User::class, ['getId', '__wakeup']);
- $user->expects($this->once())
- ->method('getId')
- ->will($this->returnValue(1));
-
- $this->storage->expects($this->any())
- ->method('getUser')
- ->will($this->returnValue($user));
-
- $this->assertTrue($this->session->isLoggedIn());
- }
-
- public function testProlong()
- {
- $name = session_name();
- $cookie = 'cookie';
- $lifetime = 900;
- $path = '/';
- $domain = 'magento2';
- $secure = true;
- $httpOnly = true;
-
- $this->config->expects($this->once())
- ->method('getValue')
- ->with(\Magento\Backend\Model\Auth\Session::XML_PATH_SESSION_LIFETIME)
- ->willReturn($lifetime);
- $cookieMetadata = $this->createMock(\Magento\Framework\Stdlib\Cookie\PublicCookieMetadata::class);
- $cookieMetadata->expects($this->once())
- ->method('setDuration')
- ->with($lifetime)
- ->will($this->returnSelf());
- $cookieMetadata->expects($this->once())
- ->method('setPath')
- ->with($path)
- ->will($this->returnSelf());
- $cookieMetadata->expects($this->once())
- ->method('setDomain')
- ->with($domain)
- ->will($this->returnSelf());
- $cookieMetadata->expects($this->once())
- ->method('setSecure')
- ->with($secure)
- ->will($this->returnSelf());
- $cookieMetadata->expects($this->once())
- ->method('setHttpOnly')
- ->with($httpOnly)
- ->will($this->returnSelf());
-
- $this->cookieMetadataFactory->expects($this->once())
- ->method('createPublicCookieMetadata')
- ->will($this->returnValue($cookieMetadata));
-
- $this->cookieManager->expects($this->once())
- ->method('getCookie')
- ->with($name)
- ->will($this->returnValue($cookie));
- $this->cookieManager->expects($this->once())
- ->method('setPublicCookie')
- ->with($name, $cookie, $cookieMetadata);
-
- $this->sessionConfig->expects($this->once())
- ->method('getCookiePath')
- ->will($this->returnValue($path));
- $this->sessionConfig->expects($this->once())
- ->method('getCookieDomain')
- ->will($this->returnValue($domain));
- $this->sessionConfig->expects($this->once())
- ->method('getCookieSecure')
- ->will($this->returnValue($secure));
- $this->sessionConfig->expects($this->once())
- ->method('getCookieHttpOnly')
- ->will($this->returnValue($httpOnly));
-
- $this->session->prolong();
-
- $this->assertLessThanOrEqual(time(), $this->session->getUpdatedAt());
- }
-
- /**
- * @dataProvider isAllowedDataProvider
- * @param bool $isUserDefined
- * @param bool $isAclDefined
- * @param bool $isAllowed
- * @param true $expectedResult
- */
- public function testIsAllowed($isUserDefined, $isAclDefined, $isAllowed, $expectedResult)
- {
- $userAclRole = 'userAclRole';
- if ($isAclDefined) {
- $aclMock = $this->getMockBuilder(\Magento\Framework\Acl::class)->disableOriginalConstructor()->getMock();
- $this->storage->expects($this->any())->method('getAcl')->willReturn($aclMock);
- }
- if ($isUserDefined) {
- $userMock = $this->getMockBuilder(\Magento\User\Model\User::class)->disableOriginalConstructor()->getMock();
- $this->storage->expects($this->once())->method('getUser')->willReturn($userMock);
- }
- if ($isAclDefined && $isUserDefined) {
- $userMock->expects($this->any())->method('getAclRole')->willReturn($userAclRole);
- $aclMock->expects($this->once())->method('isAllowed')->with($userAclRole)->willReturn($isAllowed);
- }
-
- $this->assertEquals($expectedResult, $this->session->isAllowed('resource'));
- }
-
- /**
- * @return array
- */
- public function isAllowedDataProvider()
- {
- return [
- "Negative: User not defined" => [false, true, true, false],
- "Negative: Acl not defined" => [true, false, true, false],
- "Negative: Permission denied" => [true, true, false, false],
- "Positive: Permission granted" => [true, true, false, false],
- ];
- }
-
- /**
- * @dataProvider firstPageAfterLoginDataProvider
- * @param bool $isFirstPageAfterLogin
- */
- public function testFirstPageAfterLogin($isFirstPageAfterLogin)
- {
- $this->session->setIsFirstPageAfterLogin($isFirstPageAfterLogin);
- $this->assertEquals($isFirstPageAfterLogin, $this->session->isFirstPageAfterLogin());
- }
-
- /**
- * @return array
- */
- public function firstPageAfterLoginDataProvider()
- {
- return [
- 'First page after login' => [true],
- 'Not first page after login' => [false],
- ];
- }
-}
diff --git a/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php b/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php
deleted file mode 100644
index 77c428a6a116a..0000000000000
--- a/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-_sessionMock = $this->createPartialMock(
- \Magento\Backend\Model\Auth\Session::class,
- ['getUser', 'getAclRole', 'hasUser']
- );
- $this->_model = new \Magento\Backend\Model\Authorization\RoleLocator($this->_sessionMock);
- }
-
- public function testGetAclRoleIdReturnsCurrentUserAclRoleId()
- {
- $this->_sessionMock->expects($this->once())->method('hasUser')->will($this->returnValue(true));
- $this->_sessionMock->expects($this->once())->method('getUser')->will($this->returnSelf());
- $this->_sessionMock->expects($this->once())->method('getAclRole')->will($this->returnValue('some_role'));
- $this->assertEquals('some_role', $this->_model->getAclRoleId());
- }
-}
diff --git a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php
deleted file mode 100644
index ce2b65a2249ac..0000000000000
--- a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php
+++ /dev/null
@@ -1,130 +0,0 @@
-_session = $this->createMock(\Magento\Backend\Model\Session::class);
-
- $this->_authSession = $this->createPartialMock(\Magento\Backend\Model\Auth\Session::class, ['getUser']);
-
- $this->_backendConfig = $this->getMockForAbstractClass(
- \Magento\Backend\App\ConfigInterface::class,
- [],
- '',
- false
- );
-
- $userMock = new \Magento\Framework\DataObject();
-
- $this->_authSession->expects($this->any())->method('getUser')->will($this->returnValue($userMock));
-
- $this->_translator = $this->getMockBuilder(\Magento\Framework\TranslateInterface::class)
- ->setMethods(['init', 'setLocale'])
- ->getMockForAbstractClass();
-
- $this->_translator->expects($this->any())->method('setLocale')->will($this->returnValue($this->_translator));
-
- $this->_translator->expects($this->any())->method('init')->will($this->returnValue(false));
-
- $this->_model = new \Magento\Backend\Model\Locale\Manager(
- $this->_session,
- $this->_authSession,
- $this->_translator,
- $this->_backendConfig
- );
- }
-
- /**
- * @return array
- */
- public function switchBackendInterfaceLocaleDataProvider()
- {
- return ['case1' => ['locale' => 'de_DE'], 'case2' => ['locale' => 'en_US']];
- }
-
- /**
- * @param string $locale
- * @dataProvider switchBackendInterfaceLocaleDataProvider
- * @covers \Magento\Backend\Model\Locale\Manager::switchBackendInterfaceLocale
- */
- public function testSwitchBackendInterfaceLocale($locale)
- {
- $this->_model->switchBackendInterfaceLocale($locale);
-
- $userInterfaceLocale = $this->_authSession->getUser()->getInterfaceLocale();
- $this->assertEquals($userInterfaceLocale, $locale);
-
- $sessionLocale = $this->_session->getSessionLocale();
- $this->assertEquals($sessionLocale, null);
- }
-
- /**
- * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale
- */
- public function testGetUserInterfaceLocaleDefault()
- {
- $locale = $this->_model->getUserInterfaceLocale();
-
- $this->assertEquals($locale, Resolver::DEFAULT_LOCALE);
- }
-
- /**
- * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale
- */
- public function testGetUserInterfaceLocale()
- {
- $this->_model->switchBackendInterfaceLocale('de_DE');
- $locale = $this->_model->getUserInterfaceLocale();
-
- $this->assertEquals($locale, 'de_DE');
- }
-
- /**
- * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale
- */
- public function testGetUserInterfaceGeneralLocale()
- {
- $this->_backendConfig->expects($this->any())
- ->method('getValue')
- ->with('general/locale/code')
- ->willReturn('test_locale');
- $locale = $this->_model->getUserInterfaceLocale();
- $this->assertEquals($locale, 'test_locale');
- }
-}
diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json
index f9408768136bb..e54bd136b3494 100644
--- a/app/code/Magento/Backend/composer.json
+++ b/app/code/Magento/Backend/composer.json
@@ -22,6 +22,7 @@
"magento/module-store": "*",
"magento/module-translation": "*",
"magento/module-ui": "*",
+ "magento/module-authorization": "*",
"magento/module-user": "*"
},
"suggest": {
diff --git a/app/code/Magento/Backend/etc/di.xml b/app/code/Magento/Backend/etc/di.xml
index c526703da9975..41db85b9323a8 100644
--- a/app/code/Magento/Backend/etc/di.xml
+++ b/app/code/Magento/Backend/etc/di.xml
@@ -198,4 +198,8 @@
Magento\Backend\Block\AnchorRenderer
+
+
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
index e7c98b218f5ad..e7c67463d8a5d 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
@@ -846,9 +846,14 @@ public function afterDelete()
/**
* @inheritdoc
* @since 100.0.9
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->unsetData('entity_type');
return array_diff(
parent::__sleep(),
@@ -859,9 +864,14 @@ public function __sleep()
/**
* @inheritdoc
* @since 100.0.9
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_indexerEavProcessor = $objectManager->get(\Magento\Catalog\Model\Indexer\Product\Flat\Processor::class);
diff --git a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
index 62d6531978d8a..0700d637d6144 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
@@ -48,9 +48,14 @@ public function __construct(
* Magic method called during class serialization
*
* @return string[]
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = parent::__sleep();
return array_diff($properties, ['_encryptor']);
}
@@ -59,9 +64,14 @@ public function __sleep()
* Magic method called during class un-serialization
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$this->_encryptor = \Magento\Framework\App\ObjectManager::getInstance()->get(
\Magento\Framework\Encryption\EncryptorInterface::class
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
index b013916cc221a..6dd3790191551 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
@@ -113,11 +113,12 @@ public function afterSave()
}
/**
- * Load configurable attribute by product and product's attribute
+ * Load configurable attribute by product and product's attribute.
*
* @param \Magento\Catalog\Model\Product $product
* @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
* @throws LocalizedException
+ * @return void
*/
public function loadByProductAndAttribute($product, $attribute)
{
@@ -263,9 +264,14 @@ public function setProductId($value)
/**
* @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return array_diff(
parent::__sleep(),
['metadataPool']
@@ -274,9 +280,14 @@ public function __sleep()
/**
* @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->metadataPool = $objectManager->get(MetadataPool::class);
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
index 8f2cc6ddb43ce..90b02cb112b5f 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
@@ -302,7 +302,9 @@ protected function _loadLabels()
}
/**
- * Load attribute options.
+ * Load related options' data.
+ *
+ * @return void
*/
protected function loadOptions()
{
@@ -355,9 +357,14 @@ protected function getIncludedOptions(array $usedProducts, AbstractAttribute $pr
/**
* @inheritdoc
* @since 100.0.6
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return array_diff(
parent::__sleep(),
[
@@ -374,9 +381,14 @@ public function __sleep()
/**
* @inheritdoc
* @since 100.0.6
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = ObjectManager::getInstance();
$this->_storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class);
diff --git a/app/code/Magento/Customer/Model/Attribute.php b/app/code/Magento/Customer/Model/Attribute.php
index 98a97872f15f4..ae714f993082e 100644
--- a/app/code/Magento/Customer/Model/Attribute.php
+++ b/app/code/Magento/Customer/Model/Attribute.php
@@ -202,9 +202,14 @@ public function canBeFilterableInGrid()
/**
* @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->unsetData('entity_type');
return array_diff(
parent::__sleep(),
@@ -214,9 +219,14 @@ public function __sleep()
/**
* @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->indexerRegistry = $objectManager->get(\Magento\Framework\Indexer\IndexerRegistry::class);
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php
index bb2477d4df827..1ca942ef1679c 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute.php
@@ -214,7 +214,7 @@ public function deleteEntity()
/**
* Load entity_attribute_id into $this by $this->attribute_set_id
*
- * @return $this
+ * Save additional data.
*/
public function loadEntityAttributeIdBySet()
{
@@ -311,7 +311,7 @@ public function beforeSave()
}
/**
- * Save additional data
+ * @inheritdoc
*
* @return $this
* @throws LocalizedException
@@ -489,9 +489,14 @@ public function getIdentities()
/**
* @inheritdoc
* @since 100.0.7
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->unsetData('attribute_set_info');
return array_diff(
parent::__sleep(),
@@ -502,9 +507,14 @@ public function __sleep()
/**
* @inheritdoc
* @since 100.0.7
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = ObjectManager::getInstance();
$this->_localeDate = $objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class);
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
index 3857118ae67ca..b7b712c493669 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
@@ -1405,9 +1405,14 @@ public function setExtensionAttributes(\Magento\Eav\Api\Data\AttributeExtensionI
/**
* @inheritdoc
* @since 100.0.7
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return array_diff(
parent::__sleep(),
[
@@ -1430,9 +1435,14 @@ public function __sleep()
/**
* @inheritdoc
* @since 100.0.7
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class);
diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
index 0e7a46125d872..5e7226e7a36dd 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
@@ -725,9 +725,14 @@ public function getValidAttributeIds($attributeIds)
*
* @return array
* @since 100.0.7
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = parent::__sleep();
$properties = array_diff($properties, ['_storeManager']);
return $properties;
@@ -738,9 +743,14 @@ public function __sleep()
*
* @return void
* @since 100.0.7
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$this->_storeManager = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Store\Model\StoreManagerInterface::class);
diff --git a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php
deleted file mode 100644
index e4de60cafb8ad..0000000000000
--- a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php
+++ /dev/null
@@ -1,110 +0,0 @@
-_group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class);
- $this->_element = $this->getMockForAbstractClass(
- \Magento\Framework\Data\Form\Element\AbstractElement::class,
- [],
- '',
- false,
- true,
- true,
- ['getHtmlId', 'getElementHtml', 'getName', 'getElements', 'getId']
- );
- $this->_element->expects($this->any())
- ->method('getHtmlId')
- ->will($this->returnValue('html id'));
- $this->_element->expects($this->any())
- ->method('getElementHtml')
- ->will($this->returnValue('element html'));
- $this->_element->expects($this->any())
- ->method('getName')
- ->will($this->returnValue('name'));
- $this->_element->expects($this->any())
- ->method('getElements')
- ->will($this->returnValue([]));
- $this->_element->expects($this->any())
- ->method('getId')
- ->will($this->returnValue('id'));
- $this->_user = $this->createMock(\Magento\User\Model\User::class);
- $this->_authSession = $this->createMock(\Magento\Backend\Model\Auth\Session::class);
- $this->_authSession->expects($this->any())
- ->method('__call')
- ->with('getUser')
- ->will($this->returnValue($this->_user));
- $this->_model = $helper->getObject(
- \Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Group::class,
- ['authSession' => $this->_authSession]
- );
- $this->_model->setGroup($this->_group);
- }
-
- /**
- * @param mixed $expanded
- * @param int $expected
- * @dataProvider isCollapseStateDataProvider
- */
- public function testIsCollapseState($expanded, $expected)
- {
- $this->_user->setExtra(['configState' => []]);
- $this->_element->setGroup(isset($expanded) ? ['expanded' => $expanded] : []);
- $html = $this->_model->render($this->_element);
- $this->assertContains(
- ' ',
- $html
- );
- }
-
- /**
- * @return array
- */
- public function isCollapseStateDataProvider()
- {
- return [
- [null, 0],
- [false, 0],
- ['', 0],
- [1, 1],
- ['1', 1],
- ];
- }
-}
diff --git a/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php b/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php
index 813c5f28bf4d9..a7f619863af56 100644
--- a/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php
+++ b/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php
@@ -12,6 +12,7 @@
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Backend\Model\Auth\Session;
use Magento\Framework\App\CacheInterface;
+use Magento\User\Model\User;
/**
* Class CanViewNotificationTest
@@ -36,6 +37,11 @@ class CanViewNotificationTest extends \PHPUnit\Framework\TestCase
/** @var $cacheStorageMock \PHPUnit_Framework_MockObject_MockObject|CacheInterface */
private $cacheStorageMock;
+ /**
+ * @var User|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $userMock;
+
public function setUp()
{
$this->cacheStorageMock = $this->getMockBuilder(CacheInterface::class)
@@ -44,7 +50,6 @@ public function setUp()
->getMock();
$this->sessionMock = $this->getMockBuilder(Session::class)
->disableOriginalConstructor()
- ->setMethods(['getUser', 'getId'])
->getMock();
$this->viewerLoggerMock = $this->getMockBuilder(Logger::class)
->disableOriginalConstructor()
@@ -52,6 +57,7 @@ public function setUp()
$this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class)
->disableOriginalConstructor()
->getMock();
+ $this->userMock = $this->createMock(User::class);
$objectManager = new ObjectManager($this);
$this->canViewNotification = $objectManager->getObject(
CanViewNotification::class,
@@ -68,8 +74,8 @@ public function testIsVisibleLoadDataFromCache()
{
$this->sessionMock->expects($this->once())
->method('getUser')
- ->willReturn($this->sessionMock);
- $this->sessionMock->expects($this->once())
+ ->willReturn($this->userMock);
+ $this->userMock->expects($this->once())
->method('getId')
->willReturn(1);
$this->cacheStorageMock->expects($this->once())
@@ -93,8 +99,8 @@ public function testIsVisible($expected, $version, $lastViewVersion)
->willReturn(false);
$this->sessionMock->expects($this->once())
->method('getUser')
- ->willReturn($this->sessionMock);
- $this->sessionMock->expects($this->once())
+ ->willReturn($this->userMock);
+ $this->userMock->expects($this->once())
->method('getId')
->willReturn(1);
$this->productMetadataMock->expects($this->once())
diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php
index dab9c55c216d9..eaabdedb4b7fd 100644
--- a/app/code/Magento/Store/Model/Store.php
+++ b/app/code/Magento/Store/Model/Store.php
@@ -423,9 +423,14 @@ public function __construct(
/**
* @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = parent::__sleep();
$properties = array_diff($properties, ['_coreFileStorageDatabase', '_config']);
return $properties;
@@ -435,9 +440,14 @@ public function __sleep()
* Init not serializable fields
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$this->_coreFileStorageDatabase = ObjectManager::getInstance()
->get(\Magento\MediaStorage\Helper\File\Storage\Database::class);
diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php
index dc0aa0cd38343..297f6d8814de2 100644
--- a/app/code/Magento/User/Model/User.php
+++ b/app/code/Magento/User/Model/User.php
@@ -212,9 +212,14 @@ protected function _construct()
* Removing dependencies and leaving only entity's properties.
*
* @return string[]
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = parent::__sleep();
return array_diff(
$properties,
@@ -240,9 +245,14 @@ public function __sleep()
* Restoring required objects after serialization.
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->serializer = $objectManager->get(Json::class);
@@ -409,6 +419,10 @@ public function getRoles()
*/
public function getRole()
{
+ if ($this->getData('extracted_role')) {
+ $this->_role = $this->getData('extracted_role');
+ $this->unsetData('extracted_role');
+ }
if (null === $this->_role) {
$this->_role = $this->_roleFactory->create();
$roles = $this->getRoles();
diff --git a/app/code/Magento/User/Test/Unit/Model/Authorization/AdminSessionUserContextTest.php b/app/code/Magento/User/Test/Unit/Model/Authorization/AdminSessionUserContextTest.php
deleted file mode 100644
index 23681c4b8da26..0000000000000
--- a/app/code/Magento/User/Test/Unit/Model/Authorization/AdminSessionUserContextTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-
- $this->adminSession = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class)
- ->disableOriginalConstructor()
- ->setMethods(['hasUser', 'getUser', 'getId'])
- ->getMock();
-
- $this->adminSessionUserContext = $this->objectManager->getObject(
- \Magento\User\Model\Authorization\AdminSessionUserContext::class,
- ['adminSession' => $this->adminSession]
- );
- }
-
- public function testGetUserIdExist()
- {
- $userId = 1;
-
- $this->setupUserId($userId);
-
- $this->assertEquals($userId, $this->adminSessionUserContext->getUserId());
- }
-
- public function testGetUserIdDoesNotExist()
- {
- $userId = null;
-
- $this->setupUserId($userId);
-
- $this->assertEquals($userId, $this->adminSessionUserContext->getUserId());
- }
-
- public function testGetUserType()
- {
- $this->assertEquals(UserContextInterface::USER_TYPE_ADMIN, $this->adminSessionUserContext->getUserType());
- }
-
- /**
- * @param int|null $userId
- * @return void
- */
- public function setupUserId($userId)
- {
- $this->adminSession->expects($this->once())
- ->method('hasUser')
- ->will($this->returnValue($userId));
-
- if ($userId) {
- $this->adminSession->expects($this->once())
- ->method('getUser')
- ->will($this->returnSelf());
-
- $this->adminSession->expects($this->once())
- ->method('getId')
- ->will($this->returnValue($userId));
- }
- }
-}
diff --git a/app/code/Magento/User/Test/Unit/Model/UserTest.php b/app/code/Magento/User/Test/Unit/Model/UserTest.php
index 670316c2500fc..ab06c8754b2f0 100644
--- a/app/code/Magento/User/Test/Unit/Model/UserTest.php
+++ b/app/code/Magento/User/Test/Unit/Model/UserTest.php
@@ -44,31 +44,6 @@ protected function setUp()
);
}
- /**
- * @return void
- */
- public function testSleep()
- {
- $excludedProperties = [
- '_eventManager',
- '_cacheManager',
- '_registry',
- '_appState',
- '_userData',
- '_config',
- '_validatorObject',
- '_roleFactory',
- '_encryptor',
- '_transportBuilder',
- '_storeManager',
- '_validatorBeforeSave'
- ];
- $actualResult = $this->model->__sleep();
- $this->assertNotEmpty($actualResult);
- $expectedResult = array_intersect($actualResult, $excludedProperties);
- $this->assertEmpty($expectedResult);
- }
-
/**
* @return void
*/
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php
index 5ca2bf1f73175..f1e7a10737604 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php
@@ -3,8 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Backend\Model\Auth;
+use Magento\TestFramework\Bootstrap as TestHelper;
+use Magento\TestFramework\Helper\Bootstrap;
+
/**
* @magentoAppArea adminhtml
* @magentoAppIsolation enabled
@@ -18,10 +22,15 @@ class SessionTest extends \PHPUnit\Framework\TestCase
private $auth;
/**
- * @var \Magento\Backend\Model\Auth\Session
+ * @var Session
*/
private $authSession;
+ /**
+ * @var SessionFactory
+ */
+ private $authSessionFactory;
+
/**
* @var \Magento\Framework\ObjectManagerInterface
*/
@@ -30,11 +39,12 @@ class SessionTest extends \PHPUnit\Framework\TestCase
protected function setUp()
{
parent::setUp();
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->objectManager = Bootstrap::getObjectManager();
$this->objectManager->get(\Magento\Framework\Config\ScopeInterface::class)
->setCurrentScope(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE);
$this->auth = $this->objectManager->create(\Magento\Backend\Model\Auth::class);
- $this->authSession = $this->objectManager->create(\Magento\Backend\Model\Auth\Session::class);
+ $this->authSession = $this->objectManager->create(Session::class);
+ $this->authSessionFactory = $this->objectManager->get(SessionFactory::class);
$this->auth->setAuthStorage($this->authSession);
$this->auth->logout();
}
@@ -52,8 +62,8 @@ public function testIsLoggedIn($loggedIn)
{
if ($loggedIn) {
$this->auth->login(
- \Magento\TestFramework\Bootstrap::ADMIN_NAME,
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestHelper::ADMIN_NAME,
+ TestHelper::ADMIN_PASSWORD
);
}
$this->assertEquals($loggedIn, $this->authSession->isLoggedIn());
@@ -63,4 +73,23 @@ public function loginDataProvider()
{
return [[false], [true]];
}
+
+ /**
+ * Check that persisting user data is working.
+ */
+ public function testStorage()
+ {
+ $this->auth->login(TestHelper::ADMIN_NAME, TestHelper::ADMIN_PASSWORD);
+ $user = $this->authSession->getUser();
+ $acl = $this->authSession->getAcl();
+ /** @var Session $session */
+ $session = $this->authSessionFactory->create();
+ $persistedUser = $session->getUser();
+ $persistedAcl = $session->getAcl();
+
+ $this->assertEquals($user->getData(), $persistedUser->getData());
+ $this->assertEquals($user->getAclRole(), $persistedUser->getAclRole());
+ $this->assertEquals($acl->getRoles(), $persistedAcl->getRoles());
+ $this->assertEquals($acl->getResources(), $persistedAcl->getResources());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php
index d1252be2c4b53..88662a65c7428 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php
@@ -3,9 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Backend\Model\Locale;
use Magento\Framework\Locale\Resolver;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\User\Model\User;
/**
* @magentoAppArea adminhtml
@@ -20,7 +23,7 @@ class ResolverTest extends \PHPUnit\Framework\TestCase
protected function setUp()
{
parent::setUp();
- $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ $this->_model = Bootstrap::getObjectManager()->create(
\Magento\Backend\Model\Locale\Resolver::class
);
}
@@ -38,12 +41,12 @@ public function testSetLocaleWithDefaultLocale()
*/
public function testSetLocaleWithBaseInterfaceLocale()
{
- $user = new \Magento\Framework\DataObject();
- $session = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ $user = Bootstrap::getObjectManager()->create(User::class);
+ $session = Bootstrap::getObjectManager()->get(
\Magento\Backend\Model\Auth\Session::class
);
$session->setUser($user);
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ Bootstrap::getObjectManager()->get(
\Magento\Backend\Model\Auth\Session::class
)->getUser()->setInterfaceLocale(
'fr_FR'
@@ -56,7 +59,7 @@ public function testSetLocaleWithBaseInterfaceLocale()
*/
public function testSetLocaleWithSessionLocale()
{
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ Bootstrap::getObjectManager()->get(
\Magento\Backend\Model\Session::class
)->setSessionLocale(
'es_ES'
@@ -69,7 +72,7 @@ public function testSetLocaleWithSessionLocale()
*/
public function testSetLocaleWithRequestLocale()
{
- $request = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ $request = Bootstrap::getObjectManager()
->get(\Magento\Framework\App\RequestInterface::class);
$request->setPostValue(['locale' => 'de_DE']);
$this->_checkSetLocale('de_DE');
diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php
new file mode 100644
index 0000000000000..e38fba8558bad
--- /dev/null
+++ b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php
@@ -0,0 +1,34 @@
+getName() === '__wakeup' || $method->getName() === '__sleep') {
+ $this->addViolation($method, [$method->getName(), $method->getParent()->getFullQualifiedName()]);
+ }
+ }
+}
diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml
index 53f2fe4a0084e..5f2461812bab7 100644
--- a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml
+++ b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml
@@ -60,6 +60,31 @@ class OrderProcessor
$currentOrder = $this->session->get('current_order');
...
}
+}
+ ]]>
+
+
+
+
+
+
+ 2
+
+
+
diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml
index 0e3b5fa3d341c..e65a9a089da9e 100644
--- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml
+++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml
@@ -45,5 +45,6 @@
+
diff --git a/lib/internal/Magento/Framework/App/AreaList/Proxy.php b/lib/internal/Magento/Framework/App/AreaList/Proxy.php
index d080e4cabbd87..09115add57190 100644
--- a/lib/internal/Magento/Framework/App/AreaList/Proxy.php
+++ b/lib/internal/Magento/Framework/App/AreaList/Proxy.php
@@ -3,10 +3,11 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\App\AreaList;
/**
- * Application area list
+ * Proxy for area list.
*/
class Proxy extends \Magento\Framework\App\AreaList implements
\Magento\Framework\ObjectManager\NoninterceptableInterface
@@ -57,12 +58,17 @@ public function __construct(
}
/**
- * Sleep magic method.
+ * Remove links to other objects.
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return ['_subject', '_isShared'];
}
@@ -70,9 +76,14 @@ public function __sleep()
* Retrieve ObjectManager from global scope
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/App/Response/Http.php b/lib/internal/Magento/Framework/App/Response/Http.php
index e6fff90837d9d..0c2523faa2ae1 100644
--- a/lib/internal/Magento/Framework/App/Response/Http.php
+++ b/lib/internal/Magento/Framework/App/Response/Http.php
@@ -1,10 +1,9 @@
cookieManager = $objectManager->create(\Magento\Framework\Stdlib\CookieManagerInterface::class);
$this->cookieMetadataFactory = $objectManager->get(
diff --git a/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php b/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
index 09dda9727b937..5e79315238f7d 100644
--- a/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
+++ b/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
@@ -60,12 +60,17 @@ public function __construct(
}
/**
- * Sleep magic method.
+ * Remove links to other objects.
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return ['_subject', '_isShared'];
}
@@ -73,9 +78,14 @@ public function __sleep()
* Retrieve ObjectManager from global scope
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Response/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Response/HttpTest.php
index efb35b7321c3b..9be68b379900a 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Response/HttpTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Response/HttpTest.php
@@ -290,45 +290,6 @@ public function testRepresentJson()
$this->assertEquals('json_string', $this->model->getBody('default'));
}
- /**
- *
- * @expectedException \RuntimeException
- * @expectedExceptionMessage ObjectManager isn't initialized
- */
- public function testWakeUpWithException()
- {
- /* ensure that the test preconditions are met */
- $objectManagerClass = new \ReflectionClass(\Magento\Framework\App\ObjectManager::class);
- $instanceProperty = $objectManagerClass->getProperty('_instance');
- $instanceProperty->setAccessible(true);
- $instanceProperty->setValue(null);
-
- $this->model->__wakeup();
- $this->assertNull($this->cookieMetadataFactoryMock);
- $this->assertNull($this->cookieManagerMock);
- }
-
- /**
- * Test for the magic method __wakeup
- *
- * @covers \Magento\Framework\App\Response\Http::__wakeup
- */
- public function testWakeUpWith()
- {
- $objectManagerMock = $this->createMock(\Magento\Framework\App\ObjectManager::class);
- $objectManagerMock->expects($this->once())
- ->method('create')
- ->with(\Magento\Framework\Stdlib\CookieManagerInterface::class)
- ->will($this->returnValue($this->cookieManagerMock));
- $objectManagerMock->expects($this->at(1))
- ->method('get')
- ->with(\Magento\Framework\Stdlib\Cookie\CookieMetadataFactory::class)
- ->will($this->returnValue($this->cookieMetadataFactoryMock));
-
- \Magento\Framework\App\ObjectManager::setInstance($objectManagerMock);
- $this->model->__wakeup();
- }
-
public function testSetXFrameOptions()
{
$value = 'DENY';
diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php
index 7399845215bb5..273d940babb91 100644
--- a/lib/internal/Magento/Framework/DB/Select.php
+++ b/lib/internal/Magento/Framework/DB/Select.php
@@ -400,7 +400,7 @@ public function useStraightJoin($flag = true)
/**
* Render STRAIGHT_JOIN clause
*
- * @param string $sql SQL query
+ * @param string $sql SQL query
* @return string
*/
protected function _renderStraightjoin($sql)
@@ -452,7 +452,7 @@ public function orderRand($field = null)
/**
* Render FOR UPDATE clause
*
- * @param string $sql SQL query
+ * @param string $sql SQL query
* @return string
*/
protected function _renderForupdate($sql)
@@ -509,13 +509,18 @@ public function assemble()
}
/**
- * Sleep magic method.
+ * Remove links to other objects.
*
* @return string[]
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = array_keys(get_object_vars($this));
$properties = array_diff(
$properties,
@@ -532,9 +537,14 @@ public function __sleep()
*
* @return void
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_adapter = $objectManager->get(ResourceConnection::class)->getConnection();
$this->selectRenderer = $objectManager->get(\Magento\Framework\DB\Select\SelectRenderer::class);
diff --git a/lib/internal/Magento/Framework/DB/Select/RendererProxy.php b/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
index b6d0803759842..e8030a6794a29 100644
--- a/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
+++ b/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
@@ -56,12 +56,17 @@ public function __construct(
}
/**
- * Sleep magic method.
+ * Remove links to other objects.
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return ['_subject', '_isShared'];
}
@@ -69,9 +74,14 @@ public function __sleep()
* Retrieve ObjectManager from global scope
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php
index 128d3d8e9fd3d..2f3aaad98dfe5 100644
--- a/lib/internal/Magento/Framework/Data/Collection.php
+++ b/lib/internal/Magento/Framework/Data/Collection.php
@@ -889,9 +889,14 @@ public function hasFlag($flag)
*
* @return string[]
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = array_keys(get_object_vars($this));
$properties = array_diff(
$properties,
@@ -907,9 +912,14 @@ public function __sleep()
*
* @return void
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_entityFactory = $objectManager->get(EntityFactoryInterface::class);
}
diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
index 8a22c9a1ce4fc..283d3684cf9fc 100644
--- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
@@ -896,9 +896,14 @@ private function getMainTableAlias()
/**
* @inheritdoc
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return array_diff(
parent::__sleep(),
['_fetchStrategy', '_logger', '_conn', 'extensionAttributesJoinProcessor']
@@ -908,9 +913,14 @@ public function __sleep()
/**
* @inheritdoc
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_logger = $objectManager->get(Logger::class);
diff --git a/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php b/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
index d8bb7a06e5b7d..6486643614d9e 100644
--- a/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
+++ b/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
@@ -57,12 +57,17 @@ public function __construct(
}
/**
- * Sleep magic method.
+ * Remove links to other objects.
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return ['_subject', '_isShared'];
}
@@ -70,9 +75,14 @@ public function __sleep()
* Retrieve ObjectManager from global scope
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/Interception/Interceptor.php b/lib/internal/Magento/Framework/Interception/Interceptor.php
index 07600c5168181..df1b680234220 100644
--- a/lib/internal/Magento/Framework/Interception/Interceptor.php
+++ b/lib/internal/Magento/Framework/Interception/Interceptor.php
@@ -62,9 +62,14 @@ public function ___callParent($method, array $arguments)
* Calls parent class sleep if defined, otherwise provides own implementation
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
if (method_exists(get_parent_class($this), '__sleep')) {
$properties = parent::__sleep();
} else {
@@ -78,9 +83,14 @@ public function __sleep()
* Causes Interceptor to be initialized
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
if (method_exists(get_parent_class($this), '__wakeup')) {
parent::__wakeup();
}
diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
index 949e002a14208..f80c05a959046 100644
--- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
@@ -362,17 +362,27 @@ private function populateExtensionAttributes(array $extensionAttributesData = []
/**
* @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return array_diff(parent::__sleep(), ['extensionAttributesFactory', 'customAttributeFactory']);
}
/**
* @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->extensionAttributesFactory = $objectManager->get(ExtensionAttributesFactory::class);
diff --git a/lib/internal/Magento/Framework/Model/AbstractModel.php b/lib/internal/Magento/Framework/Model/AbstractModel.php
index 8018c6176390f..91b1ff338250c 100644
--- a/lib/internal/Magento/Framework/Model/AbstractModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractModel.php
@@ -220,9 +220,14 @@ protected function _init($resourceModel)
* Remove unneeded properties from serialization
*
* @return string[]
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = array_keys(get_object_vars($this));
$properties = array_diff(
$properties,
@@ -244,9 +249,14 @@ public function __sleep()
* Init not serializable fields
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_registry = $objectManager->get(\Magento\Framework\Registry::class);
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
index fc0edf931fa9c..cf37296a7574e 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
@@ -157,9 +157,14 @@ public function __construct(
* Provide variables to serialize
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$properties = array_keys(get_object_vars($this));
$properties = array_diff($properties, ['_resources', '_connections']);
return $properties;
@@ -169,9 +174,14 @@ public function __sleep()
* Restore global dependencies
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->_resources = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\App\ResourceConnection::class);
}
@@ -220,9 +230,10 @@ protected function _setResource($connections, $tables = null)
}
/**
- * Set main entity table name and primary key field name
+ * Main table setter.
*
- * If field name is omitted {table_name}_id will be used
+ * Set main entity table name and primary key field name.
+ * If field name is omitted {table_name}_id will be used.
*
* @param string $mainTable
* @param string|null $idFieldName
@@ -255,7 +266,10 @@ public function getIdFieldName()
}
/**
- * Returns main table name - extracted from "module/table" style and validated by db adapter
+ * Main table getter.
+ *
+ * Returns main table name - extracted from "module/table" style and
+ * validated by db adapter.
*
* @throws LocalizedException
* @return string
@@ -544,7 +558,7 @@ protected function _prepareDataForSave(\Magento\Framework\Model\AbstractModel $o
}
/**
- * Check that model data fields that can be saved has really changed comparing with origData
+ * Check that model data fields that can be saved has really changed comparing with origData.
*
* @param \Magento\Framework\Model\AbstractModel $object
* @return bool
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
index cba5f133f53c8..1f7c15fe4a5bd 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
@@ -607,9 +607,14 @@ public function save()
/**
* @inheritdoc
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return array_diff(
parent::__sleep(),
['_resource', '_eventManager']
@@ -619,9 +624,14 @@ public function __sleep()
/**
* @inheritdoc
* @since 100.0.11
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_eventManager = $objectManager->get(\Magento\Framework\Event\ManagerInterface::class);
diff --git a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
index 470ba16bdd40c..cc7d831dc5e61 100644
--- a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
+++ b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
@@ -55,12 +55,17 @@ public function __construct(
}
/**
- * Sleep magic method.
+ * Remove links to objects.
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return ['subject', 'isShared'];
}
@@ -68,9 +73,14 @@ public function __sleep()
* Retrieve ObjectManager from global scope
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
index d2b0468bebde9..add5c0dee66ed 100644
--- a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
+++ b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
@@ -55,12 +55,17 @@ public function __construct(
}
/**
- * Sleep magic method.
+ * Remove links to other objects.
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return ['subject', 'isShared'];
}
@@ -68,9 +73,14 @@ public function __sleep()
* Retrieve ObjectManager from global scope
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/View/Layout/Proxy.php b/lib/internal/Magento/Framework/View/Layout/Proxy.php
index ec5ce761154ed..d4897bb5cd101 100644
--- a/lib/internal/Magento/Framework/View/Layout/Proxy.php
+++ b/lib/internal/Magento/Framework/View/Layout/Proxy.php
@@ -57,12 +57,17 @@ public function __construct(
}
/**
- * Sleep magic method.
+ * Remove links to objects.
*
* @return array
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __sleep()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
return ['_subject', '_isShared'];
}
@@ -70,9 +75,14 @@ public function __sleep()
* Retrieve ObjectManager from global scope
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.SerializationAware)
+ * @deprecated Do not use PHP serialization.
*/
public function __wakeup()
{
+ trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
+
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
From 417565976b8ead7d58db8eb8eb1e539fd1f3f22c Mon Sep 17 00:00:00 2001
From: Oleksandr Gorkun
Date: Mon, 29 Jul 2019 14:07:27 -0500
Subject: [PATCH 057/593] MAGETWO-96975: Remove __sleep and __wakeup from code
---
app/code/Magento/Authorization/Model/Role.php | 2 ++
app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php | 2 ++
app/code/Magento/Config/Model/Config/Backend/Encrypted.php | 2 ++
.../Model/Product/Type/Configurable/Attribute.php | 2 ++
.../Product/Type/Configurable/Attribute/Collection.php | 2 ++
app/code/Magento/Customer/Model/Attribute.php | 2 ++
app/code/Magento/Eav/Model/Entity/Attribute.php | 2 ++
.../Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php | 2 ++
app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php | 2 ++
app/code/Magento/Store/Model/Store.php | 2 ++
app/code/Magento/User/Model/User.php | 2 ++
lib/internal/Magento/Framework/App/AreaList/Proxy.php | 2 ++
lib/internal/Magento/Framework/App/Response/Http.php | 2 ++
.../Magento/Framework/App/Route/ConfigInterface/Proxy.php | 2 ++
lib/internal/Magento/Framework/DB/Select.php | 2 ++
lib/internal/Magento/Framework/DB/Select/RendererProxy.php | 2 ++
lib/internal/Magento/Framework/Data/Collection.php | 2 ++
lib/internal/Magento/Framework/Data/Collection/AbstractDb.php | 2 ++
.../Magento/Framework/DataObject/Copy/Config/Data/Proxy.php | 2 ++
lib/internal/Magento/Framework/Interception/Interceptor.php | 2 ++
.../Magento/Framework/Model/AbstractExtensibleModel.php | 2 ++
lib/internal/Magento/Framework/Model/AbstractModel.php | 2 ++
.../Magento/Framework/Model/ResourceModel/Db/AbstractDb.php | 2 ++
.../Model/ResourceModel/Db/Collection/AbstractCollection.php | 2 ++
lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php | 2 ++
lib/internal/Magento/Framework/Translate/Inline/Proxy.php | 2 ++
lib/internal/Magento/Framework/View/Layout/Proxy.php | 2 ++
27 files changed, 54 insertions(+)
diff --git a/app/code/Magento/Authorization/Model/Role.php b/app/code/Magento/Authorization/Model/Role.php
index 719059c6f67c4..40a1c7149a524 100644
--- a/app/code/Magento/Authorization/Model/Role.php
+++ b/app/code/Magento/Authorization/Model/Role.php
@@ -58,6 +58,7 @@ public function __construct( //phpcs:ignore Generic.CodeAnalysis.UselessOverridi
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = parent::__sleep();
@@ -72,6 +73,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
index e7c67463d8a5d..ce7e5cd42f282 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
@@ -852,6 +852,7 @@ public function afterDelete()
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->unsetData('entity_type');
@@ -870,6 +871,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
index 0700d637d6144..fc4d0a54bb13d 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
@@ -54,6 +54,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = parent::__sleep();
@@ -70,6 +71,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
index 6dd3790191551..0f34cb9f89474 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
@@ -270,6 +270,7 @@ public function setProductId($value)
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return array_diff(
@@ -286,6 +287,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
index 90b02cb112b5f..81dd0e42e3d3d 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
@@ -363,6 +363,7 @@ protected function getIncludedOptions(array $usedProducts, AbstractAttribute $pr
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return array_diff(
@@ -387,6 +388,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/Customer/Model/Attribute.php b/app/code/Magento/Customer/Model/Attribute.php
index ae714f993082e..773b9643298a5 100644
--- a/app/code/Magento/Customer/Model/Attribute.php
+++ b/app/code/Magento/Customer/Model/Attribute.php
@@ -208,6 +208,7 @@ public function canBeFilterableInGrid()
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->unsetData('entity_type');
@@ -225,6 +226,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php
index 1ca942ef1679c..b6522cdb2dda3 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute.php
@@ -495,6 +495,7 @@ public function getIdentities()
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->unsetData('attribute_set_info');
@@ -513,6 +514,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
index b7b712c493669..f33ec1d5423e3 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
@@ -1411,6 +1411,7 @@ public function setExtensionAttributes(\Magento\Eav\Api\Data\AttributeExtensionI
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return array_diff(
@@ -1441,6 +1442,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
index 5e7226e7a36dd..e2d7ef7ff3581 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
@@ -731,6 +731,7 @@ public function getValidAttributeIds($attributeIds)
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = parent::__sleep();
@@ -749,6 +750,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php
index eaabdedb4b7fd..76e8a7019007e 100644
--- a/app/code/Magento/Store/Model/Store.php
+++ b/app/code/Magento/Store/Model/Store.php
@@ -429,6 +429,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = parent::__sleep();
@@ -446,6 +447,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php
index 297f6d8814de2..a81e7af1e983a 100644
--- a/app/code/Magento/User/Model/User.php
+++ b/app/code/Magento/User/Model/User.php
@@ -218,6 +218,7 @@ protected function _construct()
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = parent::__sleep();
@@ -251,6 +252,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/lib/internal/Magento/Framework/App/AreaList/Proxy.php b/lib/internal/Magento/Framework/App/AreaList/Proxy.php
index 09115add57190..7a571814371fe 100644
--- a/lib/internal/Magento/Framework/App/AreaList/Proxy.php
+++ b/lib/internal/Magento/Framework/App/AreaList/Proxy.php
@@ -67,6 +67,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['_subject', '_isShared'];
@@ -82,6 +83,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/App/Response/Http.php b/lib/internal/Magento/Framework/App/Response/Http.php
index 0c2523faa2ae1..db3da6beeaa51 100644
--- a/lib/internal/Magento/Framework/App/Response/Http.php
+++ b/lib/internal/Magento/Framework/App/Response/Http.php
@@ -191,6 +191,7 @@ public function representJson($content)
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['content', 'isRedirect', 'statusCode', 'context', 'headers'];
@@ -207,6 +208,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$objectManager = ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php b/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
index 5e79315238f7d..4d2707d4a1c7e 100644
--- a/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
+++ b/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
@@ -69,6 +69,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['_subject', '_isShared'];
@@ -84,6 +85,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php
index 273d940babb91..c3da06a2ef924 100644
--- a/lib/internal/Magento/Framework/DB/Select.php
+++ b/lib/internal/Magento/Framework/DB/Select.php
@@ -519,6 +519,7 @@ public function assemble()
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = array_keys(get_object_vars($this));
@@ -543,6 +544,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/DB/Select/RendererProxy.php b/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
index e8030a6794a29..629b600303ebb 100644
--- a/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
+++ b/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
@@ -65,6 +65,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['_subject', '_isShared'];
@@ -80,6 +81,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php
index 2f3aaad98dfe5..2f0404f35822d 100644
--- a/lib/internal/Magento/Framework/Data/Collection.php
+++ b/lib/internal/Magento/Framework/Data/Collection.php
@@ -895,6 +895,7 @@ public function hasFlag($flag)
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = array_keys(get_object_vars($this));
@@ -918,6 +919,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
index 283d3684cf9fc..6ca28c738b3fe 100644
--- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
@@ -902,6 +902,7 @@ private function getMainTableAlias()
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return array_diff(
@@ -919,6 +920,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php b/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
index 6486643614d9e..a6e84b483261e 100644
--- a/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
+++ b/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
@@ -66,6 +66,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['_subject', '_isShared'];
@@ -81,6 +82,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/Interception/Interceptor.php b/lib/internal/Magento/Framework/Interception/Interceptor.php
index df1b680234220..3b9543e8cbb7b 100644
--- a/lib/internal/Magento/Framework/Interception/Interceptor.php
+++ b/lib/internal/Magento/Framework/Interception/Interceptor.php
@@ -68,6 +68,7 @@ public function ___callParent($method, array $arguments)
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
if (method_exists(get_parent_class($this), '__sleep')) {
@@ -89,6 +90,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
if (method_exists(get_parent_class($this), '__wakeup')) {
diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
index f80c05a959046..8ae5a234911cd 100644
--- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
@@ -368,6 +368,7 @@ private function populateExtensionAttributes(array $extensionAttributesData = []
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return array_diff(parent::__sleep(), ['extensionAttributesFactory', 'customAttributeFactory']);
@@ -381,6 +382,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/lib/internal/Magento/Framework/Model/AbstractModel.php b/lib/internal/Magento/Framework/Model/AbstractModel.php
index 91b1ff338250c..7905ae280ff0c 100644
--- a/lib/internal/Magento/Framework/Model/AbstractModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractModel.php
@@ -226,6 +226,7 @@ protected function _init($resourceModel)
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = array_keys(get_object_vars($this));
@@ -255,6 +256,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
index cf37296a7574e..8a0c78e4ff02b 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
@@ -163,6 +163,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$properties = array_keys(get_object_vars($this));
@@ -180,6 +181,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->_resources = \Magento\Framework\App\ObjectManager::getInstance()
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
index 1f7c15fe4a5bd..4af5fd7a2a986 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
@@ -613,6 +613,7 @@ public function save()
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return array_diff(
@@ -630,6 +631,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
parent::__wakeup();
diff --git a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
index cc7d831dc5e61..0de6c639812ad 100644
--- a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
+++ b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
@@ -64,6 +64,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['subject', 'isShared'];
@@ -79,6 +80,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
index add5c0dee66ed..0a9841adbfb08 100644
--- a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
+++ b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
@@ -64,6 +64,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['subject', 'isShared'];
@@ -79,6 +80,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
diff --git a/lib/internal/Magento/Framework/View/Layout/Proxy.php b/lib/internal/Magento/Framework/View/Layout/Proxy.php
index d4897bb5cd101..a4550df566ff4 100644
--- a/lib/internal/Magento/Framework/View/Layout/Proxy.php
+++ b/lib/internal/Magento/Framework/View/Layout/Proxy.php
@@ -66,6 +66,7 @@ public function __construct(
*/
public function __sleep()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
return ['_subject', '_isShared'];
@@ -81,6 +82,7 @@ public function __sleep()
*/
public function __wakeup()
{
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
From 845eb5b80d62882a6d2c8fe85ffee6731169c31e Mon Sep 17 00:00:00 2001
From: Veronika Kurochkina
Date: Wed, 31 Jul 2019 17:22:18 +0300
Subject: [PATCH 058/593] MC-15341: Default product numbers to display results
in poor display on Desktop
- Updated automated Test script
---
.../Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
index 5718844fb6df3..e5f05f1ea00c1 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
@@ -10,6 +10,7 @@
+
From f0856e8575bc0cc86543d14da2bd45d8e457fa25 Mon Sep 17 00:00:00 2001
From: David Verholen
Date: Tue, 6 Aug 2019 12:39:54 +0200
Subject: [PATCH 059/593] 24025 add caching for magento product version
fixes https://github.com/magento/magento2/issues/24025
---
.../Magento/Framework/App/ProductMetadata.php | 23 +++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/lib/internal/Magento/Framework/App/ProductMetadata.php b/lib/internal/Magento/Framework/App/ProductMetadata.php
index c9fde94352a71..2829292555790 100644
--- a/lib/internal/Magento/Framework/App/ProductMetadata.php
+++ b/lib/internal/Magento/Framework/App/ProductMetadata.php
@@ -8,9 +8,9 @@
namespace Magento\Framework\App;
use Magento\Framework\Composer\ComposerFactory;
-use \Magento\Framework\Composer\ComposerJsonFinder;
-use \Magento\Framework\App\Filesystem\DirectoryList;
-use \Magento\Framework\Composer\ComposerInformation;
+use Magento\Framework\Composer\ComposerJsonFinder;
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Composer\ComposerInformation;
/**
* Class ProductMetadata
@@ -28,6 +28,11 @@ class ProductMetadata implements ProductMetadataInterface
*/
const PRODUCT_NAME = 'Magento';
+ /**
+ * Magento Product Version Cache Key
+ */
+ const MAGENTO_PRODUCT_VERSION_CACHE_KEY = 'magento-product-version';
+
/**
* Product version
*
@@ -45,13 +50,19 @@ class ProductMetadata implements ProductMetadataInterface
* @var \Magento\Framework\Composer\ComposerInformation
*/
private $composerInformation;
+ /**
+ * @var CacheInterface
+ */
+ private $cache;
/**
* @param ComposerJsonFinder $composerJsonFinder
+ * @param CacheInterface|null $cache
*/
- public function __construct(ComposerJsonFinder $composerJsonFinder)
+ public function __construct(ComposerJsonFinder $composerJsonFinder, CacheInterface $cache = null)
{
$this->composerJsonFinder = $composerJsonFinder;
+ $this->cache = $cache ?: ObjectManager::getInstance()->get(CacheInterface::class);
}
/**
@@ -61,6 +72,9 @@ public function __construct(ComposerJsonFinder $composerJsonFinder)
*/
public function getVersion()
{
+ if ($cachedVersion = $this->cache->load(self::MAGENTO_PRODUCT_VERSION_CACHE_KEY)) {
+ $this->version = $cachedVersion;
+ }
if (!$this->version) {
if (!($this->version = $this->getSystemPackageVersion())) {
if ($this->getComposerInformation()->isMagentoRoot()) {
@@ -69,6 +83,7 @@ public function getVersion()
$this->version = 'UNKNOWN';
}
}
+ $this->cache->save($this->version, self::MAGENTO_PRODUCT_VERSION_CACHE_KEY);
}
return $this->version;
}
From c646296331120e3c20e5c73c702eb6093617eff0 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Tue, 6 Aug 2019 23:06:25 +0000
Subject: [PATCH 060/593] Deleted redundant dependency + set scopeType
---
app/code/Magento/Newsletter/Model/Config.php | 20 +++++++-------------
1 file changed, 7 insertions(+), 13 deletions(-)
diff --git a/app/code/Magento/Newsletter/Model/Config.php b/app/code/Magento/Newsletter/Model/Config.php
index 0da18b661399d..c6e4ba36591fc 100644
--- a/app/code/Magento/Newsletter/Model/Config.php
+++ b/app/code/Magento/Newsletter/Model/Config.php
@@ -7,6 +7,8 @@
namespace Magento\Newsletter\Model;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
/**
* Newsletter configuration model
*/
@@ -18,27 +20,19 @@ class Config
const XML_PATH_NEWSLETTER_ACTIVE = 'newsletter/general/active';
/**
- * @var \Magento\Framework\App\Config\ScopeConfigInterface
+ * @var ScopeConfigInterface
*/
protected $scopeConfig;
- /**
- * @var \Magento\Config\Model\ResourceModel\Config
- */
- protected $resourceConfig;
-
/**
* Config constructor.
*
- * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
- * @param \Magento\Config\Model\ResourceModel\Config $resourceConfig
+ * @param ScopeConfigInterface $scopeConfig
*/
public function __construct(
- \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
- \Magento\Config\Model\ResourceModel\Config $resourceConfig
+ ScopeConfigInterface $scopeConfig
) {
$this->scopeConfig = $scopeConfig;
- $this->resourceConfig = $resourceConfig;
}
/**
@@ -46,8 +40,8 @@ public function __construct(
*
* @return bool
*/
- public function isActive()
+ public function isActive($scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT)
{
- return $this->scopeConfig->isSetFlag(self::XML_PATH_NEWSLETTER_ACTIVE);
+ return $this->scopeConfig->isSetFlag(self::XML_PATH_NEWSLETTER_ACTIVE, $scopeType);
}
}
From bd5b7c584de95da97200be47d6f63fd9eda372f7 Mon Sep 17 00:00:00 2001
From: Stsiapan Korf
Date: Wed, 7 Aug 2019 12:58:54 +0000
Subject: [PATCH 061/593] MAGETWO-98748: Incorrect behavior in the category
menu on the Storefront
- Add cache key info about active category
---
.../Magento/Catalog/Plugin/Block/Topmenu.php | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/app/code/Magento/Catalog/Plugin/Block/Topmenu.php b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php
index 44f9193ab4012..d788525356fab 100644
--- a/app/code/Magento/Catalog/Plugin/Block/Topmenu.php
+++ b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php
@@ -193,4 +193,21 @@ protected function getCategoryTree($storeId, $rootId)
return $collection;
}
+
+ /**
+ * Add active
+ *
+ * @param \Magento\Theme\Block\Html\Topmenu $subject
+ * @param string[] $result
+ * @return string[]
+ */
+ public function afterGetCacheKeyInfo(\Magento\Theme\Block\Html\Topmenu $subject, array $result)
+ {
+ $activeCategory = $this->getCurrentCategory();
+ if ($activeCategory) {
+ $result[] = Category::CACHE_TAG . '_' . $activeCategory->getId();
+ }
+
+ return $result;
+ }
}
From ed1fb0358045fd988c59a00e68099cf3632e5865 Mon Sep 17 00:00:00 2001
From: David Verholen
Date: Wed, 7 Aug 2019 15:30:34 +0200
Subject: [PATCH 062/593] 24025 add caching for magento product version
fixes https://github.com/magento/magento2/issues/24025
---
lib/internal/Magento/Framework/App/ProductMetadata.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/internal/Magento/Framework/App/ProductMetadata.php b/lib/internal/Magento/Framework/App/ProductMetadata.php
index 2829292555790..d5da7aeba3054 100644
--- a/lib/internal/Magento/Framework/App/ProductMetadata.php
+++ b/lib/internal/Magento/Framework/App/ProductMetadata.php
@@ -14,6 +14,7 @@
/**
* Class ProductMetadata
+ *
* @package Magento\Framework\App
*/
class ProductMetadata implements ProductMetadataInterface
From 98a84f75ce696647391a688001006d975bb98263 Mon Sep 17 00:00:00 2001
From: Oleksandr Gorkun
Date: Wed, 7 Aug 2019 11:33:46 -0500
Subject: [PATCH 063/593] MAGETWO-96975: Remove __sleep and __wakeup from code
---
app/code/Magento/Authorization/Model/Role.php | 6 ----
.../Magento/Backend/Model/Auth/Session.php | 10 ++++--
.../Model/ResourceModel/Eav/Attribute.php | 6 ----
.../Config/Model/Config/Backend/Encrypted.php | 6 ----
.../Product/Type/Configurable/Attribute.php | 6 ----
.../Configurable/Attribute/Collection.php | 6 ----
app/code/Magento/Customer/Model/Attribute.php | 6 ----
.../Magento/Eav/Model/Entity/Attribute.php | 6 ----
.../Entity/Attribute/AbstractAttribute.php | 6 ----
.../Model/ResourceModel/Entity/Attribute.php | 6 ----
app/code/Magento/Store/Model/Store.php | 6 ----
app/code/Magento/User/Model/User.php | 6 ----
.../Backend/Model/Auth/SessionTest.php | 32 +++++++++++++++++++
.../Magento/Framework/App/AreaList/Proxy.php | 6 ----
.../Magento/Framework/App/Response/Http.php | 6 ----
.../App/Route/ConfigInterface/Proxy.php | 6 ----
lib/internal/Magento/Framework/DB/Select.php | 6 ----
.../Framework/DB/Select/RendererProxy.php | 6 ----
.../Magento/Framework/Data/Collection.php | 6 ----
.../Framework/Data/Collection/AbstractDb.php | 6 ----
.../DataObject/Copy/Config/Data/Proxy.php | 6 ----
.../Framework/Interception/Interceptor.php | 6 ----
.../Model/AbstractExtensibleModel.php | 6 ----
.../Magento/Framework/Model/AbstractModel.php | 6 ----
.../Model/ResourceModel/Db/AbstractDb.php | 6 ----
.../Db/Collection/AbstractCollection.php | 6 ----
.../Framework/Mview/Config/Data/Proxy.php | 6 ----
.../Framework/Translate/Inline/Proxy.php | 6 ----
.../Magento/Framework/View/Layout/Proxy.php | 6 ----
29 files changed, 40 insertions(+), 164 deletions(-)
diff --git a/app/code/Magento/Authorization/Model/Role.php b/app/code/Magento/Authorization/Model/Role.php
index 40a1c7149a524..042e95806ae18 100644
--- a/app/code/Magento/Authorization/Model/Role.php
+++ b/app/code/Magento/Authorization/Model/Role.php
@@ -58,9 +58,6 @@ public function __construct( //phpcs:ignore Generic.CodeAnalysis.UselessOverridi
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = parent::__sleep();
return array_diff($properties, ['_resource', '_resourceCollection']);
}
@@ -73,9 +70,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_resource = $objectManager->get(\Magento\Authorization\Model\ResourceModel\Role::class);
diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php
index d34323d4bec87..6d2f8f6a21d4a 100644
--- a/app/code/Magento/Backend/Model/Auth/Session.php
+++ b/app/code/Magento/Backend/Model/Auth/Session.php
@@ -325,6 +325,8 @@ public function getUser()
$user = $this->userFactory->create();
$this->userHydrator->hydrate($user, $userData);
$this->user = $user;
+ } elseif ($user = parent::getUser()) {
+ $this->setUser($user);
}
}
@@ -355,7 +357,7 @@ public function setUser($user)
*/
public function hasUser()
{
- return $this->user || $this->hasUserData();
+ return (bool)$this->getUser();
}
/**
@@ -366,6 +368,7 @@ public function hasUser()
public function unsUser()
{
$this->user = null;
+ parent::unsUser();
return $this->unsUserData();
}
@@ -383,6 +386,8 @@ public function getAcl()
$acl = $this->aclFactory->create();
$this->aclHydrator->hydrate($acl, $aclData);
$this->acl = $acl;
+ } elseif ($acl = parent::getAcl()) {
+ $this->setAcl($acl);
}
}
@@ -413,7 +418,7 @@ public function setAcl($acl)
*/
public function hasAcl()
{
- return $this->acl || $this->hasUserAclData();
+ return (bool)$this->getAcl();
}
/**
@@ -424,6 +429,7 @@ public function hasAcl()
public function unsAcl()
{
$this->acl = null;
+ parent::unsAcl();
return $this->unsUserAclData();
}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
index ce7e5cd42f282..a1eaa956e8351 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
@@ -852,9 +852,6 @@ public function afterDelete()
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->unsetData('entity_type');
return array_diff(
parent::__sleep(),
@@ -871,9 +868,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_indexerEavProcessor = $objectManager->get(\Magento\Catalog\Model\Indexer\Product\Flat\Processor::class);
diff --git a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
index fc4d0a54bb13d..80ce061a0a17e 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php
@@ -54,9 +54,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = parent::__sleep();
return array_diff($properties, ['_encryptor']);
}
@@ -71,9 +68,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$this->_encryptor = \Magento\Framework\App\ObjectManager::getInstance()->get(
\Magento\Framework\Encryption\EncryptorInterface::class
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
index 0f34cb9f89474..01549ffcd2755 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
@@ -270,9 +270,6 @@ public function setProductId($value)
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return array_diff(
parent::__sleep(),
['metadataPool']
@@ -287,9 +284,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->metadataPool = $objectManager->get(MetadataPool::class);
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
index 81dd0e42e3d3d..3aa90c7b3ce57 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php
@@ -363,9 +363,6 @@ protected function getIncludedOptions(array $usedProducts, AbstractAttribute $pr
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return array_diff(
parent::__sleep(),
[
@@ -388,9 +385,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = ObjectManager::getInstance();
$this->_storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class);
diff --git a/app/code/Magento/Customer/Model/Attribute.php b/app/code/Magento/Customer/Model/Attribute.php
index 773b9643298a5..d05bf14fbc97d 100644
--- a/app/code/Magento/Customer/Model/Attribute.php
+++ b/app/code/Magento/Customer/Model/Attribute.php
@@ -208,9 +208,6 @@ public function canBeFilterableInGrid()
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->unsetData('entity_type');
return array_diff(
parent::__sleep(),
@@ -226,9 +223,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->indexerRegistry = $objectManager->get(\Magento\Framework\Indexer\IndexerRegistry::class);
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php
index b6522cdb2dda3..f20921db769d4 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute.php
@@ -495,9 +495,6 @@ public function getIdentities()
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->unsetData('attribute_set_info');
return array_diff(
parent::__sleep(),
@@ -514,9 +511,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = ObjectManager::getInstance();
$this->_localeDate = $objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class);
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
index f33ec1d5423e3..16fe495de18db 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
@@ -1411,9 +1411,6 @@ public function setExtensionAttributes(\Magento\Eav\Api\Data\AttributeExtensionI
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return array_diff(
parent::__sleep(),
[
@@ -1442,9 +1439,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class);
diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
index e2d7ef7ff3581..a40acab5eb67a 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php
@@ -731,9 +731,6 @@ public function getValidAttributeIds($attributeIds)
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = parent::__sleep();
$properties = array_diff($properties, ['_storeManager']);
return $properties;
@@ -750,9 +747,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$this->_storeManager = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Store\Model\StoreManagerInterface::class);
diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php
index 76e8a7019007e..e8e2ddebd3f41 100644
--- a/app/code/Magento/Store/Model/Store.php
+++ b/app/code/Magento/Store/Model/Store.php
@@ -429,9 +429,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = parent::__sleep();
$properties = array_diff($properties, ['_coreFileStorageDatabase', '_config']);
return $properties;
@@ -447,9 +444,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$this->_coreFileStorageDatabase = ObjectManager::getInstance()
->get(\Magento\MediaStorage\Helper\File\Storage\Database::class);
diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php
index a81e7af1e983a..029c867d2d1dd 100644
--- a/app/code/Magento/User/Model/User.php
+++ b/app/code/Magento/User/Model/User.php
@@ -218,9 +218,6 @@ protected function _construct()
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = parent::__sleep();
return array_diff(
$properties,
@@ -252,9 +249,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->serializer = $objectManager->get(Json::class);
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php
index f1e7a10737604..5ef518fd2152f 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php
@@ -92,4 +92,36 @@ public function testStorage()
$this->assertEquals($acl->getRoles(), $persistedAcl->getRoles());
$this->assertEquals($acl->getResources(), $persistedAcl->getResources());
}
+
+ /**
+ * Check that session manager can work with user storage in the old way.
+ */
+ public function testInnerStorage(): void
+ {
+ /** @var \Magento\Framework\Session\StorageInterface $innerStorage */
+ $innerStorage = Bootstrap::getObjectManager()->get(\Magento\Framework\Session\StorageInterface::class);
+ $this->authSession = $this->authSessionFactory->create(['storage' => $innerStorage]);
+ $this->auth->login(TestHelper::ADMIN_NAME, TestHelper::ADMIN_PASSWORD);
+ $user = $this->auth->getAuthStorage()->getUser();
+ $acl = $this->auth->getAuthStorage()->getAcl();
+ $this->assertNotEmpty($user);
+ $this->assertNotEmpty($acl);
+ $this->auth->logout();
+ $this->assertEmpty($this->auth->getAuthStorage()->getUser());
+ $this->assertEmpty($this->auth->getAuthStorage()->getAcl());
+ $this->authSession->setUser($user);
+ $this->authSession->setAcl($acl);
+ $this->assertTrue($user === $this->authSession->getUser());
+ $this->assertTrue($acl === $this->authSession->getAcl());
+ $this->authSession->destroy();
+ $innerStorage->setUser($user);
+ $innerStorage->setAcl($acl);
+ $this->assertTrue($user === $this->authSession->getUser());
+ $this->assertTrue($acl === $this->authSession->getAcl());
+ /** @var Session $newSession */
+ $newSession = $this->authSessionFactory->create(['storage' => $innerStorage]);
+ $this->assertTrue($newSession->hasUser());
+ $this->assertTrue($newSession->hasAcl());
+ $this->assertEquals($user->getId(), $newSession->getUser()->getId());
+ }
}
diff --git a/lib/internal/Magento/Framework/App/AreaList/Proxy.php b/lib/internal/Magento/Framework/App/AreaList/Proxy.php
index 7a571814371fe..105ddd3727906 100644
--- a/lib/internal/Magento/Framework/App/AreaList/Proxy.php
+++ b/lib/internal/Magento/Framework/App/AreaList/Proxy.php
@@ -67,9 +67,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['_subject', '_isShared'];
}
@@ -83,9 +80,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/App/Response/Http.php b/lib/internal/Magento/Framework/App/Response/Http.php
index db3da6beeaa51..279ae9d9649f6 100644
--- a/lib/internal/Magento/Framework/App/Response/Http.php
+++ b/lib/internal/Magento/Framework/App/Response/Http.php
@@ -191,9 +191,6 @@ public function representJson($content)
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['content', 'isRedirect', 'statusCode', 'context', 'headers'];
}
@@ -208,9 +205,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$objectManager = ObjectManager::getInstance();
$this->cookieManager = $objectManager->create(\Magento\Framework\Stdlib\CookieManagerInterface::class);
$this->cookieMetadataFactory = $objectManager->get(
diff --git a/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php b/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
index 4d2707d4a1c7e..863a6d7d836d4 100644
--- a/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
+++ b/lib/internal/Magento/Framework/App/Route/ConfigInterface/Proxy.php
@@ -69,9 +69,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['_subject', '_isShared'];
}
@@ -85,9 +82,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php
index c3da06a2ef924..b98aa9a1a8aa1 100644
--- a/lib/internal/Magento/Framework/DB/Select.php
+++ b/lib/internal/Magento/Framework/DB/Select.php
@@ -519,9 +519,6 @@ public function assemble()
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = array_keys(get_object_vars($this));
$properties = array_diff(
$properties,
@@ -544,9 +541,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_adapter = $objectManager->get(ResourceConnection::class)->getConnection();
$this->selectRenderer = $objectManager->get(\Magento\Framework\DB\Select\SelectRenderer::class);
diff --git a/lib/internal/Magento/Framework/DB/Select/RendererProxy.php b/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
index 629b600303ebb..f3029a7ac2bd0 100644
--- a/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
+++ b/lib/internal/Magento/Framework/DB/Select/RendererProxy.php
@@ -65,9 +65,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['_subject', '_isShared'];
}
@@ -81,9 +78,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php
index 2f0404f35822d..bcc6de4b22e0d 100644
--- a/lib/internal/Magento/Framework/Data/Collection.php
+++ b/lib/internal/Magento/Framework/Data/Collection.php
@@ -895,9 +895,6 @@ public function hasFlag($flag)
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = array_keys(get_object_vars($this));
$properties = array_diff(
$properties,
@@ -919,9 +916,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_entityFactory = $objectManager->get(EntityFactoryInterface::class);
}
diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
index 6ca28c738b3fe..dc4b71caf5bee 100644
--- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php
@@ -902,9 +902,6 @@ private function getMainTableAlias()
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return array_diff(
parent::__sleep(),
['_fetchStrategy', '_logger', '_conn', 'extensionAttributesJoinProcessor']
@@ -920,9 +917,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_logger = $objectManager->get(Logger::class);
diff --git a/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php b/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
index a6e84b483261e..42d58daec2c93 100644
--- a/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
+++ b/lib/internal/Magento/Framework/DataObject/Copy/Config/Data/Proxy.php
@@ -66,9 +66,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['_subject', '_isShared'];
}
@@ -82,9 +79,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/Interception/Interceptor.php b/lib/internal/Magento/Framework/Interception/Interceptor.php
index 3b9543e8cbb7b..ccc311c5b3426 100644
--- a/lib/internal/Magento/Framework/Interception/Interceptor.php
+++ b/lib/internal/Magento/Framework/Interception/Interceptor.php
@@ -68,9 +68,6 @@ public function ___callParent($method, array $arguments)
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
if (method_exists(get_parent_class($this), '__sleep')) {
$properties = parent::__sleep();
} else {
@@ -90,9 +87,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
if (method_exists(get_parent_class($this), '__wakeup')) {
parent::__wakeup();
}
diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
index 8ae5a234911cd..69410b7757e44 100644
--- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php
@@ -368,9 +368,6 @@ private function populateExtensionAttributes(array $extensionAttributesData = []
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return array_diff(parent::__sleep(), ['extensionAttributesFactory', 'customAttributeFactory']);
}
@@ -382,9 +379,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->extensionAttributesFactory = $objectManager->get(ExtensionAttributesFactory::class);
diff --git a/lib/internal/Magento/Framework/Model/AbstractModel.php b/lib/internal/Magento/Framework/Model/AbstractModel.php
index 7905ae280ff0c..534c25fce8d42 100644
--- a/lib/internal/Magento/Framework/Model/AbstractModel.php
+++ b/lib/internal/Magento/Framework/Model/AbstractModel.php
@@ -226,9 +226,6 @@ protected function _init($resourceModel)
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = array_keys(get_object_vars($this));
$properties = array_diff(
$properties,
@@ -256,9 +253,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_registry = $objectManager->get(\Magento\Framework\Registry::class);
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
index 8a0c78e4ff02b..f2b3eca7243b7 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
@@ -163,9 +163,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$properties = array_keys(get_object_vars($this));
$properties = array_diff($properties, ['_resources', '_connections']);
return $properties;
@@ -181,9 +178,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->_resources = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\App\ResourceConnection::class);
}
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
index 4af5fd7a2a986..1186326ab6525 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php
@@ -613,9 +613,6 @@ public function save()
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return array_diff(
parent::__sleep(),
['_resource', '_eventManager']
@@ -631,9 +628,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
parent::__wakeup();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_eventManager = $objectManager->get(\Magento\Framework\Event\ManagerInterface::class);
diff --git a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
index 0de6c639812ad..cfa79f3e7ee60 100644
--- a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
+++ b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php
@@ -64,9 +64,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['subject', 'isShared'];
}
@@ -80,9 +77,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
index 0a9841adbfb08..62b3352e11b1a 100644
--- a/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
+++ b/lib/internal/Magento/Framework/Translate/Inline/Proxy.php
@@ -64,9 +64,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['subject', 'isShared'];
}
@@ -80,9 +77,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
diff --git a/lib/internal/Magento/Framework/View/Layout/Proxy.php b/lib/internal/Magento/Framework/View/Layout/Proxy.php
index a4550df566ff4..2ee50f8d14bc3 100644
--- a/lib/internal/Magento/Framework/View/Layout/Proxy.php
+++ b/lib/internal/Magento/Framework/View/Layout/Proxy.php
@@ -66,9 +66,6 @@ public function __construct(
*/
public function __sleep()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
return ['_subject', '_isShared'];
}
@@ -82,9 +79,6 @@ public function __sleep()
*/
public function __wakeup()
{
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED);
-
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
}
From 7170102b50c589b4bc92ca4125afd906dcf4e559 Mon Sep 17 00:00:00 2001
From: arushibansal013 <53007383+arushibansal013@users.noreply.github.com>
Date: Fri, 9 Aug 2019 00:45:01 +0530
Subject: [PATCH 064/593] Added startPath for bundle product price
Added start path for bundle product price display issue, Since similar path get formed after alerts are enabled.
---
.../Ui/DataProvider/Product/Form/Modifier/BundlePrice.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
index f431012dc3fa5..6ddb539b3c254 100644
--- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
+++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
@@ -64,7 +64,7 @@ public function modifyMeta(array $meta)
$this->arrayManager->findPath(
ProductAttributeInterface::CODE_PRICE,
$meta,
- null,
+ 'product-details/children',
'children'
) . static::META_CONFIG_PATH,
$meta,
From 87732f34eb8ee030147c7f12d6ee66f3044493a1 Mon Sep 17 00:00:00 2001
From: arushibansal013 <53007383+arushibansal013@users.noreply.github.com>
Date: Fri, 9 Aug 2019 12:50:48 +0530
Subject: [PATCH 065/593] Fixed inherit doc and fetched parent-details from
constant
Removed inherit doc as per magento documentation as well as changed parent-details to fetch from constant
---
.../DataProvider/Product/Form/Modifier/BundlePrice.php | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
index 6ddb539b3c254..7f79c76dbc9a9 100644
--- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
+++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
@@ -39,9 +39,10 @@ public function __construct(
$this->locator = $locator;
$this->arrayManager = $arrayManager;
}
-
+
/**
- * {@inheritdoc}
+ * @param array $meta
+ * @return array
*/
public function modifyMeta(array $meta)
{
@@ -64,7 +65,7 @@ public function modifyMeta(array $meta)
$this->arrayManager->findPath(
ProductAttributeInterface::CODE_PRICE,
$meta,
- 'product-details/children',
+ self::DEFAULT_GENERAL_PANEL.'/children',
'children'
) . static::META_CONFIG_PATH,
$meta,
@@ -94,7 +95,8 @@ public function modifyMeta(array $meta)
}
/**
- * {@inheritdoc}
+ * @param array $data
+ * @return array
*/
public function modifyData(array $data)
{
From 375efd1aeaa492639a7c6ba0efc64b750433fc70 Mon Sep 17 00:00:00 2001
From: arushibansal013 <53007383+arushibansal013@users.noreply.github.com>
Date: Fri, 9 Aug 2019 13:45:31 +0530
Subject: [PATCH 066/593] Added short description to comments
---
.../Ui/DataProvider/Product/Form/Modifier/BundlePrice.php | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
index 7f79c76dbc9a9..58785b96687db 100644
--- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
+++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
@@ -41,6 +41,8 @@ public function __construct(
}
/**
+ * modified modify meta for bundle product
+ *
* @param array $meta
* @return array
*/
@@ -95,6 +97,8 @@ public function modifyMeta(array $meta)
}
/**
+ * modify data for bundle product
+ *
* @param array $data
* @return array
*/
From 035800e69413d66df8ae901f3ab244c46ee51706 Mon Sep 17 00:00:00 2001
From: arushibansal013 <53007383+arushibansal013@users.noreply.github.com>
Date: Fri, 9 Aug 2019 14:15:22 +0530
Subject: [PATCH 067/593] Capitialize the short description
---
.../Ui/DataProvider/Product/Form/Modifier/BundlePrice.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
index 58785b96687db..20f0258cb7a36 100644
--- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
+++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php
@@ -41,7 +41,7 @@ public function __construct(
}
/**
- * modified modify meta for bundle product
+ * Modified modify meta for bundle product
*
* @param array $meta
* @return array
@@ -97,7 +97,7 @@ public function modifyMeta(array $meta)
}
/**
- * modify data for bundle product
+ * Modify data for bundle product
*
* @param array $data
* @return array
From 62dd8f51c6fb2217ed8af94547a9b81dcdd908c1 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Tue, 6 Aug 2019 10:40:39 -0500
Subject: [PATCH 068/593] Add custom attributes to products filter and sort
input
---
.../Model/Config/FilterAttributeReader.php | 98 +++++++++++++++++++
.../Model/Config/SortAttributeReader.php | 79 +++++++++++++++
app/code/Magento/CatalogGraphQl/etc/di.xml | 2 +
.../Magento/CatalogGraphQl/etc/graphql/di.xml | 6 ++
4 files changed, 185 insertions(+)
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
new file mode 100644
index 0000000000000..b84c18ea5ac0b
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
@@ -0,0 +1,98 @@
+mapper = $mapper;
+ $this->collectionFactory = $collectionFactory;
+ }
+
+ /**
+ * Read configuration scope
+ *
+ * @param string|null $scope
+ * @return array
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function read($scope = null) : array
+ {
+ $typeNames = $this->mapper->getMappedTypes(self::ENTITY_TYPE);
+ $config = [];
+
+ foreach ($this->getAttributeCollection() as $attribute) {
+ $attributeCode = $attribute->getAttributeCode();
+
+ foreach ($typeNames as $typeName) {
+ $config[$typeName]['fields'][$attributeCode] = [
+ 'name' => $attributeCode,
+ 'type' => self::FILTER_TYPE,
+ 'arguments' => [],
+ 'required' => false,
+ 'description' => sprintf('Attribute label: %s', $attribute->getDefaultFrontendLabel())
+ ];
+ }
+ }
+
+ return $config;
+ }
+
+ /**
+ * Create attribute collection
+ *
+ * @return Collection|\Magento\Catalog\Model\ResourceModel\Eav\Attribute[]
+ */
+ private function getAttributeCollection()
+ {
+ return $this->collectionFactory->create()
+ ->addHasOptionsFilter()
+ ->addIsSearchableFilter()
+ ->addDisplayInAdvancedSearchFilter();
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
new file mode 100644
index 0000000000000..215b28be0579c
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
@@ -0,0 +1,79 @@
+mapper = $mapper;
+ $this->attributesCollection = $attributesCollection;
+ }
+
+ /**
+ * Read configuration scope
+ *
+ * @param string|null $scope
+ * @return array
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function read($scope = null) : array
+ {
+ $map = $this->mapper->getMappedTypes(self::ENTITY_TYPE);
+ $config =[];
+ $attributes = $this->attributesCollection->addSearchableAttributeFilter()->addFilter('used_for_sort_by', 1);
+ /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
+ foreach ($attributes as $attribute) {
+ $attributeCode = $attribute->getAttributeCode();
+ $attributeLabel = $attribute->getDefaultFrontendLabel();
+ foreach ($map as $type) {
+ $config[$type]['fields'][$attributeCode] = [
+ 'name' => $attributeCode,
+ 'type' => self::FIELD_TYPE,
+ 'arguments' => [],
+ 'required' => false,
+ 'description' => __('Attribute label: ') . $attributeLabel
+ ];
+ }
+ }
+
+ return $config;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/etc/di.xml b/app/code/Magento/CatalogGraphQl/etc/di.xml
index a5006355ed265..cea1c7ce327e2 100644
--- a/app/code/Magento/CatalogGraphQl/etc/di.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/di.xml
@@ -19,6 +19,8 @@
- Magento\CatalogGraphQl\Model\Config\AttributeReader
- Magento\CatalogGraphQl\Model\Config\CategoryAttributeReader
+ - Magento\CatalogGraphQl\Model\Config\SortAttributeReader
+ - Magento\CatalogGraphQl\Model\Config\FilterAttributeReader
diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
index 2292004f3cf01..8fe3d19619fc3 100644
--- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
@@ -48,6 +48,12 @@
- CustomizableRadioOption
- CustomizableCheckboxOption
+ -
+
- ProductSortInput
+
+ -
+
- ProductFilterInput
+
From 10796a3ad68b74579deb8fe034cc3bdabc997301 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Wed, 7 Aug 2019 11:35:36 -0500
Subject: [PATCH 069/593] MC-18988: Investigate layer navigation code in
lightweight resolver implementation
---
.../Model/Resolver/LayerFilters.php | 19 +++++++++--
.../Model/Resolver/Products.php | 4 ++-
.../Model/Resolver/Products/Query/Search.php | 32 +++++++++++++------
.../CatalogGraphQl/etc/schema.graphqls | 8 +++++
.../Query/Resolver/Argument/AstConverter.php | 13 +++++++-
5 files changed, 61 insertions(+), 15 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
index 0ec7e12e42d55..d493c7ba8e66f 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
@@ -10,6 +10,7 @@
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+//use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilder;
/**
* Layered navigation filters resolver, used for GraphQL request processing.
@@ -21,13 +22,24 @@ class LayerFilters implements ResolverInterface
*/
private $filtersDataProvider;
+// /**
+// * @var LayerBuilder
+// */
+// private $layerBuilder;
+
/**
* @param \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider
+ * @param \Magento\Framework\Registry $registry
*/
public function __construct(
- \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider
+ \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider,
+ \Magento\Framework\Registry $registry
+ //LayerBuilder $layerBuilder
+
) {
$this->filtersDataProvider = $filtersDataProvider;
+ $this->registry = $registry;
+ //$this->layerBuilder = $layerBuilder;
}
/**
@@ -43,7 +55,8 @@ public function resolve(
if (!isset($value['layer_type'])) {
return null;
}
-
- return $this->filtersDataProvider->getData($value['layer_type']);
+ $aggregations = $this->registry->registry('aggregations');
+ return [];
+ //return $this->layerBuilder->build($aggregations, 1);
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index a75a9d2cf50a0..93ecf9c881548 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -89,7 +89,8 @@ public function resolve(
$searchResult = $this->searchQuery->getResult($searchCriteria, $info);
} else {
$layerType = Resolver::CATALOG_LAYER_CATEGORY;
- $searchResult = $this->filterQuery->getResult($searchCriteria, $info);
+ $searchCriteria->setRequestName('catalog_view_container');
+ $searchResult = $this->searchQuery->getResult($searchCriteria, $info);
}
//possible division by 0
if ($searchCriteria->getPageSize()) {
@@ -116,6 +117,7 @@ public function resolve(
'current_page' => $currentPage,
'total_pages' => $maxPages
],
+ //'filters' => $aggregations
'layer_type' => $layerType
];
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index bc40c664425ff..7a766269af602 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -13,6 +13,7 @@
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory;
use Magento\Search\Api\SearchInterface;
+use Magento\Framework\Api\Search\SearchCriteriaInterfaceFactory;
/**
* Full text search for catalog using given search criteria.
@@ -49,6 +50,11 @@ class Search
*/
private $pageSizeProvider;
+ /**
+ * @var SearchCriteriaInterfaceFactory
+ */
+ private $searchCriteriaFactory;
+
/**
* @param SearchInterface $search
* @param FilterHelper $filterHelper
@@ -56,6 +62,8 @@ class Search
* @param SearchResultFactory $searchResultFactory
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
* @param \Magento\Search\Model\Search\PageSizeProvider $pageSize
+ * @param SearchCriteriaInterfaceFactory $searchCriteriaFactory
+ * @param \Magento\Framework\Registry $registry
*/
public function __construct(
SearchInterface $search,
@@ -63,7 +71,9 @@ public function __construct(
Filter $filterQuery,
SearchResultFactory $searchResultFactory,
\Magento\Framework\EntityManager\MetadataPool $metadataPool,
- \Magento\Search\Model\Search\PageSizeProvider $pageSize
+ \Magento\Search\Model\Search\PageSizeProvider $pageSize,
+ SearchCriteriaInterfaceFactory $searchCriteriaFactory,
+ \Magento\Framework\Registry $registry
) {
$this->search = $search;
$this->filterHelper = $filterHelper;
@@ -71,6 +81,8 @@ public function __construct(
$this->searchResultFactory = $searchResultFactory;
$this->metadataPool = $metadataPool;
$this->pageSizeProvider = $pageSize;
+ $this->searchCriteriaFactory = $searchCriteriaFactory;
+ $this->registry = $registry;
}
/**
@@ -89,11 +101,11 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$realPageSize = $searchCriteria->getPageSize();
$realCurrentPage = $searchCriteria->getCurrentPage();
// Current page must be set to 0 and page size to max for search to grab all ID's as temporary workaround
- $pageSize = $this->pageSizeProvider->getMaxPageSize();
- $searchCriteria->setPageSize($pageSize);
+ //$pageSize = $this->pageSizeProvider->getMaxPageSize();
+ $searchCriteria->setPageSize(10000);
$searchCriteria->setCurrentPage(0);
$itemsResults = $this->search->search($searchCriteria);
-
+ $this->registry->register('aggregations', $itemsResults->getAggregations());
$ids = [];
$searchIds = [];
foreach ($itemsResults->getItems() as $item) {
@@ -101,14 +113,14 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$searchIds[] = $item->getId();
}
+ $searchCriteriaIds = $this->searchCriteriaFactory->create();
$filter = $this->filterHelper->generate($idField, 'in', $searchIds);
- $searchCriteria = $this->filterHelper->remove($searchCriteria, 'search_term');
- $searchCriteria = $this->filterHelper->add($searchCriteria, $filter);
- $searchResult = $this->filterQuery->getResult($searchCriteria, $info, true);
+ $searchCriteriaIds = $this->filterHelper->add($searchCriteriaIds, $filter);
+ $searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, true);
- $searchCriteria->setPageSize($realPageSize);
- $searchCriteria->setCurrentPage($realCurrentPage);
- $paginatedProducts = $this->paginateList($searchResult, $searchCriteria);
+ $searchCriteriaIds->setPageSize($realPageSize);
+ $searchCriteriaIds->setCurrentPage($realCurrentPage);
+ $paginatedProducts = $this->paginateList($searchResult, $searchCriteriaIds);
$products = [];
if (!isset($searchCriteria->getSortOrders()[0])) {
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index ea56faf94408e..0b71b849d09a4 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -315,6 +315,14 @@ input ProductFilterInput @doc(description: "ProductFilterInput defines the filte
country_of_manufacture: FilterTypeInput @doc(description: "The product's country of origin.")
custom_layout: FilterTypeInput @doc(description: "The name of a custom layout.")
gift_message_available: FilterTypeInput @doc(description: "Indicates whether a gift message is available.")
+ cat: FilterTypeInput @doc(description: "CATEGORY attribute name")
+ mysize: FilterTypeInput @doc(description: "size attribute name")
+ mycolor: FilterTypeInput @doc(description: "color attribute name")
+ ca_1_631447041: FilterTypeInput @doc(description: "CATEGORY attribute name")
+ attributeset2attribute1: FilterTypeInput @doc(description: "CATEGORY attribute name")
+ visibility: FilterTypeInput @doc(description: "CATEGORY attribute name")
+ price_dynamic_algorithm: FilterTypeInput @doc(description: "CATEGORY attribute name")
+ category_ids: FilterTypeInput @doc(description: "CATEGORY attribute name")
or: ProductFilterInput @doc(description: "The keyword required to perform a logical OR comparison.")
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php
index 0b03fc509c786..802d46eafcb97 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php
@@ -54,10 +54,19 @@ public function __construct(
* @param string $fieldName
* @param array $arguments
* @return array
+ * @throws \LogicException
*/
public function getClausesFromAst(string $fieldName, array $arguments) : array
{
$attributes = $this->fieldEntityAttributesPool->getEntityAttributesForEntityFromField($fieldName);
+ $attributes[] = 'cat';
+ $attributes[] = 'mysize';
+ $attributes[] = 'mycolor';
+ $attributes[] = 'ca_1_631447041';
+ $attributes[] = 'attributeset2attribute1';
+ $attributes[] = 'price_dynamic_algorithm';
+ $attributes[] = 'visibility';
+ $attributes[] = 'category_ids';
$conditions = [];
foreach ($arguments as $argumentName => $argument) {
if (in_array($argumentName, $attributes)) {
@@ -76,12 +85,14 @@ public function getClausesFromAst(string $fieldName, array $arguments) : array
$value
);
}
- } else {
+ } elseif (is_array($argument)) {
$conditions[] =
$this->connectiveFactory->create(
$this->getClausesFromAst($fieldName, $argument),
$argumentName
);
+ } else {
+ throw new \LogicException('Attribute not found in the visible attributes list');
}
}
return $conditions;
From a2156e1f623d99357c8b22f26fcc2fcc4bace3d7 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Thu, 8 Aug 2019 16:20:14 -0500
Subject: [PATCH 070/593] MC-18988: Investigate layer navigation code in
lightweight resolver implementation
---
.../DataProvider/AttributeQuery.php | 354 ++++++++++++++++++
.../Category/Query/CategoryAttributeQuery.php | 58 +++
.../DataProvider/CategoryAttributesMapper.php | 114 ++++++
.../AttributeOptionProvider.php | 107 ++++++
.../LayeredNavigation/Builder/Attribute.php | 179 +++++++++
.../LayeredNavigation/Builder/Category.php | 173 +++++++++
.../LayeredNavigation/Builder/Price.php | 107 ++++++
.../LayeredNavigation/LayerBuilder.php | 43 +++
.../LayerBuilderInterface.php | 40 ++
.../RootCategoryProvider.php | 55 +++
.../Model/Resolver/LayerFilters.php | 24 +-
.../Magento/CatalogGraphQl/etc/graphql/di.xml | 10 +
12 files changed, 1254 insertions(+), 10 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/AttributeQuery.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilder.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilderInterface.php
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/RootCategoryProvider.php
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/AttributeQuery.php b/app/code/Magento/CatalogGraphQl/DataProvider/AttributeQuery.php
new file mode 100644
index 0000000000000..b0f085932bb8e
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/AttributeQuery.php
@@ -0,0 +1,354 @@
+resourceConnection = $resourceConnection;
+ $this->metadataPool = $metadataPool;
+ $this->entityType = $entityType;
+ $this->linkedAttributes = $linkedAttributes;
+ $this->eavConfig = $eavConfig;
+ }
+
+ /**
+ * Form and return query to get eav entity $attributes for given $entityIds.
+ *
+ * If eav entities were not found, then data is fetching from $entityTableName.
+ *
+ * @param array $entityIds
+ * @param array $attributes
+ * @param int $storeId
+ * @return Select
+ * @throws \Zend_Db_Select_Exception
+ * @throws \Exception
+ */
+ public function getQuery(array $entityIds, array $attributes, int $storeId): Select
+ {
+ /** @var \Magento\Framework\EntityManager\EntityMetadataInterface $metadata */
+ $metadata = $this->metadataPool->getMetadata($this->entityType);
+ $entityTableName = $metadata->getEntityTable();
+
+ /** @var \Magento\Framework\DB\Adapter\AdapterInterface $connection */
+ $connection = $this->resourceConnection->getConnection();
+ $entityTableAttributes = \array_keys($connection->describeTable($entityTableName));
+
+ $attributeMetadataTable = $this->resourceConnection->getTableName('eav_attribute');
+ $eavAttributes = $this->getEavAttributeCodes($attributes, $entityTableAttributes);
+ $entityTableAttributes = \array_intersect($attributes, $entityTableAttributes);
+
+ $eavAttributesMetaData = $this->getAttributesMetaData($connection, $attributeMetadataTable, $eavAttributes);
+
+ if ($eavAttributesMetaData) {
+ $select = $this->getEavAttributes(
+ $connection,
+ $metadata,
+ $entityTableAttributes,
+ $entityIds,
+ $eavAttributesMetaData,
+ $entityTableName,
+ $storeId
+ );
+ } else {
+ $select = $this->getAttributesFromEntityTable(
+ $connection,
+ $entityTableAttributes,
+ $entityIds,
+ $entityTableName
+ );
+ }
+
+ return $select;
+ }
+
+ /**
+ * Form and return query to get entity $entityTableAttributes for given $entityIds
+ *
+ * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
+ * @param array $entityTableAttributes
+ * @param array $entityIds
+ * @param string $entityTableName
+ * @return Select
+ */
+ private function getAttributesFromEntityTable(
+ \Magento\Framework\DB\Adapter\AdapterInterface $connection,
+ array $entityTableAttributes,
+ array $entityIds,
+ string $entityTableName
+ ): Select {
+ $select = $connection->select()
+ ->from(['e' => $entityTableName], $entityTableAttributes)
+ ->where('e.entity_id IN (?)', $entityIds);
+
+ return $select;
+ }
+
+ /**
+ * Return ids of eav attributes by $eavAttributeCodes.
+ *
+ * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
+ * @param string $attributeMetadataTable
+ * @param array $eavAttributeCodes
+ * @return array
+ */
+ private function getAttributesMetaData(
+ \Magento\Framework\DB\Adapter\AdapterInterface $connection,
+ string $attributeMetadataTable,
+ array $eavAttributeCodes
+ ): array {
+ $eavAttributeIdsSelect = $connection->select()
+ ->from(['a' => $attributeMetadataTable], ['attribute_id', 'backend_type', 'attribute_code'])
+ ->where('a.attribute_code IN (?)', $eavAttributeCodes)
+ ->where('a.entity_type_id = ?', $this->getEntityTypeId());
+
+ return $connection->fetchAssoc($eavAttributeIdsSelect);
+ }
+
+ /**
+ * Form and return query to get eav entity $attributes for given $entityIds.
+ *
+ * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
+ * @param \Magento\Framework\EntityManager\EntityMetadataInterface $metadata
+ * @param array $entityTableAttributes
+ * @param array $entityIds
+ * @param array $eavAttributesMetaData
+ * @param string $entityTableName
+ * @param int $storeId
+ * @return Select
+ * @throws \Zend_Db_Select_Exception
+ */
+ private function getEavAttributes(
+ \Magento\Framework\DB\Adapter\AdapterInterface $connection,
+ \Magento\Framework\EntityManager\EntityMetadataInterface $metadata,
+ array $entityTableAttributes,
+ array $entityIds,
+ array $eavAttributesMetaData,
+ string $entityTableName,
+ int $storeId
+ ): Select {
+ $selects = [];
+ $attributeValueExpression = $connection->getCheckSql(
+ $connection->getIfNullSql('store_eav.value_id', -1) . ' > 0',
+ 'store_eav.value',
+ 'eav.value'
+ );
+ $linkField = $metadata->getLinkField();
+ $attributesPerTable = $this->getAttributeCodeTables($entityTableName, $eavAttributesMetaData);
+ foreach ($attributesPerTable as $attributeTable => $eavAttributes) {
+ $attributeCodeExpression = $this->buildAttributeCodeExpression($eavAttributes);
+
+ $selects[] = $connection->select()
+ ->from(['e' => $entityTableName], $entityTableAttributes)
+ ->joinLeft(
+ ['eav' => $this->resourceConnection->getTableName($attributeTable)],
+ \sprintf('e.%1$s = eav.%1$s', $linkField) .
+ $connection->quoteInto(' AND eav.attribute_id IN (?)', \array_keys($eavAttributesMetaData)) .
+ $connection->quoteInto(' AND eav.store_id = ?', \Magento\Store\Model\Store::DEFAULT_STORE_ID),
+ []
+ )
+ ->joinLeft(
+ ['store_eav' => $this->resourceConnection->getTableName($attributeTable)],
+ \sprintf(
+ 'e.%1$s = store_eav.%1$s AND store_eav.attribute_id = ' .
+ 'eav.attribute_id and store_eav.store_id = %2$d',
+ $linkField,
+ $storeId
+ ),
+ []
+ )
+ ->where('e.entity_id IN (?)', $entityIds)
+ ->columns(
+ [
+ 'attribute_code' => $attributeCodeExpression,
+ 'value' => $attributeValueExpression
+ ]
+ );
+ }
+
+ return $connection->select()->union($selects, Select::SQL_UNION_ALL);
+ }
+
+ /**
+ * Build expression for attribute code field.
+ *
+ * An example:
+ *
+ * ```
+ * CASE
+ * WHEN eav.attribute_id = '73' THEN 'name'
+ * WHEN eav.attribute_id = '121' THEN 'url_key'
+ * END
+ * ```
+ *
+ * @param array $eavAttributes
+ * @return \Zend_Db_Expr
+ */
+ private function buildAttributeCodeExpression(array $eavAttributes): \Zend_Db_Expr
+ {
+ $dbConnection = $this->resourceConnection->getConnection();
+ $expressionParts = ['CASE'];
+
+ foreach ($eavAttributes as $attribute) {
+ $expressionParts[]=
+ $dbConnection->quoteInto('WHEN eav.attribute_id = ?', $attribute['attribute_id'], \Zend_Db::INT_TYPE) .
+ $dbConnection->quoteInto(' THEN ?', $attribute['attribute_code'], 'string');
+ }
+
+ $expressionParts[]= 'END';
+
+ return new \Zend_Db_Expr(implode(' ', $expressionParts));
+ }
+
+ /**
+ * Get list of attribute tables.
+ *
+ * Returns result in the following format: *
+ * ```
+ * $attributeAttributeCodeTables = [
+ * 'm2_catalog_product_entity_varchar' =>
+ * '45' => [
+ * 'attribute_id' => 45,
+ * 'backend_type' => 'varchar',
+ * 'name' => attribute_code,
+ * ]
+ * ]
+ * ];
+ * ```
+ *
+ * @param string $entityTable
+ * @param array $eavAttributesMetaData
+ * @return array
+ */
+ private function getAttributeCodeTables($entityTable, $eavAttributesMetaData): array
+ {
+ $attributeAttributeCodeTables = [];
+ $metaTypes = \array_unique(\array_column($eavAttributesMetaData, 'backend_type'));
+
+ foreach ($metaTypes as $type) {
+ if (\in_array($type, self::SUPPORTED_BACKEND_TYPES, true)) {
+ $tableName = \sprintf('%s_%s', $entityTable, $type);
+ $attributeAttributeCodeTables[$tableName] = array_filter(
+ $eavAttributesMetaData,
+ function ($attribute) use ($type) {
+ return $attribute['backend_type'] === $type;
+ }
+ );
+ }
+ }
+
+ return $attributeAttributeCodeTables;
+ }
+
+ /**
+ * Get EAV attribute codes
+ * Remove attributes from entity table and attributes from exclude list
+ * Add linked attributes to output
+ *
+ * @param array $attributes
+ * @param array $entityTableAttributes
+ * @return array
+ */
+ private function getEavAttributeCodes($attributes, $entityTableAttributes): array
+ {
+ $attributes = \array_diff($attributes, $entityTableAttributes);
+ $unusedAttributeList = [];
+ $newAttributes = [];
+ foreach ($this->linkedAttributes as $attribute => $linkedAttributes) {
+ if (null === $linkedAttributes) {
+ $unusedAttributeList[] = $attribute;
+ } elseif (\is_array($linkedAttributes) && \in_array($attribute, $attributes, true)) {
+ $newAttributes[] = $linkedAttributes;
+ }
+ }
+ $attributes = \array_diff($attributes, $unusedAttributeList);
+
+ return \array_unique(\array_merge($attributes, ...$newAttributes));
+ }
+
+ /**
+ * Retrieve entity type id
+ *
+ * @return int
+ * @throws \Exception
+ */
+ private function getEntityTypeId(): int
+ {
+ if (!isset($this->entityTypeIdMap[$this->entityType])) {
+ $this->entityTypeIdMap[$this->entityType] = (int)$this->eavConfig->getEntityType(
+ $this->metadataPool->getMetadata($this->entityType)->getEavEntityType()
+ )->getId();
+ }
+
+ return $this->entityTypeIdMap[$this->entityType];
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php b/app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php
new file mode 100644
index 0000000000000..0b796c1457254
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php
@@ -0,0 +1,58 @@
+attributeQueryFactory = $attributeQueryFactory;
+ }
+
+ /**
+ * Form and return query to get eav attributes for given categories
+ *
+ * @param array $categoryIds
+ * @param array $categoryAttributes
+ * @param int $storeId
+ * @return Select
+ * @throws \Zend_Db_Select_Exception
+ */
+ public function getQuery(array $categoryIds, array $categoryAttributes, int $storeId): Select
+ {
+ $categoryAttributes = \array_merge($categoryAttributes, self::$requiredAttributes);
+
+ $attributeQuery = $this->attributeQueryFactory->create([
+ 'entityType' => CategoryInterface::class
+ ]);
+
+ return $attributeQuery->getQuery($categoryIds, $categoryAttributes, $storeId);
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php b/app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php
new file mode 100644
index 0000000000000..1f8aa38d5b939
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php
@@ -0,0 +1,114 @@
+graphqlConfig = $graphqlConfig;
+ }
+
+ /**
+ * Returns attribute values for given attribute codes.
+ *
+ * @param array $fetchResult
+ * @return array
+ */
+ public function getAttributesValues(array $fetchResult): array
+ {
+ $attributes = [];
+
+ foreach ($fetchResult as $row) {
+ if (!isset($attributes[$row['entity_id']])) {
+ $attributes[$row['entity_id']] = $row;
+ //TODO: do we need to introduce field mapping?
+ $attributes[$row['entity_id']]['id'] = $row['entity_id'];
+ }
+ if (isset($row['attribute_code'])) {
+ $attributes[$row['entity_id']][$row['attribute_code']] = $row['value'];
+ }
+ }
+
+ return $this->formatAttributes($attributes);
+ }
+
+ /**
+ * Format attributes that should be converted to array type
+ *
+ * @param array $attributes
+ * @return array
+ */
+ private function formatAttributes(array $attributes): array
+ {
+ $arrayTypeAttributes = $this->getFieldsOfArrayType();
+
+ return $arrayTypeAttributes
+ ? array_map(function ($data) use ($arrayTypeAttributes) {
+ foreach ($arrayTypeAttributes as $attributeCode) {
+ $data[$attributeCode] = $this->valueToArray($data[$attributeCode] ?? null);
+ }
+ return $data;
+ }, $attributes)
+ : $attributes;
+ }
+
+ /**
+ * Cast string to array
+ *
+ * @param string|null $value
+ * @return array
+ */
+ private function valueToArray($value): array
+ {
+ return $value ? \explode(',', $value) : [];
+ }
+
+ /**
+ * Get fields that should be converted to array type
+ *
+ * @return array
+ */
+ private function getFieldsOfArrayType(): array
+ {
+ $categoryTreeSchema = $this->graphqlConfig->getConfigElement('CategoryTree');
+ if (!$categoryTreeSchema instanceof Type) {
+ throw new \LogicException('CategoryTree type not defined in schema.');
+ }
+
+ $fields = [];
+ foreach ($categoryTreeSchema->getInterfaces() as $interface) {
+ /** @var InterfaceType $configElement */
+ $configElement = $this->graphqlConfig->getConfigElement($interface['interface']);
+
+ foreach ($configElement->getFields() as $field) {
+ if ($field->isList()) {
+ $fields[] = $field->getName();
+ }
+ }
+ }
+
+ return $fields;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php
new file mode 100644
index 0000000000000..7781473128754
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php
@@ -0,0 +1,107 @@
+ [
+ * attribute_code => code,
+ * attribute_label => attribute label,
+ * option_label => option label,
+ * options => [option_id => 'option label', ...],
+ * ]
+ * ...
+ * ]
+ */
+class AttributeOptionProvider
+{
+ /**
+ * @var ResourceConnection
+ */
+ private $resourceConnection;
+
+ /**
+ * @param ResourceConnection $resourceConnection
+ */
+ public function __construct(ResourceConnection $resourceConnection)
+ {
+ $this->resourceConnection = $resourceConnection;
+ }
+
+ /**
+ * Get option data. Return list of attributes with option data
+ *
+ * @param array $optionIds
+ * @return array
+ * @throws \Zend_Db_Statement_Exception
+ */
+ public function getOptions(array $optionIds): array
+ {
+ if (!$optionIds) {
+ return [];
+ }
+
+ $connection = $this->resourceConnection->getConnection();
+ $select = $connection->select()
+ ->from(
+ ['a' => $this->resourceConnection->getTableName('eav_attribute')],
+ [
+ 'attribute_id' => 'a.attribute_id',
+ 'attribute_code' => 'a.attribute_code',
+ 'attribute_label' => 'a.frontend_label',
+ ]
+ )
+ ->joinInner(
+ ['options' => $this->resourceConnection->getTableName('eav_attribute_option')],
+ 'a.attribute_id = options.attribute_id',
+ []
+ )
+ ->joinInner(
+ ['option_value' => $this->resourceConnection->getTableName('eav_attribute_option_value')],
+ 'options.option_id = option_value.option_id',
+ [
+ 'option_label' => 'option_value.value',
+ 'option_id' => 'option_value.option_id',
+ ]
+ )
+ ->where('option_value.option_id IN (?)', $optionIds);
+
+ return $this->formatResult($select);
+ }
+
+ /**
+ * Format result
+ *
+ * @param \Magento\Framework\DB\Select $select
+ * @return array
+ * @throws \Zend_Db_Statement_Exception
+ */
+ private function formatResult(\Magento\Framework\DB\Select $select): array
+ {
+ $statement = $this->resourceConnection->getConnection()->query($select);
+
+ $result = [];
+ while ($option = $statement->fetch()) {
+ if (!isset($result[$option['attribute_code']])) {
+ $result[$option['attribute_code']] = [
+ 'attribute_id' => $option['attribute_id'],
+ 'attribute_code' => $option['attribute_code'],
+ 'attribute_label' => $option['attribute_label'],
+ 'options' => [],
+ ];
+ }
+ $result[$option['attribute_code']]['options'][$option['option_id']] = $option['option_label'];
+ }
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
new file mode 100644
index 0000000000000..82d167e323fc8
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
@@ -0,0 +1,179 @@
+attributeOptionProvider = $attributeOptionProvider;
+ $this->bucketNameFilter = \array_merge($this->bucketNameFilter, $bucketNameFilter);
+ }
+
+ /**
+ * @inheritdoc
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @throws \Zend_Db_Statement_Exception
+ */
+ public function build(AggregationInterface $aggregation, ?int $storeId): array
+ {
+ $attributeOptions = $this->getAttributeOptions($aggregation);
+
+ // build layer per attribute
+ $result = [];
+ foreach ($this->getAttributeBuckets($aggregation) as $bucket) {
+ $bucketName = $bucket->getName();
+ $attributeCode = \preg_replace('~_bucket$~', '', $bucketName);
+ $attribute = $attributeOptions[$attributeCode] ?? [];
+
+ $result[$bucketName] = $this->buildLayer(
+ $attribute['attribute_label'] ?? $bucketName,
+ \count($bucket->getValues()),
+ $attribute['attribute_code'] ?? $bucketName
+ );
+
+ foreach ($bucket->getValues() as $value) {
+ $metrics = $value->getMetrics();
+ $result[$bucketName]['filter_items'][] = $this->buildItem(
+ $attribute['options'][$metrics['value']] ?? $metrics['value'],
+ $metrics['value'],
+ $metrics['count']
+ );
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get attribute buckets excluding specified bucket names
+ *
+ * @param AggregationInterface $aggregation
+ * @return \Generator|BucketInterface[]
+ */
+ private function getAttributeBuckets(AggregationInterface $aggregation)
+ {
+ foreach ($aggregation->getBuckets() as $bucket) {
+ if (\in_array($bucket->getName(), $this->bucketNameFilter, true)) {
+ continue;
+ }
+ if ($this->isBucketEmpty($bucket)) {
+ continue;
+ }
+ yield $bucket;
+ }
+ }
+
+ /**
+ * Format layer data
+ *
+ * @param string $layerName
+ * @param string $itemsCount
+ * @param string $requestName
+ * @return array
+ */
+ private function buildLayer($layerName, $itemsCount, $requestName): array
+ {
+ return [
+ 'name' => $layerName,
+ 'filter_items_count' => $itemsCount,
+ 'request_var' => $requestName
+ ];
+ }
+
+ /**
+ * Format layer item data
+ *
+ * @param string $label
+ * @param string|int $value
+ * @param string|int $count
+ * @return array
+ */
+ private function buildItem($label, $value, $count): array
+ {
+ return [
+ 'label' => $label,
+ 'value_string' => $value,
+ 'items_count' => $count,
+ ];
+ }
+
+ /**
+ * Check that bucket contains data
+ *
+ * @param BucketInterface|null $bucket
+ * @return bool
+ */
+ private function isBucketEmpty(?BucketInterface $bucket): bool
+ {
+ return null === $bucket || !$bucket->getValues();
+ }
+
+ /**
+ * Get list of attributes with options
+ *
+ * @param AggregationInterface $aggregation
+ * @return array
+ * @throws \Zend_Db_Statement_Exception
+ */
+ private function getAttributeOptions(AggregationInterface $aggregation): array
+ {
+ $attributeOptionIds = [];
+ foreach ($this->getAttributeBuckets($aggregation) as $bucket) {
+ $attributeOptionIds[] = \array_map(function (AggregationValueInterface $value) {
+ return $value->getValue();
+ }, $bucket->getValues());
+ }
+
+ if (!$attributeOptionIds) {
+ return [];
+ }
+
+ return $this->attributeOptionProvider->getOptions(\array_merge(...$attributeOptionIds));
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
new file mode 100644
index 0000000000000..c726f66e8c926
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
@@ -0,0 +1,173 @@
+ [
+ 'request_name' => 'category_id',
+ 'label' => 'Category'
+ ],
+ ];
+
+ /**
+ * @var CategoryAttributeQuery
+ */
+ private $categoryAttributeQuery;
+
+ /**
+ * @var CategoryAttributesMapper
+ */
+ private $attributesMapper;
+
+ /**
+ * @var ResourceConnection
+ */
+ private $resourceConnection;
+
+ /**
+ * @var RootCategoryProvider
+ */
+ private $rootCategoryProvider;
+
+ /**
+ * @param CategoryAttributeQuery $categoryAttributeQuery
+ * @param CategoryAttributesMapper $attributesMapper
+ * @param RootCategoryProvider $rootCategoryProvider
+ * @param ResourceConnection $resourceConnection
+ */
+ public function __construct(
+ CategoryAttributeQuery $categoryAttributeQuery,
+ CategoryAttributesMapper $attributesMapper,
+ RootCategoryProvider $rootCategoryProvider,
+ ResourceConnection $resourceConnection
+ ) {
+ $this->categoryAttributeQuery = $categoryAttributeQuery;
+ $this->attributesMapper = $attributesMapper;
+ $this->resourceConnection = $resourceConnection;
+ $this->rootCategoryProvider = $rootCategoryProvider;
+ }
+
+ /**
+ * @inheritdoc
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Zend_Db_Select_Exception
+ */
+ public function build(AggregationInterface $aggregation, ?int $storeId): array
+ {
+ $bucket = $aggregation->getBucket(self::CATEGORY_BUCKET);
+ if ($this->isBucketEmpty($bucket)) {
+ return [];
+ }
+
+ $categoryIds = \array_map(function (AggregationValueInterface $value) {
+ return (int)$value->getValue();
+ }, $bucket->getValues());
+
+ $categoryIds = \array_diff($categoryIds, [$this->rootCategoryProvider->getRootCategory($storeId)]);
+ $categoryLabels = \array_column(
+ $this->attributesMapper->getAttributesValues(
+ $this->resourceConnection->getConnection()->fetchAll(
+ $this->categoryAttributeQuery->getQuery($categoryIds, ['name'], $storeId)
+ )
+ ),
+ 'name',
+ 'entity_id'
+ );
+
+ if (!$categoryLabels) {
+ return [];
+ }
+
+ $result = $this->buildLayer(
+ self::$bucketMap[self::CATEGORY_BUCKET]['label'],
+ \count($categoryIds),
+ self::$bucketMap[self::CATEGORY_BUCKET]['request_name']
+ );
+
+ foreach ($bucket->getValues() as $value) {
+ $categoryId = $value->getValue();
+ if (!\in_array($categoryId, $categoryIds, true)) {
+ continue ;
+ }
+ $result['filter_items'][] = $this->buildItem(
+ $categoryLabels[$categoryId] ?? $categoryId,
+ $categoryId,
+ $value->getMetrics()['count']
+ );
+ }
+
+ return [$result];
+ }
+
+ /**
+ * Format layer data
+ *
+ * @param string $layerName
+ * @param string $itemsCount
+ * @param string $requestName
+ * @return array
+ */
+ private function buildLayer($layerName, $itemsCount, $requestName): array
+ {
+ return [
+ 'name' => $layerName,
+ 'filter_items_count' => $itemsCount,
+ 'request_var' => $requestName
+ ];
+ }
+
+ /**
+ * Format layer item data
+ *
+ * @param string $label
+ * @param string|int $value
+ * @param string|int $count
+ * @return array
+ */
+ private function buildItem($label, $value, $count): array
+ {
+ return [
+ 'label' => $label,
+ 'value_string' => $value,
+ 'items_count' => $count,
+ ];
+ }
+
+ /**
+ * Check that bucket contains data
+ *
+ * @param BucketInterface|null $bucket
+ * @return bool
+ */
+ private function isBucketEmpty(?BucketInterface $bucket): bool
+ {
+ return null === $bucket || !$bucket->getValues();
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
new file mode 100644
index 0000000000000..77f44afb4f679
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
@@ -0,0 +1,107 @@
+ [
+ 'request_name' => 'price',
+ 'label' => 'Price'
+ ],
+ ];
+
+ /**
+ * @inheritdoc
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function build(AggregationInterface $aggregation, ?int $storeId): array
+ {
+ $bucket = $aggregation->getBucket(self::PRICE_BUCKET);
+ if ($this->isBucketEmpty($bucket)) {
+ return [];
+ }
+
+ $result = $this->buildLayer(
+ self::$bucketMap[self::PRICE_BUCKET]['label'],
+ \count($bucket->getValues()),
+ self::$bucketMap[self::PRICE_BUCKET]['request_name']
+ );
+
+ foreach ($bucket->getValues() as $value) {
+ $metrics = $value->getMetrics();
+ $result['filter_items'][] = $this->buildItem(
+ \str_replace('_', '-', $metrics['value']),
+ $metrics['value'],
+ $metrics['count']
+ );
+ }
+
+ return [$result];
+ }
+
+ /**
+ * Format layer data
+ *
+ * @param string $layerName
+ * @param string $itemsCount
+ * @param string $requestName
+ * @return array
+ */
+ private function buildLayer($layerName, $itemsCount, $requestName): array
+ {
+ return [
+ 'name' => $layerName,
+ 'filter_items_count' => $itemsCount,
+ 'request_var' => $requestName
+ ];
+ }
+
+ /**
+ * Format layer item data
+ *
+ * @param string $label
+ * @param string|int $value
+ * @param string|int $count
+ * @return array
+ */
+ private function buildItem($label, $value, $count): array
+ {
+ return [
+ 'label' => $label,
+ 'value_string' => $value,
+ 'items_count' => $count,
+ ];
+ }
+
+ /**
+ * Check that bucket contains data
+ *
+ * @param BucketInterface|null $bucket
+ * @return bool
+ */
+ private function isBucketEmpty(?BucketInterface $bucket): bool
+ {
+ return null === $bucket || !$bucket->getValues();
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilder.php
new file mode 100644
index 0000000000000..ff661236be62f
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilder.php
@@ -0,0 +1,43 @@
+builders = $builders;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function build(AggregationInterface $aggregation, ?int $storeId): array
+ {
+ $layers = [];
+ foreach ($this->builders as $builder) {
+ $layers[] = $builder->build($aggregation, $storeId);
+ }
+ $layers = \array_merge(...$layers);
+
+ return \array_filter($layers);
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilderInterface.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilderInterface.php
new file mode 100644
index 0000000000000..bd55bc6938b39
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/LayerBuilderInterface.php
@@ -0,0 +1,40 @@
+ 'layer name',
+ * 'filter_items_count' => 'filter items count',
+ * 'request_var' => 'filter name in request',
+ * 'filter_items' => [
+ * 'label' => 'item name',
+ * 'value_string' => 'item value, e.g. category ID',
+ * 'items_count' => 'product count',
+ * ],
+ * ],
+ * ...
+ * ];
+ */
+interface LayerBuilderInterface
+{
+ /**
+ * Build layer data
+ *
+ * @param AggregationInterface $aggregation
+ * @param int|null $storeId
+ * @return array [[{layer data}], ...]
+ */
+ public function build(AggregationInterface $aggregation, ?int $storeId): array;
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/RootCategoryProvider.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/RootCategoryProvider.php
new file mode 100644
index 0000000000000..4b8a4a31b3c35
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/RootCategoryProvider.php
@@ -0,0 +1,55 @@
+resourceConnection = $resourceConnection;
+ }
+
+ /**
+ * Get root category for specified store id
+ *
+ * @param int $storeId
+ * @return int
+ */
+ public function getRootCategory(int $storeId): int
+ {
+ $connection = $this->resourceConnection->getConnection();
+
+ $select = $connection->select()
+ ->from(
+ ['store' => $this->resourceConnection->getTableName('store')],
+ []
+ )
+ ->join(
+ ['store_group' => $this->resourceConnection->getTableName('store_group')],
+ 'store.group_id = store_group.group_id',
+ ['root_category_id' => 'store_group.root_category_id']
+ )
+ ->where('store.store_id = ?', $storeId);
+
+ return (int)$connection->fetchOne($select);
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
index d493c7ba8e66f..9aa632b4fafbb 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
@@ -10,7 +10,8 @@
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-//use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilder;
+use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilder;
+use Magento\Store\Api\Data\StoreInterface;
/**
* Layered navigation filters resolver, used for GraphQL request processing.
@@ -22,24 +23,25 @@ class LayerFilters implements ResolverInterface
*/
private $filtersDataProvider;
-// /**
-// * @var LayerBuilder
-// */
-// private $layerBuilder;
+ /**
+ * @var LayerBuilder
+ */
+ private $layerBuilder;
/**
* @param \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider
* @param \Magento\Framework\Registry $registry
+ * @param LayerBuilder $layerBuilder
*/
public function __construct(
\Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider,
- \Magento\Framework\Registry $registry
- //LayerBuilder $layerBuilder
+ \Magento\Framework\Registry $registry,
+ LayerBuilder $layerBuilder
) {
$this->filtersDataProvider = $filtersDataProvider;
$this->registry = $registry;
- //$this->layerBuilder = $layerBuilder;
+ $this->layerBuilder = $layerBuilder;
}
/**
@@ -56,7 +58,9 @@ public function resolve(
return null;
}
$aggregations = $this->registry->registry('aggregations');
- return [];
- //return $this->layerBuilder->build($aggregations, 1);
+ /** @var StoreInterface $store */
+ $store = $context->getExtensionAttributes()->getStore();
+ $storeId = (int)$store->getId();
+ return $this->layerBuilder->build($aggregations, $storeId);
}
}
diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
index 8fe3d19619fc3..3ada365ec5065 100644
--- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
@@ -101,4 +101,14 @@
+
+
+
+
+ - Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Price
+ - Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Category
+ - Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Attribute
+
+
+
From 2837f1e985f1168e3630dc25c90c36cf1f132dec Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Fri, 9 Aug 2019 11:50:57 -0500
Subject: [PATCH 071/593] MC-18988: Investigate layer navigation code in
lightweight resolver implementation
---
app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 5 -----
1 file changed, 5 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index 0b71b849d09a4..1ce46cf3cbef9 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -315,11 +315,6 @@ input ProductFilterInput @doc(description: "ProductFilterInput defines the filte
country_of_manufacture: FilterTypeInput @doc(description: "The product's country of origin.")
custom_layout: FilterTypeInput @doc(description: "The name of a custom layout.")
gift_message_available: FilterTypeInput @doc(description: "Indicates whether a gift message is available.")
- cat: FilterTypeInput @doc(description: "CATEGORY attribute name")
- mysize: FilterTypeInput @doc(description: "size attribute name")
- mycolor: FilterTypeInput @doc(description: "color attribute name")
- ca_1_631447041: FilterTypeInput @doc(description: "CATEGORY attribute name")
- attributeset2attribute1: FilterTypeInput @doc(description: "CATEGORY attribute name")
visibility: FilterTypeInput @doc(description: "CATEGORY attribute name")
price_dynamic_algorithm: FilterTypeInput @doc(description: "CATEGORY attribute name")
category_ids: FilterTypeInput @doc(description: "CATEGORY attribute name")
From e6083f28870d87f676901e3a9d1739defe1bc0c4 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Mon, 12 Aug 2019 11:29:05 -0500
Subject: [PATCH 072/593] MC-19103: Get aggregation from search and expose them
to the filters resolver or field
---
.../Model/Resolver/LayerFilters.php | 22 +++++-----
.../Model/Resolver/Products.php | 2 +-
.../Model/Resolver/Products/Query/Filter.php | 41 ++++++++++++++-----
.../Model/Resolver/Products/Query/Search.php | 21 ++++++----
.../Model/Resolver/Products/SearchResult.php | 34 +++++++--------
.../Resolver/Products/SearchResultFactory.php | 10 ++---
6 files changed, 79 insertions(+), 51 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
index 9aa632b4fafbb..6ef4e72627e82 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
@@ -30,17 +30,13 @@ class LayerFilters implements ResolverInterface
/**
* @param \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider
- * @param \Magento\Framework\Registry $registry
* @param LayerBuilder $layerBuilder
*/
public function __construct(
\Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider,
- \Magento\Framework\Registry $registry,
LayerBuilder $layerBuilder
-
) {
$this->filtersDataProvider = $filtersDataProvider;
- $this->registry = $registry;
$this->layerBuilder = $layerBuilder;
}
@@ -54,13 +50,19 @@ public function resolve(
array $value = null,
array $args = null
) {
- if (!isset($value['layer_type'])) {
+ if (!isset($value['layer_type']) || !isset($value['search_result'])) {
return null;
}
- $aggregations = $this->registry->registry('aggregations');
- /** @var StoreInterface $store */
- $store = $context->getExtensionAttributes()->getStore();
- $storeId = (int)$store->getId();
- return $this->layerBuilder->build($aggregations, $storeId);
+
+ $aggregations = $value['search_result']->getSearchAggregation();
+
+ if ($aggregations) {
+ /** @var StoreInterface $store */
+ $store = $context->getExtensionAttributes()->getStore();
+ $storeId = (int)$store->getId();
+ return $this->layerBuilder->build($aggregations, $storeId);
+ } else {
+ return [];
+ }
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index 93ecf9c881548..9eca99b486df4 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -117,7 +117,7 @@ public function resolve(
'current_page' => $currentPage,
'total_pages' => $maxPages
],
- //'filters' => $aggregations
+ 'search_result' => $searchResult,
'layer_type' => $layerType
];
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php
index 62e2f0c488c6c..6771777341281 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php
@@ -7,6 +7,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query;
+use GraphQL\Language\AST\SelectionNode;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product;
@@ -79,7 +80,12 @@ public function getResult(
$productArray[$product->getId()]['model'] = $product;
}
- return $this->searchResultFactory->create($products->getTotalCount(), $productArray);
+ return $this->searchResultFactory->create(
+ [
+ 'totalCount' => $products->getTotalCount(),
+ 'productsSearchResult' => $productArray
+ ]
+ );
}
/**
@@ -99,20 +105,35 @@ private function getProductFields(ResolveInfo $info) : array
if ($selection->name->value !== 'items') {
continue;
}
+ $fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames);
+ }
+ }
+
+ $fieldNames = array_merge(...$fieldNames);
+
+ return $fieldNames;
+ }
- foreach ($selection->selectionSet->selections as $itemSelection) {
- if ($itemSelection->kind === 'InlineFragment') {
- foreach ($itemSelection->selectionSet->selections as $inlineSelection) {
- if ($inlineSelection->kind === 'InlineFragment') {
- continue;
- }
- $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value);
- }
+ /**
+ * Collect field names for each node in selection
+ *
+ * @param SelectionNode $selection
+ * @param array $fieldNames
+ * @return array
+ */
+ private function collectProductFieldNames(SelectionNode $selection, array $fieldNames = []): array
+ {
+ foreach ($selection->selectionSet->selections as $itemSelection) {
+ if ($itemSelection->kind === 'InlineFragment') {
+ foreach ($itemSelection->selectionSet->selections as $inlineSelection) {
+ if ($inlineSelection->kind === 'InlineFragment') {
continue;
}
- $fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value);
+ $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value);
}
+ continue;
}
+ $fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value);
}
return $fieldNames;
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index 7a766269af602..1fdd64f7bbc68 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -63,7 +63,6 @@ class Search
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
* @param \Magento\Search\Model\Search\PageSizeProvider $pageSize
* @param SearchCriteriaInterfaceFactory $searchCriteriaFactory
- * @param \Magento\Framework\Registry $registry
*/
public function __construct(
SearchInterface $search,
@@ -72,8 +71,7 @@ public function __construct(
SearchResultFactory $searchResultFactory,
\Magento\Framework\EntityManager\MetadataPool $metadataPool,
\Magento\Search\Model\Search\PageSizeProvider $pageSize,
- SearchCriteriaInterfaceFactory $searchCriteriaFactory,
- \Magento\Framework\Registry $registry
+ SearchCriteriaInterfaceFactory $searchCriteriaFactory
) {
$this->search = $search;
$this->filterHelper = $filterHelper;
@@ -82,7 +80,6 @@ public function __construct(
$this->metadataPool = $metadataPool;
$this->pageSizeProvider = $pageSize;
$this->searchCriteriaFactory = $searchCriteriaFactory;
- $this->registry = $registry;
}
/**
@@ -98,14 +95,16 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$idField = $this->metadataPool->getMetadata(
\Magento\Catalog\Api\Data\ProductInterface::class
)->getIdentifierField();
+
$realPageSize = $searchCriteria->getPageSize();
$realCurrentPage = $searchCriteria->getCurrentPage();
// Current page must be set to 0 and page size to max for search to grab all ID's as temporary workaround
- //$pageSize = $this->pageSizeProvider->getMaxPageSize();
- $searchCriteria->setPageSize(10000);
+ $pageSize = $this->pageSizeProvider->getMaxPageSize();
+ $searchCriteria->setPageSize($pageSize);
$searchCriteria->setCurrentPage(0);
$itemsResults = $this->search->search($searchCriteria);
- $this->registry->register('aggregations', $itemsResults->getAggregations());
+ $aggregation = $itemsResults->getAggregations();
+
$ids = [];
$searchIds = [];
foreach ($itemsResults->getItems() as $item) {
@@ -139,7 +138,13 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
}
}
- return $this->searchResultFactory->create($searchResult->getTotalCount(), $products);
+ return $this->searchResultFactory->create(
+ [
+ 'totalCount' => $searchResult->getTotalCount(),
+ 'productsSearchResult' => $products,
+ 'searchAggregation' => $aggregation
+ ]
+ );
}
/**
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php
index 6e229bdc38a31..849d9065d2449 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php
@@ -7,31 +7,21 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Products;
-use Magento\Framework\Api\SearchResultsInterface;
+use Magento\Framework\Api\Search\AggregationInterface;
/**
* Container for a product search holding the item result and the array in the GraphQL-readable product type format.
*/
class SearchResult
{
- /**
- * @var SearchResultsInterface
- */
- private $totalCount;
-
- /**
- * @var array
- */
- private $productsSearchResult;
+ private $data;
/**
- * @param int $totalCount
- * @param array $productsSearchResult
+ * @param array $data
*/
- public function __construct(int $totalCount, array $productsSearchResult)
+ public function __construct(array $data)
{
- $this->totalCount = $totalCount;
- $this->productsSearchResult = $productsSearchResult;
+ $this->data = $data;
}
/**
@@ -41,7 +31,7 @@ public function __construct(int $totalCount, array $productsSearchResult)
*/
public function getTotalCount() : int
{
- return $this->totalCount;
+ return $this->data['totalCount'] ?? 0;
}
/**
@@ -51,6 +41,16 @@ public function getTotalCount() : int
*/
public function getProductsSearchResult() : array
{
- return $this->productsSearchResult;
+ return $this->data['productsSearchResult'] ?? [];
+ }
+
+ /**
+ * Retrieve aggregated search results
+ *
+ * @return AggregationInterface|null
+ */
+ public function getSearchAggregation(): ?AggregationInterface
+ {
+ return $this->data['searchAggregation'] ?? null;
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResultFactory.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResultFactory.php
index aec9362f47c3a..479e6a3f96235 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResultFactory.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResultFactory.php
@@ -30,15 +30,15 @@ public function __construct(ObjectManagerInterface $objectManager)
/**
* Instantiate SearchResult
*
- * @param int $totalCount
- * @param array $productsSearchResult
+ * @param array $data
* @return SearchResult
*/
- public function create(int $totalCount, array $productsSearchResult) : SearchResult
- {
+ public function create(
+ array $data
+ ): SearchResult {
return $this->objectManager->create(
SearchResult::class,
- ['totalCount' => $totalCount, 'productsSearchResult' => $productsSearchResult]
+ ['data' => $data]
);
}
}
From 0fcfffab4978b3b8e3c43f2e233442a98607e080 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Mon, 12 Aug 2019 18:19:35 -0500
Subject: [PATCH 073/593] MC-19102: Adding new search request and use search
retrieve results
- use filter to paginate
---
.../Product/SearchCriteriaBuilder.php | 152 ++++++++++++++++++
.../Model/Resolver/Products/Query/Search.php | 50 +-----
2 files changed, 156 insertions(+), 46 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
new file mode 100644
index 0000000000000..f49db706e3aaf
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -0,0 +1,152 @@
+scopeConfig = $scopeConfig;
+ $this->filterBuilder = $filterBuilder;
+ $this->filterGroupBuilder = $filterGroupBuilder;
+ $this->builder = $builder;
+ $this->visibility = $visibility;
+ }
+
+ /**
+ * Build search criteria
+ *
+ * @param array $args
+ * @param bool $includeAggregation
+ * @return SearchCriteriaInterface
+ */
+ public function build(array $args, bool $includeAggregation): SearchCriteriaInterface
+ {
+ $searchCriteria = $this->builder->build('products', $args);
+ $searchCriteria->setRequestName('catalog_view_container');
+ if ($includeAggregation) {
+ $this->preparePriceAggregation($searchCriteria);
+ }
+
+ if (!empty($args['search'])) {
+ $this->addFilter($searchCriteria, 'search_term', $args['search']);
+ if (!$searchCriteria->getSortOrders()) {
+ $searchCriteria->setSortOrders(['_score' => \Magento\Framework\Api\SortOrder::SORT_DESC]);
+ }
+ }
+
+ $this->addVisibilityFilter($searchCriteria, !empty($args['search']), !empty($args['filter']));
+
+ $searchCriteria->setCurrentPage($args['currentPage']);
+ $searchCriteria->setPageSize($args['pageSize']);
+
+ return $searchCriteria;
+ }
+
+ /**
+ * Add filter by visibility
+ *
+ * @param SearchCriteriaInterface $searchCriteria
+ * @param bool $isSearch
+ * @param bool $isFilter
+ */
+ private function addVisibilityFilter(SearchCriteriaInterface $searchCriteria, bool $isSearch, bool $isFilter): void
+ {
+ if ($isFilter && $isSearch) {
+ // Index already contains products filtered by visibility: catalog, search, both
+ return ;
+ }
+ $visibilityIds = $isSearch
+ ? $this->visibility->getVisibleInSearchIds()
+ : $this->visibility->getVisibleInCatalogIds();
+
+ $this->addFilter($searchCriteria, 'visibility', $visibilityIds);
+ }
+
+ /**
+ * Prepare price aggregation algorithm
+ *
+ * @param SearchCriteriaInterface $searchCriteria
+ * @return void
+ */
+ private function preparePriceAggregation(SearchCriteriaInterface $searchCriteria): void
+ {
+ $priceRangeCalculation = $this->scopeConfig->getValue(
+ \Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory::XML_PATH_RANGE_CALCULATION,
+ \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+ );
+ if ($priceRangeCalculation) {
+ $this->addFilter($searchCriteria, 'price_dynamic_algorithm', $priceRangeCalculation);
+ }
+ }
+
+ /**
+ * Add filter to search criteria
+ *
+ * @param SearchCriteriaInterface $searchCriteria
+ * @param string $field
+ * @param mixed $value
+ */
+ private function addFilter(SearchCriteriaInterface $searchCriteria, string $field, $value): void
+ {
+ $filter = $this->filterBuilder
+ ->setField($field)
+ ->setValue($value)
+ ->create();
+ $this->filterGroupBuilder->addFilter($filter);
+ $filterGroups = $searchCriteria->getFilterGroups();
+ $filterGroups[] = $this->filterGroupBuilder->create();
+ $searchCriteria->setFilterGroups($filterGroups);
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index 1fdd64f7bbc68..11fb8b79f241e 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -115,60 +115,18 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$searchCriteriaIds = $this->searchCriteriaFactory->create();
$filter = $this->filterHelper->generate($idField, 'in', $searchIds);
$searchCriteriaIds = $this->filterHelper->add($searchCriteriaIds, $filter);
- $searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, true);
-
+ $searchCriteriaIds->setSortOrders($searchCriteria->getSortOrders());
$searchCriteriaIds->setPageSize($realPageSize);
$searchCriteriaIds->setCurrentPage($realCurrentPage);
- $paginatedProducts = $this->paginateList($searchResult, $searchCriteriaIds);
-
- $products = [];
- if (!isset($searchCriteria->getSortOrders()[0])) {
- foreach ($paginatedProducts as $product) {
- if (in_array($product[$idField], $searchIds)) {
- $ids[$product[$idField]] = $product;
- }
- }
- $products = array_filter($ids);
- } else {
- foreach ($paginatedProducts as $product) {
- $productId = isset($product['entity_id']) ? $product['entity_id'] : $product[$idField];
- if (in_array($productId, $searchIds)) {
- $products[] = $product;
- }
- }
- }
+
+ $searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, true);
return $this->searchResultFactory->create(
[
'totalCount' => $searchResult->getTotalCount(),
- 'productsSearchResult' => $products,
+ 'productsSearchResult' => $searchResult->getProductsSearchResult(),
'searchAggregation' => $aggregation
]
);
}
-
- /**
- * Paginate an array of Ids that get pulled back in search based off search criteria and total count.
- *
- * @param SearchResult $searchResult
- * @param SearchCriteriaInterface $searchCriteria
- * @return int[]
- */
- private function paginateList(SearchResult $searchResult, SearchCriteriaInterface $searchCriteria) : array
- {
- $length = $searchCriteria->getPageSize();
- // Search starts pages from 0
- $offset = $length * ($searchCriteria->getCurrentPage() - 1);
-
- if ($searchCriteria->getPageSize()) {
- $maxPages = ceil($searchResult->getTotalCount() / $searchCriteria->getPageSize());
- } else {
- $maxPages = 0;
- }
-
- if ($searchCriteria->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) {
- $offset = (int)$maxPages;
- }
- return array_slice($searchResult->getProductsSearchResult(), $offset, $length);
- }
}
From 5b0ef75ccbb1cd04adff985fbaa76cd342512de0 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Mon, 12 Aug 2019 21:32:30 -0500
Subject: [PATCH 074/593] MC-19102: Adding new search request and use search
retrieve results
- fix attribute list
- fix category_ids
- fix pagination
- deprecation of searchCriteria to be used in the new builder
---
.../Model/Resolver/Products.php | 59 +++++++++++--------
.../ProductEntityAttributesForAst.php | 37 +++++++++---
.../Model/Resolver/Products/Query/Search.php | 13 +++-
.../Model/Resolver/Products/SearchResult.php | 30 ++++++++++
.../CatalogGraphQl/etc/schema.graphqls | 3 -
.../Query/Resolver/Argument/AstConverter.php | 11 +---
6 files changed, 107 insertions(+), 46 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index 9eca99b486df4..10d0d10747eaf 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -16,6 +16,7 @@
use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\SearchFilter;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Catalog\Model\Layer\Resolver;
+use Magento\CatalogGraphQl\DataProvider\Product\SearchCriteriaBuilder;
/**
* Products field resolver, used for GraphQL request processing.
@@ -24,6 +25,7 @@ class Products implements ResolverInterface
{
/**
* @var Builder
+ * @deprecated
*/
private $searchCriteriaBuilder;
@@ -34,30 +36,41 @@ class Products implements ResolverInterface
/**
* @var Filter
+ * @deprecated
*/
private $filterQuery;
/**
* @var SearchFilter
+ * @deprecated
*/
private $searchFilter;
+ /**
+ * @var SearchCriteriaBuilder
+ */
+ private $searchApiCriteriaBuilder;
+
/**
* @param Builder $searchCriteriaBuilder
* @param Search $searchQuery
* @param Filter $filterQuery
* @param SearchFilter $searchFilter
+ * @param SearchCriteriaBuilder|null $searchCriteriaBuilder
*/
public function __construct(
Builder $searchCriteriaBuilder,
Search $searchQuery,
Filter $filterQuery,
- SearchFilter $searchFilter
+ SearchFilter $searchFilter,
+ SearchCriteriaBuilder $searchApiCriteriaBuilder = null
) {
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->searchQuery = $searchQuery;
$this->filterQuery = $filterQuery;
$this->searchFilter = $searchFilter;
+ $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder ??
+ \Magento\Framework\App\ObjectManager::getInstance()->get(SearchCriteriaBuilder::class);
}
/**
@@ -70,41 +83,37 @@ public function resolve(
array $value = null,
array $args = null
) {
- $searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args);
if ($args['currentPage'] < 1) {
throw new GraphQlInputException(__('currentPage value must be greater than 0.'));
}
if ($args['pageSize'] < 1) {
throw new GraphQlInputException(__('pageSize value must be greater than 0.'));
}
- $searchCriteria->setCurrentPage($args['currentPage']);
- $searchCriteria->setPageSize($args['pageSize']);
if (!isset($args['search']) && !isset($args['filter'])) {
throw new GraphQlInputException(
__("'search' or 'filter' input argument is required.")
);
- } elseif (isset($args['search'])) {
- $layerType = Resolver::CATALOG_LAYER_SEARCH;
- $this->searchFilter->add($args['search'], $searchCriteria);
- $searchResult = $this->searchQuery->getResult($searchCriteria, $info);
- } else {
- $layerType = Resolver::CATALOG_LAYER_CATEGORY;
- $searchCriteria->setRequestName('catalog_view_container');
- $searchResult = $this->searchQuery->getResult($searchCriteria, $info);
- }
- //possible division by 0
- if ($searchCriteria->getPageSize()) {
- $maxPages = ceil($searchResult->getTotalCount() / $searchCriteria->getPageSize());
- } else {
- $maxPages = 0;
}
- $currentPage = $searchCriteria->getCurrentPage();
- if ($searchCriteria->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) {
+ //get product children fields queried
+ $productFields = (array)$info->getFieldSelection(1);
+
+ $searchCriteria = $this->searchApiCriteriaBuilder->build(
+ $args,
+ isset($productFields['filters'])
+ );
+
+ $searchCriteria->setCurrentPage($args['currentPage']);
+ $searchCriteria->setPageSize($args['pageSize']);
+
+
+ $searchResult = $this->searchQuery->getResult($searchCriteria, $info);
+
+ if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) {
throw new GraphQlInputException(
__(
'currentPage value %1 specified is greater than the %2 page(s) available.',
- [$currentPage, $maxPages]
+ [$searchResult->getCurrentPage(), $searchResult->getTotalPages()]
)
);
}
@@ -113,12 +122,12 @@ public function resolve(
'total_count' => $searchResult->getTotalCount(),
'items' => $searchResult->getProductsSearchResult(),
'page_info' => [
- 'page_size' => $searchCriteria->getPageSize(),
- 'current_page' => $currentPage,
- 'total_pages' => $maxPages
+ 'page_size' => $searchResult->getPageSize(),
+ 'current_page' => $searchResult->getCurrentPage(),
+ 'total_pages' => $searchResult->getTotalPages()
],
'search_result' => $searchResult,
- 'layer_type' => $layerType
+ 'layer_type' => isset($args['search']) ? Resolver::CATALOG_LAYER_SEARCH : Resolver::CATALOG_LAYER_CATEGORY,
];
return $data;
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php
index a547f63b217fe..17409210808cc 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php
@@ -23,24 +23,41 @@ class ProductEntityAttributesForAst implements FieldEntityAttributesInterface
private $config;
/**
+ * Additional attributes that are not retrieved by getting fields from ProductInterface
+ *
* @var array
*/
private $additionalAttributes = ['min_price', 'max_price', 'category_id'];
+ /**
+ * Array to translate graphql field to internal entity attribute
+ *
+ * @var array
+ */
+ private $translatedAttributes = ['category_id' => 'category_ids'];
+
/**
* @param ConfigInterface $config
- * @param array $additionalAttributes
+ * @param string[] $additionalAttributes
+ * @param array $translatedAttributes
*/
public function __construct(
ConfigInterface $config,
- array $additionalAttributes = []
+ array $additionalAttributes = [],
+ array $translatedAttributes = []
) {
$this->config = $config;
$this->additionalAttributes = array_merge($this->additionalAttributes, $additionalAttributes);
+ $this->translatedAttributes = array_merge($this->translatedAttributes, $translatedAttributes);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ *
+ * Gather all the product entity attributes that can be filtered by search criteria.
+ * Example format ['attributeNameInGraphQl' => ['type' => 'String'. 'fieldName' => 'attributeNameInSearchCriteria']]
+ *
+ * @return array
*/
public function getEntityAttributes() : array
{
@@ -55,14 +72,20 @@ public function getEntityAttributes() : array
$configElement = $this->config->getConfigElement($interface['interface']);
foreach ($configElement->getFields() as $field) {
- $fields[$field->getName()] = 'String';
+ $fields[$field->getName()] = [
+ 'type' => 'String',
+ 'fieldName' => $this->translatedAttributes[$field->getName()] ?? $field->getName(),
+ ];
}
}
- foreach ($this->additionalAttributes as $attribute) {
- $fields[$attribute] = 'String';
+ foreach ($this->additionalAttributes as $attributeName) {
+ $fields[$attributeName] = [
+ 'type' => 'String',
+ 'fieldName' => $this->translatedAttributes[$attributeName] ?? $attributeName,
+ ];
}
- return array_keys($fields);
+ return $fields;
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index 11fb8b79f241e..daa3619d17b83 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -103,7 +103,6 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$searchCriteria->setPageSize($pageSize);
$searchCriteria->setCurrentPage(0);
$itemsResults = $this->search->search($searchCriteria);
- $aggregation = $itemsResults->getAggregations();
$ids = [];
$searchIds = [];
@@ -121,11 +120,21 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, true);
+ //possible division by 0
+ if ($realPageSize) {
+ $maxPages = (int)ceil($searchResult->getTotalCount() / $realPageSize);
+ } else {
+ $maxPages = 0;
+ }
+
return $this->searchResultFactory->create(
[
'totalCount' => $searchResult->getTotalCount(),
'productsSearchResult' => $searchResult->getProductsSearchResult(),
- 'searchAggregation' => $aggregation
+ 'searchAggregation' => $itemsResults->getAggregations(),
+ 'pageSize' => $realPageSize,
+ 'currentPage' => $realCurrentPage,
+ 'totalPages' => $maxPages,
]
);
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php
index 849d9065d2449..e4a137413b4c5 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php
@@ -53,4 +53,34 @@ public function getSearchAggregation(): ?AggregationInterface
{
return $this->data['searchAggregation'] ?? null;
}
+
+ /**
+ * Retrieve the page size for the search
+ *
+ * @return int
+ */
+ public function getPageSize(): int
+ {
+ return $this->data['pageSize'] ?? 0;
+ }
+
+ /**
+ * Retrieve the current page for the search
+ *
+ * @return int
+ */
+ public function getCurrentPage(): int
+ {
+ return $this->data['currentPage'] ?? 0;
+ }
+
+ /**
+ * Retrieve total pages for the search
+ *
+ * @return int
+ */
+ public function getTotalPages(): int
+ {
+ return $this->data['totalPages'] ?? 0;
+ }
}
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index 1ce46cf3cbef9..ea56faf94408e 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -315,9 +315,6 @@ input ProductFilterInput @doc(description: "ProductFilterInput defines the filte
country_of_manufacture: FilterTypeInput @doc(description: "The product's country of origin.")
custom_layout: FilterTypeInput @doc(description: "The name of a custom layout.")
gift_message_available: FilterTypeInput @doc(description: "Indicates whether a gift message is available.")
- visibility: FilterTypeInput @doc(description: "CATEGORY attribute name")
- price_dynamic_algorithm: FilterTypeInput @doc(description: "CATEGORY attribute name")
- category_ids: FilterTypeInput @doc(description: "CATEGORY attribute name")
or: ProductFilterInput @doc(description: "The keyword required to perform a logical OR comparison.")
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php
index 802d46eafcb97..baf165b0298c3 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/AstConverter.php
@@ -59,17 +59,10 @@ public function __construct(
public function getClausesFromAst(string $fieldName, array $arguments) : array
{
$attributes = $this->fieldEntityAttributesPool->getEntityAttributesForEntityFromField($fieldName);
- $attributes[] = 'cat';
- $attributes[] = 'mysize';
- $attributes[] = 'mycolor';
- $attributes[] = 'ca_1_631447041';
- $attributes[] = 'attributeset2attribute1';
- $attributes[] = 'price_dynamic_algorithm';
- $attributes[] = 'visibility';
- $attributes[] = 'category_ids';
$conditions = [];
foreach ($arguments as $argumentName => $argument) {
- if (in_array($argumentName, $attributes)) {
+ if (key_exists($argumentName, $attributes)) {
+ $argumentName = $attributes[$argumentName]['fieldName'] ?? $argumentName;
foreach ($argument as $clauseType => $clause) {
if (is_array($clause)) {
$value = [];
From 2df1ba3712b841c334374b123d311e2841111662 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Tue, 13 Aug 2019 09:08:22 -0500
Subject: [PATCH 075/593] MC-19102: Adding new search request and use search
retrieve results
- Add new search_request types for graphql product search
---
.../Product/SearchCriteriaBuilder.php | 4 +-
.../Model/Resolver/Products.php | 3 +-
.../Plugin/Search/Request/ConfigReader.php | 221 ++++++++++++++++++
app/code/Magento/CatalogGraphQl/etc/di.xml | 4 +
.../CatalogGraphQl/etc/search_request.xml | 90 +++++++
5 files changed, 319 insertions(+), 3 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
create mode 100644 app/code/Magento/CatalogGraphQl/etc/search_request.xml
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
index f49db706e3aaf..6970c13aecbc6 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -74,7 +74,9 @@ public function __construct(
public function build(array $args, bool $includeAggregation): SearchCriteriaInterface
{
$searchCriteria = $this->builder->build('products', $args);
- $searchCriteria->setRequestName('catalog_view_container');
+ $searchCriteria->setRequestName(
+ $includeAggregation ? 'graphql_product_search_with_aggregation' : 'graphql_product_search'
+ );
if ($includeAggregation) {
$this->preparePriceAggregation($searchCriteria);
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index 10d0d10747eaf..9c8d3266c4d2d 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -56,7 +56,7 @@ class Products implements ResolverInterface
* @param Search $searchQuery
* @param Filter $filterQuery
* @param SearchFilter $searchFilter
- * @param SearchCriteriaBuilder|null $searchCriteriaBuilder
+ * @param SearchCriteriaBuilder|null $searchApiCriteriaBuilder
*/
public function __construct(
Builder $searchCriteriaBuilder,
@@ -106,7 +106,6 @@ public function resolve(
$searchCriteria->setCurrentPage($args['currentPage']);
$searchCriteria->setPageSize($args['pageSize']);
-
$searchResult = $this->searchQuery->getResult($searchCriteria, $info);
if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) {
diff --git a/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php b/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
new file mode 100644
index 0000000000000..70e312ff4e2ee
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
@@ -0,0 +1,221 @@
+dataProvider = $dataProvider;
+ $this->generatorResolver = $generatorResolver;
+ }
+
+ /**
+ * Merge reader's value with generated
+ *
+ * @param \Magento\Framework\Config\ReaderInterface $subject
+ * @param array $result
+ * @return array
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterRead(
+ \Magento\Framework\Config\ReaderInterface $subject,
+ array $result
+ ) {
+ $searchRequestNameWithAggregation = $this->generateRequest();
+ $searchRequest = $searchRequestNameWithAggregation;
+ $searchRequest['queries'][$this->requestName] = $searchRequest['queries'][$this->requestNameWithAggregation];
+ unset($searchRequest['queries'][$this->requestNameWithAggregation], $searchRequest['aggregations']);
+
+ return array_merge_recursive(
+ $result,
+ [
+ $this->requestNameWithAggregation => $searchRequestNameWithAggregation,
+ $this->requestName => $searchRequest,
+ ]
+ );
+ }
+
+ /**
+ * Retrieve searchable attributes
+ *
+ * @return \Magento\Eav\Model\Entity\Attribute[]
+ */
+ private function getSearchableAttributes(): array
+ {
+ $attributes = [];
+ foreach ($this->dataProvider->getSearchableAttributes() as $attribute) {
+ $attributes[$attribute->getAttributeCode()] = $attribute;
+ }
+ return $attributes;
+ }
+
+ /**
+ * Generate search request for search products via GraphQL
+ *
+ * @return array
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+ */
+ private function generateRequest()
+ {
+ $request = [];
+ foreach ($this->getSearchableAttributes() as $attribute) {
+ if (\in_array($attribute->getAttributeCode(), ['price', 'visibility', 'category_ids'])) {
+ //same fields have special semantics
+ continue;
+ }
+ $queryName = $attribute->getAttributeCode() . '_query';
+ $request['queries'][$this->requestNameWithAggregation]['queryReference'][] = [
+ 'clause' => 'must',
+ 'ref' => $queryName,
+ ];
+ switch ($attribute->getBackendType()) {
+ case 'static':
+ case 'text':
+ case 'varchar':
+ if ($attribute->getFrontendInput() === 'multiselect') {
+ $filterName = $attribute->getAttributeCode() . RequestGenerator::FILTER_SUFFIX;
+ $request['queries'][$queryName] = [
+ 'name' => $queryName,
+ 'type' => QueryInterface::TYPE_FILTER,
+ 'filterReference' => [
+ [
+ 'ref' => $filterName,
+ ],
+ ],
+ ];
+ $request['filters'][$filterName] = [
+ 'type' => FilterInterface::TYPE_TERM,
+ 'name' => $filterName,
+ 'field' => $attribute->getAttributeCode(),
+ 'value' => '$' . $attribute->getAttributeCode() . '$',
+ ];
+ } else {
+ $request['queries'][$queryName] = [
+ 'name' => $queryName,
+ 'type' => 'matchQuery',
+ 'value' => '$' . $attribute->getAttributeCode() . '$',
+ 'match' => [
+ [
+ 'field' => $attribute->getAttributeCode(),
+ 'boost' => $attribute->getSearchWeight() ?: 1,
+ ],
+ ],
+ ];
+ }
+ break;
+ case 'decimal':
+ case 'datetime':
+ case 'date':
+ $filterName = $attribute->getAttributeCode() . RequestGenerator::FILTER_SUFFIX;
+ $request['queries'][$queryName] = [
+ 'name' => $queryName,
+ 'type' => QueryInterface::TYPE_FILTER,
+ 'filterReference' => [
+ [
+ 'ref' => $filterName,
+ ],
+ ],
+ ];
+ $request['filters'][$filterName] = [
+ 'field' => $attribute->getAttributeCode(),
+ 'name' => $filterName,
+ 'type' => FilterInterface::TYPE_RANGE,
+ 'from' => '$' . $attribute->getAttributeCode() . '.from$',
+ 'to' => '$' . $attribute->getAttributeCode() . '.to$',
+ ];
+ break;
+ default:
+ $filterName = $attribute->getAttributeCode() . RequestGenerator::FILTER_SUFFIX;
+ $request['queries'][$queryName] = [
+ 'name' => $queryName,
+ 'type' => QueryInterface::TYPE_FILTER,
+ 'filterReference' => [
+ [
+ 'ref' => $filterName,
+ ],
+ ],
+ ];
+ $request['filters'][$filterName] = [
+ 'type' => FilterInterface::TYPE_TERM,
+ 'name' => $filterName,
+ 'field' => $attribute->getAttributeCode(),
+ 'value' => '$' . $attribute->getAttributeCode() . '$',
+ ];
+ }
+ $generator = $this->generatorResolver->getGeneratorForType($attribute->getBackendType());
+
+ if ($attribute->getData(EavAttributeInterface::IS_FILTERABLE)) {
+ $bucketName = $attribute->getAttributeCode() . self::BUCKET_SUFFIX;
+ $request['aggregations'][$bucketName] = $generator->getAggregationData($attribute, $bucketName);
+ }
+
+ $this->addSearchAttributeToFullTextSearch($attribute, $request);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Add attribute with specified boost to "search" query used in full text search
+ *
+ * @param \Magento\Eav\Model\Entity\Attribute $attribute
+ * @param array $request
+ * @return void
+ */
+ private function addSearchAttributeToFullTextSearch(\Magento\Eav\Model\Entity\Attribute $attribute, &$request): void
+ {
+ // Match search by custom price attribute isn't supported
+ if ($attribute->getFrontendInput() !== 'price') {
+ $request['queries']['search']['match'][] = [
+ 'field' => $attribute->getAttributeCode(),
+ 'boost' => $attribute->getSearchWeight() ?: 1,
+ ];
+ }
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/etc/di.xml b/app/code/Magento/CatalogGraphQl/etc/di.xml
index cea1c7ce327e2..0fe30eb0503ea 100644
--- a/app/code/Magento/CatalogGraphQl/etc/di.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/di.xml
@@ -57,4 +57,8 @@
Magento\Catalog\Model\Api\SearchCriteria\ProductCollectionProcessor
+
+
+
+
diff --git a/app/code/Magento/CatalogGraphQl/etc/search_request.xml b/app/code/Magento/CatalogGraphQl/etc/search_request.xml
new file mode 100644
index 0000000000000..ab1eea9eb6fda
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/etc/search_request.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 10000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 10000
+
+
From 8d9424cbd35f134a15f4ab323a4e913d5fc15482 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Tue, 13 Aug 2019 12:56:54 -0500
Subject: [PATCH 076/593] MC-18512: Dynamically inject all searchable custom
attributes for product filtering
---
.../CatalogGraphQl/Model/Config/FilterAttributeReader.php | 4 ++++
.../CatalogGraphQl/Model/Config/SortAttributeReader.php | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
index b84c18ea5ac0b..1dd218329112c 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
@@ -67,6 +67,10 @@ public function read($scope = null) : array
$config = [];
foreach ($this->getAttributeCollection() as $attribute) {
+ if (!$attribute->getIsUserDefined()) {
+ //do not override fields defined in schema.graphqls
+ continue;
+ }
$attributeCode = $attribute->getAttributeCode();
foreach ($typeNames as $typeName) {
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
index 215b28be0579c..079084e95b9de 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
@@ -61,6 +61,10 @@ public function read($scope = null) : array
$attributes = $this->attributesCollection->addSearchableAttributeFilter()->addFilter('used_for_sort_by', 1);
/** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
foreach ($attributes as $attribute) {
+ if (!$attribute->getIsUserDefined()) {
+ //do not override fields defined in schema.graphqls
+ continue;
+ }
$attributeCode = $attribute->getAttributeCode();
$attributeLabel = $attribute->getDefaultFrontendLabel();
foreach ($map as $type) {
From f326e0789e937f13cbb45fed0a5597ca6548a984 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Tue, 13 Aug 2019 13:39:25 -0500
Subject: [PATCH 077/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- added data fixture
---
.../attribute_set_based_on_default_set.php | 26 ++++
...th_layered_navigation_custom_attribute.php | 144 ++++++++++++++++++
...d_navigation_custom_attribute_rollback.php | 46 ++++++
3 files changed, 216 insertions(+)
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_set.php
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_set.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_set.php
new file mode 100644
index 0000000000000..929b88367dd78
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_set.php
@@ -0,0 +1,26 @@
+create(\Magento\Eav\Model\Entity\Attribute\Set::class);
+
+$entityType = $objectManager->create(\Magento\Eav\Model\Entity\Type::class)->loadByCode('catalog_product');
+$defaultSetId = $objectManager->create(\Magento\Catalog\Model\Product::class)->getDefaultAttributeSetid();
+
+$data = [
+ 'attribute_set_name' => 'second_attribute_set',
+ 'entity_type_id' => $entityType->getId(),
+ 'sort_order' => 200,
+];
+
+$attributeSet->setData($data);
+$attributeSet->validate();
+$attributeSet->save();
+$attributeSet->initFromSkeleton($defaultSetId);
+$attributeSet->save();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
new file mode 100644
index 0000000000000..00678ae904def
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
@@ -0,0 +1,144 @@
+get(\Magento\Eav\Model\Config::class);
+$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
+
+$eavConfig->clear();
+
+$attribute1 = $eavConfig->getAttribute('catalog_product', ' second_test_configurable');
+$eavConfig->clear();
+
+/** @var $installer \Magento\Catalog\Setup\CategorySetup */
+$installer = Bootstrap::getObjectManager()->create(\Magento\Catalog\Setup\CategorySetup::class);
+
+if (!$attribute->getId()) {
+
+ /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
+ $attribute = Bootstrap::getObjectManager()->create(
+ \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
+ );
+
+ /** @var AttributeRepositoryInterface $attributeRepository */
+ $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class);
+
+ $attribute->setData(
+ [
+ 'attribute_code' => 'test_configurable',
+ 'entity_type_id' => $installer->getEntityTypeId('catalog_product'),
+ 'is_global' => 1,
+ 'is_user_defined' => 1,
+ 'frontend_input' => 'select',
+ 'is_unique' => 0,
+ 'is_required' => 0,
+ 'is_searchable' => 1,
+ 'is_visible_in_advanced_search' => 1,
+ 'is_comparable' => 1,
+ 'is_filterable' => 1,
+ 'is_filterable_in_search' => 1,
+ 'is_used_for_promo_rules' => 0,
+ 'is_html_allowed_on_front' => 1,
+ 'is_visible_on_front' => 1,
+ 'used_in_product_listing' => 1,
+ 'used_for_sort_by' => 1,
+ 'frontend_label' => ['Test Configurable'],
+ 'backend_type' => 'int',
+ 'option' => [
+ 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']],
+ 'order' => ['option_0' => 1, 'option_1' => 2],
+ ],
+ 'default' => ['option_0']
+ ]
+ );
+
+ $attributeRepository->save($attribute);
+
+ /* Assign attribute to attribute set */
+ $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId());
+ CacheCleaner::cleanAll();
+}
+// create a second attribute
+if (!$attribute1->getId()) {
+
+ /** @var $attribute1 \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
+ $attribute1 = Bootstrap::getObjectManager()->create(
+ \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
+ );
+
+ /** @var AttributeRepositoryInterface $attributeRepository */
+ $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class);
+
+ $attribute1->setData(
+ [
+ 'attribute_code' => 'second_test_configurable',
+ 'entity_type_id' => $installer->getEntityTypeId('catalog_product'),
+ 'is_global' => 1,
+ 'is_user_defined' => 1,
+ 'frontend_input' => 'select',
+ 'is_unique' => 0,
+ 'is_required' => 0,
+ 'is_searchable' => 1,
+ 'is_visible_in_advanced_search' => 1,
+ 'is_comparable' => 1,
+ 'is_filterable' => 1,
+ 'is_filterable_in_search' => 1,
+ 'is_used_for_promo_rules' => 0,
+ 'is_html_allowed_on_front' => 1,
+ 'is_visible_on_front' => 1,
+ 'used_in_product_listing' => 1,
+ 'used_for_sort_by' => 1,
+ 'frontend_label' => ['Second Test Configurable'],
+ 'backend_type' => 'int',
+ 'option' => [
+ 'value' => ['option_0' => ['Option 3'], 'option_1' => ['Option 4']],
+ 'order' => ['option_0' => 1, 'option_1' => 2],
+ ],
+ 'default' => ['option_0']
+ ]
+ );
+
+ $attributeRepository->save($attribute1);
+
+ /* Assign attribute to attribute set */
+ $installer->addAttributeToGroup('catalog_product', $attributeSet->getId(), $attributeSet->getDefaultGroupId(), $attribute1->getId());
+ CacheCleaner::cleanAll();
+}
+
+$eavConfig->clear();
+
+/** @var \Magento\Framework\ObjectManagerInterface $objectManager */
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+/** @var $productRepository \Magento\Catalog\Api\ProductRepositoryInterface */
+$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+$productsWithNewAttributeSet = ['simple', 'simple-4'];
+
+foreach ($productsWithNewAttributeSet as $sku) {
+ try {
+ $product = $productRepository->get($sku, false, null, true);
+ $product->setAttributeSetId($attributeSet->getId());
+ $productRepository->save($product);
+ } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+
+ }
+}
+/** @var \Magento\Indexer\Model\Indexer\Collection $indexerCollection */
+$indexerCollection = Bootstrap::getObjectManager()->get(\Magento\Indexer\Model\Indexer\Collection::class);
+$indexerCollection->load();
+/** @var \Magento\Indexer\Model\Indexer $indexer */
+foreach ($indexerCollection->getItems() as $indexer) {
+ $indexer->reindexAll();
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php
new file mode 100644
index 0000000000000..4212075c312c9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute_rollback.php
@@ -0,0 +1,46 @@
+get(\Magento\Eav\Model\Config::class);
+$attributesToDelete = ['test_configurable', 'second_test_configurable'];
+/** @var AttributeRepositoryInterface $attributeRepository */
+$attributeRepository = Bootstrap::getObjectManager()->get(AttributeRepositoryInterface::class);
+
+foreach ($attributesToDelete as $attributeCode) {
+ /** @var \Magento\Eav\Api\Data\AttributeInterface $attribute */
+ $attribute = $attributeRepository->get('catalog_product', $attributeCode);
+ $attributeRepository->delete($attribute);
+}
+/** @var $product \Magento\Catalog\Model\Product */
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+$entityType = $objectManager->create(\Magento\Eav\Model\Entity\Type::class)->loadByCode('catalog_product');
+
+// remove attribute set
+
+/** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection $attributeSetCollection */
+$attributeSetCollection = $objectManager->create(
+ \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection::class
+);
+$attributeSetCollection->addFilter('attribute_set_name', 'second_attribute_set');
+$attributeSetCollection->addFilter('entity_type_id', $entityType->getId());
+$attributeSetCollection->setOrder('attribute_set_id'); // descending is default value
+$attributeSetCollection->setPageSize(1);
+$attributeSetCollection->load();
+
+/** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */
+$attributeSet = $attributeSetCollection->fetchItem();
+$attributeSet->delete();
From b92cd06916cb62447fe11501a7dbafb7b866566d Mon Sep 17 00:00:00 2001
From: Nikita Chubukov
Date: Tue, 13 Aug 2019 21:52:06 +0300
Subject: [PATCH 078/593] MC-15256: Exported customer without modification can
not be imported
- Fix CR comments
---
app/code/Magento/CustomerImportExport/Model/Import/Customer.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
index 33dc7e55228bf..914b4fd2a3ca9 100644
--- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
+++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
@@ -608,7 +608,7 @@ protected function _validateRowForUpdate(array $rowData, $rowNumber)
}
if (isset($rowData[$attributeCode]) && strlen((string)$rowData[$attributeCode])) {
- if ($attributeParams['type'] == 'select' && empty($rowData[$attributeCode])) {
+ if ($attributeParams['type'] == 'select') {
continue;
}
From 0af4c6c425cf2e1b7dbebbc1ad2aee753a3ead29 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Tue, 13 Aug 2019 16:08:26 -0500
Subject: [PATCH 079/593] MC-19102: Adding new search request and use search
retrieve results
---
.../Product/SearchCriteriaBuilder.php | 28 +++++++++++++++++--
.../CatalogGraphQl/etc/search_request.xml | 4 +--
2 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
index 6970c13aecbc6..9e381307d8139 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -10,9 +10,11 @@
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\Search\FilterGroupBuilder;
use Magento\Framework\Api\Search\SearchCriteriaInterface;
+use Magento\Framework\Api\SortOrder;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder;
use Magento\Catalog\Model\Product\Visibility;
+use Magento\Framework\Api\SortOrderBuilder;
/**
* Build search criteria
@@ -43,25 +45,33 @@ class SearchCriteriaBuilder
*/
private $visibility;
+ /**
+ * @var SortOrderBuilder
+ */
+ private $sortOrderBuilder;
+
/**
* @param Builder $builder
* @param ScopeConfigInterface $scopeConfig
* @param FilterBuilder $filterBuilder
* @param FilterGroupBuilder $filterGroupBuilder
* @param Visibility $visibility
+ * @param SortOrderBuilder $sortOrderBuilder
*/
public function __construct(
Builder $builder,
ScopeConfigInterface $scopeConfig,
FilterBuilder $filterBuilder,
FilterGroupBuilder $filterGroupBuilder,
- Visibility $visibility
+ Visibility $visibility,
+ SortOrderBuilder $sortOrderBuilder
) {
$this->scopeConfig = $scopeConfig;
$this->filterBuilder = $filterBuilder;
$this->filterGroupBuilder = $filterGroupBuilder;
$this->builder = $builder;
$this->visibility = $visibility;
+ $this->sortOrderBuilder = $sortOrderBuilder;
}
/**
@@ -84,7 +94,7 @@ public function build(array $args, bool $includeAggregation): SearchCriteriaInte
if (!empty($args['search'])) {
$this->addFilter($searchCriteria, 'search_term', $args['search']);
if (!$searchCriteria->getSortOrders()) {
- $searchCriteria->setSortOrders(['_score' => \Magento\Framework\Api\SortOrder::SORT_DESC]);
+ $this->addDefaultSortOrder($searchCriteria);
}
}
@@ -151,4 +161,18 @@ private function addFilter(SearchCriteriaInterface $searchCriteria, string $fiel
$filterGroups[] = $this->filterGroupBuilder->create();
$searchCriteria->setFilterGroups($filterGroups);
}
+
+ /**
+ * Sort by _score DESC if no sort order is set
+ *
+ * @param SearchCriteriaInterface $searchCriteria
+ */
+ private function addDefaultSortOrder(SearchCriteriaInterface $searchCriteria): void
+ {
+ $sortOrder = $this->sortOrderBuilder
+ ->setField('_score')
+ ->setDirection(SortOrder::SORT_DESC)
+ ->create();
+ $searchCriteria->setSortOrders([$sortOrder]);
+ }
}
diff --git a/app/code/Magento/CatalogGraphQl/etc/search_request.xml b/app/code/Magento/CatalogGraphQl/etc/search_request.xml
index ab1eea9eb6fda..5e962d8467a4f 100644
--- a/app/code/Magento/CatalogGraphQl/etc/search_request.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/search_request.xml
@@ -34,7 +34,7 @@
-
+
@@ -80,7 +80,7 @@
-
+
From 39eb1e82a3cae66401f8cac96e749b9d4410b930 Mon Sep 17 00:00:00 2001
From: Veronika Kurochkina
Date: Wed, 14 Aug 2019 11:09:18 +0300
Subject: [PATCH 080/593] MC-15341: Default product numbers to display results
in poor display on Desktop
- Updated automated Test script
---
.../Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
index e5f05f1ea00c1..cc8a31cc8034e 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
@@ -10,7 +10,7 @@
-
+
From c09d8c0ae032fe6c0fe324dd81d0ee08f5aa63a0 Mon Sep 17 00:00:00 2001
From: Veronika Kurochkina
Date: Wed, 14 Aug 2019 16:08:07 +0300
Subject: [PATCH 081/593] MAGETWO-44170: Not pass function test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Fix mftf
---
.../Mftf/ActionGroup/AdminCategoryActionGroup.xml | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
index 90d732c9654e1..af919e5562729 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
@@ -75,7 +75,11 @@
-
+
+
+ /magento-logo(_[0-9]+)*?\.png$/
+ grabCategoryFileName
+
@@ -96,7 +100,11 @@
-
+
+
+ /magento-logo(_[0-9]+)*?\.png$/
+ grabCategoryFileName
+
From f07847f86b7d335aed937b4160d6156dcb2950b3 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Wed, 14 Aug 2019 11:56:18 -0500
Subject: [PATCH 082/593] MC-19102: Adding new search request and use search
retrieve results
---
.../Model/Resolver/Category/Products.php | 41 ++++++++++++++-----
.../Model/Resolver/Products.php | 3 --
.../Model/Resolver/Products/Query/Search.php | 2 +
3 files changed, 33 insertions(+), 13 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php
index e0580213ddea7..abc5ae7e1da7f 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php
@@ -8,6 +8,9 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Category;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\CatalogGraphQl\DataProvider\Product\SearchCriteriaBuilder;
+use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
@@ -27,27 +30,46 @@ class Products implements ResolverInterface
/**
* @var Builder
+ * @deprecated
*/
private $searchCriteriaBuilder;
/**
* @var Filter
+ * @deprecated
*/
private $filterQuery;
+ /**
+ * @var Search
+ */
+ private $searchQuery;
+
+ /**
+ * @var SearchCriteriaBuilder
+ */
+ private $searchApiCriteriaBuilder;
+
/**
* @param ProductRepositoryInterface $productRepository
* @param Builder $searchCriteriaBuilder
* @param Filter $filterQuery
+ * @param Search $searchQuery
+ * @param SearchCriteriaBuilder $searchApiCriteriaBuilder
*/
public function __construct(
ProductRepositoryInterface $productRepository,
Builder $searchCriteriaBuilder,
- Filter $filterQuery
+ Filter $filterQuery,
+ Search $searchQuery = null,
+ SearchCriteriaBuilder $searchApiCriteriaBuilder = null
) {
$this->productRepository = $productRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->filterQuery = $filterQuery;
+ $this->searchQuery = $searchQuery ?? ObjectManager::getInstance()->get(Search::class);
+ $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder ??
+ ObjectManager::getInstance()->get(SearchCriteriaBuilder::class);
}
/**
@@ -60,21 +82,20 @@ public function resolve(
array $value = null,
array $args = null
) {
- $args['filter'] = [
- 'category_id' => [
- 'eq' => $value['id']
- ]
- ];
- $searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args);
if ($args['currentPage'] < 1) {
throw new GraphQlInputException(__('currentPage value must be greater than 0.'));
}
if ($args['pageSize'] < 1) {
throw new GraphQlInputException(__('pageSize value must be greater than 0.'));
}
- $searchCriteria->setCurrentPage($args['currentPage']);
- $searchCriteria->setPageSize($args['pageSize']);
- $searchResult = $this->filterQuery->getResult($searchCriteria, $info);
+
+ $args['filter'] = [
+ 'category_id' => [
+ 'eq' => $value['id']
+ ]
+ ];
+ $searchCriteria = $this->searchApiCriteriaBuilder->build($args, false);
+ $searchResult = $this->searchQuery->getResult($searchCriteria, $info);
//possible division by 0
if ($searchCriteria->getPageSize()) {
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index 9c8d3266c4d2d..bb94502c41ef1 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -103,9 +103,6 @@ public function resolve(
isset($productFields['filters'])
);
- $searchCriteria->setCurrentPage($args['currentPage']);
- $searchCriteria->setPageSize($args['pageSize']);
-
$searchResult = $this->searchQuery->getResult($searchCriteria, $info);
if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) {
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index daa3619d17b83..7d55b56f854a2 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -120,6 +120,8 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, true);
+ $searchCriteria->setPageSize($realPageSize);
+ $searchCriteria->setCurrentPage($realCurrentPage);
//possible division by 0
if ($realPageSize) {
$maxPages = (int)ceil($searchResult->getTotalCount() / $realPageSize);
From 03a990747b74019321ab6077e4691635358a9a24 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Wed, 14 Aug 2019 13:11:47 -0500
Subject: [PATCH 083/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- updated data fixture
---
.../products_with_layered_navigation_custom_attribute.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
index 00678ae904def..d913e3075622e 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
@@ -124,12 +124,13 @@
/** @var $productRepository \Magento\Catalog\Api\ProductRepositoryInterface */
$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
-$productsWithNewAttributeSet = ['simple', 'simple-4'];
+$productsWithNewAttributeSet = ['simple', '12345', 'simple-4'];
foreach ($productsWithNewAttributeSet as $sku) {
try {
$product = $productRepository->get($sku, false, null, true);
$product->setAttributeSetId($attributeSet->getId());
+ $product->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
$productRepository->save($product);
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
From ae7016a8ef7221bc8512791b1e678f442ccf82ab Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Thu, 15 Aug 2019 11:38:46 -0500
Subject: [PATCH 084/593] MC-19102: Adding new search request and use search
retrieve results
---
.../Model/Resolver/Products.php | 2 +-
.../Model/Resolver/Products/Query/Search.php | 11 ++++--
.../Plugin/Search/Request/ConfigReader.php | 35 ++++++++++++-------
3 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index bb94502c41ef1..6a17f730c0f1a 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -103,7 +103,7 @@ public function resolve(
isset($productFields['filters'])
);
- $searchResult = $this->searchQuery->getResult($searchCriteria, $info);
+ $searchResult = $this->searchQuery->getResult($searchCriteria, $info, $args);
if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) {
throw new GraphQlInputException(
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index 7d55b56f854a2..c7dd2a8b05b79 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -87,15 +87,20 @@ public function __construct(
*
* @param SearchCriteriaInterface $searchCriteria
* @param ResolveInfo $info
+ * @param array $args
* @return SearchResult
* @throws \Exception
*/
- public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $info) : SearchResult
- {
+ public function getResult(
+ SearchCriteriaInterface $searchCriteria,
+ ResolveInfo $info,
+ array $args = []
+ ): SearchResult {
$idField = $this->metadataPool->getMetadata(
\Magento\Catalog\Api\Data\ProductInterface::class
)->getIdentifierField();
+ $isSearch = isset($args['search']);
$realPageSize = $searchCriteria->getPageSize();
$realCurrentPage = $searchCriteria->getCurrentPage();
// Current page must be set to 0 and page size to max for search to grab all ID's as temporary workaround
@@ -118,7 +123,7 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$searchCriteriaIds->setPageSize($realPageSize);
$searchCriteriaIds->setCurrentPage($realCurrentPage);
- $searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, true);
+ $searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, $isSearch);
$searchCriteria->setPageSize($realPageSize);
$searchCriteria->setCurrentPage($realCurrentPage);
diff --git a/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php b/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
index 70e312ff4e2ee..151be89177744 100644
--- a/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
+++ b/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
@@ -6,11 +6,11 @@
namespace Magento\CatalogGraphQl\Plugin\Search\Request;
use Magento\Catalog\Api\Data\EavAttributeInterface;
-use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider;
use Magento\CatalogSearch\Model\Search\RequestGenerator;
use Magento\CatalogSearch\Model\Search\RequestGenerator\GeneratorResolver;
use Magento\Framework\Search\Request\FilterInterface;
use Magento\Framework\Search\Request\QueryInterface;
+use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory;
/**
* Add search request configuration to config for give ability filter and search products during GraphQL request
@@ -33,26 +33,28 @@ class ConfigReader
private $requestName = 'graphql_product_search';
/**
- * @var DataProvider
+ * @var GeneratorResolver
*/
- private $dataProvider;
+ private $generatorResolver;
/**
- * @var GeneratorResolver
+ * @var CollectionFactory
*/
- private $generatorResolver;
+ private $productAttributeCollectionFactory;
/** Bucket name suffix */
private const BUCKET_SUFFIX = '_bucket';
/**
- * @param DataProvider $dataProvider
* @param GeneratorResolver $generatorResolver
+ * @param CollectionFactory $productAttributeCollectionFactory
*/
- public function __construct(DataProvider $dataProvider, GeneratorResolver $generatorResolver)
- {
- $this->dataProvider = $dataProvider;
+ public function __construct(
+ GeneratorResolver $generatorResolver,
+ CollectionFactory $productAttributeCollectionFactory
+ ) {
$this->generatorResolver = $generatorResolver;
+ $this->productAttributeCollectionFactory = $productAttributeCollectionFactory;
}
/**
@@ -89,9 +91,18 @@ public function afterRead(
private function getSearchableAttributes(): array
{
$attributes = [];
- foreach ($this->dataProvider->getSearchableAttributes() as $attribute) {
- $attributes[$attribute->getAttributeCode()] = $attribute;
+ /** @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection $productAttributes */
+ $productAttributes = $this->productAttributeCollectionFactory->create();
+ $productAttributes->addFieldToFilter(
+ ['is_searchable', 'is_visible_in_advanced_search', 'is_filterable', 'is_filterable_in_search'],
+ [1, 1, [1, 2], 1]
+ );
+
+ /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
+ foreach ($productAttributes->getItems() as $attribute) {
+ $attributes[$attribute->getAttributeCode()] = $attribute;
}
+
return $attributes;
}
@@ -106,7 +117,7 @@ private function generateRequest()
$request = [];
foreach ($this->getSearchableAttributes() as $attribute) {
if (\in_array($attribute->getAttributeCode(), ['price', 'visibility', 'category_ids'])) {
- //same fields have special semantics
+ //some fields have special semantics
continue;
}
$queryName = $attribute->getAttributeCode() . '_query';
From 3c6eec41102cdccb609d0345b01563160e9ad931 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Thu, 15 Aug 2019 12:17:28 -0500
Subject: [PATCH 085/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- added use cases
---
.../GraphQl/Catalog/ProductSearchTest.php | 267 ++++++++++++++++++
...th_layered_navigation_custom_attribute.php | 8 +-
2 files changed, 272 insertions(+), 3 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 4ce8ad8dab393..e80387394ffaf 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -18,6 +18,7 @@
use Magento\TestFramework\TestCase\GraphQlAbstract;
use Magento\Catalog\Model\Product;
use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Eav\Api\Data\AttributeOptionInterface;
use Magento\Framework\DataObject;
/**
@@ -89,6 +90,272 @@ public function testFilterLn()
);
}
+ /**
+ * Advanced Search which uses product attribute to filter out the results
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testAdvancedSearchByOneCustomAttribute()
+ {
+ $optionValue = $this->getDefaultAttributeOptionValue('second_test_configurable');
+
+ $query = <<get(ProductRepositoryInterface::class);
+ $product1 = $productRepository->get('simple');
+ $product2 = $productRepository->get('12345');
+ $product3 = $productRepository->get('simple-4');
+ $filteredProducts = [$product1, $product2, $product3 ];
+ $response = $this->graphQlQuery($query);
+ $this->assertEquals(3, $response['products']['total_count']);
+ $this->assertTrue(count($response['products']['filters']) > 0, 'Product filters is not empty');
+ $productItemsInResponse = array_map(null, $response['products']['items'], $filteredProducts);
+ // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall
+ for ($itemIndex = 0; $itemIndex < count($filteredProducts); $itemIndex++) {
+ $this->assertNotEmpty($productItemsInResponse[$itemIndex]);
+ //validate that correct products are returned
+ $this->assertResponseFields(
+ $productItemsInResponse[$itemIndex][0],
+ [ 'name' => $filteredProducts[$itemIndex]->getName(),
+ 'sku' => $filteredProducts[$itemIndex]->getSku()
+ ]
+ );
+ }
+
+ /** @var \Magento\Eav\Model\Config $eavConfig */
+ $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+ $attribute = $eavConfig->getAttribute('catalog_product', 'second_test_configurable');
+
+ // Validate custom attribute filter layer data
+ $this->assertResponseFields(
+ $response['products']['filters'][2],
+ [
+ 'name' => $attribute->getDefaultFrontendLabel(),
+ 'request_var'=> $attribute->getAttributeCode(),
+ 'filter_items_count'=> 1,
+ 'filter_items' => [
+ [
+ 'label' => 'Option 3',
+ 'items_count' => 3,
+ 'value_string' => $optionValue,
+ '__typename' =>'LayerFilterItem'
+ ],
+ ],
+ ]
+ );
+ }
+
+ /**
+ * Get the option value for the custom attribute to be used in the graphql query
+ *
+ * @param string $attributeCode
+ * @return string
+ */
+ private function getDefaultAttributeOptionValue(string $attributeCode) : string
+ {
+ /** @var \Magento\Eav\Model\Config $eavConfig */
+ $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+ $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode);
+ /** @var AttributeOptionInterface[] $options */
+ $options = $attribute->getOptions();
+ $defaultOptionValue = $options[1]->getValue();
+ return $defaultOptionValue;
+ }
+
+ /**
+ * Full text search for Product and then filter the results by custom attribute
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testFullTextSearchForProductAndFilterByCustomAttribute()
+ {
+ $optionValue = $this->getDefaultAttributeOptionValue('second_test_configurable');
+
+ $query = <<graphQlQuery($query);
+ //Verify total count of the products returned
+ $this->assertEquals(3, $response['products']['total_count']);
+ $expectedFilterLayers =
+ [
+ ['name' => 'Price',
+ 'request_var'=> 'price'
+ ],
+ ['name' => 'Category',
+ 'request_var'=> 'category_id'
+ ],
+ ['name' => 'Second Test Configurable',
+ 'request_var'=> 'second_test_configurable'
+ ],
+ ];
+ $layers = array_map(null, $expectedFilterLayers, $response['products']['filters']);
+
+ //Verify all the three layers : Price, Category and Custom attribute layers are created
+ foreach ($layers as $layerIndex => $layerFilterData) {
+ $this->assertNotEmpty($layerFilterData);
+ $this->assertEquals(
+ $layers[$layerIndex][0]['name'],
+ $response['products']['filters'][$layerIndex]['name'],
+ 'Layer name does not match'
+ );
+ $this->assertEquals(
+ $layers[$layerIndex][0]['request_var'],
+ $response['products']['filters'][$layerIndex]['request_var'],
+ 'request_var does not match'
+ ) ;
+ }
+
+ // Validate the price filter layer data from the response
+ $this->assertResponseFields(
+ $response['products']['filters'][0],
+ [
+ 'name' => 'Price',
+ 'request_var'=> 'price',
+ 'filter_items_count'=> 2,
+ 'filter_items' => [
+ [
+ 'label' => '10-20',
+ 'items_count' => 2,
+ 'value_string' => '10_20',
+ '__typename' =>'LayerFilterItem'
+ ],
+ [
+ 'label' => '40-*',
+ 'items_count' => 1,
+ 'value_string' => '40_*',
+ '__typename' =>'LayerFilterItem'
+ ],
+ ],
+ ]
+ );
+ }
+
+ /**
+ * Filter by mu;ltiple attributes like category_id and custom attribute
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testFilterByCategoryIdAndCustomAttribute()
+ {
+ $categoryId = 13;
+ $optionValue = $this->getDefaultAttributeOptionValue('second_test_configurable');
+ $query = <<graphQlQuery($query);
+ $this->assertEquals(2, $response['products']['total_count']);
+ }
+
+
+
/**
* Get array with expected data for layered navigation filters
*
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
index d913e3075622e..9167bbc1db092 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
@@ -68,7 +68,6 @@
/* Assign attribute to attribute set */
$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId());
- CacheCleaner::cleanAll();
}
// create a second attribute
if (!$attribute1->getId()) {
@@ -113,8 +112,11 @@
$attributeRepository->save($attribute1);
/* Assign attribute to attribute set */
- $installer->addAttributeToGroup('catalog_product', $attributeSet->getId(), $attributeSet->getDefaultGroupId(), $attribute1->getId());
- CacheCleaner::cleanAll();
+ $installer->addAttributeToGroup('catalog_product',
+ $attributeSet->getId(),
+ $attributeSet->getDefaultGroupId(),
+ $attribute1->getId()
+ );
}
$eavConfig->clear();
From 73ba36616fbd3244b75ef07129aeb967c299765f Mon Sep 17 00:00:00 2001
From: Vitalii Zabaznov
Date: Thu, 15 Aug 2019 14:05:34 -0500
Subject: [PATCH 086/593] MC-18719: private cookie version increments each post
request
---
.../Checkout/etc/frontend/sections.xml | 1 +
.../view/frontend/web/js/customer-data.js | 27 +++----------------
2 files changed, 4 insertions(+), 24 deletions(-)
diff --git a/app/code/Magento/Checkout/etc/frontend/sections.xml b/app/code/Magento/Checkout/etc/frontend/sections.xml
index 90c2878f501cf..2427890c06694 100644
--- a/app/code/Magento/Checkout/etc/frontend/sections.xml
+++ b/app/code/Magento/Checkout/etc/frontend/sections.xml
@@ -9,6 +9,7 @@
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
+
diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
index 41cf05df2b1d5..9df8d75e43172 100644
--- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
+++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
@@ -198,30 +198,9 @@ define([
* Customer data initialization
*/
init: function () {
- var privateContentVersion = 'private_content_version',
- privateContent = $.cookieStorage.get(privateContentVersion),
- localPrivateContent = $.localStorage.get(privateContentVersion),
- needVersion = 'need_version',
- expiredSectionNames = this.getExpiredSectionNames();
-
- if (privateContent &&
- !$.cookieStorage.isSet(privateContentVersion) &&
- !$.localStorage.isSet(privateContentVersion)
- ) {
- $.cookieStorage.set(privateContentVersion, needVersion);
- $.localStorage.set(privateContentVersion, needVersion);
- this.reload([], false);
- } else if (localPrivateContent !== privateContent) {
- if (!$.cookieStorage.isSet(privateContentVersion)) {
- privateContent = needVersion;
- $.cookieStorage.set(privateContentVersion, privateContent);
- }
- $.localStorage.set(privateContentVersion, privateContent);
- _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
- buffer.notify(sectionName, sectionData);
- });
- this.reload([], false);
- } else if (expiredSectionNames.length > 0) {
+ var expiredSectionNames = this.getExpiredSectionNames();
+
+ if (expiredSectionNames.length > 0) {
_.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
buffer.notify(sectionName, sectionData);
});
From 35a868adf054a39160b53fe3abb663b6b0a452bb Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Thu, 15 Aug 2019 16:44:12 -0500
Subject: [PATCH 087/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- added use cases
---
.../GraphQl/Catalog/ProductSearchTest.php | 45 ++++++++++++++++++-
...th_layered_navigation_custom_attribute.php | 14 +++---
2 files changed, 53 insertions(+), 6 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index e80387394ffaf..e607e2d608786 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -258,7 +258,7 @@ public function testFullTextSearchForProductAndFilterByCustomAttribute()
'request_var'=> 'category_id'
],
['name' => 'Second Test Configurable',
- 'request_var'=> 'second_test_configurable'
+ 'request_var'=> 'second_test_configurable'
],
];
$layers = array_map(null, $expectedFilterLayers, $response['products']['filters']);
@@ -352,6 +352,49 @@ public function testFilterByCategoryIdAndCustomAttribute()
QUERY;
$response = $this->graphQlQuery($query);
$this->assertEquals(2, $response['products']['total_count']);
+ $actualCategoryFilterItems = $response['products']['filters'][1]['filter_items'];
+ //Validate the number of categories/sub-categories that contain the products with the custom attribute
+ $this->assertCount(6,$actualCategoryFilterItems);
+
+ $expectedCategoryFilterItems =
+ [
+ [ 'label' => 'Category 1',
+ 'items_count'=> 2
+ ],
+ [ 'label' => 'Category 1.1',
+ 'items_count'=> 1
+ ],
+ [ 'label' => 'Movable Position 2',
+ 'items_count'=> 1
+ ],
+ [ 'label' => 'Movable Position 3',
+ 'items_count'=> 1
+ ],
+ [ 'label' => 'Category 12',
+ 'items_count'=> 1
+ ],
+ [ 'label' => 'Category 1.2',
+ 'items_count'=> 2
+ ],
+ ];
+ $categoryFilterItems = array_map(null, $expectedCategoryFilterItems,$actualCategoryFilterItems);
+
+//Validate the categories and sub-categories data in the filter layer
+ foreach ($categoryFilterItems as $index => $categoryFilterData) {
+ $this->assertNotEmpty($categoryFilterData);
+ $this->assertEquals(
+ $categoryFilterItems[$index][0]['label'],
+ $actualCategoryFilterItems[$index]['label'],
+ 'Category is incorrect'
+ );
+ $this->assertEquals(
+ $categoryFilterItems[$index][0]['items_count'],
+ $actualCategoryFilterItems[$index]['items_count'],
+ 'Products count in the category is incorrect'
+ ) ;
+ }
+
+
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
index 9167bbc1db092..6cd2819c2d296 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
@@ -12,7 +12,6 @@
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Eav\Api\AttributeRepositoryInterface;
-use Magento\TestFramework\Helper\CacheCleaner;
$eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
@@ -112,11 +111,11 @@
$attributeRepository->save($attribute1);
/* Assign attribute to attribute set */
- $installer->addAttributeToGroup('catalog_product',
+ $installer->addAttributeToGroup(
+ 'catalog_product',
$attributeSet->getId(),
$attributeSet->getDefaultGroupId(),
- $attribute1->getId()
- );
+ $attribute1->getId());
}
$eavConfig->clear();
@@ -132,7 +131,12 @@
try {
$product = $productRepository->get($sku, false, null, true);
$product->setAttributeSetId($attributeSet->getId());
- $product->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+ $product->setStockData(
+ ['use_config_manage_stock' => 1,
+ 'qty' => 50,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1]
+ );
$productRepository->save($product);
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
From 85902fb5a0825e5981e4d967db51d3bc7725165c Mon Sep 17 00:00:00 2001
From: Lusine Papyan
Date: Fri, 16 Aug 2019 10:26:24 +0400
Subject: [PATCH 088/593] MAGETWO-98748: Incorrect behavior in the category
menu on the Storefront
- Added automated test script
---
.../AdminCategorySidebarTreeSection.xml | 2 +
...goryHighlightedAndProductDisplayedTest.xml | 73 +++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml
index fba28b3feaff1..bc552721e6ab8 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml
@@ -11,6 +11,8 @@
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml
new file mode 100644
index 0000000000000..7d09cb419f312
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 34d6d5599178d5939baf9d11bb4780b145f46e84 Mon Sep 17 00:00:00 2001
From: Vinai Kopp
Date: Fri, 16 Aug 2019 09:08:41 -0500
Subject: [PATCH 089/593] Fix config backend model PDF file name extension
---
.../Config/Model/Config/Backend/File/Pdf.php | 21 +++++++++++++++++++
.../Config/Model/Config/Backend/Image/Pdf.php | 10 +++------
2 files changed, 24 insertions(+), 7 deletions(-)
create mode 100644 app/code/Magento/Config/Model/Config/Backend/File/Pdf.php
diff --git a/app/code/Magento/Config/Model/Config/Backend/File/Pdf.php b/app/code/Magento/Config/Model/Config/Backend/File/Pdf.php
new file mode 100644
index 0000000000000..f4acd6ed21dbb
--- /dev/null
+++ b/app/code/Magento/Config/Model/Config/Backend/File/Pdf.php
@@ -0,0 +1,21 @@
+
- */
namespace Magento\Config\Model\Config\Backend\Image;
/**
* @api
- * @since 100.0.2
+ * @deprecated The wrong file type extensions are returned.
+ * @see \Magento\Config\Model\Config\Backend\File\Pdf
*/
class Pdf extends \Magento\Config\Model\Config\Backend\Image
{
@@ -22,6 +18,6 @@ class Pdf extends \Magento\Config\Model\Config\Backend\Image
*/
protected function _getAllowedExtensions()
{
- return ['tif', 'tiff', 'png', 'jpg', 'jpe', 'jpeg'];
+ return ['tif', 'tiff', 'png', 'jpg', 'jpe', 'jpeg', 'pdf'];
}
}
From 02654a6baea7fd138a4dd731b9544a530dbafe58 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Fri, 16 Aug 2019 21:37:56 +0000
Subject: [PATCH 090/593] Added changes from PR #807
---
.../Magento/Customer/Block/Form/Register.php | 15 ++++--
.../Test/Unit/Block/Form/RegisterTest.php | 18 +++----
app/code/Magento/Newsletter/Model/Config.php | 3 +-
.../PredispatchNewsletterObserver.php | 25 ++++++---
.../PredispatchNewsletterObserverTest.php | 52 ++++++++-----------
5 files changed, 61 insertions(+), 52 deletions(-)
diff --git a/app/code/Magento/Customer/Block/Form/Register.php b/app/code/Magento/Customer/Block/Form/Register.php
index a190ccde50b5a..be16046d69075 100644
--- a/app/code/Magento/Customer/Block/Form/Register.php
+++ b/app/code/Magento/Customer/Block/Form/Register.php
@@ -6,7 +6,8 @@
namespace Magento\Customer\Block\Form;
use Magento\Customer\Model\AccountManagement;
-use Magento\Newsletter\Observer\PredispatchNewsletterObserver;
+use Magento\Framework\App\ObjectManager;
+use Magento\Newsletter\Model\Config;
/**
* Customer register form block
@@ -32,6 +33,11 @@ class Register extends \Magento\Directory\Block\Data
*/
protected $_customerUrl;
+ /**
+ * @var Config
+ */
+ private $newsLetterConfig;
+
/**
* Constructor
*
@@ -45,6 +51,7 @@ class Register extends \Magento\Directory\Block\Data
* @param \Magento\Customer\Model\Session $customerSession
* @param \Magento\Customer\Model\Url $customerUrl
* @param array $data
+ * @param Config $newsLetterConfig
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -58,11 +65,13 @@ public function __construct(
\Magento\Framework\Module\ModuleManagerInterface $moduleManager,
\Magento\Customer\Model\Session $customerSession,
\Magento\Customer\Model\Url $customerUrl,
- array $data = []
+ array $data = [],
+ Config $newsLetterConfig = null
) {
$this->_customerUrl = $customerUrl;
$this->_moduleManager = $moduleManager;
$this->_customerSession = $customerSession;
+ $this->newsLetterConfig = $newsLetterConfig ?: ObjectManager::getInstance()->get(Config::class);
parent::__construct(
$context,
$directoryHelper,
@@ -170,7 +179,7 @@ public function getRegion()
public function isNewsletterEnabled()
{
return $this->_moduleManager->isOutputEnabled('Magento_Newsletter')
- && $this->getConfig(PredispatchNewsletterObserver::XML_PATH_NEWSLETTER_ACTIVE);
+ && $this->newsLetterConfig->isActive();
}
/**
diff --git a/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php b/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php
index b93b9f40d75b2..e1adfdf4ed8f1 100644
--- a/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php
@@ -7,7 +7,6 @@
use Magento\Customer\Block\Form\Register;
use Magento\Customer\Model\AccountManagement;
-use Magento\Newsletter\Observer\PredispatchNewsletterObserver;
/**
* Test class for \Magento\Customer\Block\Form\Register.
@@ -49,6 +48,9 @@ class RegisterTest extends \PHPUnit\Framework\TestCase
/** @var Register */
private $_block;
+ /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Newsletter\Model\Config */
+ private $newsletterConfig;
+
protected function setUp()
{
$this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
@@ -59,9 +61,9 @@ protected function setUp()
\Magento\Customer\Model\Session::class,
['getCustomerFormData']
);
+ $this->newsletterConfig = $this->createMock(\Magento\Newsletter\Model\Config::class);
$context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class);
$context->expects($this->any())->method('getScopeConfig')->will($this->returnValue($this->_scopeConfig));
-
$this->_block = new \Magento\Customer\Block\Form\Register(
$context,
$this->directoryHelperMock,
@@ -71,7 +73,9 @@ protected function setUp()
$this->createMock(\Magento\Directory\Model\ResourceModel\Country\CollectionFactory::class),
$this->_moduleManager,
$this->_customerSession,
- $this->_customerUrl
+ $this->_customerUrl,
+ [],
+ $this->newsletterConfig
);
}
@@ -292,17 +296,13 @@ public function testIsNewsletterEnabled($isNewsletterEnabled, $isNewsletterActiv
)->will(
$this->returnValue($isNewsletterEnabled)
);
-
- $this->_scopeConfig->expects(
+ $this->newsletterConfig->expects(
$this->any()
)->method(
- 'getValue'
- )->with(
- PredispatchNewsletterObserver::XML_PATH_NEWSLETTER_ACTIVE
+ 'isActive'
)->will(
$this->returnValue($isNewsletterActive)
);
-
$this->assertEquals($expectedValue, $this->_block->isNewsletterEnabled());
}
diff --git a/app/code/Magento/Newsletter/Model/Config.php b/app/code/Magento/Newsletter/Model/Config.php
index c6e4ba36591fc..be1ad759d650d 100644
--- a/app/code/Magento/Newsletter/Model/Config.php
+++ b/app/code/Magento/Newsletter/Model/Config.php
@@ -38,9 +38,10 @@ public function __construct(
/**
* Returns newsletter's enabled status
*
+ * @param string $scopeType
* @return bool
*/
- public function isActive($scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT)
+ public function isActive(string $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT): bool
{
return $this->scopeConfig->isSetFlag(self::XML_PATH_NEWSLETTER_ACTIVE, $scopeType);
}
diff --git a/app/code/Magento/Newsletter/Observer/PredispatchNewsletterObserver.php b/app/code/Magento/Newsletter/Observer/PredispatchNewsletterObserver.php
index 9860798b2b9f3..f63520e79496f 100644
--- a/app/code/Magento/Newsletter/Observer/PredispatchNewsletterObserver.php
+++ b/app/code/Magento/Newsletter/Observer/PredispatchNewsletterObserver.php
@@ -12,6 +12,8 @@
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\UrlInterface;
use Magento\Store\Model\ScopeInterface;
+use Magento\Newsletter\Model\Config;
+use Magento\Framework\App\ObjectManager;
/**
* Class PredispatchNewsletterObserver
@@ -19,10 +21,16 @@
class PredispatchNewsletterObserver implements ObserverInterface
{
/**
- * Configuration path to newsletter active setting
+ * @deprecated
+ * @see \Magento\Newsletter\Model\Config::isActive()
*/
const XML_PATH_NEWSLETTER_ACTIVE = 'newsletter/general/active';
+ /**
+ * @var Config
+ */
+ private $newsletterConfig;
+
/**
* @var ScopeConfigInterface
*/
@@ -38,11 +46,16 @@ class PredispatchNewsletterObserver implements ObserverInterface
*
* @param ScopeConfigInterface $scopeConfig
* @param UrlInterface $url
+ * @param Config|null $newsletterConfig
*/
- public function __construct(ScopeConfigInterface $scopeConfig, UrlInterface $url)
- {
+ public function __construct(
+ ScopeConfigInterface $scopeConfig,
+ UrlInterface $url,
+ Config $newsletterConfig = null
+ ) {
$this->scopeConfig = $scopeConfig;
$this->url = $url;
+ $this->newsletterConfig = $newsletterConfig ?: ObjectManager::getInstance()->get(Config::class);
}
/**
@@ -52,11 +65,7 @@ public function __construct(ScopeConfigInterface $scopeConfig, UrlInterface $url
*/
public function execute(Observer $observer) : void
{
- if (!$this->scopeConfig->getValue(
- self::XML_PATH_NEWSLETTER_ACTIVE,
- ScopeInterface::SCOPE_STORE
- )
- ) {
+ if (!$this->newsletterConfig->isActive(ScopeInterface::SCOPE_STORE)) {
$defaultNoRouteUrl = $this->scopeConfig->getValue(
'web/default/no_route',
ScopeInterface::SCOPE_STORE
diff --git a/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php b/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
index 38d69e5128af1..f88537397ae66 100644
--- a/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
+++ b/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
@@ -13,6 +13,7 @@
use Magento\Framework\Event\Observer;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Framework\UrlInterface;
+use Magento\Newsletter\Model\Config;
use Magento\Newsletter\Observer\PredispatchNewsletterObserver;
use Magento\Store\Model\ScopeInterface;
use PHPUnit\Framework\TestCase;
@@ -52,30 +53,29 @@ class PredispatchNewsletterObserverTest extends TestCase
*/
private $objectManager;
+ /**
+ * @var Config
+ */
+ private $newsletterConfig;
+
/**
* @inheritdoc
*/
protected function setUp() : void
{
- $this->configMock = $this->getMockBuilder(ScopeConfigInterface::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->urlMock = $this->getMockBuilder(UrlInterface::class)
- ->disableOriginalConstructor()
- ->getMock();
+ $this->configMock = $this->createMock(ScopeConfigInterface::class);
+ $this->urlMock = $this->createMock(UrlInterface::class);
$this->responseMock = $this->getMockBuilder(ResponseInterface::class)
->disableOriginalConstructor()
->setMethods(['setRedirect'])
->getMockForAbstractClass();
- $this->redirectMock = $this->getMockBuilder(RedirectInterface::class)
- ->getMock();
+ $this->redirectMock = $this->createMock(RedirectInterface::class);
+ $this->newsletterConfig = $this->createMock(Config::class);
$this->objectManager = new ObjectManager($this);
- $this->mockObject = $this->objectManager->getObject(
- PredispatchNewsletterObserver::class,
- [
- 'scopeConfig' => $this->configMock,
- 'url' => $this->urlMock
- ]
+ $this->mockObject = new PredispatchNewsletterObserver(
+ $this->configMock,
+ $this->urlMock,
+ $this->newsletterConfig
);
}
@@ -88,19 +88,17 @@ public function testNewsletterEnabled() : void
->disableOriginalConstructor()
->setMethods(['getResponse', 'getData', 'setRedirect'])
->getMockForAbstractClass();
-
- $this->configMock->method('getValue')
- ->with(PredispatchNewsletterObserver::XML_PATH_NEWSLETTER_ACTIVE, ScopeInterface::SCOPE_STORE)
+ $this->newsletterConfig->expects($this->once())
+ ->method('isActive')
+ ->with(ScopeInterface::SCOPE_STORE)
->willReturn(true);
$observerMock->expects($this->never())
->method('getData')
->with('controller_action')
->willReturnSelf();
-
$observerMock->expects($this->never())
->method('getResponse')
->willReturnSelf();
-
$this->assertNull($this->mockObject->execute($observerMock));
}
@@ -113,35 +111,27 @@ public function testNewsletterDisabled() : void
->disableOriginalConstructor()
->setMethods(['getControllerAction', 'getResponse'])
->getMockForAbstractClass();
-
- $this->configMock->expects($this->at(0))
- ->method('getValue')
- ->with(PredispatchNewsletterObserver::XML_PATH_NEWSLETTER_ACTIVE, ScopeInterface::SCOPE_STORE)
+ $this->newsletterConfig->expects($this->once())
+ ->method('isActive')
+ ->with(ScopeInterface::SCOPE_STORE)
->willReturn(false);
-
$expectedRedirectUrl = 'https://test.com/index';
-
- $this->configMock->expects($this->at(1))
+ $this->configMock->expects($this->once())
->method('getValue')
->with('web/default/no_route', ScopeInterface::SCOPE_STORE)
->willReturn($expectedRedirectUrl);
-
$this->urlMock->expects($this->once())
->method('getUrl')
->willReturn($expectedRedirectUrl);
-
$observerMock->expects($this->once())
->method('getControllerAction')
->willReturnSelf();
-
$observerMock->expects($this->once())
->method('getResponse')
->willReturn($this->responseMock);
-
$this->responseMock->expects($this->once())
->method('setRedirect')
->with($expectedRedirectUrl);
-
$this->assertNull($this->mockObject->execute($observerMock));
}
}
From 1b626a590dca46470f73f7bd05c8ad73d2ac0fe5 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Fri, 16 Aug 2019 17:36:28 -0500
Subject: [PATCH 091/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- added configurable attribute use case and added fixture
---
.../GraphQl/Catalog/ProductSearchTest.php | 116 +++++++++++++++++-
...th_custom_attribute_layered_navigation.php | 48 ++++++++
..._attribute_layered_navigation_rollback.php | 9 ++
3 files changed, 169 insertions(+), 4 deletions(-)
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index e607e2d608786..286843e9b53e9 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -20,6 +20,7 @@
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Eav\Api\Data\AttributeOptionInterface;
use Magento\Framework\DataObject;
+use Magento\TestFramework\Helper\Bootstrap;
/**
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
@@ -99,12 +100,11 @@ public function testFilterLn()
public function testAdvancedSearchByOneCustomAttribute()
{
$optionValue = $this->getDefaultAttributeOptionValue('second_test_configurable');
-
$query = <<get(ProductRepositoryInterface::class);
$product1 = $productRepository->get('simple');
@@ -194,7 +196,8 @@ private function getDefaultAttributeOptionValue(string $attributeCode) : string
$attribute = $eavConfig->getAttribute('catalog_product', $attributeCode);
/** @var AttributeOptionInterface[] $options */
$options = $attribute->getOptions();
- $defaultOptionValue = $options[1]->getValue();
+ array_shift($options);
+ $defaultOptionValue = $options[0]->getValue();
return $defaultOptionValue;
}
@@ -393,10 +396,115 @@ public function testFilterByCategoryIdAndCustomAttribute()
'Products count in the category is incorrect'
) ;
}
+ }
+ private function getQueryProductsWithCustomAttribute($optionValue)
+ {
+ return <<get(\Magento\Eav\Model\Config::class);
+ $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode);
+ /** @var AttributeOptionInterface[] $options */
+ $options = $attribute->getOptions();
+ array_shift($options);
+ $firstOption = $options[0]->getValue();
+ $secondOption = $options[1]->getValue();
+ $query = $this->getQueryProductsWithCustomAttribute($firstOption);
+ $response = $this->graphQlQuery($query);
+
+ //Only 1 product will be returned since only one child product with attribute option1 from 1st Configurable product is OOS
+ $this->assertEquals(1, $response['products']['total_count']);
+
+ // Custom attribute filter layer data
+ $this->assertResponseFields(
+ $response['products']['filters'][1],
+ [
+ 'name' => $attribute->getDefaultFrontendLabel(),
+ 'request_var'=> $attribute->getAttributeCode(),
+ 'filter_items_count'=> 2,
+ 'filter_items' => [
+ [
+ 'label' => 'Option 1',
+ 'items_count' => 1,
+ 'value_string' => $firstOption,
+ '__typename' =>'LayerFilterItem'
+ ],
+ [
+ 'label' => 'Option 2',
+ 'items_count' => 1,
+ 'value_string' => $secondOption,
+ '__typename' =>'LayerFilterItem'
+ ]
+ ],
+ ]
+ );
+
+ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+ $productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+ $outOfStockChildProduct = $productRepository->get('simple_30');
+ // Set another child product from 2nd Configurable product with attribute option1 to OOS
+ $outOfStockChildProduct->setStockData(
+ ['use_config_manage_stock' => 1,
+ 'qty' => 0,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 0]
+ );
+ $productRepository->save($outOfStockChildProduct);
+ $query = $this->getQueryProductsWithCustomAttribute($firstOption);
+ $response = $this->graphQlQuery($query);
+ $this->assertEquals(0, $response['products']['total_count']);
+ $this->assertEmpty($response['products']['items']);
+ $this->assertEmpty($response['products']['filters']);
+ $i = 0;
+ }
/**
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php
new file mode 100644
index 0000000000000..03a4baabf088e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php
@@ -0,0 +1,48 @@
+get(\Magento\Eav\Model\Config::class);
+
+/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
+$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
+
+$eavConfig->clear();
+
+$attribute->setIsSearchable(1)
+ ->setIsVisibleInAdvancedSearch(1)
+ ->setIsFilterable(true)
+ ->setIsFilterableInSearch(true)
+ ->setIsVisibleOnFront(1);
+
+/** @var AttributeRepositoryInterface $attributeRepository */
+$attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class);
+$attributeRepository->save($attribute);
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+$outOfStockChildProduct = $productRepository->get('simple_10');
+$outOfStockChildProduct->setStockData(
+ ['use_config_manage_stock' => 1,
+ 'qty' => 0,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 0]
+);
+$productRepository->save($outOfStockChildProduct);
+
+/** @var \Magento\Indexer\Model\Indexer\Collection $indexerCollection */
+$indexerCollection = Bootstrap::getObjectManager()->get(\Magento\Indexer\Model\Indexer\Collection::class);
+$indexerCollection->load();
+/** @var \Magento\Indexer\Model\Indexer $indexer */
+foreach ($indexerCollection->getItems() as $indexer) {
+ $indexer->reindexAll();
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php
new file mode 100644
index 0000000000000..49e2a8e88a1ac
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php
@@ -0,0 +1,9 @@
+
Date: Sat, 17 Aug 2019 21:31:39 +0300
Subject: [PATCH 092/593] MC-15256: Exported customer without modification can
not be imported
- Fix static test
---
app/code/Magento/CustomerImportExport/Model/Import/Customer.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
index 914b4fd2a3ca9..563bd2cd7f2b9 100644
--- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
+++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php
@@ -521,8 +521,10 @@ protected function _importData()
);
} elseif ($this->getBehavior($rowData) == \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE) {
$processedData = $this->_prepareDataForUpdate($rowData);
+ // phpcs:disable Magento2.Performance.ForeachArrayMerge
$entitiesToCreate = array_merge($entitiesToCreate, $processedData[self::ENTITIES_TO_CREATE_KEY]);
$entitiesToUpdate = array_merge($entitiesToUpdate, $processedData[self::ENTITIES_TO_UPDATE_KEY]);
+ // phpcs:enable
foreach ($processedData[self::ATTRIBUTES_TO_SAVE_KEY] as $tableName => $customerAttributes) {
if (!isset($attributesToSave[$tableName])) {
$attributesToSave[$tableName] = [];
From 9c856a56a7c4599a9e377251ac30219076e7b5cf Mon Sep 17 00:00:00 2001
From: Evgeny Petrov
Date: Mon, 19 Aug 2019 14:02:30 +0300
Subject: [PATCH 093/593] MC-15341: Default product numbers to display results
in poor display on Desktop
---
...ml => StorefrontCheckDefaultNumberProductsToDisplayTest.xml} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename app/code/Magento/Catalog/Test/Mftf/Test/{CheckDefaultNumberProductsToDisplayTest.xml => StorefrontCheckDefaultNumberProductsToDisplayTest.xml} (99%)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml
similarity index 99%
rename from app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
rename to app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml
index 5718844fb6df3..1ce856831ceaa 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml
@@ -7,7 +7,7 @@
-->
-
+
From 25bb02e9ff1943b3e6a194bea4c943b528d43630 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Mon, 19 Aug 2019 11:51:47 -0500
Subject: [PATCH 094/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- fix existing test
---
.../GraphQl/Catalog/ProductSearchTest.php | 95 ++++++-------------
1 file changed, 29 insertions(+), 66 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 286843e9b53e9..709779e88da54 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -99,44 +99,9 @@ public function testFilterLn()
*/
public function testAdvancedSearchByOneCustomAttribute()
{
- $optionValue = $this->getDefaultAttributeOptionValue('second_test_configurable');
- $query = <<getDefaultAttributeOptionValue($attributeCode);
+ $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $optionValue);
/** @var ProductRepositoryInterface $productRepository */
$productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
@@ -380,7 +345,7 @@ public function testFilterByCategoryIdAndCustomAttribute()
'items_count'=> 2
],
];
- $categoryFilterItems = array_map(null, $expectedCategoryFilterItems,$actualCategoryFilterItems);
+ $categoryFilterItems = array_map(null, $expectedCategoryFilterItems, $actualCategoryFilterItems);
//Validate the categories and sub-categories data in the filter layer
foreach ($categoryFilterItems as $index => $categoryFilterData) {
@@ -398,12 +363,12 @@ public function testFilterByCategoryIdAndCustomAttribute()
}
}
- private function getQueryProductsWithCustomAttribute($optionValue)
+ private function getQueryProductsWithCustomAttribute($attributeCode, $optionValue)
{
return <<getValue();
$secondOption = $options[1]->getValue();
- $query = $this->getQueryProductsWithCustomAttribute($firstOption);
+ $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
$response = $this->graphQlQuery($query);
- //Only 1 product will be returned since only one child product with attribute option1 from 1st Configurable product is OOS
+ //1 product will be returned since only one child product with attribute option1 from 1st Configurable product is OOS
$this->assertEquals(1, $response['products']['total_count']);
// Custom attribute filter layer data
@@ -498,15 +463,13 @@ public function testLayeredNavigationForConfigurableProductWithOutOfStockOption(
'is_in_stock' => 0]
);
$productRepository->save($outOfStockChildProduct);
- $query = $this->getQueryProductsWithCustomAttribute($firstOption);
+ $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
$response = $this->graphQlQuery($query);
$this->assertEquals(0, $response['products']['total_count']);
$this->assertEmpty($response['products']['items']);
$this->assertEmpty($response['products']['filters']);
- $i = 0;
}
-
/**
* Get array with expected data for layered navigation filters
*
@@ -521,15 +484,32 @@ private function getExpectedFiltersDataSet()
$options = $attribute->getOptions();
// Fetching option ID is required for continuous debug as of autoincrement IDs.
return [
+ [
+ 'name' => 'Price',
+ 'filter_items_count' => 2,
+ 'request_var' => 'price',
+ 'filter_items' => [
+ [
+ 'label' => '*-10',
+ 'value_string' => '*_10',
+ 'items_count' => 1,
+ ],
+ [
+ 'label' => '10-*',
+ 'value_string' => '10_*',
+ 'items_count' => 1,
+ ],
+ ],
+ ],
[
'name' => 'Category',
'filter_items_count' => 1,
- 'request_var' => 'cat',
+ 'request_var' => 'category_id',
'filter_items' => [
[
'label' => 'Category 1',
'value_string' => '333',
- 'items_count' => 3,
+ 'items_count' => 2,
],
],
],
@@ -544,24 +524,7 @@ private function getExpectedFiltersDataSet()
'items_count' => 1,
],
],
- ],
- [
- 'name' => 'Price',
- 'filter_items_count' => 2,
- 'request_var' => 'price',
- 'filter_items' => [
- [
- 'label' => '$0.00 - $9.99 ',
- 'value_string' => '-10',
- 'items_count' => 1,
- ],
- [
- 'label' => '$10.00 and above',
- 'value_string' => '10-',
- 'items_count' => 1,
- ],
- ],
- ],
+ ]
];
}
From 0cd00e6e3b1f5c35a7502f45cec1bb8478b51904 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Mon, 19 Aug 2019 11:53:45 -0500
Subject: [PATCH 095/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- fix existing fixture
---
.../_files/products_with_layered_navigation_attribute.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php
index 48c47c9988d59..7bee46bc2078f 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attribute.php
@@ -36,7 +36,7 @@
'is_unique' => 0,
'is_required' => 0,
'is_searchable' => 1,
- 'is_visible_in_advanced_search' => 0,
+ 'is_visible_in_advanced_search' => 1,
'is_comparable' => 1,
'is_filterable' => 1,
'is_filterable_in_search' => 1,
From 21ea200d96b86dc9261e0217915667c85f7ff6aa Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Mon, 19 Aug 2019 16:30:34 -0500
Subject: [PATCH 096/593] MC-18512: Dynamically inject all searchable custom
attributes for product filtering
---
.../Model/Config/FilterAttributeReader.php | 30 +++++++++++++++----
.../Model/Config/SortAttributeReader.php | 4 ---
.../Magento/CatalogGraphQl/etc/graphql/di.xml | 4 +--
.../CatalogGraphQl/etc/schema.graphqls | 17 ++++++++---
app/code/Magento/GraphQl/etc/schema.graphqls | 15 ++++++++++
5 files changed, 54 insertions(+), 16 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
index 1dd218329112c..9310dc593d40c 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
@@ -30,7 +30,9 @@ class FilterAttributeReader implements ReaderInterface
/**
* Filter input types
*/
- private const FILTER_TYPE = 'FilterTypeInput';
+ private const FILTER_EQUAL_TYPE = 'FilterEqualTypeInput';
+ private const FILTER_RANGE_TYPE = 'FilterRangeTypeInput';
+ private const FILTER_LIKE_TYPE = 'FilterLikeTypeInput';
/**
* @var MapperInterface
@@ -67,16 +69,12 @@ public function read($scope = null) : array
$config = [];
foreach ($this->getAttributeCollection() as $attribute) {
- if (!$attribute->getIsUserDefined()) {
- //do not override fields defined in schema.graphqls
- continue;
- }
$attributeCode = $attribute->getAttributeCode();
foreach ($typeNames as $typeName) {
$config[$typeName]['fields'][$attributeCode] = [
'name' => $attributeCode,
- 'type' => self::FILTER_TYPE,
+ 'type' => $this->getFilterType($attribute->getFrontendInput()),
'arguments' => [],
'required' => false,
'description' => sprintf('Attribute label: %s', $attribute->getDefaultFrontendLabel())
@@ -87,6 +85,26 @@ public function read($scope = null) : array
return $config;
}
+ /**
+ * Map attribute type to filter type
+ *
+ * @param string $attributeType
+ * @return string
+ */
+ private function getFilterType($attributeType): string
+ {
+ $filterTypeMap = [
+ 'price' => self::FILTER_RANGE_TYPE,
+ 'date' => self::FILTER_RANGE_TYPE,
+ 'select' => self::FILTER_EQUAL_TYPE,
+ 'boolean' => self::FILTER_EQUAL_TYPE,
+ 'text' => self::FILTER_LIKE_TYPE,
+ 'textarea' => self::FILTER_LIKE_TYPE,
+ ];
+
+ return $filterTypeMap[$attributeType] ?? self::FILTER_LIKE_TYPE;
+ }
+
/**
* Create attribute collection
*
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
index 079084e95b9de..215b28be0579c 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/SortAttributeReader.php
@@ -61,10 +61,6 @@ public function read($scope = null) : array
$attributes = $this->attributesCollection->addSearchableAttributeFilter()->addFilter('used_for_sort_by', 1);
/** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
foreach ($attributes as $attribute) {
- if (!$attribute->getIsUserDefined()) {
- //do not override fields defined in schema.graphqls
- continue;
- }
$attributeCode = $attribute->getAttributeCode();
$attributeLabel = $attribute->getDefaultFrontendLabel();
foreach ($map as $type) {
diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
index 3ada365ec5065..ea2704f1a4ee5 100644
--- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
@@ -49,10 +49,10 @@
- CustomizableCheckboxOption
-
-
- ProductSortInput
+ - ProductAttributeSortInput
-
-
- ProductFilterInput
+ - ProductAttributeFilterInput
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index ea56faf94408e..e6daf4f4d753e 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -4,10 +4,10 @@
type Query {
products (
search: String @doc(description: "Performs a full-text search using the specified key words."),
- filter: ProductFilterInput @doc(description: "Identifies which product attributes to search for and return."),
+ filter: ProductAttributeFilterInput @doc(description: "Identifies which product attributes to search for and return."),
pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."),
currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."),
- sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.")
+ sort: ProductAttributeSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.")
): Products
@resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity")
category (
@@ -280,7 +280,11 @@ type CategoryProducts @doc(description: "The category products object returned i
total_count: Int @doc(description: "The number of products returned.")
}
-input ProductFilterInput @doc(description: "ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") {
+input ProductAttributeFilterInput @doc(description: "ProductAttributeFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") {
+ category_id: FilterEqualTypeInput @doc(description: "Filter product by category id")
+}
+
+input ProductFilterInput @deprecated(reason: "Attributes used in this input are hardcoded and some of them are not searcheable. Use @ProductAttributeFilterInput instead") @doc(description: "ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") {
name: FilterTypeInput @doc(description: "The product name. Customers use this name to identify the product.")
sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.")
description: FilterTypeInput @doc(description: "Detailed information about the product. The value can include simple HTML tags.")
@@ -333,7 +337,7 @@ type ProductMediaGalleryEntriesVideoContent @doc(description: "ProductMediaGalle
video_metadata: String @doc(description: "Optional data about the video.")
}
-input ProductSortInput @doc(description: "ProductSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order.") {
+input ProductSortInput @deprecated(reason: "Attributes used in this input are hardcoded and some of them are not searcheable. Use @ProductAttributeSortInput instead") @doc(description: "ProductSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order.") {
name: SortEnum @doc(description: "The product name. Customers use this name to identify the product.")
sku: SortEnum @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.")
description: SortEnum @doc(description: "Detailed information about the product. The value can include simple HTML tags.")
@@ -367,6 +371,11 @@ input ProductSortInput @doc(description: "ProductSortInput specifies the attribu
gift_message_available: SortEnum @doc(description: "Indicates whether a gift message is available.")
}
+input ProductAttributeSortInput @doc(description: "ProductAttributeSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order. It's possible to sort products using searchable attributes with enabled 'Use in Filter Options' option")
+{
+ position: SortEnum @doc(description: "The position of products")
+}
+
type MediaGalleryEntry @doc(description: "MediaGalleryEntry defines characteristics about images and videos associated with a specific product.") {
id: Int @doc(description: "The identifier assigned to the object.")
media_type: String @doc(description: "image or video.")
diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls
index eb6a88a4d487d..997572471122f 100644
--- a/app/code/Magento/GraphQl/etc/schema.graphqls
+++ b/app/code/Magento/GraphQl/etc/schema.graphqls
@@ -65,6 +65,21 @@ input FilterTypeInput @doc(description: "FilterTypeInput specifies which action
nin: [String] @doc(description: "Not in. The value can contain a set of comma-separated values")
}
+input FilterEqualTypeInput @doc(description: "Specifies which action will be performed in a query ") {
+ in: [String]
+ eq: String
+}
+
+input FilterRangeTypeInput @doc(description: "Specifies which action will be performed in a query ") {
+ from: String
+ to: String
+}
+
+input FilterLikeTypeInput @doc(description: "Specifies which action will be performed in a query ") {
+ like: String
+ eq: String
+}
+
type SearchResultPageInfo @doc(description: "SearchResultPageInfo provides navigation for the query response") {
page_size: Int @doc(description: "Specifies the maximum number of items to return")
current_page: Int @doc(description: "Specifies which page of results to return")
From 88ee6cf2824b9b12b46d82cffe6d26045f4ed74f Mon Sep 17 00:00:00 2001
From: Eden
Date: Tue, 20 Aug 2019 14:53:43 +0700
Subject: [PATCH 097/593] Rss Model Refactor
---
app/code/Magento/Review/Model/Rss.php | 33 ++++++++++++++++++++++++---
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/Review/Model/Rss.php b/app/code/Magento/Review/Model/Rss.php
index df8a5dbb96841..876a3722da61e 100644
--- a/app/code/Magento/Review/Model/Rss.php
+++ b/app/code/Magento/Review/Model/Rss.php
@@ -3,11 +3,17 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
+declare(strict_types=1);
+
namespace Magento\Review\Model;
+use Magento\Framework\App\ObjectManager;
+
/**
- * Class Rss
- * @package Magento\Catalog\Model\Rss\Product
+ * Model Rss
+ *
+ * Class \Magento\Catalog\Model\Rss\Product\Rss
*/
class Rss extends \Magento\Framework\Model\AbstractModel
{
@@ -24,18 +30,39 @@ class Rss extends \Magento\Framework\Model\AbstractModel
protected $eventManager;
/**
+ * Rss constructor.
+ *
* @param \Magento\Framework\Event\ManagerInterface $eventManager
* @param ReviewFactory $reviewFactory
+ * @param \Magento\Framework\Model\Context|null $context
+ * @param \Magento\Framework\Registry|null $registry
+ * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
+ * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
+ * @param array $data
*/
public function __construct(
\Magento\Framework\Event\ManagerInterface $eventManager,
- \Magento\Review\Model\ReviewFactory $reviewFactory
+ \Magento\Review\Model\ReviewFactory $reviewFactory,
+ \Magento\Framework\Model\Context $context = null,
+ \Magento\Framework\Registry $registry = null,
+ \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
+ \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
+ array $data = []
) {
$this->reviewFactory = $reviewFactory;
$this->eventManager = $eventManager;
+ $context = $context ?? ObjectManager::getInstance()->get(
+ \Magento\Framework\Model\Context::class
+ );
+ $registry = $registry ?? ObjectManager::getInstance()->get(
+ \Magento\Framework\Registry::class
+ );
+ parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}
/**
+ * Get Product Collection
+ *
* @return $this|\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
*/
public function getProductCollection()
From 0be11cc9db67398006ffed770eab52cea25cfb2b Mon Sep 17 00:00:00 2001
From: Eden
Date: Tue, 20 Aug 2019 15:24:03 +0700
Subject: [PATCH 098/593] Fix static test rss model
---
app/code/Magento/Review/Model/Rss.php | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/Review/Model/Rss.php b/app/code/Magento/Review/Model/Rss.php
index 876a3722da61e..f5abdbb4d3c9e 100644
--- a/app/code/Magento/Review/Model/Rss.php
+++ b/app/code/Magento/Review/Model/Rss.php
@@ -51,12 +51,8 @@ public function __construct(
) {
$this->reviewFactory = $reviewFactory;
$this->eventManager = $eventManager;
- $context = $context ?? ObjectManager::getInstance()->get(
- \Magento\Framework\Model\Context::class
- );
- $registry = $registry ?? ObjectManager::getInstance()->get(
- \Magento\Framework\Registry::class
- );
+ $context = $context ?? ObjectManager::getInstance()->get(\Magento\Framework\Model\Context::class);
+ $registry = $registry ?? ObjectManager::getInstance()->get(\Magento\Framework\Registry::class);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}
From 3d55863b67754d553e10c1d5311405f5c9d4b4c1 Mon Sep 17 00:00:00 2001
From: Veronika Kurochkina
Date: Tue, 20 Aug 2019 13:31:48 +0300
Subject: [PATCH 099/593] MAGETWO-44170: Not pass function test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Fix modularity issue
---
...AdminProductTypeSwitchingOnEditingTest.xml | 280 ------------------
...AdminProductTypeSwitchingOnEditingTest.xml | 243 +++++++++++++++
2 files changed, 243 insertions(+), 280 deletions(-)
create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
index cac5e209af9fd..45f404478809a 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -8,195 +8,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -241,12 +52,6 @@
-
-
-
-
-
-
@@ -281,12 +86,6 @@
-
-
-
-
-
-
@@ -336,89 +135,10 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
new file mode 100644
index 0000000000000..071ee8d18920f
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -0,0 +1,243 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 3b110166c45c30265124d1ae491447337c22f5b6 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Tue, 20 Aug 2019 13:57:27 -0500
Subject: [PATCH 100/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- clean cache to regenerate schema
---
.../GraphQl/Catalog/ProductSearchTest.php | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 709779e88da54..3284a4a7172fe 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -13,6 +13,7 @@
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Category;
use Magento\Catalog\Model\CategoryLinkManagement;
+use Magento\Framework\Config\Data;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\TestFramework\ObjectManager;
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -21,6 +22,7 @@
use Magento\Eav\Api\Data\AttributeOptionInterface;
use Magento\Framework\DataObject;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\CacheCleaner;
/**
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
@@ -37,6 +39,7 @@ class ProductSearchTest extends GraphQlAbstract
*/
public function testFilterLn()
{
+ CacheCleaner::cleanAll();
$query = <<getDefaultAttributeOptionValue($attributeCode);
$query = $this->getQueryProductsWithCustomAttribute($attributeCode, $optionValue);
@@ -174,6 +178,7 @@ private function getDefaultAttributeOptionValue(string $attributeCode) : string
*/
public function testFullTextSearchForProductAndFilterByCustomAttribute()
{
+ CacheCleaner::cleanAll();
$optionValue = $this->getDefaultAttributeOptionValue('second_test_configurable');
$query = <<
Date: Tue, 20 Aug 2019 19:44:04 +0000
Subject: [PATCH 101/593] Corrected Code Style
---
.../testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php | 2 +-
.../testsuite/Magento/Customer/_files/customer_subscribe.php | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
index 2ff9026cb6a89..d6c7e590639f0 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
@@ -241,7 +241,7 @@ public function testCreateCustomerIfPassedAttributeDosNotExistsInCustomerInput()
$this->graphQlMutation($query);
}
- /**
+ /**
* @expectedException \Exception
* @expectedExceptionMessage Required parameters are missing: First Name
*/
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
index 9585ad8b02740..66ec54641d74e 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
@@ -13,4 +13,4 @@
false,
'default',
0
-);
\ No newline at end of file
+);
From a249557376f33273eaaad29c4e14718b5a4377b2 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Tue, 20 Aug 2019 15:37:23 -0500
Subject: [PATCH 102/593] MC-18512: Dynamically inject all searchable custom
attributes for product filtering
---
app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php | 6 +-----
app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 2 +-
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index 6a17f730c0f1a..3bc61a0fb3f2c 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -98,11 +98,7 @@ public function resolve(
//get product children fields queried
$productFields = (array)$info->getFieldSelection(1);
- $searchCriteria = $this->searchApiCriteriaBuilder->build(
- $args,
- isset($productFields['filters'])
- );
-
+ $searchCriteria = $this->searchApiCriteriaBuilder->build($args, isset($productFields['filters']));
$searchResult = $this->searchQuery->getResult($searchCriteria, $info, $args);
if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) {
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index e6daf4f4d753e..aadd43fafbd7f 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -221,7 +221,7 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model
products(
pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."),
currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."),
- sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.")
+ sort: ProductAttributeSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.")
): CategoryProducts @doc(description: "The list of products assigned to the category.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products")
breadcrumbs: [Breadcrumb] @doc(description: "Breadcrumbs, parent categories info.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Breadcrumbs")
}
From 8d765a32dd7480b3a17d9745aeb2ee6d7f80bbe2 Mon Sep 17 00:00:00 2001
From: Prabhu Ram
Date: Tue, 20 Aug 2019 16:53:20 -0500
Subject: [PATCH 103/593] MC-19441: Fix web api test failures - fixed 3 tests
---
.../GraphQl/Catalog/ProductSearchTest.php | 26 ++++++-------------
1 file changed, 8 insertions(+), 18 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 3284a4a7172fe..bcd0ffedf542f 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -717,6 +717,7 @@ public function testFilterVisibleProductsWithMatchingSkuOrNameWithSpecialPrice()
public function testSearchWithFilterWithPageSizeEqualTotalCount()
{
+ CacheCleaner::cleanAll();
$query
= <<
Date: Tue, 20 Aug 2019 22:47:18 -0400
Subject: [PATCH 104/593] Upgrade graphql-php to v0.13.6
Follows upgrade path outlined in https://github.com/webonyx/graphql-php/blob/master/UPGRADE.md
---
composer.json | 2 +-
composer.lock | 45 +++++++++++--------
.../Schema/Type/ResolveInfoFactory.php | 18 +++++++-
.../GraphQl/Schema/Type/ScalarTypes.php | 8 +++-
4 files changed, 50 insertions(+), 23 deletions(-)
diff --git a/composer.json b/composer.json
index 293cb06ef403c..d90eadffb9db5 100644
--- a/composer.json
+++ b/composer.json
@@ -52,7 +52,7 @@
"symfony/process": "~4.1.0|~4.2.0|~4.3.0",
"tedivm/jshrink": "~1.3.0",
"tubalmartin/cssmin": "4.1.1",
- "webonyx/graphql-php": "^0.12.6",
+ "webonyx/graphql-php": "^0.13.6",
"zendframework/zend-captcha": "^2.7.1",
"zendframework/zend-code": "~3.3.0",
"zendframework/zend-config": "^2.6.0",
diff --git a/composer.lock b/composer.lock
index f67eb50675314..d4348362255ab 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "a4299e3f4f0d4dd4915f37a5dde8e2ed",
+ "content-hash": "6fd70adf831929273a251803fa361cd4",
"packages": [
{
"name": "braintree/braintree_php",
@@ -1552,28 +1552,28 @@
"authors": [
{
"name": "Jim Wigginton",
- "role": "Lead Developer",
- "email": "terrafrost@php.net"
+ "email": "terrafrost@php.net",
+ "role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
- "role": "Developer",
- "email": "pm@datasphere.ch"
+ "email": "pm@datasphere.ch",
+ "role": "Developer"
},
{
"name": "Andreas Fischer",
- "role": "Developer",
- "email": "bantu@phpbb.com"
+ "email": "bantu@phpbb.com",
+ "role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
- "role": "Developer",
- "email": "petrich@tronic-media.com"
+ "email": "petrich@tronic-media.com",
+ "role": "Developer"
},
{
"name": "Graham Campbell",
- "role": "Developer",
- "email": "graham@alt-three.com"
+ "email": "graham@alt-three.com",
+ "role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
@@ -2402,7 +2402,7 @@
},
{
"name": "Gert de Pagter",
- "email": "BackEndTea@gmail.com"
+ "email": "backendtea@gmail.com"
}
],
"description": "Symfony polyfill for ctype functions",
@@ -2670,24 +2670,31 @@
},
{
"name": "webonyx/graphql-php",
- "version": "v0.12.6",
+ "version": "v0.13.6",
"source": {
"type": "git",
"url": "https://github.com/webonyx/graphql-php.git",
- "reference": "4c545e5ec4fc37f6eb36c19f5a0e7feaf5979c95"
+ "reference": "123af49e46d26b0cd2e7a71a387253aa01ea9a6b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/4c545e5ec4fc37f6eb36c19f5a0e7feaf5979c95",
- "reference": "4c545e5ec4fc37f6eb36c19f5a0e7feaf5979c95",
+ "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/123af49e46d26b0cd2e7a71a387253aa01ea9a6b",
+ "reference": "123af49e46d26b0cd2e7a71a387253aa01ea9a6b",
"shasum": ""
},
"require": {
+ "ext-json": "*",
"ext-mbstring": "*",
- "php": ">=5.6"
+ "php": "^7.1||^8.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.8",
+ "doctrine/coding-standard": "^6.0",
+ "phpbench/phpbench": "^0.14.0",
+ "phpstan/phpstan": "^0.11.4",
+ "phpstan/phpstan-phpunit": "^0.11.0",
+ "phpstan/phpstan-strict-rules": "^0.11.0",
+ "phpunit/phpcov": "^5.0",
+ "phpunit/phpunit": "^7.2",
"psr/http-message": "^1.0",
"react/promise": "2.*"
},
@@ -2711,7 +2718,7 @@
"api",
"graphql"
],
- "time": "2018-09-02T14:59:54+00:00"
+ "time": "2019-08-07T08:16:55+00:00"
},
{
"name": "wikimedia/less.php",
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/ResolveInfoFactory.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/ResolveInfoFactory.php
index 1dde923bb2a98..335b991f693c8 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/ResolveInfoFactory.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/ResolveInfoFactory.php
@@ -7,11 +7,27 @@
namespace Magento\Framework\GraphQl\Schema\Type;
+use Magento\Framework\ObjectManagerInterface;
+
/**
* Factory for wrapper of GraphQl ResolveInfo
*/
class ResolveInfoFactory
{
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $objectManager;
+
+ /**
+ * @param ObjectManagerInterface $objectManager
+ */
+ public function __construct(
+ ObjectManagerInterface $objectManager
+ ) {
+ $this->objectManager = $objectManager;
+ }
+
/**
* Create a wrapper resolver info from the instance of the library object
*
@@ -25,6 +41,6 @@ public function create(\GraphQL\Type\Definition\ResolveInfo $info) : ResolveInfo
$values[$key] = $value;
}
- return new ResolveInfo($values);
+ return $this->objectManager->create(ResolveInfo::class, $values);
}
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php
index 508cada1a6958..04b9354855ea9 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php
@@ -13,23 +13,27 @@
class ScalarTypes
{
/**
+ * Check if type is scalar
+ *
* @param string $typeName
* @return bool
*/
public function isScalarType(string $typeName) : bool
{
- $internalTypes = \GraphQL\Type\Definition\Type::getInternalTypes();
+ $internalTypes = \GraphQL\Type\Definition\Type::getStandardTypes();
return isset($internalTypes[$typeName]) ? true : false;
}
/**
+ * Get instance of scalar type
+ *
* @param string $typeName
* @return \GraphQL\Type\Definition\ScalarType|\GraphQL\Type\Definition\Type
* @throws \LogicException
*/
public function getScalarTypeInstance(string $typeName) : \GraphQL\Type\Definition\Type
{
- $internalTypes = \GraphQL\Type\Definition\Type::getInternalTypes();
+ $internalTypes = \GraphQL\Type\Definition\Type::getStandardTypes();
if ($this->isScalarType($typeName)) {
return $internalTypes[$typeName];
} else {
From 86151cec2b662b91d000fdba886c05096569f84a Mon Sep 17 00:00:00 2001
From: Volodymyr Vygovskyi
Date: Wed, 21 Aug 2019 16:57:05 +0300
Subject: [PATCH 105/593] #813: [Test coverage] Update item qty for
configurable product
---
.../UpdateConfigurableCartItemsTest.php | 151 ++++++++++++++++++
1 file changed, 151 insertions(+)
create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
new file mode 100644
index 0000000000000..26ec331dcbce1
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
@@ -0,0 +1,151 @@
+getMaskedQuoteId($reservedOrderId);
+ $productSku = 'simple_10';
+ $newQuantity = 123;
+ $quoteItem = $this->getQuoteItemBySku($productSku, $reservedOrderId);
+
+ $query = $this->getQuery($maskedQuoteId, (int)$quoteItem->getId(), $newQuantity);
+ $response = $this->graphQlMutation($query);
+
+ self::assertArrayHasKey('updateCartItems', $response);
+ self::assertArrayHasKey('quantity', $response['updateCartItems']['cart']['items']['0']);
+ self::assertEquals($newQuantity, $response['updateCartItems']['cart']['items']['0']['quantity']);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ $objectManager = Bootstrap::getObjectManager();
+ $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->quoteFactory = $objectManager->get(QuoteFactory::class);
+ $this->quoteResource = $objectManager->get(QuoteResource::class);
+ $this->productRepository = $objectManager->get(ProductRepositoryInterface::class);
+ $this->quoteIdMaskFactory = Bootstrap::getObjectManager()->get(QuoteIdMaskFactory::class);
+ }
+
+ /**
+ * @param string $maskedQuoteId
+ * @param int $quoteItemId
+ * @param int $newQuantity
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId, int $quoteItemId, int $newQuantity): string
+ {
+ return <<quoteFactory->create();
+ $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id');
+ $item = false;
+ foreach ($quote->getAllItems() as $quoteItem) {
+ if ($quoteItem->getSku() == $sku && $quoteItem->getProductType() == Configurable::TYPE_CODE &&
+ !$quoteItem->getParentItemId()) {
+ $item = $quoteItem;
+ break;
+ }
+ }
+
+ return $item;
+ }
+
+ /**
+ * @param $reservedOrderId
+ * @return string
+ * @throws NoSuchEntityException
+ */
+ private function getMaskedQuoteId(string $reservedOrderId): string
+ {
+ $quote = $this->quoteFactory->create();
+ $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id');
+ $quoteIdMask = $this->quoteIdMaskFactory->create();
+ $quoteIdMask->setQuoteId($quote->getId())
+ ->save();
+
+ return $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ }
+}
From e5708b0fa06f12b4a4ff0080d286dd1651e7b7cb Mon Sep 17 00:00:00 2001
From: Volodymyr Vygovskyi
Date: Wed, 21 Aug 2019 17:10:18 +0300
Subject: [PATCH 106/593] #813: removed redundant dependency
---
.../UpdateConfigurableCartItemsTest.php | 7 -------
1 file changed, 7 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
index 26ec331dcbce1..b13a75fa8c4e7 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
@@ -8,7 +8,6 @@
namespace Magento\GraphQl\ConfigurableProduct;
-use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
use Magento\Framework\Exception\NoSuchEntityException as NoSuchEntityException;
use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId;
@@ -34,11 +33,6 @@ class UpdateConfigurableCartItemsTest extends GraphQlAbstract
*/
private $getMaskedQuoteIdByReservedOrderId;
- /**
- * @var ProductRepositoryInterface
- */
- private $productRepository;
-
/**
* @var QuoteFactory
*/
@@ -77,7 +71,6 @@ protected function setUp()
$this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
$this->quoteFactory = $objectManager->get(QuoteFactory::class);
$this->quoteResource = $objectManager->get(QuoteResource::class);
- $this->productRepository = $objectManager->get(ProductRepositoryInterface::class);
$this->quoteIdMaskFactory = Bootstrap::getObjectManager()->get(QuoteIdMaskFactory::class);
}
From 6a4ecbe6ea560da3ff1bdba7a70a5a3fd1607d8e Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 21 Aug 2019 20:12:38 +0000
Subject: [PATCH 107/593] Added lines *Deleted unnecessary uses
---
.../Customer/Test/Unit/Block/Form/RegisterTest.php | 3 +++
.../Observer/PredispatchNewsletterObserverTest.php | 11 +++++++++++
.../Magento/Customer/_files/customer_subscribe.php | 2 --
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php b/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php
index e1adfdf4ed8f1..3022177ffb9e1 100644
--- a/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php
@@ -64,6 +64,7 @@ protected function setUp()
$this->newsletterConfig = $this->createMock(\Magento\Newsletter\Model\Config::class);
$context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class);
$context->expects($this->any())->method('getScopeConfig')->will($this->returnValue($this->_scopeConfig));
+
$this->_block = new \Magento\Customer\Block\Form\Register(
$context,
$this->directoryHelperMock,
@@ -296,6 +297,7 @@ public function testIsNewsletterEnabled($isNewsletterEnabled, $isNewsletterActiv
)->will(
$this->returnValue($isNewsletterEnabled)
);
+
$this->newsletterConfig->expects(
$this->any()
)->method(
@@ -303,6 +305,7 @@ public function testIsNewsletterEnabled($isNewsletterEnabled, $isNewsletterActiv
)->will(
$this->returnValue($isNewsletterActive)
);
+
$this->assertEquals($expectedValue, $this->_block->isNewsletterEnabled());
}
diff --git a/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php b/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
index f88537397ae66..d0cfa507eb42f 100644
--- a/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
+++ b/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
@@ -88,17 +88,21 @@ public function testNewsletterEnabled() : void
->disableOriginalConstructor()
->setMethods(['getResponse', 'getData', 'setRedirect'])
->getMockForAbstractClass();
+
$this->newsletterConfig->expects($this->once())
->method('isActive')
->with(ScopeInterface::SCOPE_STORE)
->willReturn(true);
+
$observerMock->expects($this->never())
->method('getData')
->with('controller_action')
->willReturnSelf();
+
$observerMock->expects($this->never())
->method('getResponse')
->willReturnSelf();
+
$this->assertNull($this->mockObject->execute($observerMock));
}
@@ -111,27 +115,34 @@ public function testNewsletterDisabled() : void
->disableOriginalConstructor()
->setMethods(['getControllerAction', 'getResponse'])
->getMockForAbstractClass();
+
$this->newsletterConfig->expects($this->once())
->method('isActive')
->with(ScopeInterface::SCOPE_STORE)
->willReturn(false);
+
$expectedRedirectUrl = 'https://test.com/index';
$this->configMock->expects($this->once())
->method('getValue')
->with('web/default/no_route', ScopeInterface::SCOPE_STORE)
->willReturn($expectedRedirectUrl);
+
$this->urlMock->expects($this->once())
->method('getUrl')
->willReturn($expectedRedirectUrl);
+
$observerMock->expects($this->once())
->method('getControllerAction')
->willReturnSelf();
+
$observerMock->expects($this->once())
->method('getResponse')
->willReturn($this->responseMock);
+
$this->responseMock->expects($this->once())
->method('setRedirect')
->with($expectedRedirectUrl);
+
$this->assertNull($this->mockObject->execute($observerMock));
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
index 66ec54641d74e..c776eeab48ded 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
@@ -3,8 +3,6 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-use Magento\Customer\Model\CustomerRegistry;
-
$resourceConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->create(\Magento\Config\Model\ResourceModel\Config::class);
From 79a52cdab65636e17f3c4188ff40a75a19b1a74f Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Wed, 21 Aug 2019 15:18:16 -0500
Subject: [PATCH 108/593] MC-19541: Fix the filtering by price
---
.../Product/SearchCriteriaBuilder.php | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
index 9e381307d8139..4872c627ded59 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -84,9 +84,11 @@ public function __construct(
public function build(array $args, bool $includeAggregation): SearchCriteriaInterface
{
$searchCriteria = $this->builder->build('products', $args);
+ $this->updateRangeFilters($searchCriteria);
$searchCriteria->setRequestName(
$includeAggregation ? 'graphql_product_search_with_aggregation' : 'graphql_product_search'
);
+
if ($includeAggregation) {
$this->preparePriceAggregation($searchCriteria);
}
@@ -175,4 +177,24 @@ private function addDefaultSortOrder(SearchCriteriaInterface $searchCriteria): v
->create();
$searchCriteria->setSortOrders([$sortOrder]);
}
+
+ /**
+ * Format range filters so replacement works
+ *
+ * Range filter fields in search request must replace value like '%field.from%' or '%field.to%'
+ *
+ * @param SearchCriteriaInterface $searchCriteria
+ */
+ private function updateRangeFilters(SearchCriteriaInterface $searchCriteria): void
+ {
+ $filterGroups = $searchCriteria->getFilterGroups();
+ foreach ($filterGroups as $filterGroup) {
+ $filters = $filterGroup->getFilters();
+ foreach ($filters as $filter) {
+ if (in_array($filter->getConditionType(), ['from', 'to'])) {
+ $filter->setField($filter->getField() . '.' . $filter->getConditionType());
+ }
+ }
+ }
+ }
}
From b4bdc73fa35d865fdc460487ca217f8848930183 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 21 Aug 2019 21:17:51 +0000
Subject: [PATCH 109/593] Removed customer_subscribe.php and
customer_subscribe_rollback.php
---
.../GraphQl/Customer/CreateCustomerTest.php | 2 +-
.../Customer/_files/customer_subscribe.php | 14 --------------
.../_files/customer_subscribe_rollback.php | 16 ----------------
3 files changed, 1 insertion(+), 31 deletions(-)
delete mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
delete mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
index d6c7e590639f0..9654be4f5e3cc 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
@@ -276,7 +276,7 @@ public function testCreateCustomerIfNameEmpty()
}
/**
- * @magentoApiDataFixture Magento/Customer/_files/customer_subscribe.php
+ * @magentoConfigFixture default_store newsletter/general/active 0
* @throws \Exception
*/
public function testCreateCustomerSubscribed()
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
deleted file mode 100644
index c776eeab48ded..0000000000000
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
+++ /dev/null
@@ -1,14 +0,0 @@
-create(\Magento\Config\Model\ResourceModel\Config::class);
-
-$resourceConfig->saveConfig(
- 'newsletter/general/active',
- false,
- 'default',
- 0
-);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
deleted file mode 100644
index d8773e97b5db7..0000000000000
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
+++ /dev/null
@@ -1,16 +0,0 @@
-create(WriterInterface::class);
-$configWriter->delete('newsletter/general/active');
From c1e34125e9bdb17ca3ab82ea9a422754c56e365d Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Wed, 21 Aug 2019 18:19:45 -0500
Subject: [PATCH 110/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- fix failing tests and removing duplicates
---
.../GraphQl/Catalog/ProductSearchTest.php | 123 +++++-------------
.../GraphQl/Catalog/ProductViewTest.php | 2 +-
.../GraphQl/VariablesSupportQueryTest.php | 6 +-
3 files changed, 36 insertions(+), 95 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index bcd0ffedf542f..a565ac656124a 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -1110,60 +1110,6 @@ public function testQuerySortByPriceDESCWithDefaultPageSize()
$this->assertEquals(1, $response['products']['page_info']['current_page']);
}
- /**
- * @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products_2.php
- */
- public function testProductQueryUsingFromAndToFilterInput()
- {
- $query
- = <<graphQlQuery($query);
- $this->assertEquals(2, $response['products']['total_count']);
- /** @var ProductRepositoryInterface $productRepository */
- $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
- $product1 = $productRepository->get('simple1');
- $product2 = $productRepository->get('simple2');
- $filteredProducts = [$product2, $product1];
-
- $this->assertProductItemsWithMaximalAndMinimalPriceCheck($filteredProducts, $response);
- }
-
/**
* @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products_2.php
*/
@@ -1230,7 +1176,7 @@ public function testProductBasicFullTextSearchQuery()
/**
* @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products_2.php
*/
- public function testProductsThatMatchWithPricesFromList()
+ public function testProductsThatMatchWithinASpecificPriceRange()
{
$query
=<<get('simple2');
$prod2 = $productRepository->get('simple1');
$filteredProducts = [$prod1, $prod2];
- $productItemsInResponse = array_map(null, $response['products']['items'], $filteredProducts);
- foreach ($productItemsInResponse as $itemIndex => $itemArray) {
- $this->assertNotEmpty($itemArray);
- $this->assertResponseFields(
- $productItemsInResponse[$itemIndex][0],
- ['attribute_set_id' => $filteredProducts[$itemIndex]->getAttributeSetId(),
- 'sku' => $filteredProducts[$itemIndex]->getSku(),
- 'name' => $filteredProducts[$itemIndex]->getName(),
- 'price' => [
- 'regularPrice' => [
- 'amount' => [
- 'value' => $filteredProducts[$itemIndex]->getPrice(),
- 'currency' => 'USD'
- ]
- ]
- ],
- 'type_id' =>$filteredProducts[$itemIndex]->getTypeId(),
- 'weight' => $filteredProducts[$itemIndex]->getWeight()
- ]
- );
- }
+ $this->assertProductItemsWithPriceCheck($filteredProducts, $response);
}
/**
@@ -1320,21 +1258,17 @@ public function testQueryFilterNoMatchingItems()
{
products(
filter:
- {
- special_price:{lt:"15"}
- price:{lt:"50"}
- weight:{gt:"4"}
- or:
- {
- sku:{like:"simple%"}
- name:{like:"%simple%"}
- }
+ {
+ price:{from:"50"}
+ sku:{like:"simple%"}
+ name:{like:"simple%"}
+
}
pageSize:2
currentPage:1
sort:
{
- sku:ASC
+ position:ASC
}
)
{
@@ -1384,7 +1318,7 @@ public function testQueryPageOutOfBoundException()
products(
filter:
{
- price:{eq:"10"}
+ price:{to:"10"}
}
pageSize:2
currentPage:2
@@ -1605,7 +1539,7 @@ private function assertProductItems(array $filteredProducts, array $actualRespon
}
}
- private function assertProductItemsWithMaximalAndMinimalPriceCheck(array $filteredProducts, array $actualResponse)
+ private function assertProductItemsWithPriceCheck(array $filteredProducts, array $actualResponse)
{
$productItemsInResponse = array_map(null, $actualResponse['products']['items'], $filteredProducts);
@@ -1628,7 +1562,14 @@ private function assertProductItemsWithMaximalAndMinimalPriceCheck(array $filter
'value' => $filteredProducts[$itemIndex]->getSpecialPrice(),
'currency' => 'USD'
]
- ]
+ ],
+ 'regularPrice' => [
+ 'amount' => [
+ 'value' => $filteredProducts[$itemIndex]->getPrice(),
+ 'currency' => 'USD'
+ ]
+ ]
+
],
'type_id' =>$filteredProducts[$itemIndex]->getTypeId(),
'weight' => $filteredProducts[$itemIndex]->getWeight()
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
index e11e2e8d108c2..5990211f1e47d 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
@@ -592,7 +592,7 @@ public function testProductPrices()
$secondProductSku = 'simple-156';
$query = << 1,
'priceSort' => 'ASC',
'filterInput' => [
- 'min_price' => [
- 'gt' => 150,
+ 'price' => [
+ 'from' => 150,
],
],
];
From fe9d262f5998448fe63ecf5aecb7ac0a7dd0b92d Mon Sep 17 00:00:00 2001
From: Patrick McLain
Date: Wed, 21 Aug 2019 20:24:59 -0400
Subject: [PATCH 111/593] Update tests for later version error messaging
---
.../Magento/GraphQl/Customer/CreateCustomerAddressTest.php | 4 ++--
.../Magento/GraphQl/Customer/UpdateCustomerAddressTest.php | 4 ++--
.../Magento/GraphQl/Controller/GraphQlControllerTest.php | 2 +-
.../Model/Resolver/Guest/PaypalExpressTokenTest.php | 4 ++--
.../Resolver/Guest/PaypalPayflowProTokenExceptionTest.php | 2 +-
.../Model/Resolver/Guest/PaypalPayflowProTokenTest.php | 2 +-
.../Model/Resolver/Guest/PlaceOrderWithHostedProTest.php | 4 ++--
.../Model/Resolver/Guest/PlaceOrderWithPayflowLinkTest.php | 2 +-
.../Resolver/Guest/PlaceOrderWithPaymentsAdvancedTest.php | 4 ++--
.../Resolver/Guest/SetPaymentMethodAsPayflowLinkTest.php | 2 +-
10 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php
index 47c4d3ad91cb6..203e9b5cb42e5 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php
@@ -303,8 +303,8 @@ public function invalidInputDataProvider()
{
return [
['', 'Syntax Error: Expected Name, found )'],
- ['input: ""', 'Expected type CustomerAddressInput!, found "".'],
- ['input: "foo"', 'Expected type CustomerAddressInput!, found "foo".']
+ ['input: ""', 'requires type CustomerAddressInput!, found "".'],
+ ['input: "foo"', 'requires type CustomerAddressInput!, found "foo".']
];
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
index f2e82398df49b..9840236dc9896 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
@@ -306,8 +306,8 @@ public function invalidInputDataProvider()
{
return [
['', '"input" value must be specified'],
- ['input: ""', 'Expected type CustomerAddressInput, found ""'],
- ['input: "foo"', 'Expected type CustomerAddressInput, found "foo"']
+ ['input: ""', 'requires type CustomerAddressInput, found ""'],
+ ['input: "foo"', 'requires type CustomerAddressInput, found "foo"']
];
}
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php
index d0d746812ec44..91e1df21fafc8 100644
--- a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php
@@ -251,7 +251,7 @@ public function testError() : void
foreach ($outputResponse['errors'] as $error) {
$this->assertEquals(
\Magento\Framework\GraphQl\Exception\GraphQlInputException::EXCEPTION_CATEGORY,
- $error['category']
+ $error['extensions']['category']
);
if (isset($error['message'])) {
$this->assertEquals($error['message'], 'Invalid entity_type specified: invalid');
diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressTokenTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressTokenTest.php
index 3c7bd4a8c0bd3..24c64bce54438 100644
--- a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressTokenTest.php
+++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressTokenTest.php
@@ -136,7 +136,7 @@ public function testResolveWithPaypalError($paymentMethod): void
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
/**
@@ -173,7 +173,7 @@ public function testResolveWithInvalidRedirectUrl($paymentMethod): void
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenExceptionTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenExceptionTest.php
index b5de225b7abbc..25a9a01c4c4c7 100644
--- a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenExceptionTest.php
+++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenExceptionTest.php
@@ -73,6 +73,6 @@ public function testResolveWithPaypalError(): void
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenTest.php
index df7f80ccd35a8..248f5d297be32 100644
--- a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenTest.php
+++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalPayflowProTokenTest.php
@@ -130,6 +130,6 @@ public function testResolveWithInvalidRedirectUrl(): void
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithHostedProTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithHostedProTest.php
index 0920aa2eb36b8..a8136fda73c09 100644
--- a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithHostedProTest.php
+++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithHostedProTest.php
@@ -206,7 +206,7 @@ public function testOrderWithHostedProDeclined(): void
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
/**
@@ -258,6 +258,6 @@ public function testSetPaymentMethodInvalidUrls()
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPayflowLinkTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPayflowLinkTest.php
index c2ec5e6bddde3..f4fe3e7e60fd8 100644
--- a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPayflowLinkTest.php
+++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPayflowLinkTest.php
@@ -273,6 +273,6 @@ public function testResolveWithPayflowLinkDeclined(): void
$expectedExceptionMessage,
$actualError['message']
);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPaymentsAdvancedTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPaymentsAdvancedTest.php
index 7ef5db4a2ddab..a40a56be5faee 100644
--- a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPaymentsAdvancedTest.php
+++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PlaceOrderWithPaymentsAdvancedTest.php
@@ -178,7 +178,7 @@ public function testResolvePaymentsAdvancedWithInvalidUrl(): void
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
/**
@@ -230,7 +230,7 @@ public function testResolveWithPaymentAdvancedDeclined(): void
$this->assertArrayHasKey('errors', $responseData);
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/SetPaymentMethodAsPayflowLinkTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/SetPaymentMethodAsPayflowLinkTest.php
index 7c23ec08af652..224159348ada4 100644
--- a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/SetPaymentMethodAsPayflowLinkTest.php
+++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/SetPaymentMethodAsPayflowLinkTest.php
@@ -168,6 +168,6 @@ public function testInvalidUrl(): void
$expectedExceptionMessage = "Invalid Url.";
$actualError = $responseData['errors'][0];
$this->assertEquals($expectedExceptionMessage, $actualError['message']);
- $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']);
+ $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['extensions']['category']);
}
}
From 5d16cb1a5897adf439209c3c9cde1254cf3be20b Mon Sep 17 00:00:00 2001
From: Lusine Papyan
Date: Thu, 22 Aug 2019 10:02:20 +0400
Subject: [PATCH 112/593] MAGETWO-98748: Incorrect behavior in the category
menu on the Storefront
- Updated automated test script
---
.../StorefrontCategoryHighlightedAndProductDisplayedTest.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml
index 7d09cb419f312..c036712398bf9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml
@@ -8,7 +8,7 @@
-
+
From 8628a3a1acf0f34ada315b2e80b11f0c6d17ad62 Mon Sep 17 00:00:00 2001
From: Pavel Bystritsky
Date: Thu, 22 Aug 2019 14:46:22 +0300
Subject: [PATCH 113/593] magento/magento2#24153: Static tests fix.
---
app/code/Magento/Config/Model/Config/Backend/File/Pdf.php | 6 ++++--
app/code/Magento/Config/Model/Config/Backend/Image/Pdf.php | 4 +++-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/Config/Model/Config/Backend/File/Pdf.php b/app/code/Magento/Config/Model/Config/Backend/File/Pdf.php
index f4acd6ed21dbb..9e3a5ec507a4b 100644
--- a/app/code/Magento/Config/Model/Config/Backend/File/Pdf.php
+++ b/app/code/Magento/Config/Model/Config/Backend/File/Pdf.php
@@ -4,15 +4,17 @@
* See COPYING.txt for license details.
*/
-namespace Magento\Config\Model\Config\Backend\Image;
+namespace Magento\Config\Model\Config\Backend\File;
/**
+ * System config PDF field backend model.
+ *
* @api
*/
class Pdf extends \Magento\Config\Model\Config\Backend\File
{
/**
- * @return string[]
+ * @inheritdoc
*/
protected function _getAllowedExtensions()
{
diff --git a/app/code/Magento/Config/Model/Config/Backend/Image/Pdf.php b/app/code/Magento/Config/Model/Config/Backend/Image/Pdf.php
index 6e1ff24a65ce3..bde392547a7f9 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Image/Pdf.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Image/Pdf.php
@@ -7,6 +7,8 @@
namespace Magento\Config\Model\Config\Backend\Image;
/**
+ * System config PDF field backend model.
+ *
* @api
* @deprecated The wrong file type extensions are returned.
* @see \Magento\Config\Model\Config\Backend\File\Pdf
@@ -14,7 +16,7 @@
class Pdf extends \Magento\Config\Model\Config\Backend\Image
{
/**
- * @return string[]
+ * @inheritdoc
*/
protected function _getAllowedExtensions()
{
From b2c8e78eead88e4b1cbd6d4135929f94811dbb21 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Thu, 22 Aug 2019 12:00:04 -0500
Subject: [PATCH 114/593] MC-19579: Multiple sort parameters does not work with
ElasticSearch
---
.../Magento/Framework/Search/Request/Builder.php | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/lib/internal/Magento/Framework/Search/Request/Builder.php b/lib/internal/Magento/Framework/Search/Request/Builder.php
index 74bc65010a934..0cf959b657c76 100644
--- a/lib/internal/Magento/Framework/Search/Request/Builder.php
+++ b/lib/internal/Magento/Framework/Search/Request/Builder.php
@@ -6,6 +6,7 @@
namespace Magento\Framework\Search\Request;
+use Magento\Framework\Api\SortOrder;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Magento\Framework\Search\RequestInterface;
@@ -173,7 +174,13 @@ public function create()
private function prepareSorts(array $sorts)
{
$sortData = [];
- foreach ($sorts as $sortField => $direction) {
+ foreach ($sorts as $sortField => $sort) {
+ if ($sort instanceof SortOrder) {
+ $sortField = $sort->getField();
+ $direction = $sort->getDirection();
+ } else {
+ $direction = $sort;
+ }
$sortData[] = [
'field' => $sortField,
'direction' => $direction,
From 8bba2b7f2d8c4edbdfd684c54174d5abfe64a55f Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Thu, 22 Aug 2019 12:19:19 -0500
Subject: [PATCH 115/593] MC-19572: Fix filtering by attribute of type
select/multiselect using filter input type "in"
---
.../Product/SearchCriteriaBuilder.php | 7 ++++---
.../Model/Config/FilterAttributeReader.php | 1 +
.../ProductEntityAttributesForAst.php | 16 +++-------------
.../CatalogGraphQl/etc/search_request.xml | 4 ++--
4 files changed, 10 insertions(+), 18 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
index 4872c627ded59..1e646b3c0b74b 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -85,13 +85,14 @@ public function build(array $args, bool $includeAggregation): SearchCriteriaInte
{
$searchCriteria = $this->builder->build('products', $args);
$this->updateRangeFilters($searchCriteria);
- $searchCriteria->setRequestName(
- $includeAggregation ? 'graphql_product_search_with_aggregation' : 'graphql_product_search'
- );
if ($includeAggregation) {
$this->preparePriceAggregation($searchCriteria);
+ $requestName = 'graphql_product_search_with_aggregation';
+ } else {
+ $requestName = 'graphql_product_search';
}
+ $searchCriteria->setRequestName($requestName);
if (!empty($args['search'])) {
$this->addFilter($searchCriteria, 'search_term', $args['search']);
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
index 9310dc593d40c..b2cb4dca28bde 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
@@ -97,6 +97,7 @@ private function getFilterType($attributeType): string
'price' => self::FILTER_RANGE_TYPE,
'date' => self::FILTER_RANGE_TYPE,
'select' => self::FILTER_EQUAL_TYPE,
+ 'multiselect' => self::FILTER_EQUAL_TYPE,
'boolean' => self::FILTER_EQUAL_TYPE,
'text' => self::FILTER_LIKE_TYPE,
'textarea' => self::FILTER_LIKE_TYPE,
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php
index 17409210808cc..973b8fbcd6b0f 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php
@@ -29,26 +29,16 @@ class ProductEntityAttributesForAst implements FieldEntityAttributesInterface
*/
private $additionalAttributes = ['min_price', 'max_price', 'category_id'];
- /**
- * Array to translate graphql field to internal entity attribute
- *
- * @var array
- */
- private $translatedAttributes = ['category_id' => 'category_ids'];
-
/**
* @param ConfigInterface $config
* @param string[] $additionalAttributes
- * @param array $translatedAttributes
*/
public function __construct(
ConfigInterface $config,
- array $additionalAttributes = [],
- array $translatedAttributes = []
+ array $additionalAttributes = []
) {
$this->config = $config;
$this->additionalAttributes = array_merge($this->additionalAttributes, $additionalAttributes);
- $this->translatedAttributes = array_merge($this->translatedAttributes, $translatedAttributes);
}
/**
@@ -74,7 +64,7 @@ public function getEntityAttributes() : array
foreach ($configElement->getFields() as $field) {
$fields[$field->getName()] = [
'type' => 'String',
- 'fieldName' => $this->translatedAttributes[$field->getName()] ?? $field->getName(),
+ 'fieldName' => $field->getName(),
];
}
}
@@ -82,7 +72,7 @@ public function getEntityAttributes() : array
foreach ($this->additionalAttributes as $attributeName) {
$fields[$attributeName] = [
'type' => 'String',
- 'fieldName' => $this->translatedAttributes[$attributeName] ?? $attributeName,
+ 'fieldName' => $attributeName,
];
}
diff --git a/app/code/Magento/CatalogGraphQl/etc/search_request.xml b/app/code/Magento/CatalogGraphQl/etc/search_request.xml
index 5e962d8467a4f..ab1eea9eb6fda 100644
--- a/app/code/Magento/CatalogGraphQl/etc/search_request.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/search_request.xml
@@ -34,7 +34,7 @@
-
+
@@ -80,7 +80,7 @@
-
+
From ce673592342445b582697c2817b23e809cddaefa Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 11:56:35 -0500
Subject: [PATCH 116/593] Removed static root tree, Getting it from store
manager
---
.../Model/Resolver/CategoryTree.php | 43 ++++++++++++++-----
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index 89d3805383e1a..65534572ac07a 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -27,7 +27,7 @@ class CategoryTree implements ResolverInterface
const CATEGORY_INTERFACE = 'CategoryInterface';
/**
- * @var \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree
+ * @var Products\DataProvider\CategoryTree
*/
private $categoryTree;
@@ -41,19 +41,32 @@ class CategoryTree implements ResolverInterface
*/
private $checkCategoryIsActive;
+ /**
+ * @var \Magento\Store\Model\StoreManagerInterface
+ */
+ private $storeManager;
+
+ /**
+ * @var int
+ */
+ private $rootCategoryId = null;
+
/**
* @param \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree $categoryTree
* @param ExtractDataFromCategoryTree $extractDataFromCategoryTree
* @param CheckCategoryIsActive $checkCategoryIsActive
+ * @param \Magento\Store\Model\StoreManagerInterface $storeManager
*/
public function __construct(
- \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree $categoryTree,
+ Products\DataProvider\CategoryTree $categoryTree,
ExtractDataFromCategoryTree $extractDataFromCategoryTree,
- CheckCategoryIsActive $checkCategoryIsActive
+ CheckCategoryIsActive $checkCategoryIsActive,
+ \Magento\Store\Model\StoreManagerInterface $storeManager
) {
$this->categoryTree = $categoryTree;
$this->extractDataFromCategoryTree = $extractDataFromCategoryTree;
$this->checkCategoryIsActive = $checkCategoryIsActive;
+ $this->storeManager = $storeManager;
}
/**
@@ -61,15 +74,11 @@ public function __construct(
*
* @param array $args
* @return int
- * @throws GraphQlInputException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
private function getCategoryId(array $args) : int
{
- if (!isset($args['id'])) {
- throw new GraphQlInputException(__('"id for category should be specified'));
- }
-
- return (int)$args['id'];
+ return isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId();
}
/**
@@ -82,7 +91,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
}
$rootCategoryId = $this->getCategoryId($args);
- if ($rootCategoryId !== Category::TREE_ROOT_ID) {
+ if ($rootCategoryId !== $this->getRootCategoryId() && $rootCategoryId > 0) {
$this->checkCategoryIsActive->execute($rootCategoryId);
}
$categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId);
@@ -94,4 +103,18 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
$result = $this->extractDataFromCategoryTree->execute($categoriesTree);
return current($result);
}
+
+ /**
+ * Get Root Category Id
+ * @return int
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function getRootCategoryId()
+ {
+ if ($this->rootCategoryId == null) {
+ $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
+ }
+
+ return $this->rootCategoryId;
+ }
}
From 1c2dd94a6ac0dd00020ec4793c92d9f663717d79 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 12:19:18 -0500
Subject: [PATCH 117/593] Adding root_category_id to storeconfig
---
.../Model/Resolver/CategoryRoot.php | 86 +++++++++++++++++++
.../CatalogGraphQl/etc/schema.graphqls | 1 +
2 files changed, 87 insertions(+)
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
new file mode 100644
index 0000000000000..a906a0ff14f70
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
@@ -0,0 +1,86 @@
+storeManager = $storeManager;
+ }
+
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+
+ return $this->getRootCategoryId();
+ }
+
+ /**
+ * Get Root Category Id
+ * @return int
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function getRootCategoryId()
+ {
+ if ($this->rootCategoryId == null) {
+ $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
+ }
+
+ return $this->rootCategoryId;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index ea56faf94408e..554ad4b45cf99 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -416,6 +416,7 @@ type StoreConfig @doc(description: "The type contains information about a store
grid_per_page : Int @doc(description: "Products per Page on Grid Default Value.")
list_per_page : Int @doc(description: "Products per Page on List Default Value.")
catalog_default_sort_by : String @doc(description: "Default Sort By.")
+ root_category_id: Int @doc(description: "The ID ot root category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryRoot")
}
type ProductVideo @doc(description: "Contains information about a product video.") implements MediaGalleryInterface {
From 3ad13cd43259fb27354e33c5a94badf1b2f052eb Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Fri, 23 Aug 2019 13:54:21 -0500
Subject: [PATCH 118/593] MC-19572: Fix filtering by attribute of type
select/multiselect using filter input type "in"
---
.../Model/Adapter/Mysql/Filter/Preprocessor.php | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
index c758e773f43c1..37d2dda886259 100644
--- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
+++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
@@ -165,7 +165,10 @@ private function processQueryWithField(FilterInterface $filter, $isNegation, $qu
$this->customerSession->getCustomerGroupId()
);
} elseif ($filter->getField() === 'category_ids') {
- return 'category_ids_index.category_id = ' . (int) $filter->getValue();
+ return $this->connection->quoteInto(
+ 'category_ids_index.category_id in (?)',
+ $filter->getValue()
+ );
} elseif ($attribute->isStatic()) {
$alias = $this->aliasResolver->getAlias($filter);
$resultQuery = str_replace(
From 61286c86b6026b07bc214c6ead5d96128396668e Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 14:04:25 -0500
Subject: [PATCH 119/593] Functional Testing
---
.../Magento/GraphQl/Catalog/CategoryTest.php | 74 +++++++++++++++++++
.../GraphQl/Catalog/StoreConfigTest.php | 4 +-
2 files changed, 77 insertions(+), 1 deletion(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
index df8e399ce6c61..fdd19cdfbb04a 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
@@ -113,6 +113,80 @@ public function testCategoriesTree()
);
}
+ /**
+ * @magentoApiDataFixture Magento/Catalog/_files/categories.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testRootCategoryTree()
+ {
+ $query = <<graphQlQuery($query);
+ $responseDataObject = new DataObject($response);
+ //Some sort of smoke testing
+ self::assertEquals(
+ 'Its a description of Test Category 1.2',
+ $responseDataObject->getData('category/children/0/children/1/description')
+ );
+ self::assertEquals(
+ 'default-category',
+ $responseDataObject->getData('category/url_key')
+ );
+ self::assertEquals(
+ [],
+ $responseDataObject->getData('category/children/0/available_sort_by')
+ );
+ self::assertEquals(
+ 'name',
+ $responseDataObject->getData('category/children/0/default_sort_by')
+ );
+ self::assertCount(
+ 7,
+ $responseDataObject->getData('category/children')
+ );
+ self::assertCount(
+ 2,
+ $responseDataObject->getData('category/children/0/children')
+ );
+ self::assertEquals(
+ 13,
+ $responseDataObject->getData('category/children/0/children/1/id')
+ );
+ }
+
/**
* @magentoApiDataFixture Magento/Catalog/_files/categories.php
*/
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php
index 21e608da4800a..7a30023c89f7e 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php
@@ -40,7 +40,8 @@ public function testGetStoreConfig()
list_per_page_values,
grid_per_page,
list_per_page,
- catalog_default_sort_by
+ catalog_default_sort_by,
+ root_category_id
}
}
QUERY;
@@ -56,5 +57,6 @@ public function testGetStoreConfig()
$this->assertEquals('8', $response['storeConfig']['list_per_page_values']);
$this->assertEquals(8, $response['storeConfig']['list_per_page']);
$this->assertEquals('asc', $response['storeConfig']['catalog_default_sort_by']);
+ $this->assertEquals(2, $response['storeConfig']['root_category_id']);
}
}
From 98c2cb71012a7a7eea8119a6b8e498c8d81fe1e6 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 14:39:18 -0500
Subject: [PATCH 120/593] Change to Different Class for reuse
---
.../Model/Category/GetRootCategoryId.php | 40 +++++++++++++++++++
.../Model/Resolver/CategoryTree.php | 39 +++++-------------
2 files changed, 49 insertions(+), 30 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
new file mode 100644
index 0000000000000..deb002afda2f7
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
@@ -0,0 +1,40 @@
+storeManager = $storeManager;
+ }
+
+ /**
+ * Get Root Category Id
+ * @return int
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function execute()
+ {
+ if ($this->rootCategoryId == null) {
+ $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
+ }
+
+ return $this->rootCategoryId;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index 65534572ac07a..516bb576431da 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -10,11 +10,10 @@
use Magento\Catalog\Model\Category;
use Magento\CatalogGraphQl\Model\Resolver\Category\CheckCategoryIsActive;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree;
-use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
+use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
/**
* Category tree field resolver, used for GraphQL request processing.
@@ -40,33 +39,27 @@ class CategoryTree implements ResolverInterface
* @var CheckCategoryIsActive
*/
private $checkCategoryIsActive;
-
- /**
- * @var \Magento\Store\Model\StoreManagerInterface
- */
- private $storeManager;
-
/**
- * @var int
+ * @var \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId
*/
- private $rootCategoryId = null;
+ private $getRootCategoryId;
/**
- * @param \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree $categoryTree
+ * @param Products\DataProvider\CategoryTree $categoryTree
* @param ExtractDataFromCategoryTree $extractDataFromCategoryTree
* @param CheckCategoryIsActive $checkCategoryIsActive
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+ * @param \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId $getRootCategoryId
*/
public function __construct(
Products\DataProvider\CategoryTree $categoryTree,
ExtractDataFromCategoryTree $extractDataFromCategoryTree,
CheckCategoryIsActive $checkCategoryIsActive,
- \Magento\Store\Model\StoreManagerInterface $storeManager
+ \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId $getRootCategoryId
) {
$this->categoryTree = $categoryTree;
$this->extractDataFromCategoryTree = $extractDataFromCategoryTree;
$this->checkCategoryIsActive = $checkCategoryIsActive;
- $this->storeManager = $storeManager;
+ $this->getRootCategoryId = $getRootCategoryId;
}
/**
@@ -78,7 +71,7 @@ public function __construct(
*/
private function getCategoryId(array $args) : int
{
- return isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId();
+ return isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId->execute();
}
/**
@@ -91,7 +84,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
}
$rootCategoryId = $this->getCategoryId($args);
- if ($rootCategoryId !== $this->getRootCategoryId() && $rootCategoryId > 0) {
+ if ($rootCategoryId !== $this->getRootCategoryId->execute() && $rootCategoryId > 0) {
$this->checkCategoryIsActive->execute($rootCategoryId);
}
$categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId);
@@ -103,18 +96,4 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
$result = $this->extractDataFromCategoryTree->execute($categoriesTree);
return current($result);
}
-
- /**
- * Get Root Category Id
- * @return int
- * @throws \Magento\Framework\Exception\NoSuchEntityException
- */
- public function getRootCategoryId()
- {
- if ($this->rootCategoryId == null) {
- $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
- }
-
- return $this->rootCategoryId;
- }
}
From 706f3c970a538705c8920fd347c576e162be0805 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 14:39:18 -0500
Subject: [PATCH 121/593] Change to Different Class for reuse
---
.../Model/Category/GetRootCategoryId.php | 40 +++++++++++++++++++
.../Model/Resolver/CategoryTree.php | 39 +++++-------------
2 files changed, 49 insertions(+), 30 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
new file mode 100644
index 0000000000000..deb002afda2f7
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
@@ -0,0 +1,40 @@
+storeManager = $storeManager;
+ }
+
+ /**
+ * Get Root Category Id
+ * @return int
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function execute()
+ {
+ if ($this->rootCategoryId == null) {
+ $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
+ }
+
+ return $this->rootCategoryId;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index 65534572ac07a..516bb576431da 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -10,11 +10,10 @@
use Magento\Catalog\Model\Category;
use Magento\CatalogGraphQl\Model\Resolver\Category\CheckCategoryIsActive;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree;
-use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
+use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
/**
* Category tree field resolver, used for GraphQL request processing.
@@ -40,33 +39,27 @@ class CategoryTree implements ResolverInterface
* @var CheckCategoryIsActive
*/
private $checkCategoryIsActive;
-
- /**
- * @var \Magento\Store\Model\StoreManagerInterface
- */
- private $storeManager;
-
/**
- * @var int
+ * @var \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId
*/
- private $rootCategoryId = null;
+ private $getRootCategoryId;
/**
- * @param \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree $categoryTree
+ * @param Products\DataProvider\CategoryTree $categoryTree
* @param ExtractDataFromCategoryTree $extractDataFromCategoryTree
* @param CheckCategoryIsActive $checkCategoryIsActive
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+ * @param \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId $getRootCategoryId
*/
public function __construct(
Products\DataProvider\CategoryTree $categoryTree,
ExtractDataFromCategoryTree $extractDataFromCategoryTree,
CheckCategoryIsActive $checkCategoryIsActive,
- \Magento\Store\Model\StoreManagerInterface $storeManager
+ \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId $getRootCategoryId
) {
$this->categoryTree = $categoryTree;
$this->extractDataFromCategoryTree = $extractDataFromCategoryTree;
$this->checkCategoryIsActive = $checkCategoryIsActive;
- $this->storeManager = $storeManager;
+ $this->getRootCategoryId = $getRootCategoryId;
}
/**
@@ -78,7 +71,7 @@ public function __construct(
*/
private function getCategoryId(array $args) : int
{
- return isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId();
+ return isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId->execute();
}
/**
@@ -91,7 +84,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
}
$rootCategoryId = $this->getCategoryId($args);
- if ($rootCategoryId !== $this->getRootCategoryId() && $rootCategoryId > 0) {
+ if ($rootCategoryId !== $this->getRootCategoryId->execute() && $rootCategoryId > 0) {
$this->checkCategoryIsActive->execute($rootCategoryId);
}
$categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId);
@@ -103,18 +96,4 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
$result = $this->extractDataFromCategoryTree->execute($categoriesTree);
return current($result);
}
-
- /**
- * Get Root Category Id
- * @return int
- * @throws \Magento\Framework\Exception\NoSuchEntityException
- */
- public function getRootCategoryId()
- {
- if ($this->rootCategoryId == null) {
- $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
- }
-
- return $this->rootCategoryId;
- }
}
From da0446ee60f2db9bf5d984df90901cd52a283e19 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 15:23:45 -0500
Subject: [PATCH 122/593] Removing unnecessary variables and adding the
categoryRoot model
---
.../Model/Resolver/CategoryRoot.php | 61 +++----------------
1 file changed, 8 insertions(+), 53 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
index a906a0ff14f70..1e0271c9fc788 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
@@ -7,14 +7,10 @@
namespace Magento\CatalogGraphQl\Model\Resolver;
-use Magento\Catalog\Model\Category;
-use Magento\CatalogGraphQl\Model\Resolver\Category\CheckCategoryIsActive;
-use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree;
-use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+use Magento\CatalogGraphQl\Model\Category\GetRootCategoryId;
use Magento\Framework\GraphQl\Config\Element\Field;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
-use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
+use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
/**
* Category tree field resolver, used for GraphQL request processing.
@@ -22,65 +18,24 @@
class CategoryRoot implements ResolverInterface
{
/**
- * Name of type in GraphQL
+ * @var GetRootCategoryId
*/
- const CATEGORY_INTERFACE = 'CategoryInterface';
+ private $getRootCategoryId;
/**
- * @var Products\DataProvider\CategoryTree
- */
- private $categoryTree;
-
- /**
- * @var ExtractDataFromCategoryTree
- */
- private $extractDataFromCategoryTree;
-
- /**
- * @var CheckCategoryIsActive
- */
- private $checkCategoryIsActive;
-
- /**
- * @var \Magento\Store\Model\StoreManagerInterface
- */
- private $storeManager;
-
- /**
- * @var int
- */
- private $rootCategoryId = null;
-
- /**
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+ * @param GetRootCategoryId $getRootCategoryId
*/
public function __construct(
- \Magento\Store\Model\StoreManagerInterface $storeManager
+ GetRootCategoryId $getRootCategoryId
) {
- $this->storeManager = $storeManager;
+ $this->getRootCategoryId = $getRootCategoryId;
}
-
/**
* @inheritdoc
*/
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
-
- return $this->getRootCategoryId();
- }
-
- /**
- * Get Root Category Id
- * @return int
- * @throws \Magento\Framework\Exception\NoSuchEntityException
- */
- public function getRootCategoryId()
- {
- if ($this->rootCategoryId == null) {
- $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
- }
-
- return $this->rootCategoryId;
+ return $this->getRootCategoryId->execute();
}
}
From e3ae7c2ed20dff8a915ec8dd7e9e9cbe1518b3bf Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 16:05:05 -0500
Subject: [PATCH 123/593] Comments
---
app/code/Magento/BundleSampleData | 1 +
.../Model/Category/GetRootCategoryId.php | 26 ++++++++++++++-----
.../Model/Resolver/CategoryRoot.php | 10 ++++++-
.../Model/Resolver/CategoryTree.php | 11 ++++++--
app/code/Magento/CatalogRuleSampleData | 1 +
app/code/Magento/CatalogSampleData | 1 +
app/code/Magento/CmsSampleData | 1 +
app/code/Magento/ConfigurableSampleData | 1 +
app/code/Magento/CustomerSampleData | 1 +
app/code/Magento/DownloadableSampleData | 1 +
app/code/Magento/GroupedProductSampleData | 1 +
app/code/Magento/MsrpSampleData | 1 +
app/code/Magento/OfflineShippingSampleData | 1 +
app/code/Magento/ProductLinksSampleData | 1 +
app/code/Magento/ReviewSampleData | 1 +
app/code/Magento/SalesRuleSampleData | 1 +
app/code/Magento/SalesSampleData | 1 +
app/code/Magento/SwatchesSampleData | 1 +
app/code/Magento/TaxSampleData | 1 +
app/code/Magento/ThemeSampleData | 1 +
app/code/Magento/WidgetSampleData | 1 +
app/code/Magento/WishlistSampleData | 1 +
22 files changed, 56 insertions(+), 10 deletions(-)
create mode 120000 app/code/Magento/BundleSampleData
create mode 120000 app/code/Magento/CatalogRuleSampleData
create mode 120000 app/code/Magento/CatalogSampleData
create mode 120000 app/code/Magento/CmsSampleData
create mode 120000 app/code/Magento/ConfigurableSampleData
create mode 120000 app/code/Magento/CustomerSampleData
create mode 120000 app/code/Magento/DownloadableSampleData
create mode 120000 app/code/Magento/GroupedProductSampleData
create mode 120000 app/code/Magento/MsrpSampleData
create mode 120000 app/code/Magento/OfflineShippingSampleData
create mode 120000 app/code/Magento/ProductLinksSampleData
create mode 120000 app/code/Magento/ReviewSampleData
create mode 120000 app/code/Magento/SalesRuleSampleData
create mode 120000 app/code/Magento/SalesSampleData
create mode 120000 app/code/Magento/SwatchesSampleData
create mode 120000 app/code/Magento/TaxSampleData
create mode 120000 app/code/Magento/ThemeSampleData
create mode 120000 app/code/Magento/WidgetSampleData
create mode 120000 app/code/Magento/WishlistSampleData
diff --git a/app/code/Magento/BundleSampleData b/app/code/Magento/BundleSampleData
new file mode 120000
index 0000000000000..7e35e78f27e91
--- /dev/null
+++ b/app/code/Magento/BundleSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/BundleSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
index deb002afda2f7..c8b80622433cb 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
@@ -1,25 +1,33 @@
storeManager = $storeManager;
}
@@ -27,12 +35,16 @@ public function __construct(
/**
* Get Root Category Id
* @return int
- * @throws \Magento\Framework\Exception\NoSuchEntityException
+ * @throws LocalizedException
*/
public function execute()
{
if ($this->rootCategoryId == null) {
- $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
+ try {
+ $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
+ } catch (NoSuchEntityException $noSuchEntityException) {
+ throw new LocalizedException(__("Store does not exist."));
+ }
}
return $this->rootCategoryId;
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
index 1e0271c9fc788..9720a6d6244d5 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
@@ -8,7 +8,9 @@
namespace Magento\CatalogGraphQl\Model\Resolver;
use Magento\CatalogGraphQl\Model\Category\GetRootCategoryId;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
+use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
@@ -36,6 +38,12 @@ public function __construct(
*/
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
- return $this->getRootCategoryId->execute();
+ $rootCategoryId = 0;
+ try {
+ $rootCategoryId = $this->getRootCategoryId->execute();
+ } catch (LocalizedException $exception) {
+ throw new GraphQlNoSuchEntityException(__('Store doesn\'t exist'));
+ }
+ return $rootCategoryId;
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index 516bb576431da..f99e80eea493c 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -10,6 +10,7 @@
use Magento\Catalog\Model\Category;
use Magento\CatalogGraphQl\Model\Resolver\Category\CheckCategoryIsActive;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
@@ -67,11 +68,17 @@ public function __construct(
*
* @param array $args
* @return int
- * @throws \Magento\Framework\Exception\NoSuchEntityException
+ * @throws GraphQlNoSuchEntityException
*/
private function getCategoryId(array $args) : int
{
- return isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId->execute();
+ $rootCategoryId = 0;
+ try {
+ $rootCategoryId = isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId->execute();
+ } catch (LocalizedException $exception) {
+ throw new GraphQlNoSuchEntityException(__('Store doesn\'t exist'));
+ }
+ return $rootCategoryId;
}
/**
diff --git a/app/code/Magento/CatalogRuleSampleData b/app/code/Magento/CatalogRuleSampleData
new file mode 120000
index 0000000000000..688c91d844c6a
--- /dev/null
+++ b/app/code/Magento/CatalogRuleSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/CatalogRuleSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSampleData b/app/code/Magento/CatalogSampleData
new file mode 120000
index 0000000000000..f8e606a9034ec
--- /dev/null
+++ b/app/code/Magento/CatalogSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/CatalogSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CmsSampleData b/app/code/Magento/CmsSampleData
new file mode 120000
index 0000000000000..ca842cd07d207
--- /dev/null
+++ b/app/code/Magento/CmsSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/CmsSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ConfigurableSampleData b/app/code/Magento/ConfigurableSampleData
new file mode 120000
index 0000000000000..7ca081235faeb
--- /dev/null
+++ b/app/code/Magento/ConfigurableSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/ConfigurableSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CustomerSampleData b/app/code/Magento/CustomerSampleData
new file mode 120000
index 0000000000000..9c5c51b432d54
--- /dev/null
+++ b/app/code/Magento/CustomerSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/CustomerSampleData
\ No newline at end of file
diff --git a/app/code/Magento/DownloadableSampleData b/app/code/Magento/DownloadableSampleData
new file mode 120000
index 0000000000000..c13ec39b755be
--- /dev/null
+++ b/app/code/Magento/DownloadableSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/DownloadableSampleData
\ No newline at end of file
diff --git a/app/code/Magento/GroupedProductSampleData b/app/code/Magento/GroupedProductSampleData
new file mode 120000
index 0000000000000..d2d359906cd7f
--- /dev/null
+++ b/app/code/Magento/GroupedProductSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/GroupedProductSampleData
\ No newline at end of file
diff --git a/app/code/Magento/MsrpSampleData b/app/code/Magento/MsrpSampleData
new file mode 120000
index 0000000000000..bb6290ac49197
--- /dev/null
+++ b/app/code/Magento/MsrpSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/MsrpSampleData
\ No newline at end of file
diff --git a/app/code/Magento/OfflineShippingSampleData b/app/code/Magento/OfflineShippingSampleData
new file mode 120000
index 0000000000000..9156b62ece0da
--- /dev/null
+++ b/app/code/Magento/OfflineShippingSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/OfflineShippingSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ProductLinksSampleData b/app/code/Magento/ProductLinksSampleData
new file mode 120000
index 0000000000000..e4a134517d611
--- /dev/null
+++ b/app/code/Magento/ProductLinksSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/ProductLinksSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ReviewSampleData b/app/code/Magento/ReviewSampleData
new file mode 120000
index 0000000000000..cbf588c39b8d7
--- /dev/null
+++ b/app/code/Magento/ReviewSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/ReviewSampleData
\ No newline at end of file
diff --git a/app/code/Magento/SalesRuleSampleData b/app/code/Magento/SalesRuleSampleData
new file mode 120000
index 0000000000000..ac7ac93163887
--- /dev/null
+++ b/app/code/Magento/SalesRuleSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/SalesRuleSampleData
\ No newline at end of file
diff --git a/app/code/Magento/SalesSampleData b/app/code/Magento/SalesSampleData
new file mode 120000
index 0000000000000..452e488f7c099
--- /dev/null
+++ b/app/code/Magento/SalesSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/SalesSampleData
\ No newline at end of file
diff --git a/app/code/Magento/SwatchesSampleData b/app/code/Magento/SwatchesSampleData
new file mode 120000
index 0000000000000..785ec021e4a26
--- /dev/null
+++ b/app/code/Magento/SwatchesSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/SwatchesSampleData
\ No newline at end of file
diff --git a/app/code/Magento/TaxSampleData b/app/code/Magento/TaxSampleData
new file mode 120000
index 0000000000000..ab74032bbf626
--- /dev/null
+++ b/app/code/Magento/TaxSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/TaxSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ThemeSampleData b/app/code/Magento/ThemeSampleData
new file mode 120000
index 0000000000000..3d79314969a0f
--- /dev/null
+++ b/app/code/Magento/ThemeSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/ThemeSampleData
\ No newline at end of file
diff --git a/app/code/Magento/WidgetSampleData b/app/code/Magento/WidgetSampleData
new file mode 120000
index 0000000000000..19d0ceb6d688e
--- /dev/null
+++ b/app/code/Magento/WidgetSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/WidgetSampleData
\ No newline at end of file
diff --git a/app/code/Magento/WishlistSampleData b/app/code/Magento/WishlistSampleData
new file mode 120000
index 0000000000000..c2e787fc3e452
--- /dev/null
+++ b/app/code/Magento/WishlistSampleData
@@ -0,0 +1 @@
+/var/www/html/magento2-sample-data/app/code/Magento/WishlistSampleData
\ No newline at end of file
From aa45743f0bc17ab763e4ce4aa7000db374ac39ef Mon Sep 17 00:00:00 2001
From: Rus0
Date: Fri, 23 Aug 2019 16:06:56 -0500
Subject: [PATCH 124/593] amend
---
app/code/Magento/BundleSampleData | 1 -
app/code/Magento/CatalogRuleSampleData | 1 -
app/code/Magento/CatalogSampleData | 1 -
app/code/Magento/CmsSampleData | 1 -
app/code/Magento/ConfigurableSampleData | 1 -
app/code/Magento/CustomerSampleData | 1 -
app/code/Magento/DownloadableSampleData | 1 -
app/code/Magento/GroupedProductSampleData | 1 -
app/code/Magento/MsrpSampleData | 1 -
app/code/Magento/OfflineShippingSampleData | 1 -
app/code/Magento/ProductLinksSampleData | 1 -
app/code/Magento/ReviewSampleData | 1 -
app/code/Magento/SalesRuleSampleData | 1 -
app/code/Magento/SalesSampleData | 1 -
app/code/Magento/SwatchesSampleData | 1 -
app/code/Magento/TaxSampleData | 1 -
app/code/Magento/ThemeSampleData | 1 -
app/code/Magento/WidgetSampleData | 1 -
app/code/Magento/WishlistSampleData | 1 -
19 files changed, 19 deletions(-)
delete mode 120000 app/code/Magento/BundleSampleData
delete mode 120000 app/code/Magento/CatalogRuleSampleData
delete mode 120000 app/code/Magento/CatalogSampleData
delete mode 120000 app/code/Magento/CmsSampleData
delete mode 120000 app/code/Magento/ConfigurableSampleData
delete mode 120000 app/code/Magento/CustomerSampleData
delete mode 120000 app/code/Magento/DownloadableSampleData
delete mode 120000 app/code/Magento/GroupedProductSampleData
delete mode 120000 app/code/Magento/MsrpSampleData
delete mode 120000 app/code/Magento/OfflineShippingSampleData
delete mode 120000 app/code/Magento/ProductLinksSampleData
delete mode 120000 app/code/Magento/ReviewSampleData
delete mode 120000 app/code/Magento/SalesRuleSampleData
delete mode 120000 app/code/Magento/SalesSampleData
delete mode 120000 app/code/Magento/SwatchesSampleData
delete mode 120000 app/code/Magento/TaxSampleData
delete mode 120000 app/code/Magento/ThemeSampleData
delete mode 120000 app/code/Magento/WidgetSampleData
delete mode 120000 app/code/Magento/WishlistSampleData
diff --git a/app/code/Magento/BundleSampleData b/app/code/Magento/BundleSampleData
deleted file mode 120000
index 7e35e78f27e91..0000000000000
--- a/app/code/Magento/BundleSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/BundleSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CatalogRuleSampleData b/app/code/Magento/CatalogRuleSampleData
deleted file mode 120000
index 688c91d844c6a..0000000000000
--- a/app/code/Magento/CatalogRuleSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/CatalogRuleSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSampleData b/app/code/Magento/CatalogSampleData
deleted file mode 120000
index f8e606a9034ec..0000000000000
--- a/app/code/Magento/CatalogSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/CatalogSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CmsSampleData b/app/code/Magento/CmsSampleData
deleted file mode 120000
index ca842cd07d207..0000000000000
--- a/app/code/Magento/CmsSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/CmsSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ConfigurableSampleData b/app/code/Magento/ConfigurableSampleData
deleted file mode 120000
index 7ca081235faeb..0000000000000
--- a/app/code/Magento/ConfigurableSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/ConfigurableSampleData
\ No newline at end of file
diff --git a/app/code/Magento/CustomerSampleData b/app/code/Magento/CustomerSampleData
deleted file mode 120000
index 9c5c51b432d54..0000000000000
--- a/app/code/Magento/CustomerSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/CustomerSampleData
\ No newline at end of file
diff --git a/app/code/Magento/DownloadableSampleData b/app/code/Magento/DownloadableSampleData
deleted file mode 120000
index c13ec39b755be..0000000000000
--- a/app/code/Magento/DownloadableSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/DownloadableSampleData
\ No newline at end of file
diff --git a/app/code/Magento/GroupedProductSampleData b/app/code/Magento/GroupedProductSampleData
deleted file mode 120000
index d2d359906cd7f..0000000000000
--- a/app/code/Magento/GroupedProductSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/GroupedProductSampleData
\ No newline at end of file
diff --git a/app/code/Magento/MsrpSampleData b/app/code/Magento/MsrpSampleData
deleted file mode 120000
index bb6290ac49197..0000000000000
--- a/app/code/Magento/MsrpSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/MsrpSampleData
\ No newline at end of file
diff --git a/app/code/Magento/OfflineShippingSampleData b/app/code/Magento/OfflineShippingSampleData
deleted file mode 120000
index 9156b62ece0da..0000000000000
--- a/app/code/Magento/OfflineShippingSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/OfflineShippingSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ProductLinksSampleData b/app/code/Magento/ProductLinksSampleData
deleted file mode 120000
index e4a134517d611..0000000000000
--- a/app/code/Magento/ProductLinksSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/ProductLinksSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ReviewSampleData b/app/code/Magento/ReviewSampleData
deleted file mode 120000
index cbf588c39b8d7..0000000000000
--- a/app/code/Magento/ReviewSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/ReviewSampleData
\ No newline at end of file
diff --git a/app/code/Magento/SalesRuleSampleData b/app/code/Magento/SalesRuleSampleData
deleted file mode 120000
index ac7ac93163887..0000000000000
--- a/app/code/Magento/SalesRuleSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/SalesRuleSampleData
\ No newline at end of file
diff --git a/app/code/Magento/SalesSampleData b/app/code/Magento/SalesSampleData
deleted file mode 120000
index 452e488f7c099..0000000000000
--- a/app/code/Magento/SalesSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/SalesSampleData
\ No newline at end of file
diff --git a/app/code/Magento/SwatchesSampleData b/app/code/Magento/SwatchesSampleData
deleted file mode 120000
index 785ec021e4a26..0000000000000
--- a/app/code/Magento/SwatchesSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/SwatchesSampleData
\ No newline at end of file
diff --git a/app/code/Magento/TaxSampleData b/app/code/Magento/TaxSampleData
deleted file mode 120000
index ab74032bbf626..0000000000000
--- a/app/code/Magento/TaxSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/TaxSampleData
\ No newline at end of file
diff --git a/app/code/Magento/ThemeSampleData b/app/code/Magento/ThemeSampleData
deleted file mode 120000
index 3d79314969a0f..0000000000000
--- a/app/code/Magento/ThemeSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/ThemeSampleData
\ No newline at end of file
diff --git a/app/code/Magento/WidgetSampleData b/app/code/Magento/WidgetSampleData
deleted file mode 120000
index 19d0ceb6d688e..0000000000000
--- a/app/code/Magento/WidgetSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/WidgetSampleData
\ No newline at end of file
diff --git a/app/code/Magento/WishlistSampleData b/app/code/Magento/WishlistSampleData
deleted file mode 120000
index c2e787fc3e452..0000000000000
--- a/app/code/Magento/WishlistSampleData
+++ /dev/null
@@ -1 +0,0 @@
-/var/www/html/magento2-sample-data/app/code/Magento/WishlistSampleData
\ No newline at end of file
From dbc514c634cf4d447291e2cf7cf2f83d112e9480 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torben=20Ho=CC=88hn?=
Date: Fri, 23 Aug 2019 23:08:24 +0200
Subject: [PATCH 125/593] add config for disabling swatch tooltips
---
.../Block/Product/Renderer/Configurable.php | 82 +++++++++++++------
.../Magento/Swatches/etc/adminhtml/system.xml | 4 +
app/code/Magento/Swatches/etc/config.xml | 1 +
.../templates/product/listing/renderer.phtml | 5 +-
.../templates/product/view/renderer.phtml | 3 +-
.../view/frontend/web/js/swatch-renderer.js | 13 +--
6 files changed, 74 insertions(+), 34 deletions(-)
diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
index 0848f566f67bb..35bf199681720 100644
--- a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
+++ b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types = 1);
namespace Magento\Swatches\Block\Product\Renderer;
use Magento\Catalog\Block\Product\Context;
@@ -57,6 +58,16 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
*/
const SWATCH_THUMBNAIL_NAME = 'swatchThumb';
+ /**
+ * Config path which contains number of swatches per product
+ */
+ const XML_PATH_SWATCHES_PER_PRODUCT = 'catalog/frontend/swatches_per_product';
+
+ /**
+ * Config path if swatch tooltips are enabled
+ */
+ const XML_PATH_SHOW_SWATCH_TOOLTIP = 'catalog/frontend/show_swatch_tooltip';
+
/**
* @var Product
*/
@@ -93,19 +104,19 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
- * @param Context $context
- * @param ArrayUtils $arrayUtils
- * @param EncoderInterface $jsonEncoder
- * @param Data $helper
- * @param CatalogProduct $catalogProduct
- * @param CurrentCustomer $currentCustomer
- * @param PriceCurrencyInterface $priceCurrency
- * @param ConfigurableAttributeData $configurableAttributeData
- * @param SwatchData $swatchHelper
- * @param Media $swatchMediaHelper
- * @param array $data
+ * @param Context $context
+ * @param ArrayUtils $arrayUtils
+ * @param EncoderInterface $jsonEncoder
+ * @param Data $helper
+ * @param CatalogProduct $catalogProduct
+ * @param CurrentCustomer $currentCustomer
+ * @param PriceCurrencyInterface $priceCurrency
+ * @param ConfigurableAttributeData $configurableAttributeData
+ * @param SwatchData $swatchHelper
+ * @param Media $swatchMediaHelper
+ * @param array $data
* @param SwatchAttributesProvider|null $swatchAttributesProvider
- * @param UrlBuilder|null $imageUrlBuilder
+ * @param UrlBuilder|null $imageUrlBuilder
*/
public function __construct(
Context $context,
@@ -172,7 +183,6 @@ public function getJsonSwatchConfig()
$attributesData = $this->getSwatchAttributesData();
$allOptionIds = $this->getConfigurableOptionsIds($attributesData);
$swatchesData = $this->swatchHelper->getSwatchesByOptionsId($allOptionIds);
-
$config = [];
foreach ($attributesData as $attributeId => $attributeDataArray) {
if (isset($attributeDataArray['options'])) {
@@ -200,7 +210,20 @@ public function getJsonSwatchConfig()
public function getNumberSwatchesPerProduct()
{
return $this->_scopeConfig->getValue(
- 'catalog/frontend/swatches_per_product',
+ self::XML_PATH_SWATCHES_PER_PRODUCT,
+ ScopeInterface::SCOPE_STORE
+ );
+ }
+
+ /**
+ * Get config if swatch tooltips should be rendered.
+ *
+ * @return string
+ */
+ public function getShowSwatchTooltip()
+ {
+ return $this->_scopeConfig->getValue(
+ self::XML_PATH_SHOW_SWATCH_TOOLTIP,
ScopeInterface::SCOPE_STORE
);
}
@@ -209,11 +232,13 @@ public function getNumberSwatchesPerProduct()
* Set product to block
*
* @param Product $product
+ *
* @return $this
*/
public function setProduct(Product $product)
{
$this->product = $product;
+
return $this;
}
@@ -244,10 +269,10 @@ protected function getSwatchAttributesData()
/**
* Init isProductHasSwatchAttribute.
*
+ * @return void
* @deprecated 100.1.5 Method isProductHasSwatchAttribute() is used instead of this.
*
* @codeCoverageIgnore
- * @return void
*/
protected function initIsProductHasSwatchAttribute()
{
@@ -263,6 +288,7 @@ protected function initIsProductHasSwatchAttribute()
protected function isProductHasSwatchAttribute()
{
$swatchAttributes = $this->swatchAttributesProvider->provide($this->getProduct());
+
return count($swatchAttributes) > 0;
}
@@ -272,6 +298,7 @@ protected function isProductHasSwatchAttribute()
* @param array $options
* @param array $swatchesCollectionArray
* @param array $attributeDataArray
+ *
* @return array
*/
protected function addSwatchDataForAttribute(
@@ -294,9 +321,10 @@ protected function addSwatchDataForAttribute(
/**
* Add media from variation
*
- * @param array $swatch
+ * @param array $swatch
* @param integer $optionId
- * @param array $attributeDataArray
+ * @param array $attributeDataArray
+ *
* @return array
*/
protected function addAdditionalMediaData(array $swatch, $optionId, array $attributeDataArray)
@@ -305,11 +333,12 @@ protected function addAdditionalMediaData(array $swatch, $optionId, array $attri
&& $attributeDataArray['use_product_image_for_swatch']
) {
$variationMedia = $this->getVariationMedia($attributeDataArray['attribute_code'], $optionId);
- if (! empty($variationMedia)) {
+ if (!empty($variationMedia)) {
$swatch['type'] = Swatch::SWATCH_TYPE_VISUAL_IMAGE;
$swatch = array_merge($swatch, $variationMedia);
}
}
+
return $swatch;
}
@@ -317,12 +346,12 @@ protected function addAdditionalMediaData(array $swatch, $optionId, array $attri
* Retrieve Swatch data for config
*
* @param array $swatchDataArray
+ *
* @return array
*/
protected function extractNecessarySwatchData(array $swatchDataArray)
{
$result['type'] = $swatchDataArray['type'];
-
if ($result['type'] == Swatch::SWATCH_TYPE_VISUAL_IMAGE && !empty($swatchDataArray['value'])) {
$result['value'] = $this->swatchMediaHelper->getSwatchAttributeImage(
Swatch::SWATCH_IMAGE_NAME,
@@ -342,8 +371,9 @@ protected function extractNecessarySwatchData(array $swatchDataArray)
/**
* Generate Product Media array
*
- * @param string $attributeCode
+ * @param string $attributeCode
* @param integer $optionId
+ *
* @return array
*/
protected function getVariationMedia($attributeCode, $optionId)
@@ -352,14 +382,12 @@ protected function getVariationMedia($attributeCode, $optionId)
$this->getProduct(),
[$attributeCode => $optionId]
);
-
if (!$variationProduct) {
$variationProduct = $this->swatchHelper->loadFirstVariationWithImage(
$this->getProduct(),
[$attributeCode => $optionId]
);
}
-
$variationMediaArray = [];
if ($variationProduct) {
$variationMediaArray = [
@@ -375,7 +403,8 @@ protected function getVariationMedia($attributeCode, $optionId)
* Get swatch product image.
*
* @param Product $childProduct
- * @param string $imageType
+ * @param string $imageType
+ *
* @return string
*/
protected function getSwatchProductImage(Product $childProduct, $imageType)
@@ -387,7 +416,6 @@ protected function getSwatchProductImage(Product $childProduct, $imageType)
$swatchImageId = $imageType == Swatch::SWATCH_IMAGE_NAME ? 'swatch_image_base' : 'swatch_thumb_base';
$imageAttributes = ['type' => 'image'];
}
-
if (!empty($swatchImageId) && !empty($imageAttributes['type'])) {
return $this->imageUrlBuilder->getUrl($childProduct->getData($imageAttributes['type']), $swatchImageId);
}
@@ -397,7 +425,8 @@ protected function getSwatchProductImage(Product $childProduct, $imageType)
* Check if product have image.
*
* @param Product $product
- * @param string $imageType
+ * @param string $imageType
+ *
* @return bool
*/
protected function isProductHasImage(Product $product, $imageType)
@@ -409,6 +438,7 @@ protected function isProductHasImage(Product $product, $imageType)
* Get configurable options ids.
*
* @param array $attributeData
+ *
* @return array
* @since 100.0.3
*/
@@ -425,6 +455,7 @@ protected function getConfigurableOptionsIds(array $attributeData)
}
}
}
+
return array_keys($ids);
}
@@ -509,7 +540,6 @@ public function getJsonSwatchSizeConfig()
{
$imageConfig = $this->swatchMediaHelper->getImageConfig();
$sizeConfig = [];
-
$sizeConfig[self::SWATCH_IMAGE_NAME]['width'] = $imageConfig[Swatch::SWATCH_IMAGE_NAME]['width'];
$sizeConfig[self::SWATCH_IMAGE_NAME]['height'] = $imageConfig[Swatch::SWATCH_IMAGE_NAME]['height'];
$sizeConfig[self::SWATCH_THUMBNAIL_NAME]['height'] = $imageConfig[Swatch::SWATCH_THUMBNAIL_NAME]['height'];
diff --git a/app/code/Magento/Swatches/etc/adminhtml/system.xml b/app/code/Magento/Swatches/etc/adminhtml/system.xml
index 2cf40ae83cc3b..6fbf110fadcd3 100644
--- a/app/code/Magento/Swatches/etc/adminhtml/system.xml
+++ b/app/code/Magento/Swatches/etc/adminhtml/system.xml
@@ -17,6 +17,10 @@
Show Swatches in Product List
Magento\Config\Model\Config\Source\Yesno
+
+ Show Swatch Tooltip
+ Magento\Config\Model\Config\Source\Yesno
+
diff --git a/app/code/Magento/Swatches/etc/config.xml b/app/code/Magento/Swatches/etc/config.xml
index 65b36558c2796..4140acc4974d6 100644
--- a/app/code/Magento/Swatches/etc/config.xml
+++ b/app/code/Magento/Swatches/etc/config.xml
@@ -11,6 +11,7 @@
16
1
+ 1
diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml
index 777277a15d8cd..3c2f15dea0ab4 100644
--- a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml
+++ b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml
@@ -22,7 +22,8 @@ $productId = $block->getProduct()->getId();
"jsonConfig": = /* @noEscape */ $block->getJsonConfig() ?>,
"jsonSwatchConfig": = /* @noEscape */ $block->getJsonSwatchConfig() ?>,
"mediaCallback": "= $block->escapeJs($block->escapeUrl($block->getMediaCallback())) ?>",
- "jsonSwatchImageSizeConfig": = /* @noEscape */ $block->getJsonSwatchSizeConfig() ?>
+ "jsonSwatchImageSizeConfig": = /* @noEscape */ $block->getJsonSwatchSizeConfig() ?>,
+ "showTooltip": = $block->escapeJs($block->getShowSwatchTooltip()) ?>
}
}
}
@@ -39,4 +40,4 @@ $productId = $block->getProduct()->getId();
}
}
}
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml
index c85a6908413b5..46666e880bb03 100644
--- a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml
+++ b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml
@@ -15,7 +15,8 @@
"jsonSwatchConfig": = /* @noEscape */ $swatchOptions = $block->getJsonSwatchConfig() ?>,
"mediaCallback": "= $block->escapeJs($block->escapeUrl($block->getMediaCallback())) ?>",
"gallerySwitchStrategy": "= $block->escapeJs($block->getVar('gallery_switch_strategy', 'Magento_ConfigurableProduct')) ?: 'replace'; ?>",
- "jsonSwatchImageSizeConfig": = /* @noEscape */ $block->getJsonSwatchSizeConfig() ?>
+ "jsonSwatchImageSizeConfig": = /* @noEscape */ $block->getJsonSwatchSizeConfig() ?>,
+ "showTooltip": = $block->escapeJs($block->getShowSwatchTooltip()) ?>
}
},
"*" : {
diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
index d6302cff83bff..aa7d0a08ce697 100644
--- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
+++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
@@ -385,7 +385,8 @@ define([
var $widget = this,
container = this.element,
classes = this.options.classes,
- chooseText = this.options.jsonConfig.chooseText;
+ chooseText = this.options.jsonConfig.chooseText,
+ showTooltip = this.options.showTooltip;
$widget.optionsMap = {};
@@ -452,10 +453,12 @@ define([
});
});
- // Connect Tooltip
- container
- .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]')
- .SwatchRendererTooltip();
+ if (showTooltip === 1) {
+ // Connect Tooltip
+ container
+ .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]')
+ .SwatchRendererTooltip();
+ }
// Hide all elements below more button
$('.' + classes.moreButton).nextAll().hide();
From 9b2297afa71cbd38456fb8bc206e02a14d456f59 Mon Sep 17 00:00:00 2001
From: Patrick McLain
Date: Fri, 23 Aug 2019 21:28:15 -0400
Subject: [PATCH 126/593] update variable name
---
.../Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php
index 04b9354855ea9..dfb8b748469b8 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/ScalarTypes.php
@@ -20,8 +20,8 @@ class ScalarTypes
*/
public function isScalarType(string $typeName) : bool
{
- $internalTypes = \GraphQL\Type\Definition\Type::getStandardTypes();
- return isset($internalTypes[$typeName]) ? true : false;
+ $standardTypes = \GraphQL\Type\Definition\Type::getStandardTypes();
+ return isset($standardTypes[$typeName]) ? true : false;
}
/**
@@ -33,9 +33,9 @@ public function isScalarType(string $typeName) : bool
*/
public function getScalarTypeInstance(string $typeName) : \GraphQL\Type\Definition\Type
{
- $internalTypes = \GraphQL\Type\Definition\Type::getStandardTypes();
+ $standardTypes = \GraphQL\Type\Definition\Type::getStandardTypes();
if ($this->isScalarType($typeName)) {
- return $internalTypes[$typeName];
+ return $standardTypes[$typeName];
} else {
throw new \LogicException(sprintf('Scalar type %s doesn\'t exist', $typeName));
}
From 3e6104de227981da9631986aacb60a249fa063bc Mon Sep 17 00:00:00 2001
From: Raul E Watson
Date: Sun, 25 Aug 2019 07:04:18 +0100
Subject: [PATCH 127/593] Update Magento_Analytics ReadMe
---
app/code/Magento/Analytics/README.md | 42 +++++++++++++++++-----------
1 file changed, 26 insertions(+), 16 deletions(-)
diff --git a/app/code/Magento/Analytics/README.md b/app/code/Magento/Analytics/README.md
index aa424182e2ebd..cb78a27dae8e0 100644
--- a/app/code/Magento/Analytics/README.md
+++ b/app/code/Magento/Analytics/README.md
@@ -1,18 +1,28 @@
-# Magento_Analytics Module
+# Magento_Analytics module
The Magento_Analytics module integrates your Magento instance with the [Magento Business Intelligence (MBI)](https://magento.com/products/business-intelligence) to use [Advanced Reporting](https://devdocs.magento.com/guides/v2.3/advanced-reporting/modules.html) functionality.
The module implements the following functionality:
-* enabling subscription to the MBI and automatic re-subscription
-* changing the base URL with the same MBI account remained
-* declaring the configuration schemas for report data collection
-* collecting the Magento instance data as reports for the MBI
-* introducing API that provides the collected data
-* extending Magento configuration with the module parameters:
- * subscription status (enabled/disabled)
- * industry (a business area in which the instance website works)
- * time of data collection (time of the day when the module collects data)
+- Enabling subscription to the MBI and automatic re-subscription
+- Changing the base URL with the same MBI account remained
+- Declaring the configuration schemas for report data collection
+- Collecting the Magento instance data as reports for the MBI
+- Introducing API that provides the collected data
+- Extending Magento configuration with the module parameters:
+ - Subscription status (enabled/disabled)
+ - Industry (a business area in which the instance website works)
+ - Time of data collection (time of the day when the module collects data)
+
+## Installation details
+
+Before disabling or uninstalling this module, note that the following modules depends on this module:
+- Magento_CatalogAnalytics
+- Magento_CustomerAnalytics
+- Magento_QuoteAnalytics
+- Magento_ReviewAnalytics
+- Magento_SalesAnalytics
+- Magento_WishlistAnalytics
## Structure
@@ -29,12 +39,12 @@ The subscription to the MBI service is enabled during the installation process o
Configuration settings for the Analytics module can be modified in the Admin Panel on the Stores > Configuration page under the General > Advanced Reporting tab.
The following options can be adjusted:
-* Advanced Reporting Service (Enabled/Disabled)
- * Alters the status of the Advanced Reporting subscription
-* Time of day to send data (Hour/Minute/Second in the store's time zone)
- * Defines when the data collection process for the Advanced Reporting service occurs
-* Industry
- * Defines the industry of the store in order to create a personalized Advanced Reporting experience
+- Advanced Reporting Service (Enabled/Disabled)
+ - Alters the status of the Advanced Reporting subscription
+- Time of day to send data (Hour/Minute/Second in the store's time zone)
+ - Defines when the data collection process for the Advanced Reporting service occurs
+- Industry
+ - Defines the industry of the store in order to create a personalized Advanced Reporting experience
## Extensibility
From 8031a9c589799ef15d99b600e06797d51086244a Mon Sep 17 00:00:00 2001
From: Raul E Watson
Date: Sun, 25 Aug 2019 08:09:49 +0100
Subject: [PATCH 128/593] Update Magento_Authorizenet ReadMe
---
app/code/Magento/Authorizenet/README.md | 43 ++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/Authorizenet/README.md b/app/code/Magento/Authorizenet/README.md
index 380161d8b264e..ef022a6e236a2 100644
--- a/app/code/Magento/Authorizenet/README.md
+++ b/app/code/Magento/Authorizenet/README.md
@@ -1 +1,42 @@
-The Magento_Authorizenet module implements the integration with the Authorize.Net payment gateway and makes the latter available as a payment method in Magento.
+# Magento_Authorizenet module
+
+The Magento_Authorizenet module is a part of the staging functionality in Magento EE. The module adds the “Configurations” tab and the configuration wizard to the Schedule Update form of a product. You can change the Configurable Product attributes in campaigns. These updates are shown on the campaign dashboard.
+
+## Extensibility
+
+Extension developers can interact with the Magento_Authorizenet module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html).
+
+[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_Authorizenet module.
+
+### Events
+
+This module dispatches the following events:
+
+ - `checkout_directpost_placeOrder` event in the `\Magento\Authorizenet\Controller\Directpost\Payment\Place::placeCheckoutOrder()` method. Parameters:
+ - `result` is a data object (`\Magento\Framework\DataObject` class).
+ - `action` is a controller object (`\Magento\Authorizenet\Controller\Directpost\Payment\Place`).
+
+ - `order_cancel_after` event in the `\Magento\Authorizenet\Model\Directpost::declineOrder()` method. Parameters:
+ - `order` is an order object (`\Magento\Sales\Model\Order` class).
+
+
+This module observes the following events:
+
+ - `checkout_submit_all_after` event in `Magento\Authorizenet\Observer\SaveOrderAfterSubmitObserver` file.
+ - `checkout_directpost_placeOrder` event in `Magento\Authorizenet\Observer\AddFieldsToResponseObserver` file.
+
+For information about an event in Magento 2, see [Events and observers](http://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events).
+
+### Layouts
+
+This module introduces the following layouts and layout handles in the `view/adminhtml/layout` directory:
+
+- `adminhtml_authorizenet_directpost_payment_redirect`
+
+This module introduces the following layouts and layout handles in the `view/frontend/layout` directory:
+
+- `authorizenet_directpost_payment_backendresponse`
+- `authorizenet_directpost_payment_redirect`
+- `authorizenet_directpost_payment_response`
+
+For more information about layouts in Magento 2, see the [Layout documentation](https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/layouts/layout-overview.html).
From b089cc2088fc68a8424561b3585d5ff49d0d020b Mon Sep 17 00:00:00 2001
From: yuriichayka
Date: Mon, 26 Aug 2019 00:57:04 +0300
Subject: [PATCH 129/593] initial commit for the issue
---
.../ProductAlert/Controller/Add/StockTest.php | 103 ++++++++++++++++++
.../Controller/Unsubscribe/StockTest.php | 76 +++++++++++++
.../_files/customer_unsubscribe_stock.php | 30 +++++
3 files changed, 209 insertions(+)
create mode 100644 dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php
create mode 100644 dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php
create mode 100644 dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php
new file mode 100644
index 0000000000000..2235876de9822
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php
@@ -0,0 +1,103 @@
+objectManager = Bootstrap::getObjectManager();
+
+ $this->customerSession = $this->objectManager->get(Session::class);
+ $this->dataUrlHelper = $this->objectManager->get(Data::class);
+
+ $this->resource = $this->objectManager->get(ResourceConnection::class);
+ $this->connectionMock = $this->resource->getConnection();
+ }
+
+ /**
+ * @magentoAppArea frontend
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testSubscribe()
+ {
+ $productId = 1;
+ $customerId = 1;
+
+ $this->customerSession->setCustomerId($customerId);
+
+ $encodedParameterValue = $this->getUrlEncodedParameter($productId);
+ $this->getRequest()->setMethod('GET');
+ $this->getRequest()->setQueryValue('product_id', $productId);
+ $this->getRequest()->setQueryValue(Action::PARAM_NAME_URL_ENCODED, $encodedParameterValue);
+ $this->dispatch('productalert/add/stock');
+
+ $select = $this->connectionMock->select()->from($this->resource->getTableName('product_alert_stock'))
+ ->where('`customer_id` LIKE ?', '1');
+ $result = $this->connectionMock->fetchAll($select);
+ $this->assertCount(1, $result);
+ }
+
+ /**
+ * @param int $productId
+ *
+ * @return string
+ */
+ private function getUrlEncodedParameter(int $productId):string
+ {
+ $baseUrl = $this->objectManager->get(StoreManagerInterface::class)->getStore()->getBaseUrl();
+ $encodedParameterValue = urlencode(
+ $this->dataUrlHelper->getEncodedUrl($baseUrl . 'productalert/add/stock/product_id/' . $productId)
+ );
+
+ return $encodedParameterValue;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php
new file mode 100644
index 0000000000000..b57f046b0925e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php
@@ -0,0 +1,76 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->customerSession = $this->objectManager->get(Session::class);
+ $this->resource = $this->objectManager->get(ResourceConnection::class);
+ $this->connectionMock = $this->resource->getConnection();
+ }
+
+ /**
+ * @magentoAppArea frontend
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/ProductAlert/_files/customer_unsubscribe_stock.php
+ */
+ public function testSubscribe()
+ {
+ $productId = 1;
+ $customerId = 1;
+
+ $this->customerSession->setCustomerId($customerId);
+ $select = $this->connectionMock->select()->from($this->resource->getTableName('product_alert_stock'))
+ ->where('`customer_id` LIKE ?', $customerId);
+
+ $this->getRequest()->setPostValue('product', $productId)->setMethod('POST');
+ $this->dispatch('productalert/unsubscribe/stock');
+
+ $result = $this->connectionMock->fetchAll($select);
+ $this->assertCount(0, $result);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php
new file mode 100644
index 0000000000000..e0f0f4f8d83df
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php
@@ -0,0 +1,30 @@
+create(Stock::class);
+
+/** @var \Magento\Framework\Stdlib\DateTime\DateTime $dateTime */
+$dateTime = $objectManager->get(DateTimeFactory::class)->create();
+$date = $dateTime->gmtDate(null, ($dateTime->gmtTimestamp() - 3600));
+
+$resource->getConnection()->insert(
+ $resource->getMainTable(),
+ [
+ 'customer_id' => 1,
+ 'product_id' => 1,
+ 'website_id' => 1,
+ 'store_id' => 1,
+ 'add_date' => $date,
+ 'send_date' => null,
+ 'send_count' => 0,
+ 'status' => 0
+ ]
+);
From 36c00d2d7f0b1992ab84ed7df961959dca76de8c Mon Sep 17 00:00:00 2001
From: yuriichayka
Date: Mon, 26 Aug 2019 13:07:47 +0300
Subject: [PATCH 130/593] magento/magento2#23279: Integration test for
unsubscribing from 'Back in stock notification' Added integration tests for
ProductAlert\Controller\Unsubscribe\Stock and
ProductAlert\Controller\Add\Stock
---
.../ProductAlert/Controller/Add/StockTest.php | 18 ++++++++++++-----
.../Controller/Unsubscribe/StockTest.php | 20 +++++++++++++------
.../_files/customer_unsubscribe_stock.php | 7 +++++--
3 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php
index 2235876de9822..2e4da9992642f 100644
--- a/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Add/StockTest.php
@@ -7,6 +7,7 @@
namespace Magento\ProductAlert\Controller\Add;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\App\Action\Action;
@@ -20,6 +21,7 @@
* Test for Magento\ProductAlert\Controller\Add\Stock class.
*
* @magentoAppIsolation enabled
+ * @magentoDbIsolation enabled
*/
class StockTest extends AbstractController
{
@@ -50,6 +52,11 @@ class StockTest extends AbstractController
*/
protected $connectionMock;
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
protected function setUp()
{
parent::setUp();
@@ -60,6 +67,7 @@ protected function setUp()
$this->resource = $this->objectManager->get(ResourceConnection::class);
$this->connectionMock = $this->resource->getConnection();
+ $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
}
/**
@@ -67,9 +75,9 @@ protected function setUp()
* @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php
* @magentoDataFixture Magento/Customer/_files/customer.php
*/
- public function testSubscribe()
+ public function testSubscribeStockNotification()
{
- $productId = 1;
+ $productId = $this->productRepository->get('simple-out-of-stock')->getId();
$customerId = 1;
$this->customerSession->setCustomerId($customerId);
@@ -81,17 +89,17 @@ public function testSubscribe()
$this->dispatch('productalert/add/stock');
$select = $this->connectionMock->select()->from($this->resource->getTableName('product_alert_stock'))
- ->where('`customer_id` LIKE ?', '1');
+ ->where('`product_id` LIKE ?', $productId);
$result = $this->connectionMock->fetchAll($select);
$this->assertCount(1, $result);
}
/**
- * @param int $productId
+ * @param $productId
*
* @return string
*/
- private function getUrlEncodedParameter(int $productId):string
+ private function getUrlEncodedParameter($productId):string
{
$baseUrl = $this->objectManager->get(StoreManagerInterface::class)->getStore()->getBaseUrl();
$encodedParameterValue = urlencode(
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php
index b57f046b0925e..1d56edab9a8a5 100644
--- a/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/Controller/Unsubscribe/StockTest.php
@@ -7,9 +7,10 @@
namespace Magento\ProductAlert\Controller\Unsubscribe;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\ObjectManagerInterface;
-use Magento\Framework\Url\Helper\Data;
use Magento\Customer\Model\Session;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\AbstractController;
@@ -18,6 +19,7 @@
* Test for Magento\ProductAlert\Controller\Unsubscribe\Stock class.
*
* @magentoAppIsolation enabled
+ * @magentoDbIsolation enabled
*/
class StockTest extends AbstractController
{
@@ -39,10 +41,15 @@ class StockTest extends AbstractController
/**
* Connection adapter
*
- * @var \Magento\Framework\DB\Adapter\AdapterInterface
+ * @var AdapterInterface
*/
protected $connectionMock;
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
protected function setUp()
{
parent::setUp();
@@ -50,6 +57,7 @@ protected function setUp()
$this->customerSession = $this->objectManager->get(Session::class);
$this->resource = $this->objectManager->get(ResourceConnection::class);
$this->connectionMock = $this->resource->getConnection();
+ $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
}
/**
@@ -58,18 +66,18 @@ protected function setUp()
* @magentoDataFixture Magento/Customer/_files/customer.php
* @magentoDataFixture Magento/ProductAlert/_files/customer_unsubscribe_stock.php
*/
- public function testSubscribe()
+ public function testUnsubscribeStockNotification()
{
- $productId = 1;
$customerId = 1;
+ $productId = $this->productRepository->get('simple-out-of-stock')->getId();
$this->customerSession->setCustomerId($customerId);
- $select = $this->connectionMock->select()->from($this->resource->getTableName('product_alert_stock'))
- ->where('`customer_id` LIKE ?', $customerId);
$this->getRequest()->setPostValue('product', $productId)->setMethod('POST');
$this->dispatch('productalert/unsubscribe/stock');
+ $select = $this->connectionMock->select()->from($this->resource->getTableName('product_alert_stock'))
+ ->where('`product_id` LIKE ?', $productId);
$result = $this->connectionMock->fetchAll($select);
$this->assertCount(0, $result);
}
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php
index e0f0f4f8d83df..3308b2b1829db 100644
--- a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock.php
@@ -4,22 +4,25 @@
* See COPYING.txt for license details.
*/
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Stdlib\DateTime\DateTimeFactory;
use Magento\ProductAlert\Model\ResourceModel\Stock;
use Magento\TestFramework\Helper\Bootstrap;
$objectManager = Bootstrap::getObjectManager();
-$resource = $objectManager->create(Stock::class);
+$resource = $objectManager->get(Stock::class);
/** @var \Magento\Framework\Stdlib\DateTime\DateTime $dateTime */
$dateTime = $objectManager->get(DateTimeFactory::class)->create();
$date = $dateTime->gmtDate(null, ($dateTime->gmtTimestamp() - 3600));
+$productRepository = $objectManager->create(ProductRepositoryInterface::class);
+$productId = $productRepository->get('simple-out-of-stock')->getId();
$resource->getConnection()->insert(
$resource->getMainTable(),
[
'customer_id' => 1,
- 'product_id' => 1,
+ 'product_id' => $productId,
'website_id' => 1,
'store_id' => 1,
'add_date' => $date,
From 8c33bbd093c9680f2e9d04616818047ec9893203 Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Mon, 26 Aug 2019 14:08:52 +0300
Subject: [PATCH 131/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Replace using category collection to using db.
---
.../Catalog/Model/ResourceModel/Category.php | 40 ++++++++++++++++++
.../ResourceModel/Product/Collection.php | 41 +++++++++++--------
2 files changed, 63 insertions(+), 18 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
index 536fda7e093d3..3794dbf46579f 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
@@ -9,11 +9,14 @@
*
* @author Magento Core Team
*/
+declare(strict_types=1);
+
namespace Magento\Catalog\Model\ResourceModel;
use Magento\Catalog\Model\Indexer\Category\Product\Processor;
use Magento\Framework\DataObject;
use Magento\Framework\EntityManager\EntityManager;
+use Magento\Catalog\Setup\CategorySetup;
/**
* Resource model for category entity
@@ -1132,4 +1135,41 @@ private function getAggregateCount()
}
return $this->aggregateCount;
}
+
+ /**
+ * Get category with children.
+ *
+ * @param int $categoryId
+ * @return array
+ */
+ public function getCategoryWithChildren(int $categoryId): array
+ {
+ $connection = $this->getConnection();
+
+ $select = $connection->select()
+ ->from(
+ ['eav_attribute' => $this->getTable('eav_attribute')],
+ ['attribute_id']
+ )->where('entity_type_id = ?', CategorySetup::CATEGORY_ENTITY_TYPE_ID)
+ ->where('attribute_code = ?', 'is_anchor')
+ ->limit(1);
+ $attributeId = $connection->fetchRow($select);
+
+ $select = $connection->select()
+ ->from(
+ ['cce' => $this->getTable('catalog_category_entity')],
+ ['entity_id', 'parent_id', 'path']
+ )->join(
+ ['cce_int' => $this->getTable('catalog_category_entity_int')],
+ 'cce.entity_id = cce_int.entity_id',
+ ['is_anchor' => 'cce_int.value']
+ )->where(
+ 'cce_int.attribute_id = ?',
+ $attributeId['attribute_id']
+ )->where(
+ "cce.path LIKE '%/{$categoryId}' OR cce.path LIKE '%/{$categoryId}/%'"
+ )->order('path');
+
+ return $connection->fetchAll($select);
+ }
}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index dffa49d97838c..71b03620b342b 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -23,7 +23,7 @@
use Magento\Framework\Indexer\DimensionFactory;
use Magento\Store\Model\Indexer\WebsiteDimensionProvider;
use Magento\Store\Model\Store;
-use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
+use Magento\Catalog\Model\ResourceModel\Category;
/**
* Product collection
@@ -305,9 +305,9 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
private $urlFinder;
/**
- * @var CollectionFactory
+ * @var Category
*/
- private $categoryCollectionFactory;
+ private $categoryResourceModel;
/**
* Collection constructor
@@ -337,7 +337,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
* @param TableMaintainer|null $tableMaintainer
* @param PriceTableResolver|null $priceTableResolver
* @param DimensionFactory|null $dimensionFactory
- * @param CollectionFactory|null $categoryCollectionFactory
+ * @param Category|null $categoryResourceModel
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -367,7 +367,7 @@ public function __construct(
TableMaintainer $tableMaintainer = null,
PriceTableResolver $priceTableResolver = null,
DimensionFactory $dimensionFactory = null,
- CollectionFactory $categoryCollectionFactory = null
+ Category $categoryResourceModel = null
) {
$this->moduleManager = $moduleManager;
$this->_catalogProductFlatState = $catalogProductFlatState;
@@ -401,8 +401,8 @@ public function __construct(
$this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get(PriceTableResolver::class);
$this->dimensionFactory = $dimensionFactory
?: ObjectManager::getInstance()->get(DimensionFactory::class);
- $this->categoryCollectionFactory = $categoryCollectionFactory ?: ObjectManager::getInstance()
- ->get(CollectionFactory::class);
+ $this->categoryResourceModel = $categoryResourceModel ?: ObjectManager::getInstance()
+ ->get(Category::class);
}
/**
@@ -2104,18 +2104,23 @@ protected function _applyZeroStoreProductLimitations()
private function getChildrenCategories(int $categoryId): array
{
$categoryIds[] = $categoryId;
-
- /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */
- $categoryCollection = $this->categoryCollectionFactory->create();
- $categories = $categoryCollection
- ->addAttributeToFilter(
- ['is_anchor', 'path'],
- [1, ['like' => $categoryId . '/%']]
- )->getItems();
- foreach ($categories as $category) {
- $categoryChildren = $category->getChildren();
- $categoryIds = array_merge($categoryIds, explode(',', $categoryChildren));
+ $anchorCategory = [];
+
+ $categories = $this->categoryResourceModel->getCategoryWithChildren($categoryId);
+ $firstCategory = array_shift($categories);
+ if ($firstCategory['is_anchor'] == 1) {
+ $anchorCategory[] = (int)$firstCategory['entity_id'];
+ foreach ($categories as $category) {
+ if (in_array($category['parent_id'], $categoryIds)
+ && in_array($category['parent_id'], $anchorCategory)) {
+ $categoryIds[] = (int)$category['entity_id'];
+ if ($category['is_anchor'] == 1) {
+ $anchorCategory[] = (int)$category['entity_id'];
+ }
+ }
+ }
}
+
return $categoryIds;
}
From 3ccf1d99d3db16ae2dc9a6ebd97e62b931af6720 Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Mon, 26 Aug 2019 17:43:38 +0300
Subject: [PATCH 132/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix magento category installation.
---
app/code/Magento/Catalog/Model/ResourceModel/Category.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
index cfc82d9b4ae94..d50570930923e 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
@@ -278,7 +278,7 @@ protected function _beforeSave(\Magento\Framework\DataObject $object)
if ($object->getPosition() === null) {
$object->setPosition($this->_getMaxPosition($object->getPath()) + 1);
}
- $path = explode('/', $object->getPath());
+ $path = explode('/', (string)$object->getPath());
$level = count($path) - ($object->getId() ? 1 : 0);
$toUpdateChild = array_diff($path, [$object->getId()]);
@@ -317,7 +317,7 @@ protected function _afterSave(\Magento\Framework\DataObject $object)
/**
* Add identifier for new category
*/
- if (substr($object->getPath(), -1) == '/') {
+ if (substr((string)$object->getPath(), -1) == '/') {
$object->setPath($object->getPath() . $object->getId());
$this->_savePath($object);
}
From 3babf2ae50c7ec79aa5bd2a6906a244f6f4cfed5 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Mon, 26 Aug 2019 09:47:19 -0500
Subject: [PATCH 133/593] Code Style changes
---
.../CatalogGraphQl/Model/Category/GetRootCategoryId.php | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
index c8b80622433cb..22687851a2de9 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
@@ -11,6 +11,9 @@
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\StoreManagerInterface;
+/**
+ * Gets Root Category for Current Store.
+ */
class GetRootCategoryId
{
/**
@@ -50,3 +53,4 @@ public function execute()
return $this->rootCategoryId;
}
}
+
From 3f2f2bcc4ae99fb57c932125a7531e63c19e3136 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Mon, 26 Aug 2019 10:13:03 -0500
Subject: [PATCH 134/593] MC-19572: Fix filtering by attribute of type
select/multiselect using filter input type in
- added test and fixtures for multiselect
---
.../GraphQl/Catalog/ProductSearchTest.php | 116 ++++++++++++++++--
..._attribute_layered_navigation_rollback.php | 11 ++
..._navigation_with_multiselect_attribute.php | 98 +++++++++++++++
...on_with_multiselect_attribute_rollback.php | 32 +++++
4 files changed, 246 insertions(+), 11 deletions(-)
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute_rollback.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index a565ac656124a..6356f7b44db84 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -95,7 +95,7 @@ public function testFilterLn()
}
/**
- * Advanced Search which uses product attribute to filter out the results
+ * Filter products using custom attribute of input type select(dropdown) and filterTypeInput eq
*
* @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@@ -105,7 +105,42 @@ public function testAdvancedSearchByOneCustomAttribute()
CacheCleaner::cleanAll();
$attributeCode = 'second_test_configurable';
$optionValue = $this->getDefaultAttributeOptionValue($attributeCode);
- $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $optionValue);
+ $query = <<get(ProductRepositoryInterface::class);
@@ -151,6 +186,67 @@ public function testAdvancedSearchByOneCustomAttribute()
]
);
}
+ /**
+ * Filter products using custom attribute of input type select(dropdown) and filterTypeInput eq
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testFilterProductsByMultiSelectCustomAttribute()
+ {
+ CacheCleaner::cleanAll();
+ $attributeCode = 'multiselect_attribute';
+ /** @var \Magento\Eav\Model\Config $eavConfig */
+ $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+ $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode);
+ /** @var AttributeOptionInterface[] $options */
+ $options = $attribute->getOptions();
+ array_shift($options);
+ $optionValues = array();
+ for ($i = 0; $i < count($options); $i++) {
+ $optionValues[] = $options[$i]->getValue();
+ }
+ $query = <<graphQlQuery($query);
+ $this->assertEquals(3, $response['products']['total_count']);
+ $this->assertNotEmpty($response['products']['filters']);
+ }
/**
* Get the option value for the custom attribute to be used in the graphql query
@@ -462,7 +558,7 @@ public function testLayeredNavigationWithConfigurableChildrenOutOfStock()
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
$productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
$outOfStockChildProduct = $productRepository->get('simple_30');
- // Set another child product from 2nd Configurable product with attribute option1 to OOS
+ // All child variations with this attribute are now set to Out of Stock
$outOfStockChildProduct->setStockData(
['use_config_manage_stock' => 1,
'qty' => 0,
@@ -784,18 +880,17 @@ public function testQueryProductsInCurrentPageSortedByPriceASC()
products(
filter:
{
- price:{gt: "5", lt: "50"}
- or:
- {
- sku:{like:"simple%"}
- name:{like:"simple%"}
- }
+ price:{to :"50"}
+ sku:{like:"simple%"}
+ name:{like:"simple%"}
+
}
pageSize:4
currentPage:1
sort:
{
price:ASC
+ name:ASC
}
)
{
@@ -1062,7 +1157,7 @@ public function testQuerySortByPriceDESCWithDefaultPageSize()
products(
filter:
{
- sku:{like:"%simple%"}
+ sku:{like:"simple%"}
}
sort:
{
@@ -1109,7 +1204,6 @@ public function testQuerySortByPriceDESCWithDefaultPageSize()
$this->assertEquals(20, $response['products']['page_info']['page_size']);
$this->assertEquals(1, $response['products']['page_info']['current_page']);
}
-
/**
* @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products_2.php
*/
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php
index 49e2a8e88a1ac..2e4227eb35392 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation_rollback.php
@@ -7,3 +7,14 @@
// phpcs:ignore Magento2.Security.IncludeFile
require __DIR__ . '/../../ConfigurableProduct/_files/configurable_products_rollback.php';
+
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Eav\Api\AttributeRepositoryInterface;
+
+$eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+
+/** @var AttributeRepositoryInterface $attributeRepository */
+$attributeRepository = Bootstrap::getObjectManager()->get(AttributeRepositoryInterface::class);
+/** @var \Magento\Eav\Api\Data\AttributeInterface $attribute */
+$attribute = $attributeRepository->get('catalog_product', 'test_configurable');
+$attributeRepository->delete($attribute);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php
new file mode 100644
index 0000000000000..7d4f22e154030
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute.php
@@ -0,0 +1,98 @@
+create(
+ \Magento\Catalog\Setup\CategorySetup::class
+);
+
+/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */
+$options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class
+);
+$eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+
+/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
+$attribute = $eavConfig->getAttribute('catalog_product', 'multiselect_attribute');
+
+$eavConfig->clear();
+$attribute->setIsSearchable(1)
+ ->setIsVisibleInAdvancedSearch(1)
+ ->setIsFilterable(true)
+ ->setIsFilterableInSearch(true)
+ ->setIsVisibleOnFront(1);
+/** @var AttributeRepositoryInterface $attributeRepository */
+$attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class);
+$attributeRepository->save($attribute);
+
+$options->setAttributeFilter($attribute->getId());
+$optionIds = $options->getAllIds();
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+
+/** @var $product \Magento\Catalog\Model\Product */
+$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setId($optionIds[0] * 10)
+ ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default'))
+ ->setWebsiteIds([1])
+ ->setName('With Multiselect 1 and 2')
+ ->setSku('simple_ms_1')
+ ->setPrice(10)
+ ->setDescription('Hello " &" Bring the water bottle when you can!')
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setMultiselectAttribute([$optionIds[1],$optionIds[2]])
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+$productRepository->save($product);
+
+$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setId($optionIds[1] * 10)
+ ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default'))
+ ->setWebsiteIds([1])
+ ->setName('With Multiselect 2 and 3')
+ ->setSku('simple_ms_2')
+ ->setPrice(10)
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setMultiselectAttribute([$optionIds[2], $optionIds[3]])
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+$productRepository->save($product);
+
+$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setId($optionIds[2] * 10)
+ ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default'))
+ ->setWebsiteIds([1])
+ ->setName('With Multiselect 1 and 3')
+ ->setSku('simple_ms_2')
+ ->setPrice(10)
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setMultiselectAttribute([$optionIds[2], $optionIds[3]])
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+
+$productRepository->save($product);
+
+/** @var \Magento\Indexer\Model\Indexer\Collection $indexerCollection */
+$indexerCollection = Bootstrap::getObjectManager()->get(\Magento\Indexer\Model\Indexer\Collection::class);
+$indexerCollection->load();
+/** @var \Magento\Indexer\Model\Indexer $indexer */
+foreach ($indexerCollection->getItems() as $indexer) {
+ $indexer->reindexAll();
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute_rollback.php
new file mode 100644
index 0000000000000..eb8201f04e6cc
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_with_multiselect_attribute_rollback.php
@@ -0,0 +1,32 @@
+get('Magento\Framework\Registry');
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var $productCollection \Magento\Catalog\Model\ResourceModel\Product */
+$productCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create('Magento\Catalog\Model\Product')
+ ->getCollection();
+
+foreach ($productCollection as $product) {
+ $product->delete();
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(IndexerRegistry::class)
+ ->get(Magento\CatalogInventory\Model\Indexer\Stock\Processor::INDEXER_ID)
+ ->reindexAll();
From 8b2f00207dec18bc6fb22cb35fdd98850805b577 Mon Sep 17 00:00:00 2001
From: Raoul Rego
Date: Mon, 26 Aug 2019 11:31:28 -0500
Subject: [PATCH 135/593] MC-17627: Dependency static test does not analyze
content of phtml files
- Fixed regular expression to include php echo syntax
---
.../static/testsuite/Magento/Test/Integrity/DependencyTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
index 16ba295588b58..540fcc76349d1 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
@@ -301,7 +301,7 @@ protected function _getCleanedFileContents($fileType, $file)
//Removing html
$contentsWithoutHtml = '';
preg_replace_callback(
- '~(<\?php\s+.*\?>)~sU',
+ '~(<\?(php|=)\s+.*\?>)~sU',
function ($matches) use ($contents, &$contentsWithoutHtml) {
$contentsWithoutHtml .= $matches[1];
return $contents;
From bb996868319ae8efa47088c9f8f2b217c4727819 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Mon, 26 Aug 2019 13:59:22 -0500
Subject: [PATCH 136/593] MC-19441: Fix web api test failures
- fix fixture and tests
---
.../GraphQl/Catalog/ProductSearchTest.php | 286 ++++++------------
..._attribute_layered_navigation_rollback.php | 11 -
2 files changed, 100 insertions(+), 197 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 6356f7b44db84..cd7239d808b41 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -94,6 +94,73 @@ public function testFilterLn()
);
}
+ /**
+ * Layered navigation for Configurable products with out of stock options
+ * Two configurable products each having two variations and one of the child products of one Configurable set to OOS
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testLayeredNavigationWithConfigurableChildrenOutOfStock()
+ {
+ CacheCleaner::cleanAll();
+ $attributeCode = 'test_configurable';
+ /** @var \Magento\Eav\Model\Config $eavConfig */
+ $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+ $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode);
+ /** @var AttributeOptionInterface[] $options */
+ $options = $attribute->getOptions();
+ array_shift($options);
+ $firstOption = $options[0]->getValue();
+ $secondOption = $options[1]->getValue();
+ $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
+ $response = $this->graphQlQuery($query);
+
+ // 1 product is returned since only one child product with attribute option1 from 1st Configurable product is OOS
+ $this->assertEquals(1, $response['products']['total_count']);
+
+ // Custom attribute filter layer data
+ $this->assertResponseFields(
+ $response['products']['filters'][1],
+ [
+ 'name' => $attribute->getDefaultFrontendLabel(),
+ 'request_var'=> $attribute->getAttributeCode(),
+ 'filter_items_count'=> 2,
+ 'filter_items' => [
+ [
+ 'label' => 'Option 1',
+ 'items_count' => 1,
+ 'value_string' => $firstOption,
+ '__typename' =>'LayerFilterItem'
+ ],
+ [
+ 'label' => 'Option 2',
+ 'items_count' => 1,
+ 'value_string' => $secondOption,
+ '__typename' =>'LayerFilterItem'
+ ]
+ ],
+ ]
+ );
+
+ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+ $productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+ $outOfStockChildProduct = $productRepository->get('simple_30');
+ // All child variations with this attribute are now set to Out of Stock
+ $outOfStockChildProduct->setStockData(
+ ['use_config_manage_stock' => 1,
+ 'qty' => 0,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 0]
+ );
+ $productRepository->save($outOfStockChildProduct);
+ $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
+ $response = $this->graphQlQuery($query);
+ $this->assertEquals(0, $response['products']['total_count']);
+ $this->assertEmpty($response['products']['items']);
+ $this->assertEmpty($response['products']['filters']);
+ }
+
/**
* Filter products using custom attribute of input type select(dropdown) and filterTypeInput eq
*
@@ -373,7 +440,7 @@ public function testFullTextSearchForProductAndFilterByCustomAttribute()
}
/**
- * Filter by category_id and custom attribute
+ * Filter by single category and custom attribute
*
* @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@@ -507,72 +574,6 @@ private function getQueryProductsWithCustomAttribute($attributeCode, $optionValu
QUERY;
}
- /**
- * Layered navigation for Configurable products with out of stock options
- * Two configurable products each having two variations and one of the child products of one Configurable set to OOS
- *
- * @magentoApiDataFixture Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- public function testLayeredNavigationWithConfigurableChildrenOutOfStock()
- {
- $attributeCode = 'test_configurable';
- /** @var \Magento\Eav\Model\Config $eavConfig */
- $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
- $attribute = $eavConfig->getAttribute('catalog_product', $attributeCode);
- /** @var AttributeOptionInterface[] $options */
- $options = $attribute->getOptions();
- array_shift($options);
- $firstOption = $options[0]->getValue();
- $secondOption = $options[1]->getValue();
- $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
- $response = $this->graphQlQuery($query);
-
- //1 product will be returned since only one child product with attribute option1 from 1st Configurable product is OOS
- $this->assertEquals(1, $response['products']['total_count']);
-
- // Custom attribute filter layer data
- $this->assertResponseFields(
- $response['products']['filters'][1],
- [
- 'name' => $attribute->getDefaultFrontendLabel(),
- 'request_var'=> $attribute->getAttributeCode(),
- 'filter_items_count'=> 2,
- 'filter_items' => [
- [
- 'label' => 'Option 1',
- 'items_count' => 1,
- 'value_string' => $firstOption,
- '__typename' =>'LayerFilterItem'
- ],
- [
- 'label' => 'Option 2',
- 'items_count' => 1,
- 'value_string' => $secondOption,
- '__typename' =>'LayerFilterItem'
- ]
- ],
- ]
- );
-
- /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
- $productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
- $outOfStockChildProduct = $productRepository->get('simple_30');
- // All child variations with this attribute are now set to Out of Stock
- $outOfStockChildProduct->setStockData(
- ['use_config_manage_stock' => 1,
- 'qty' => 0,
- 'is_qty_decimal' => 0,
- 'is_in_stock' => 0]
- );
- $productRepository->save($outOfStockChildProduct);
- $query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
- $response = $this->graphQlQuery($query);
- $this->assertEquals(0, $response['products']['total_count']);
- $this->assertEmpty($response['products']['items']);
- $this->assertEmpty($response['products']['filters']);
- }
-
/**
* Get array with expected data for layered navigation filters
*
@@ -672,12 +673,9 @@ public function testFilterProductsWithinSpecificPriceRangeSortedByNameDesc()
products(
filter:
{
- price:{gt: "5", lt: "50"}
- or:
- {
- sku:{like:"simple%"}
- name:{like:"Simple%"}
- }
+ price:{from: "5", to: "50"}
+ sku:{like:"simple%"}
+ name:{like:"Simple%"}
}
pageSize:4
currentPage:1
@@ -729,79 +727,6 @@ public function testFilterProductsWithinSpecificPriceRangeSortedByNameDesc()
$this->assertEquals(4, $response['products']['page_info']['page_size']);
}
- /**
- * Test a visible product with matching sku or name with special price
- *
- * Requesting for items that has a special price and price < $60, that are visible in Catalog, Search or Both which
- * either has a sku like “simple” or name like “configurable”sorted by price in DESC
- *
- * @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products_2.php
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- public function testFilterVisibleProductsWithMatchingSkuOrNameWithSpecialPrice()
- {
- $query
- = <<get(ProductRepositoryInterface::class);
- $product1 = $productRepository->get('simple1');
- $product2 = $productRepository->get('simple2');
- $filteredProducts = [$product2, $product1];
-
- $response = $this->graphQlQuery($query);
- $this->assertArrayHasKey('total_count', $response['products']);
- $this->assertEquals(2, $response['products']['total_count']);
- $this->assertProductItems($filteredProducts, $response);
- }
-
/**
* pageSize = total_count and current page = 2
* expected - error is thrown
@@ -813,7 +738,7 @@ public function testFilterVisibleProductsWithMatchingSkuOrNameWithSpecialPrice()
public function testSearchWithFilterWithPageSizeEqualTotalCount()
{
- CacheCleaner::cleanAll();
+
$query
= <<graphQlQuery($query);
/** @var ProductRepositoryInterface $productRepository */
- $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
- /** @var ProductInterface $product */
- $product = $productRepository->get('simple333');
- $categoryIds = $product->getCategoryIds();
- foreach ($categoryIds as $index => $value) {
- $categoryIds[$index] = [ 'id' => (int)$value];
- }
- $this->assertNotEmpty($response['products']['items'][0]['categories'], "Categories must not be empty");
- $this->assertNotNull($response['products']['items'][0]['categories'], "categories must not be null");
- $this->assertEquals($categoryIds, $response['products']['items'][0]['categories']);
- /** @var MetadataPool $metaData */
- $metaData = ObjectManager::getInstance()->get(MetadataPool::class);
- $linkField = $metaData->getMetadata(ProductInterface::class)->getLinkField();
- $assertionMap = [
-
- ['response_field' => 'id', 'expected_value' => $product->getData($linkField)],
- ['response_field' => 'sku', 'expected_value' => $product->getSku()],
- ['response_field' => 'name', 'expected_value' => $product->getName()],
- ['response_field' => 'attribute_set_id', 'expected_value' => $product->getAttributeSetId()]
- ];
- $this->assertResponseFields($response['products']['items'][0], $assertionMap);
+ $this->assertEquals(3, $response['products']['total_count']);
}
/**
+ * Filter products by category only
+ *
* @magentoApiDataFixture Magento/Catalog/_files/product_in_multiple_categories.php
* @return void
*/
@@ -1150,7 +1064,7 @@ public function testFilterProductsBySingleCategoryId()
*/
public function testQuerySortByPriceDESCWithDefaultPageSize()
{
- CacheCleaner::cleanAll();
+
$query
= <<get(\Magento\Eav\Model\Config::class);
-
-/** @var AttributeRepositoryInterface $attributeRepository */
-$attributeRepository = Bootstrap::getObjectManager()->get(AttributeRepositoryInterface::class);
-/** @var \Magento\Eav\Api\Data\AttributeInterface $attribute */
-$attribute = $attributeRepository->get('catalog_product', 'test_configurable');
-$attributeRepository->delete($attribute);
From 6730caed941cf5971f22bb7f1b151d4163622b5b Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Mon, 26 Aug 2019 17:38:51 -0500
Subject: [PATCH 137/593] MC-19441: Fix web api test failures
- fix fixture and tests
---
.../testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index cd7239d808b41..169aba7c15734 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -746,7 +746,7 @@ public function testSearchWithFilterWithPageSizeEqualTotalCount()
search : "simple"
filter:
{
- price:{from:"60"}
+ price:{from:"5.59"}
}
pageSize:2
currentPage:2
From 52cdae7e754ab4a12b06e9bf2df05127cb5a7a53 Mon Sep 17 00:00:00 2001
From: Raul E Watson
Date: Tue, 27 Aug 2019 00:35:46 +0100
Subject: [PATCH 138/593] Add changes requested during CR
---
app/code/Magento/Analytics/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Analytics/README.md b/app/code/Magento/Analytics/README.md
index cb78a27dae8e0..e0f1c818c7a94 100644
--- a/app/code/Magento/Analytics/README.md
+++ b/app/code/Magento/Analytics/README.md
@@ -4,10 +4,10 @@ The Magento_Analytics module integrates your Magento instance with the [Magento
The module implements the following functionality:
-- Enabling subscription to the MBI and automatic re-subscription
+- Enabling subscription to Magento Business Intelligence (MBI) and automatic re-subscription
- Changing the base URL with the same MBI account remained
- Declaring the configuration schemas for report data collection
-- Collecting the Magento instance data as reports for the MBI
+- Collecting the Magento instance data as reports for MBI
- Introducing API that provides the collected data
- Extending Magento configuration with the module parameters:
- Subscription status (enabled/disabled)
From c9737d0e388b08696967bc60941b139dfd55a244 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Mon, 26 Aug 2019 18:56:28 -0500
Subject: [PATCH 139/593] change code styles
---
.../Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
index 22687851a2de9..4da1443c9e05f 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
@@ -37,6 +37,7 @@ public function __construct(
/**
* Get Root Category Id
+ *
* @return int
* @throws LocalizedException
*/
@@ -53,4 +54,3 @@ public function execute()
return $this->rootCategoryId;
}
}
-
From 0ca4b90cab8998ea5516459c29dd1412ce3a779d Mon Sep 17 00:00:00 2001
From: Raul E Watson
Date: Tue, 27 Aug 2019 01:54:00 +0100
Subject: [PATCH 140/593] Add changes requested during CR
---
app/code/Magento/Authorizenet/README.md | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/app/code/Magento/Authorizenet/README.md b/app/code/Magento/Authorizenet/README.md
index ef022a6e236a2..6d157860e42d6 100644
--- a/app/code/Magento/Authorizenet/README.md
+++ b/app/code/Magento/Authorizenet/README.md
@@ -1,7 +1,6 @@
# Magento_Authorizenet module
-The Magento_Authorizenet module is a part of the staging functionality in Magento EE. The module adds the “Configurations” tab and the configuration wizard to the Schedule Update form of a product. You can change the Configurable Product attributes in campaigns. These updates are shown on the campaign dashboard.
-
+The Magento_Authorizenet module is a part of the staging functionality in Magento Commerce. The module adds the “Configurations” tab and the configuration wizard to the Schedule Update form of a product. You can change the Configurable Product attributes in campaigns. These updates are shown on the campaign dashboard.
## Extensibility
Extension developers can interact with the Magento_Authorizenet module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html).
@@ -22,10 +21,10 @@ This module dispatches the following events:
This module observes the following events:
- - `checkout_submit_all_after` event in `Magento\Authorizenet\Observer\SaveOrderAfterSubmitObserver` file.
- - `checkout_directpost_placeOrder` event in `Magento\Authorizenet\Observer\AddFieldsToResponseObserver` file.
+ - `checkout_submit_all_after` event in the `Magento\Authorizenet\Observer\SaveOrderAfterSubmitObserver` file.
+ - `checkout_directpost_placeOrder` event in the `Magento\Authorizenet\Observer\AddFieldsToResponseObserver` file.
-For information about an event in Magento 2, see [Events and observers](http://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events).
+For information about events in Magento 2, see [Events and observers](http://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html#events).
### Layouts
From 8048c09d7f37460c8dda1938fde85a2e36bce49b Mon Sep 17 00:00:00 2001
From: Rus0
Date: Mon, 26 Aug 2019 21:23:46 -0500
Subject: [PATCH 141/593] Adding verification of stock and catalog flag for
showing out of stock products, the wishlist shows the products if they are
out of stock with the flag show out of stock.
---
app/code/Magento/Wishlist/Model/Wishlist.php | 160 ++++++++++++-------
1 file changed, 105 insertions(+), 55 deletions(-)
diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php
index 9797ab58b0766..bf43fbd6f4363 100644
--- a/app/code/Magento/Wishlist/Model/Wishlist.php
+++ b/app/code/Magento/Wishlist/Model/Wishlist.php
@@ -7,30 +7,49 @@
namespace Magento\Wishlist\Model;
+use Exception;
+use InvalidArgumentException;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\ProductFactory;
+use Magento\CatalogInventory\Model\Configuration;
+use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
+use Magento\Framework\DataObject;
+use Magento\Framework\DataObject\IdentityInterface;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Math\Random;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Framework\Model\Context;
+use Magento\Framework\Registry;
use Magento\Framework\Serialize\Serializer\Json;
+use Magento\Framework\Stdlib\DateTime;
+use Magento\Store\Model\ScopeInterface;
+use Magento\Store\Model\Store;
+use Magento\Store\Model\StoreManagerInterface;
+use Magento\Wishlist\Helper\Data;
use Magento\Wishlist\Model\ResourceModel\Item\CollectionFactory;
use Magento\Wishlist\Model\ResourceModel\Wishlist as ResourceWishlist;
use Magento\Wishlist\Model\ResourceModel\Wishlist\Collection;
+use Magento\CatalogInventory\Model\Stock\StockItemRepository;
/**
* Wishlist model
*
* @method int getShared()
- * @method \Magento\Wishlist\Model\Wishlist setShared(int $value)
+ * @method Wishlist setShared(int $value)
* @method string getSharingCode()
- * @method \Magento\Wishlist\Model\Wishlist setSharingCode(string $value)
+ * @method Wishlist setSharingCode(string $value)
* @method string getUpdatedAt()
- * @method \Magento\Wishlist\Model\Wishlist setUpdatedAt(string $value)
+ * @method Wishlist setUpdatedAt(string $value)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyFields)
*
* @api
* @since 100.0.2
*/
-class Wishlist extends \Magento\Framework\Model\AbstractModel implements \Magento\Framework\DataObject\IdentityInterface
+class Wishlist extends AbstractModel implements IdentityInterface
{
/**
* Cache tag
@@ -47,14 +66,14 @@ class Wishlist extends \Magento\Framework\Model\AbstractModel implements \Magent
/**
* Wishlist item collection
*
- * @var \Magento\Wishlist\Model\ResourceModel\Item\Collection
+ * @var ResourceModel\Item\Collection
*/
protected $_itemCollection;
/**
* Store filter for wishlist
*
- * @var \Magento\Store\Model\Store
+ * @var Store
*/
protected $_store;
@@ -68,7 +87,7 @@ class Wishlist extends \Magento\Framework\Model\AbstractModel implements \Magent
/**
* Wishlist data
*
- * @var \Magento\Wishlist\Helper\Data
+ * @var Data
*/
protected $_wishlistData;
@@ -80,12 +99,12 @@ class Wishlist extends \Magento\Framework\Model\AbstractModel implements \Magent
protected $_catalogProduct;
/**
- * @var \Magento\Store\Model\StoreManagerInterface
+ * @var StoreManagerInterface
*/
protected $_storeManager;
/**
- * @var \Magento\Framework\Stdlib\DateTime\DateTime
+ * @var DateTime\DateTime
*/
protected $_date;
@@ -100,17 +119,17 @@ class Wishlist extends \Magento\Framework\Model\AbstractModel implements \Magent
protected $_wishlistCollectionFactory;
/**
- * @var \Magento\Catalog\Model\ProductFactory
+ * @var ProductFactory
*/
protected $_productFactory;
/**
- * @var \Magento\Framework\Math\Random
+ * @var Random
*/
protected $mathRandom;
/**
- * @var \Magento\Framework\Stdlib\DateTime
+ * @var DateTime
*/
protected $dateTime;
@@ -129,43 +148,56 @@ class Wishlist extends \Magento\Framework\Model\AbstractModel implements \Magent
*/
private $serializer;
+ /**
+ * @var StockItemRepository
+ */
+ private $stockItemRepository;
+
+ /**
+ * @var ScopeConfigInterface
+ */
+ private $scopeConfig;
+
/**
* Constructor
*
- * @param \Magento\Framework\Model\Context $context
- * @param \Magento\Framework\Registry $registry
+ * @param Context $context
+ * @param Registry $registry
* @param \Magento\Catalog\Helper\Product $catalogProduct
- * @param \Magento\Wishlist\Helper\Data $wishlistData
+ * @param Data $wishlistData
* @param ResourceWishlist $resource
* @param Collection $resourceCollection
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
- * @param \Magento\Framework\Stdlib\DateTime\DateTime $date
+ * @param StoreManagerInterface $storeManager
+ * @param DateTime\DateTime $date
* @param ItemFactory $wishlistItemFactory
* @param CollectionFactory $wishlistCollectionFactory
- * @param \Magento\Catalog\Model\ProductFactory $productFactory
- * @param \Magento\Framework\Math\Random $mathRandom
- * @param \Magento\Framework\Stdlib\DateTime $dateTime
+ * @param ProductFactory $productFactory
+ * @param Random $mathRandom
+ * @param DateTime $dateTime
* @param ProductRepositoryInterface $productRepository
+ * @param StockItemRepository $stockItemRepository
* @param bool $useCurrentWebsite
* @param array $data
* @param Json|null $serializer
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
- \Magento\Framework\Model\Context $context,
- \Magento\Framework\Registry $registry,
+ Context $context,
+ Registry $registry,
\Magento\Catalog\Helper\Product $catalogProduct,
- \Magento\Wishlist\Helper\Data $wishlistData,
+ Data $wishlistData,
ResourceWishlist $resource,
Collection $resourceCollection,
- \Magento\Store\Model\StoreManagerInterface $storeManager,
- \Magento\Framework\Stdlib\DateTime\DateTime $date,
+ StoreManagerInterface $storeManager,
+ DateTime\DateTime $date,
ItemFactory $wishlistItemFactory,
CollectionFactory $wishlistCollectionFactory,
- \Magento\Catalog\Model\ProductFactory $productFactory,
- \Magento\Framework\Math\Random $mathRandom,
- \Magento\Framework\Stdlib\DateTime $dateTime,
+ ProductFactory $productFactory,
+ Random $mathRandom,
+ DateTime $dateTime,
ProductRepositoryInterface $productRepository,
+ StockItemRepository $stockItemRepository,
+ ScopeConfigInterface $scopeConfig = null,
$useCurrentWebsite = true,
array $data = [],
Json $serializer = null
@@ -183,6 +215,8 @@ public function __construct(
$this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
$this->productRepository = $productRepository;
+ $this->stockItemRepository = $stockItemRepository;
+ $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
}
/**
@@ -290,13 +324,13 @@ public function afterSave()
/**
* Add catalog product object data to wishlist
*
- * @param \Magento\Catalog\Model\Product $product
+ * @param Product $product
* @param int $qty
* @param bool $forciblySetQty
*
* @return Item
*/
- protected function _addCatalogProduct(\Magento\Catalog\Model\Product $product, $qty = 1, $forciblySetQty = false)
+ protected function _addCatalogProduct(Product $product, $qty = 1, $forciblySetQty = false)
{
$item = null;
foreach ($this->getItemCollection() as $_item) {
@@ -311,7 +345,7 @@ protected function _addCatalogProduct(\Magento\Catalog\Model\Product $product, $
$item = $this->_wishlistItemFactory->create();
$item->setProductId($product->getId());
$item->setWishlistId($this->getId());
- $item->setAddedAt((new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT));
+ $item->setAddedAt((new \DateTime())->format(DateTime::DATETIME_PHP_FORMAT));
$item->setStoreId($storeId);
$item->setOptions($product->getCustomOptions());
$item->setProduct($product);
@@ -333,7 +367,7 @@ protected function _addCatalogProduct(\Magento\Catalog\Model\Product $product, $
/**
* Retrieve wishlist item collection
*
- * @return \Magento\Wishlist\Model\ResourceModel\Item\Collection
+ * @return ResourceModel\Item\Collection
*/
public function getItemCollection()
{
@@ -365,8 +399,9 @@ public function getItem($itemId)
/**
* Adding item to wishlist
*
- * @param Item $item
+ * @param Item $item
* @return $this
+ * @throws Exception
*/
public function addItem(Item $item)
{
@@ -383,13 +418,13 @@ public function addItem(Item $item)
*
* Returns new item or string on error.
*
- * @param int|\Magento\Catalog\Model\Product $product
- * @param \Magento\Framework\DataObject|array|string|null $buyRequest
+ * @param int|Product $product
+ * @param DataObject|array|string|null $buyRequest
* @param bool $forciblySetQty
- * @throws \Magento\Framework\Exception\LocalizedException
* @return Item|string
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
+ *@throws LocalizedException
*/
public function addNewItem($product, $buyRequest = null, $forciblySetQty = false)
{
@@ -398,7 +433,7 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
* a) we have new instance and do not interfere with other products in wishlist
* b) product has full set of attributes
*/
- if ($product instanceof \Magento\Catalog\Model\Product) {
+ if ($product instanceof Product) {
$productId = $product->getId();
// Maybe force some store by wishlist internal properties
$storeId = $product->hasWishlistStoreId() ? $product->getWishlistStoreId() : $product->getStoreId();
@@ -414,10 +449,19 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
try {
$product = $this->productRepository->getById($productId, false, $storeId);
} catch (NoSuchEntityException $e) {
- throw new \Magento\Framework\Exception\LocalizedException(__('Cannot specify product.'));
+ throw new LocalizedException(__('Cannot specify product.'));
+ }
+
+ $stockItem = $this->stockItemRepository->get($product->getId());
+ $showOutOfStock = $this->scopeConfig->isSetFlag(
+ Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
+ ScopeInterface::SCOPE_STORE
+ );
+ if (!$stockItem->getIsInStock() && !$showOutOfStock) {
+ throw new LocalizedException(__('Cannot add product without stock to wishlist.'));
}
- if ($buyRequest instanceof \Magento\Framework\DataObject) {
+ if ($buyRequest instanceof DataObject) {
$_buyRequest = $buyRequest;
} elseif (is_string($buyRequest)) {
$isInvalidItemConfiguration = false;
@@ -426,20 +470,20 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
if (!is_array($buyRequestData)) {
$isInvalidItemConfiguration = true;
}
- } catch (\InvalidArgumentException $exception) {
+ } catch (InvalidArgumentException $exception) {
$isInvalidItemConfiguration = true;
}
if ($isInvalidItemConfiguration) {
- throw new \InvalidArgumentException('Invalid wishlist item configuration.');
+ throw new InvalidArgumentException('Invalid wishlist item configuration.');
}
- $_buyRequest = new \Magento\Framework\DataObject($buyRequestData);
+ $_buyRequest = new DataObject($buyRequestData);
} elseif (is_array($buyRequest)) {
- $_buyRequest = new \Magento\Framework\DataObject($buyRequest);
+ $_buyRequest = new DataObject($buyRequest);
} else {
- $_buyRequest = new \Magento\Framework\DataObject();
+ $_buyRequest = new DataObject();
}
- /* @var $product \Magento\Catalog\Model\Product */
+ /* @var $product Product */
$cartCandidates = $product->getTypeInstance()->processConfiguration($_buyRequest, clone $product);
/**
@@ -486,6 +530,7 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
*
* @param int $customerId
* @return $this
+ * @throws LocalizedException
*/
public function setCustomerId($customerId)
{
@@ -496,6 +541,7 @@ public function setCustomerId($customerId)
* Retrieve customer id
*
* @return int
+ * @throws LocalizedException
*/
public function getCustomerId()
{
@@ -506,6 +552,7 @@ public function getCustomerId()
* Retrieve data for save
*
* @return array
+ * @throws LocalizedException
*/
public function getDataForSave()
{
@@ -520,6 +567,7 @@ public function getDataForSave()
* Retrieve shared store ids for current website or all stores if $current is false
*
* @return array
+ * @throws NoSuchEntityException
*/
public function getSharedStoreIds()
{
@@ -553,7 +601,8 @@ public function setSharedStoreIds($storeIds)
/**
* Retrieve wishlist store object
*
- * @return \Magento\Store\Model\Store
+ * @return Store
+ * @throws NoSuchEntityException
*/
public function getStore()
{
@@ -566,7 +615,7 @@ public function getStore()
/**
* Set wishlist store
*
- * @param \Magento\Store\Model\Store $store
+ * @param Store $store
* @return $this
*/
public function setStore($store)
@@ -605,6 +654,7 @@ public function isSalable()
*
* @param int $customerId
* @return bool
+ * @throws LocalizedException
*/
public function isOwner($customerId)
{
@@ -626,10 +676,10 @@ public function isOwner($customerId)
* For more options see \Magento\Catalog\Helper\Product->addParamsToBuyRequest()
*
* @param int|Item $itemId
- * @param \Magento\Framework\DataObject $buyRequest
- * @param null|array|\Magento\Framework\DataObject $params
+ * @param DataObject $buyRequest
+ * @param null|array|DataObject $params
* @return $this
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*
* @see \Magento\Catalog\Helper\Product::addParamsToBuyRequest()
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -645,16 +695,16 @@ public function updateItem($itemId, $buyRequest, $params = null)
$item = $this->getItem((int)$itemId);
}
if (!$item) {
- throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t specify a wish list item.'));
+ throw new LocalizedException(__('We can\'t specify a wish list item.'));
}
$product = $item->getProduct();
$productId = $product->getId();
if ($productId) {
if (!$params) {
- $params = new \Magento\Framework\DataObject();
+ $params = new DataObject();
} elseif (is_array($params)) {
- $params = new \Magento\Framework\DataObject($params);
+ $params = new DataObject($params);
}
$params->setCurrentConfig($item->getBuyRequest());
$buyRequest = $this->_catalogProduct->addParamsToBuyRequest($buyRequest, $params);
@@ -677,7 +727,7 @@ public function updateItem($itemId, $buyRequest, $params = null)
* Error message
*/
if (is_string($resultItem)) {
- throw new \Magento\Framework\Exception\LocalizedException(__($resultItem));
+ throw new LocalizedException(__($resultItem));
}
if ($resultItem->getId() != $itemId) {
@@ -691,7 +741,7 @@ public function updateItem($itemId, $buyRequest, $params = null)
$resultItem->setOrigData('qty', 0);
}
} else {
- throw new \Magento\Framework\Exception\LocalizedException(__('The product does not exist.'));
+ throw new LocalizedException(__('The product does not exist.'));
}
return $this;
}
From 35620e2723c70ee1284682fe4abd2504594e2e6b Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Tue, 27 Aug 2019 11:04:42 +0300
Subject: [PATCH 142/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix strict types.
---
app/code/Magento/Catalog/Model/ResourceModel/Category.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
index d50570930923e..b6f565f5fae4e 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
@@ -355,7 +355,7 @@ protected function _getMaxPosition($path)
{
$connection = $this->getConnection();
$positionField = $connection->quoteIdentifier('position');
- $level = count(explode('/', $path));
+ $level = count(explode('/', (string)$path));
$bind = ['c_level' => $level, 'c_path' => $path . '/%'];
$select = $connection->select()->from(
$this->getTable('catalog_category_entity'),
@@ -720,7 +720,7 @@ public function getCategories($parent, $recursionLevel = 0, $sorted = false, $as
*/
public function getParentCategories($category)
{
- $pathIds = array_reverse(explode(',', $category->getPathInStore()));
+ $pathIds = array_reverse(explode(',', (string)$category->getPathInStore()));
/** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categories */
$categories = $this->_categoryCollectionFactory->create();
return $categories->setStore(
From 665aad5966ff70c0fdbda79786062a97128ff8d8 Mon Sep 17 00:00:00 2001
From: yuriichayka
Date: Tue, 27 Aug 2019 14:11:14 +0300
Subject: [PATCH 143/593] magento/magento2#23279: Integration test for
unsubscribing from 'Back in stock notification' Added
customer_unsubscribe_stock_rollback.php for cleaning entries
---
.../customer_unsubscribe_stock_rollback.php | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock_rollback.php
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock_rollback.php b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock_rollback.php
new file mode 100644
index 0000000000000..2952dfbca11e4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/customer_unsubscribe_stock_rollback.php
@@ -0,0 +1,20 @@
+get(Stock::class);
+
+$productRepository = $objectManager->create(ProductRepositoryInterface::class);
+$productId = $productRepository->get('simple-out-of-stock')->getId();
+
+$resource->getConnection()->delete(
+ $resource->getMainTable(),
+ 'product_id = ' . $productId
+);
From 211f2a3860562c73a9f65fbaeea2761411bda213 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Tue, 27 Aug 2019 09:28:07 -0500
Subject: [PATCH 144/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
- Add relevance to sort options
- Refactor and decouple searching and filtering
---
.../Products/DataProvider/Product.php | 30 ++--
.../Product/CollectionPostProcessor.php | 42 +++++
.../Products/DataProvider/ProductSearch.php | 144 ++++++++++++++++++
.../Products/Query/FieldSelection.php | 93 +++++++++++
.../Model/Resolver/Products/Query/Filter.php | 69 +--------
.../Model/Resolver/Products/Query/Search.php | 88 +++++------
.../CatalogGraphQl/etc/schema.graphqls | 1 +
7 files changed, 339 insertions(+), 128 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionPostProcessor.php
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product.php
index e5e0d1aea4285..2076ec6726988 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product.php
@@ -8,6 +8,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider;
use Magento\Catalog\Model\Product\Visibility;
+use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessor;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory;
@@ -32,7 +33,12 @@ class Product
/**
* @var CollectionProcessorInterface
*/
- private $collectionProcessor;
+ private $collectionPreProcessor;
+
+ /**
+ * @var CollectionPostProcessor
+ */
+ private $collectionPostProcessor;
/**
* @var Visibility
@@ -44,17 +50,20 @@ class Product
* @param ProductSearchResultsInterfaceFactory $searchResultsFactory
* @param Visibility $visibility
* @param CollectionProcessorInterface $collectionProcessor
+ * @param CollectionPostProcessor $collectionPostProcessor
*/
public function __construct(
CollectionFactory $collectionFactory,
ProductSearchResultsInterfaceFactory $searchResultsFactory,
Visibility $visibility,
- CollectionProcessorInterface $collectionProcessor
+ CollectionProcessorInterface $collectionProcessor,
+ CollectionPostProcessor $collectionPostProcessor
) {
$this->collectionFactory = $collectionFactory;
$this->searchResultsFactory = $searchResultsFactory;
$this->visibility = $visibility;
- $this->collectionProcessor = $collectionProcessor;
+ $this->collectionPreProcessor = $collectionProcessor;
+ $this->collectionPostProcessor = $collectionPostProcessor;
}
/**
@@ -75,7 +84,7 @@ public function getList(
/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
$collection = $this->collectionFactory->create();
- $this->collectionProcessor->process($collection, $searchCriteria, $attributes);
+ $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes);
if (!$isChildSearch) {
$visibilityIds = $isSearch
@@ -83,18 +92,9 @@ public function getList(
: $this->visibility->getVisibleInCatalogIds();
$collection->setVisibility($visibilityIds);
}
- $collection->load();
- // Methods that perform extra fetches post-load
- if (in_array('media_gallery_entries', $attributes)) {
- $collection->addMediaGalleryData();
- }
- if (in_array('media_gallery', $attributes)) {
- $collection->addMediaGalleryData();
- }
- if (in_array('options', $attributes)) {
- $collection->addOptionsToResult();
- }
+ $collection->load();
+ $this->collectionPostProcessor->process($collection, $attributes);
$searchResult = $this->searchResultsFactory->create();
$searchResult->setSearchCriteria($searchCriteria);
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionPostProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionPostProcessor.php
new file mode 100644
index 0000000000000..fadf22e7643af
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionPostProcessor.php
@@ -0,0 +1,42 @@
+isLoaded()) {
+ $collection->load();
+ }
+ // Methods that perform extra fetches post-load
+ if (in_array('media_gallery_entries', $attributeNames)) {
+ $collection->addMediaGalleryData();
+ }
+ if (in_array('media_gallery', $attributeNames)) {
+ $collection->addMediaGalleryData();
+ }
+ if (in_array('options', $attributeNames)) {
+ $collection->addOptionsToResult();
+ }
+
+ return $collection;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
new file mode 100644
index 0000000000000..8c5fe42c730f6
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
@@ -0,0 +1,144 @@
+collectionFactory = $collectionFactory;
+ $this->searchResultsFactory = $searchResultsFactory;
+ $this->collectionPreProcessor = $collectionPreProcessor;
+ $this->collectionPostProcessor = $collectionPostProcessor;
+ $this->searchResultApplierFactory = $searchResultsApplierFactory;
+ }
+
+ /**
+ * Get list of product data with full data set. Adds eav attributes to result set from passed in array
+ *
+ * @param SearchCriteriaInterface $searchCriteria
+ * @param SearchResultInterface $searchResult
+ * @param array $attributes
+ * @return SearchResultsInterface
+ */
+ public function getList(
+ SearchCriteriaInterface $searchCriteria,
+ SearchResultsInterface $searchResult,
+ array $attributes = []
+ ): SearchResultsInterface {
+ /** @var Collection $collection */
+ $collection = $this->collectionFactory->create();
+
+ //Join search results
+ $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply();
+
+ $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes);
+ $collection->load();
+ $this->collectionPostProcessor->process($collection, $attributes);
+
+ $searchResult = $this->searchResultsFactory->create();
+ $searchResult->setSearchCriteria($searchCriteria);
+ $searchResult->setItems($collection->getItems());
+ $searchResult->setTotalCount($collection->getSize());
+ return $searchResult;
+ }
+
+ /**
+ * Create searchResultApplier
+ *
+ * @param SearchResultInterface $searchResult
+ * @param Collection $collection
+ * @param array $orders
+ * @return SearchResultApplierInterface
+ */
+ private function getSearchResultsApplier(
+ SearchResultInterface $searchResult,
+ Collection $collection,
+ array $orders
+ ): SearchResultApplierInterface {
+ return $this->searchResultApplierFactory->create(
+ [
+ 'collection' => $collection,
+ 'searchResult' => $searchResult,
+ 'orders' => $orders
+ ]
+ );
+ }
+
+ /**
+ * Format sort orders into associative array
+ *
+ * E.g. ['field1' => 'DESC', 'field2' => 'ASC", ...]
+ *
+ * @param SearchCriteriaInterface $searchCriteria
+ * @return array
+ */
+ private function getSortOrderArray(SearchCriteriaInterface $searchCriteria)
+ {
+ $ordersArray = [];
+ $sortOrders = $searchCriteria->getSortOrders();
+ if (is_array($sortOrders)) {
+ foreach ($sortOrders as $sortOrder) {
+ $ordersArray[$sortOrder->getField()] = $sortOrder->getDirection();
+ }
+ }
+
+ return $ordersArray;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php
new file mode 100644
index 0000000000000..3912bab05ebbe
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php
@@ -0,0 +1,93 @@
+fieldTranslator = $fieldTranslator;
+ }
+
+ /**
+ * Get requested fields from products query
+ *
+ * @param ResolveInfo $resolveInfo
+ * @return string[]
+ */
+ public function getProductsFieldSelection(ResolveInfo $resolveInfo): array
+ {
+ return $this->getProductFields($resolveInfo);
+ }
+
+ /**
+ * Return field names for all requested product fields.
+ *
+ * @param ResolveInfo $info
+ * @return string[]
+ */
+ private function getProductFields(ResolveInfo $info): array
+ {
+ $fieldNames = [];
+ foreach ($info->fieldNodes as $node) {
+ if ($node->name->value !== 'products') {
+ continue;
+ }
+ foreach ($node->selectionSet->selections as $selection) {
+ if ($selection->name->value !== 'items') {
+ continue;
+ }
+ $fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames);
+ }
+ }
+
+ $fieldNames = array_merge(...$fieldNames);
+
+ return $fieldNames;
+ }
+
+ /**
+ * Collect field names for each node in selection
+ *
+ * @param SelectionNode $selection
+ * @param array $fieldNames
+ * @return array
+ */
+ private function collectProductFieldNames(SelectionNode $selection, array $fieldNames = []): array
+ {
+ foreach ($selection->selectionSet->selections as $itemSelection) {
+ if ($itemSelection->kind === 'InlineFragment') {
+ foreach ($itemSelection->selectionSet->selections as $inlineSelection) {
+ if ($inlineSelection->kind === 'InlineFragment') {
+ continue;
+ }
+ $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value);
+ }
+ continue;
+ }
+ $fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value);
+ }
+
+ return $fieldNames;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php
index 6771777341281..cc25af44fdfbe 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php
@@ -7,13 +7,11 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query;
-use GraphQL\Language\AST\SelectionNode;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory;
-use Magento\Framework\GraphQl\Query\FieldTranslator;
/**
* Retrieve filtered product data based off given search criteria in a format that GraphQL can interpret.
@@ -31,31 +29,31 @@ class Filter
private $productDataProvider;
/**
- * @var FieldTranslator
+ * @var \Magento\Catalog\Model\Layer\Resolver
*/
- private $fieldTranslator;
+ private $layerResolver;
/**
- * @var \Magento\Catalog\Model\Layer\Resolver
+ * FieldSelection
*/
- private $layerResolver;
+ private $fieldSelection;
/**
* @param SearchResultFactory $searchResultFactory
* @param Product $productDataProvider
* @param \Magento\Catalog\Model\Layer\Resolver $layerResolver
- * @param FieldTranslator $fieldTranslator
+ * @param FieldSelection $fieldSelection
*/
public function __construct(
SearchResultFactory $searchResultFactory,
Product $productDataProvider,
\Magento\Catalog\Model\Layer\Resolver $layerResolver,
- FieldTranslator $fieldTranslator
+ FieldSelection $fieldSelection
) {
$this->searchResultFactory = $searchResultFactory;
$this->productDataProvider = $productDataProvider;
- $this->fieldTranslator = $fieldTranslator;
$this->layerResolver = $layerResolver;
+ $this->fieldSelection = $fieldSelection;
}
/**
@@ -71,7 +69,7 @@ public function getResult(
ResolveInfo $info,
bool $isSearch = false
): SearchResult {
- $fields = $this->getProductFields($info);
+ $fields = $this->fieldSelection->getProductsFieldSelection($info);
$products = $this->productDataProvider->getList($searchCriteria, $fields, $isSearch);
$productArray = [];
/** @var \Magento\Catalog\Model\Product $product */
@@ -87,55 +85,4 @@ public function getResult(
]
);
}
-
- /**
- * Return field names for all requested product fields.
- *
- * @param ResolveInfo $info
- * @return string[]
- */
- private function getProductFields(ResolveInfo $info) : array
- {
- $fieldNames = [];
- foreach ($info->fieldNodes as $node) {
- if ($node->name->value !== 'products') {
- continue;
- }
- foreach ($node->selectionSet->selections as $selection) {
- if ($selection->name->value !== 'items') {
- continue;
- }
- $fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames);
- }
- }
-
- $fieldNames = array_merge(...$fieldNames);
-
- return $fieldNames;
- }
-
- /**
- * Collect field names for each node in selection
- *
- * @param SelectionNode $selection
- * @param array $fieldNames
- * @return array
- */
- private function collectProductFieldNames(SelectionNode $selection, array $fieldNames = []): array
- {
- foreach ($selection->selectionSet->selections as $itemSelection) {
- if ($itemSelection->kind === 'InlineFragment') {
- foreach ($itemSelection->selectionSet->selections as $inlineSelection) {
- if ($inlineSelection->kind === 'InlineFragment') {
- continue;
- }
- $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value);
- }
- continue;
- }
- $fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value);
- }
-
- return $fieldNames;
- }
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index c7dd2a8b05b79..a6d7f8641bd86 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -7,9 +7,9 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query;
+use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Api\Search\SearchCriteriaInterface;
-use Magento\CatalogGraphQl\Model\Resolver\Products\SearchCriteria\Helper\Filter as FilterHelper;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory;
use Magento\Search\Api\SearchInterface;
@@ -25,26 +25,11 @@ class Search
*/
private $search;
- /**
- * @var FilterHelper
- */
- private $filterHelper;
-
- /**
- * @var Filter
- */
- private $filterQuery;
-
/**
* @var SearchResultFactory
*/
private $searchResultFactory;
- /**
- * @var \Magento\Framework\EntityManager\MetadataPool
- */
- private $metadataPool;
-
/**
* @var \Magento\Search\Model\Search\PageSizeProvider
*/
@@ -55,31 +40,38 @@ class Search
*/
private $searchCriteriaFactory;
+ /**
+ * @var FieldSelection
+ */
+ private $fieldSelection;
+
+ /**
+ * @var ProductSearch
+ */
+ private $productsProvider;
+
/**
* @param SearchInterface $search
- * @param FilterHelper $filterHelper
- * @param Filter $filterQuery
* @param SearchResultFactory $searchResultFactory
- * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
* @param \Magento\Search\Model\Search\PageSizeProvider $pageSize
* @param SearchCriteriaInterfaceFactory $searchCriteriaFactory
+ * @param FieldSelection $fieldSelection
+ * @param ProductSearch $productsProvider
*/
public function __construct(
SearchInterface $search,
- FilterHelper $filterHelper,
- Filter $filterQuery,
SearchResultFactory $searchResultFactory,
- \Magento\Framework\EntityManager\MetadataPool $metadataPool,
\Magento\Search\Model\Search\PageSizeProvider $pageSize,
- SearchCriteriaInterfaceFactory $searchCriteriaFactory
+ SearchCriteriaInterfaceFactory $searchCriteriaFactory,
+ FieldSelection $fieldSelection,
+ ProductSearch $productsProvider
) {
$this->search = $search;
- $this->filterHelper = $filterHelper;
- $this->filterQuery = $filterQuery;
$this->searchResultFactory = $searchResultFactory;
- $this->metadataPool = $metadataPool;
$this->pageSizeProvider = $pageSize;
$this->searchCriteriaFactory = $searchCriteriaFactory;
+ $this->fieldSelection = $fieldSelection;
+ $this->productsProvider = $productsProvider;
}
/**
@@ -87,20 +79,15 @@ public function __construct(
*
* @param SearchCriteriaInterface $searchCriteria
* @param ResolveInfo $info
- * @param array $args
* @return SearchResult
* @throws \Exception
*/
public function getResult(
SearchCriteriaInterface $searchCriteria,
- ResolveInfo $info,
- array $args = []
+ ResolveInfo $info
): SearchResult {
- $idField = $this->metadataPool->getMetadata(
- \Magento\Catalog\Api\Data\ProductInterface::class
- )->getIdentifierField();
+ $queryFields = $this->fieldSelection->getProductsFieldSelection($info);
- $isSearch = isset($args['search']);
$realPageSize = $searchCriteria->getPageSize();
$realCurrentPage = $searchCriteria->getCurrentPage();
// Current page must be set to 0 and page size to max for search to grab all ID's as temporary workaround
@@ -109,35 +96,32 @@ public function getResult(
$searchCriteria->setCurrentPage(0);
$itemsResults = $this->search->search($searchCriteria);
- $ids = [];
- $searchIds = [];
- foreach ($itemsResults->getItems() as $item) {
- $ids[$item->getId()] = null;
- $searchIds[] = $item->getId();
- }
-
- $searchCriteriaIds = $this->searchCriteriaFactory->create();
- $filter = $this->filterHelper->generate($idField, 'in', $searchIds);
- $searchCriteriaIds = $this->filterHelper->add($searchCriteriaIds, $filter);
- $searchCriteriaIds->setSortOrders($searchCriteria->getSortOrders());
- $searchCriteriaIds->setPageSize($realPageSize);
- $searchCriteriaIds->setCurrentPage($realCurrentPage);
+ //Create copy of search criteria without conditions (conditions will be applied by joining search result)
+ $searchCriteriaCopy = $this->searchCriteriaFactory->create()
+ ->setSortOrders($searchCriteria->getSortOrders())
+ ->setPageSize($realPageSize)
+ ->setCurrentPage($realCurrentPage);
- $searchResult = $this->filterQuery->getResult($searchCriteriaIds, $info, $isSearch);
+ $searchResults = $this->productsProvider->getList($searchCriteriaCopy, $itemsResults, $queryFields);
- $searchCriteria->setPageSize($realPageSize);
- $searchCriteria->setCurrentPage($realCurrentPage);
//possible division by 0
if ($realPageSize) {
- $maxPages = (int)ceil($searchResult->getTotalCount() / $realPageSize);
+ $maxPages = (int)ceil($searchResults->getTotalCount() / $realPageSize);
} else {
$maxPages = 0;
}
+ $productArray = [];
+ /** @var \Magento\Catalog\Model\Product $product */
+ foreach ($searchResults->getItems() as $product) {
+ $productArray[$product->getId()] = $product->getData();
+ $productArray[$product->getId()]['model'] = $product;
+ }
+
return $this->searchResultFactory->create(
[
- 'totalCount' => $searchResult->getTotalCount(),
- 'productsSearchResult' => $searchResult->getProductsSearchResult(),
+ 'totalCount' => $searchResults->getTotalCount(),
+ 'productsSearchResult' => $productArray,
'searchAggregation' => $itemsResults->getAggregations(),
'pageSize' => $realPageSize,
'currentPage' => $realCurrentPage,
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index aadd43fafbd7f..9d8b6f69f5842 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -374,6 +374,7 @@ input ProductSortInput @deprecated(reason: "Attributes used in this input are ha
input ProductAttributeSortInput @doc(description: "ProductAttributeSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order. It's possible to sort products using searchable attributes with enabled 'Use in Filter Options' option")
{
position: SortEnum @doc(description: "The position of products")
+ relevance: SortEnum @doc(description: "The search relevance score (default)")
}
type MediaGalleryEntry @doc(description: "MediaGalleryEntry defines characteristics about images and videos associated with a specific product.") {
From 4d6a89c6b97deb4e21c23a944288004a367505bc Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Tue, 27 Aug 2019 10:10:49 -0500
Subject: [PATCH 145/593] MC-19572: Fix filtering by attribute of type
select/multiselect using filter input type in
- fix unit test
---
.../Adapter/Mysql/Filter/PreprocessorTest.php | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php
index 7e3de7534e8c4..a79ffcc33cabe 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php
@@ -129,7 +129,7 @@ protected function setUp()
->getMock();
$this->connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class)
->disableOriginalConstructor()
- ->setMethods(['select', 'getIfNullSql', 'quote'])
+ ->setMethods(['select', 'getIfNullSql', 'quote', 'quoteInto'])
->getMockForAbstractClass();
$this->select = $this->getMockBuilder(\Magento\Framework\DB\Select::class)
->disableOriginalConstructor()
@@ -222,9 +222,10 @@ public function testProcessPrice()
public function processCategoryIdsDataProvider()
{
return [
- ['5', 'category_ids_index.category_id = 5'],
- [3, 'category_ids_index.category_id = 3'],
- ["' and 1 = 0", 'category_ids_index.category_id = 0'],
+ ['5', "category_ids_index.category_id in ('5')"],
+ [3, "category_ids_index.category_id in (3)"],
+ ["' and 1 = 0", "category_ids_index.category_id in ('\' and 1 = 0')"],
+ [['5', '10'], "category_ids_index.category_id in ('5', '10')"]
];
}
@@ -251,6 +252,12 @@ public function testProcessCategoryIds($categoryId, $expectedResult)
->with(\Magento\Catalog\Model\Product::ENTITY, 'category_ids')
->will($this->returnValue($this->attribute));
+ $this->connection
+ ->expects($this->once())
+ ->method('quoteInto')
+ ->with('category_ids_index.category_id in (?)', $categoryId)
+ ->willReturn($expectedResult);
+
$actualResult = $this->target->process($this->filter, $isNegation, $query);
$this->assertSame($expectedResult, $this->removeWhitespaces($actualResult));
}
From 865566d79282333cc6f5c75f2730b7ffe032e35f Mon Sep 17 00:00:00 2001
From: Prabhu Ram
Date: Tue, 27 Aug 2019 12:05:42 -0500
Subject: [PATCH 146/593] MC-19618: Update schema for layered navigation output
- Schema changes, implementation and static fixes
---
.../Category/Query/CategoryAttributeQuery.php | 6 +-
.../DataProvider/CategoryAttributesMapper.php | 15 ++--
.../LayeredNavigation/Builder/Attribute.php | 58 +++++-----------
.../LayeredNavigation/Builder/Category.php | 49 ++++---------
.../Builder/Formatter/LayerFormatter.php | 48 +++++++++++++
.../LayeredNavigation/Builder/Price.php | 53 +++++----------
.../Model/Resolver/Aggregations.php | 68 +++++++++++++++++++
.../Model/Resolver/LayerFilters.php | 25 +------
.../CatalogGraphQl/etc/schema.graphqls | 16 ++++-
9 files changed, 194 insertions(+), 144 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Formatter/LayerFormatter.php
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Aggregations.php
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php b/app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php
index 0b796c1457254..e3dfa38c78258 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Category/Query/CategoryAttributeQuery.php
@@ -49,9 +49,11 @@ public function getQuery(array $categoryIds, array $categoryAttributes, int $sto
{
$categoryAttributes = \array_merge($categoryAttributes, self::$requiredAttributes);
- $attributeQuery = $this->attributeQueryFactory->create([
+ $attributeQuery = $this->attributeQueryFactory->create(
+ [
'entityType' => CategoryInterface::class
- ]);
+ ]
+ );
return $attributeQuery->getQuery($categoryIds, $categoryAttributes, $storeId);
}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php b/app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php
index 1f8aa38d5b939..ea3c0b608d212 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/CategoryAttributesMapper.php
@@ -65,12 +65,15 @@ private function formatAttributes(array $attributes): array
$arrayTypeAttributes = $this->getFieldsOfArrayType();
return $arrayTypeAttributes
- ? array_map(function ($data) use ($arrayTypeAttributes) {
- foreach ($arrayTypeAttributes as $attributeCode) {
- $data[$attributeCode] = $this->valueToArray($data[$attributeCode] ?? null);
- }
- return $data;
- }, $attributes)
+ ? array_map(
+ function ($data) use ($arrayTypeAttributes) {
+ foreach ($arrayTypeAttributes as $attributeCode) {
+ $data[$attributeCode] = $this->valueToArray($data[$attributeCode] ?? null);
+ }
+ return $data;
+ },
+ $attributes
+ )
: $attributes;
}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
index 82d167e323fc8..89df1be8d59f2 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
@@ -12,6 +12,7 @@
use Magento\Framework\Api\Search\AggregationInterface;
use Magento\Framework\Api\Search\AggregationValueInterface;
use Magento\Framework\Api\Search\BucketInterface;
+use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Formatter\LayerFormatter;
/**
* @inheritdoc
@@ -35,6 +36,11 @@ class Attribute implements LayerBuilderInterface
*/
private $attributeOptionProvider;
+ /**
+ * @var LayerFormatter
+ */
+ private $layerFormatter;
+
/**
* @var array
*/
@@ -45,13 +51,16 @@ class Attribute implements LayerBuilderInterface
/**
* @param AttributeOptionProvider $attributeOptionProvider
- * @param array $bucketNameFilter List with buckets name to be removed from filter
+ * @param LayerFormatter $layerFormatter
+ * @param array $bucketNameFilter
*/
public function __construct(
AttributeOptionProvider $attributeOptionProvider,
+ LayerFormatter $layerFormatter,
$bucketNameFilter = []
) {
$this->attributeOptionProvider = $attributeOptionProvider;
+ $this->layerFormatter = $layerFormatter;
$this->bucketNameFilter = \array_merge($this->bucketNameFilter, $bucketNameFilter);
}
@@ -71,7 +80,7 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
$attributeCode = \preg_replace('~_bucket$~', '', $bucketName);
$attribute = $attributeOptions[$attributeCode] ?? [];
- $result[$bucketName] = $this->buildLayer(
+ $result[$bucketName] = $this->layerFormatter->buildLayer(
$attribute['attribute_label'] ?? $bucketName,
\count($bucket->getValues()),
$attribute['attribute_code'] ?? $bucketName
@@ -79,7 +88,7 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
foreach ($bucket->getValues() as $value) {
$metrics = $value->getMetrics();
- $result[$bucketName]['filter_items'][] = $this->buildItem(
+ $result[$bucketName]['options'][] = $this->layerFormatter->buildItem(
$attribute['options'][$metrics['value']] ?? $metrics['value'],
$metrics['value'],
$metrics['count']
@@ -109,40 +118,6 @@ private function getAttributeBuckets(AggregationInterface $aggregation)
}
}
- /**
- * Format layer data
- *
- * @param string $layerName
- * @param string $itemsCount
- * @param string $requestName
- * @return array
- */
- private function buildLayer($layerName, $itemsCount, $requestName): array
- {
- return [
- 'name' => $layerName,
- 'filter_items_count' => $itemsCount,
- 'request_var' => $requestName
- ];
- }
-
- /**
- * Format layer item data
- *
- * @param string $label
- * @param string|int $value
- * @param string|int $count
- * @return array
- */
- private function buildItem($label, $value, $count): array
- {
- return [
- 'label' => $label,
- 'value_string' => $value,
- 'items_count' => $count,
- ];
- }
-
/**
* Check that bucket contains data
*
@@ -165,9 +140,12 @@ private function getAttributeOptions(AggregationInterface $aggregation): array
{
$attributeOptionIds = [];
foreach ($this->getAttributeBuckets($aggregation) as $bucket) {
- $attributeOptionIds[] = \array_map(function (AggregationValueInterface $value) {
- return $value->getValue();
- }, $bucket->getValues());
+ $attributeOptionIds[] = \array_map(
+ function (AggregationValueInterface $value) {
+ return $value->getValue();
+ },
+ $bucket->getValues()
+ );
}
if (!$attributeOptionIds) {
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
index c726f66e8c926..97644328abc2a 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
@@ -15,6 +15,7 @@
use Magento\Framework\Api\Search\AggregationValueInterface;
use Magento\Framework\Api\Search\BucketInterface;
use Magento\Framework\App\ResourceConnection;
+use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Formatter\LayerFormatter;
/**
* @inheritdoc
@@ -56,22 +57,30 @@ class Category implements LayerBuilderInterface
*/
private $rootCategoryProvider;
+ /**
+ * @var LayerFormatter
+ */
+ private $layerFormatter;
+
/**
* @param CategoryAttributeQuery $categoryAttributeQuery
* @param CategoryAttributesMapper $attributesMapper
* @param RootCategoryProvider $rootCategoryProvider
* @param ResourceConnection $resourceConnection
+ * @param LayerFormatter $layerFormatter
*/
public function __construct(
CategoryAttributeQuery $categoryAttributeQuery,
CategoryAttributesMapper $attributesMapper,
RootCategoryProvider $rootCategoryProvider,
- ResourceConnection $resourceConnection
+ ResourceConnection $resourceConnection,
+ LayerFormatter $layerFormatter
) {
$this->categoryAttributeQuery = $categoryAttributeQuery;
$this->attributesMapper = $attributesMapper;
$this->resourceConnection = $resourceConnection;
$this->rootCategoryProvider = $rootCategoryProvider;
+ $this->layerFormatter = $layerFormatter;
}
/**
@@ -105,7 +114,7 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
return [];
}
- $result = $this->buildLayer(
+ $result = $this->layerFormatter->buildLayer(
self::$bucketMap[self::CATEGORY_BUCKET]['label'],
\count($categoryIds),
self::$bucketMap[self::CATEGORY_BUCKET]['request_name']
@@ -116,7 +125,7 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
if (!\in_array($categoryId, $categoryIds, true)) {
continue ;
}
- $result['filter_items'][] = $this->buildItem(
+ $result['options'][] = $this->layerFormatter->buildItem(
$categoryLabels[$categoryId] ?? $categoryId,
$categoryId,
$value->getMetrics()['count']
@@ -126,40 +135,6 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
return [$result];
}
- /**
- * Format layer data
- *
- * @param string $layerName
- * @param string $itemsCount
- * @param string $requestName
- * @return array
- */
- private function buildLayer($layerName, $itemsCount, $requestName): array
- {
- return [
- 'name' => $layerName,
- 'filter_items_count' => $itemsCount,
- 'request_var' => $requestName
- ];
- }
-
- /**
- * Format layer item data
- *
- * @param string $label
- * @param string|int $value
- * @param string|int $count
- * @return array
- */
- private function buildItem($label, $value, $count): array
- {
- return [
- 'label' => $label,
- 'value_string' => $value,
- 'items_count' => $count,
- ];
- }
-
/**
* Check that bucket contains data
*
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Formatter/LayerFormatter.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Formatter/LayerFormatter.php
new file mode 100644
index 0000000000000..72495f8ef8524
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Formatter/LayerFormatter.php
@@ -0,0 +1,48 @@
+ $layerName,
+ 'count' => $itemsCount,
+ 'attribute_code' => $requestName
+ ];
+ }
+
+ /**
+ * Format layer item data
+ *
+ * @param string $label
+ * @param string|int $value
+ * @param string|int $count
+ * @return array
+ */
+ public function buildItem($label, $value, $count): array
+ {
+ return [
+ 'label' => $label,
+ 'value' => $value,
+ 'count' => $count,
+ ];
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
index 77f44afb4f679..e84a8f1b11078 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
@@ -10,6 +10,7 @@
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilderInterface;
use Magento\Framework\Api\Search\AggregationInterface;
use Magento\Framework\Api\Search\BucketInterface;
+use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Formatter\LayerFormatter;
/**
* @inheritdoc
@@ -21,6 +22,11 @@ class Price implements LayerBuilderInterface
*/
private const PRICE_BUCKET = 'price_bucket';
+ /**
+ * @var LayerFormatter
+ */
+ private $layerFormatter;
+
/**
* @var array
*/
@@ -31,6 +37,15 @@ class Price implements LayerBuilderInterface
],
];
+ /**
+ * @param LayerFormatter $layerFormatter
+ */
+ public function __construct(
+ LayerFormatter $layerFormatter
+ ) {
+ $this->layerFormatter = $layerFormatter;
+ }
+
/**
* @inheritdoc
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
@@ -42,7 +57,7 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
return [];
}
- $result = $this->buildLayer(
+ $result = $this->layerFormatter->buildLayer(
self::$bucketMap[self::PRICE_BUCKET]['label'],
\count($bucket->getValues()),
self::$bucketMap[self::PRICE_BUCKET]['request_name']
@@ -50,7 +65,7 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
foreach ($bucket->getValues() as $value) {
$metrics = $value->getMetrics();
- $result['filter_items'][] = $this->buildItem(
+ $result['options'][] = $this->layerFormatter->buildItem(
\str_replace('_', '-', $metrics['value']),
$metrics['value'],
$metrics['count']
@@ -60,40 +75,6 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
return [$result];
}
- /**
- * Format layer data
- *
- * @param string $layerName
- * @param string $itemsCount
- * @param string $requestName
- * @return array
- */
- private function buildLayer($layerName, $itemsCount, $requestName): array
- {
- return [
- 'name' => $layerName,
- 'filter_items_count' => $itemsCount,
- 'request_var' => $requestName
- ];
- }
-
- /**
- * Format layer item data
- *
- * @param string $label
- * @param string|int $value
- * @param string|int $count
- * @return array
- */
- private function buildItem($label, $value, $count): array
- {
- return [
- 'label' => $label,
- 'value_string' => $value,
- 'items_count' => $count,
- ];
- }
-
/**
* Check that bucket contains data
*
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Aggregations.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Aggregations.php
new file mode 100644
index 0000000000000..47a1d1f977f9b
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Aggregations.php
@@ -0,0 +1,68 @@
+filtersDataProvider = $filtersDataProvider;
+ $this->layerBuilder = $layerBuilder;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!isset($value['layer_type']) || !isset($value['search_result'])) {
+ return null;
+ }
+
+ $aggregations = $value['search_result']->getSearchAggregation();
+
+ if ($aggregations) {
+ /** @var StoreInterface $store */
+ $store = $context->getExtensionAttributes()->getStore();
+ $storeId = (int)$store->getId();
+ return $this->layerBuilder->build($aggregations, $storeId);
+ } else {
+ return [];
+ }
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
index 6ef4e72627e82..0ec7e12e42d55 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php
@@ -10,8 +10,6 @@
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilder;
-use Magento\Store\Api\Data\StoreInterface;
/**
* Layered navigation filters resolver, used for GraphQL request processing.
@@ -23,21 +21,13 @@ class LayerFilters implements ResolverInterface
*/
private $filtersDataProvider;
- /**
- * @var LayerBuilder
- */
- private $layerBuilder;
-
/**
* @param \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider
- * @param LayerBuilder $layerBuilder
*/
public function __construct(
- \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider,
- LayerBuilder $layerBuilder
+ \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider
) {
$this->filtersDataProvider = $filtersDataProvider;
- $this->layerBuilder = $layerBuilder;
}
/**
@@ -50,19 +40,10 @@ public function resolve(
array $value = null,
array $args = null
) {
- if (!isset($value['layer_type']) || !isset($value['search_result'])) {
+ if (!isset($value['layer_type'])) {
return null;
}
- $aggregations = $value['search_result']->getSearchAggregation();
-
- if ($aggregations) {
- /** @var StoreInterface $store */
- $store = $context->getExtensionAttributes()->getStore();
- $storeId = (int)$store->getId();
- return $this->layerBuilder->build($aggregations, $storeId);
- } else {
- return [];
- }
+ return $this->filtersDataProvider->getData($value['layer_type']);
}
}
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index aadd43fafbd7f..8900eb869d268 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -270,7 +270,8 @@ type Products @doc(description: "The Products object is the top-level object ret
items: [ProductInterface] @doc(description: "An array of products that match the specified search criteria.")
page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.")
total_count: Int @doc(description: "The number of products returned.")
- filters: [LayerFilter] @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\LayerFilters") @doc(description: "Layered navigation filters array.")
+ filters: [LayerFilter] @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\LayerFilters") @doc(description: "Layered navigation filters array.") @deprecated(reason: "Use aggregations instead")
+ aggregations: [Aggregation] @doc(description: "Layered navigation aggregations.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Aggregations")
sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFields")
}
@@ -401,6 +402,19 @@ interface LayerFilterItemInterface @typeResolver(class: "Magento\\CatalogGraphQl
items_count: Int @doc(description: "Count of items by filter.")
}
+type Aggregation {
+ count: Int @doc(description: "Count of filter items in filter group.")
+ label: String @doc(description: "Layered navigation filter name.")
+ attribute_code: String! @doc(description: "Attribute code of the filter item.")
+ options: [AggregationOption] @doc(description: "Array of aggregation options.")
+}
+
+type AggregationOption {
+ count: Int @doc(description: "Count of items by filter.")
+ label: String! @doc(description: "Filter label.")
+ value: String! @doc(description: "Value for filter request variable to be used in query.")
+}
+
type LayerFilterItem implements LayerFilterItemInterface {
}
From c4025da024cbdf7b2f9fa694caee9171d6e71bd1 Mon Sep 17 00:00:00 2001
From: Prabhu Ram
Date: Tue, 27 Aug 2019 12:49:19 -0500
Subject: [PATCH 147/593] MC-19618: Update schema for layered navigation output
- static fixes
---
app/code/Magento/CatalogGraphQl/composer.json | 1 +
.../Magento/GraphQl/Catalog/ProductSearchTest.php | 14 ++++++++------
...ts_with_layered_navigation_custom_attribute.php | 3 ++-
3 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/composer.json b/app/code/Magento/CatalogGraphQl/composer.json
index 13fcbe9a7d357..1582f29c25951 100644
--- a/app/code/Magento/CatalogGraphQl/composer.json
+++ b/app/code/Magento/CatalogGraphQl/composer.json
@@ -10,6 +10,7 @@
"magento/module-search": "*",
"magento/module-store": "*",
"magento/module-eav-graph-ql": "*",
+ "magento/module-catalog-search": "*",
"magento/framework": "*"
},
"suggest": {
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 169aba7c15734..990e2e0f31ac3 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -116,7 +116,8 @@ public function testLayeredNavigationWithConfigurableChildrenOutOfStock()
$query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
$response = $this->graphQlQuery($query);
- // 1 product is returned since only one child product with attribute option1 from 1st Configurable product is OOS
+ // 1 product is returned since only one child product with
+ // attribute option1 from 1st Configurable product is OOS
$this->assertEquals(1, $response['products']['total_count']);
// Custom attribute filter layer data
@@ -269,8 +270,9 @@ public function testFilterProductsByMultiSelectCustomAttribute()
/** @var AttributeOptionInterface[] $options */
$options = $attribute->getOptions();
array_shift($options);
- $optionValues = array();
- for ($i = 0; $i < count($options); $i++) {
+ $optionValues = [];
+ $count = count($options);
+ for ($i = 0; $i < $count; $i++) {
$optionValues[] = $options[$i]->getValue();
}
$query = <<assertEquals(2, $response['products']['total_count']);
$actualCategoryFilterItems = $response['products']['filters'][1]['filter_items'];
//Validate the number of categories/sub-categories that contain the products with the custom attribute
- $this->assertCount(6,$actualCategoryFilterItems);
+ $this->assertCount(6, $actualCategoryFilterItems);
$expectedCategoryFilterItems =
[
@@ -527,7 +529,7 @@ public function testFilterByCategoryIdAndCustomAttribute()
$categoryFilterItems[$index][0]['items_count'],
$actualCategoryFilterItems[$index]['items_count'],
'Products count in the category is incorrect'
- ) ;
+ );
}
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
index 6cd2819c2d296..864a931359bdd 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
@@ -115,7 +115,8 @@
'catalog_product',
$attributeSet->getId(),
$attributeSet->getDefaultGroupId(),
- $attribute1->getId());
+ $attribute1->getId()
+ );
}
$eavConfig->clear();
From 9f5714841e063f1d54b41a1b5374059552277162 Mon Sep 17 00:00:00 2001
From: Krissy Hiserote
Date: Tue, 27 Aug 2019 13:56:47 -0500
Subject: [PATCH 148/593] MC-16650: Product Attribute Type Price Not Displaying
- use a different request generator for price, fix tests, and code
---
.../Catalog/Model/Layer/FilterList.php | 4 +-
.../Test/Unit/Model/Layer/FilterListTest.php | 9 +-
.../Model/Layer/Filter/Decimal.php | 27 ++----
.../Model/Search/RequestGenerator.php | 9 +-
.../Model/Search/RequestGenerator/Price.php | 46 +++++++++++
.../Search/RequestGenerator/DecimalTest.php | 18 +---
.../Search/RequestGenerator/PriceTest.php | 82 +++++++++++++++++++
app/code/Magento/CatalogSearch/etc/di.xml | 1 +
8 files changed, 150 insertions(+), 46 deletions(-)
create mode 100644 app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php
create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php
diff --git a/app/code/Magento/Catalog/Model/Layer/FilterList.php b/app/code/Magento/Catalog/Model/Layer/FilterList.php
index 7f06c97d3e8d9..b8e9b8ad4aaa5 100644
--- a/app/code/Magento/Catalog/Model/Layer/FilterList.php
+++ b/app/code/Magento/Catalog/Model/Layer/FilterList.php
@@ -7,8 +7,6 @@
namespace Magento\Catalog\Model\Layer;
-use Magento\Catalog\Model\Product\Attribute\Backend\Price;
-
/**
* Layer navigation filters
*/
@@ -112,7 +110,7 @@ protected function getAttributeFilterClass(\Magento\Catalog\Model\ResourceModel\
{
$filterClassName = $this->filterTypes[self::ATTRIBUTE_FILTER];
- if ($attribute->getBackendModel() === Price::class) {
+ if ($attribute->getFrontendInput() === 'price') {
$filterClassName = $this->filterTypes[self::PRICE_FILTER];
} elseif ($attribute->getBackendType() === 'decimal') {
$filterClassName = $this->filterTypes[self::DECIMAL_FILTER];
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
index 2d3c764cb6907..6943bc27fd77c 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
@@ -8,7 +8,6 @@
namespace Magento\Catalog\Test\Unit\Model\Layer;
use \Magento\Catalog\Model\Layer\FilterList;
-use Magento\Catalog\Model\Product\Attribute\Backend\Price;
class FilterListTest extends \PHPUnit\Framework\TestCase
{
@@ -97,8 +96,8 @@ public function getFiltersDataProvider()
{
return [
[
- 'method' => 'getBackendModel',
- 'value' => Price::class,
+ 'method' => 'getFrontendInput',
+ 'value' => 'price',
'expectedClass' => 'PriceFilterClass',
],
[
@@ -107,8 +106,8 @@ public function getFiltersDataProvider()
'expectedClass' => 'DecimalFilterClass',
],
[
- 'method' => 'getBackendModel',
- 'value' => null,
+ 'method' => 'getFrontendInput',
+ 'value' => 'text',
'expectedClass' => 'AttributeFilterClass',
]
];
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
index 4c83c3f7184f1..596dc1bfc561d 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php
@@ -72,11 +72,17 @@ public function apply(\Magento\Framework\App\RequestInterface $request)
list($from, $to) = explode('-', $filter);
+ // When the range is 10-20 we only need to get products that are in the 10-19.99 range.
+ $toValue = $to;
+ if (!empty($toValue) && $from !== $toValue) {
+ $toValue -= 0.001;
+ }
+
$this->getLayer()
->getProductCollection()
->addFieldToFilter(
$this->getAttributeModel()->getAttributeCode(),
- ['from' => $from, 'to' => $this->getToRangeValue($from, $to)]
+ ['from' => $from, 'to' => $toValue]
);
$this->getLayer()->getState()->addFilter(
@@ -149,23 +155,4 @@ protected function renderRangeLabel($fromPrice, $toPrice)
return __('%1 - %2', $formattedFromPrice, $this->priceCurrency->format($toPrice));
}
}
-
- /**
- * Get the to range value
- *
- * When the range is 10-20 we only need to get products that are in the 10-19.99 range.
- * 20 should be in the next range group.
- *
- * @param float|string $from
- * @param float|string $to
- * @return float|string
- */
- private function getToRangeValue($from, $to)
- {
- if (!empty($to) && $from !== $to) {
- $to -= 0.001;
- }
-
- return $to;
- }
}
diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php
index 0adc2fcecbfa7..82264c5d4cc23 100644
--- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php
+++ b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\CatalogSearch\Model\Search;
use Magento\Catalog\Api\Data\EavAttributeInterface;
@@ -78,6 +80,7 @@ private function generateRequest($attributeType, $container, $useFulltext)
{
$request = [];
foreach ($this->getSearchableAttributes() as $attribute) {
+ /** @var $attribute Attribute */
if ($attribute->getData($attributeType)) {
if (!in_array($attribute->getAttributeCode(), ['price', 'category_ids'], true)) {
$queryName = $attribute->getAttributeCode() . '_query';
@@ -97,12 +100,14 @@ private function generateRequest($attributeType, $container, $useFulltext)
],
];
$bucketName = $attribute->getAttributeCode() . self::BUCKET_SUFFIX;
- $generator = $this->generatorResolver->getGeneratorForType($attribute->getBackendType());
+ $generatorType = $attribute->getFrontendInput() === 'price'
+ ? $attribute->getFrontendInput()
+ : $attribute->getBackendType();
+ $generator = $this->generatorResolver->getGeneratorForType($generatorType);
$request['filters'][$filterName] = $generator->getFilterData($attribute, $filterName);
$request['aggregations'][$bucketName] = $generator->getAggregationData($attribute, $bucketName);
}
}
- /** @var $attribute Attribute */
if (!$attribute->getIsSearchable() || in_array($attribute->getAttributeCode(), ['price', 'sku'], true)) {
// Some fields have their own specific handlers
continue;
diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php
new file mode 100644
index 0000000000000..7bf124844c618
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Price.php
@@ -0,0 +1,46 @@
+ FilterInterface::TYPE_RANGE,
+ 'name' => $filterName,
+ 'field' => $attribute->getAttributeCode(),
+ 'from' => '$' . $attribute->getAttributeCode() . '.from$',
+ 'to' => '$' . $attribute->getAttributeCode() . '.to$',
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getAggregationData(Attribute $attribute, $bucketName)
+ {
+ return [
+ 'type' => BucketInterface::TYPE_DYNAMIC,
+ 'name' => $bucketName,
+ 'field' => $attribute->getAttributeCode(),
+ 'method' => '$price_dynamic_algorithm$',
+ 'metric' => [['type' => 'count']],
+ ];
+ }
+}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php
index f04188fbf7bdd..350344372612a 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php
@@ -11,7 +11,6 @@
use Magento\CatalogSearch\Model\Search\RequestGenerator\Decimal;
use Magento\Framework\Search\Request\BucketInterface;
use Magento\Framework\Search\Request\FilterInterface;
-use Magento\Framework\App\Config\ScopeConfigInterface;
/**
* Test catalog search range request generator.
@@ -24,23 +23,14 @@ class DecimalTest extends \PHPUnit\Framework\TestCase
/** @var Attribute|\PHPUnit_Framework_MockObject_MockObject */
private $attribute;
- /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */
- private $scopeConfigMock;
-
protected function setUp()
{
$this->attribute = $this->getMockBuilder(Attribute::class)
->disableOriginalConstructor()
->setMethods(['getAttributeCode'])
->getMockForAbstractClass();
- $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)
- ->setMethods(['getValue'])
- ->getMockForAbstractClass();
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
- $this->decimal = $objectManager->getObject(
- Decimal::class,
- ['scopeConfig' => $this->scopeConfigMock]
- );
+ $this->decimal = $objectManager->getObject(Decimal::class);
}
public function testGetFilterData()
@@ -65,20 +55,16 @@ public function testGetAggregationData()
{
$bucketName = 'test_bucket_name';
$attributeCode = 'test_attribute_code';
- $method = 'manual';
$expected = [
'type' => BucketInterface::TYPE_DYNAMIC,
'name' => $bucketName,
'field' => $attributeCode,
- 'method' => $method,
+ 'method' => 'manual',
'metric' => [['type' => 'count']],
];
$this->attribute->expects($this->atLeastOnce())
->method('getAttributeCode')
->willReturn($attributeCode);
- $this->scopeConfigMock->expects($this->once())
- ->method('getValue')
- ->willReturn($method);
$actual = $this->decimal->getAggregationData($this->attribute, $bucketName);
$this->assertEquals($expected, $actual);
}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php
new file mode 100644
index 0000000000000..3635430197591
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/PriceTest.php
@@ -0,0 +1,82 @@
+attribute = $this->getMockBuilder(Attribute::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAttributeCode'])
+ ->getMockForAbstractClass();
+ $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)
+ ->setMethods(['getValue'])
+ ->getMockForAbstractClass();
+ $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->price = $objectManager->getObject(
+ Price::class,
+ ['scopeConfig' => $this->scopeConfigMock]
+ );
+ }
+
+ public function testGetFilterData()
+ {
+ $filterName = 'test_filter_name';
+ $attributeCode = 'test_attribute_code';
+ $expected = [
+ 'type' => FilterInterface::TYPE_RANGE,
+ 'name' => $filterName,
+ 'field' => $attributeCode,
+ 'from' => '$' . $attributeCode . '.from$',
+ 'to' => '$' . $attributeCode . '.to$',
+ ];
+ $this->attribute->expects($this->atLeastOnce())
+ ->method('getAttributeCode')
+ ->willReturn($attributeCode);
+ $actual = $this->price->getFilterData($this->attribute, $filterName);
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testGetAggregationData()
+ {
+ $bucketName = 'test_bucket_name';
+ $attributeCode = 'test_attribute_code';
+ $method = 'price_dynamic_algorithm';
+ $expected = [
+ 'type' => BucketInterface::TYPE_DYNAMIC,
+ 'name' => $bucketName,
+ 'field' => $attributeCode,
+ 'method' => '$'. $method . '$',
+ 'metric' => [['type' => 'count']],
+ ];
+ $this->attribute->expects($this->atLeastOnce())
+ ->method('getAttributeCode')
+ ->willReturn($attributeCode);
+ $actual = $this->price->getAggregationData($this->attribute, $bucketName);
+ $this->assertEquals($expected, $actual);
+ }
+}
diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml
index 7359bd6b454b9..6c9010f306e14 100644
--- a/app/code/Magento/CatalogSearch/etc/di.xml
+++ b/app/code/Magento/CatalogSearch/etc/di.xml
@@ -279,6 +279,7 @@
\Magento\CatalogSearch\Model\Search\RequestGenerator\General
- Magento\CatalogSearch\Model\Search\RequestGenerator\Decimal
+ - Magento\CatalogSearch\Model\Search\RequestGenerator\Price
From 39ca7234706d32a69eb01e175e4574cc0cb2b504 Mon Sep 17 00:00:00 2001
From: Lena Orobei
Date: Tue, 27 Aug 2019 14:19:49 -0500
Subject: [PATCH 149/593] magento/graphql-ce#816: Root category ID
---
.../Model/Category/GetRootCategoryId.php | 56 -------------------
.../Model/Resolver/CategoryRoot.php | 49 ----------------
.../Model/Resolver/CategoryTree.php | 37 ++----------
.../Model/Resolver/RootCategoryId.php | 26 +++++++++
.../CatalogGraphQl/etc/schema.graphqls | 2 +-
5 files changed, 32 insertions(+), 138 deletions(-)
delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
deleted file mode 100644
index 4da1443c9e05f..0000000000000
--- a/app/code/Magento/CatalogGraphQl/Model/Category/GetRootCategoryId.php
+++ /dev/null
@@ -1,56 +0,0 @@
-storeManager = $storeManager;
- }
-
- /**
- * Get Root Category Id
- *
- * @return int
- * @throws LocalizedException
- */
- public function execute()
- {
- if ($this->rootCategoryId == null) {
- try {
- $this->rootCategoryId = (int)$this->storeManager->getStore()->getRootCategoryId();
- } catch (NoSuchEntityException $noSuchEntityException) {
- throw new LocalizedException(__("Store does not exist."));
- }
- }
-
- return $this->rootCategoryId;
- }
-}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
deleted file mode 100644
index 9720a6d6244d5..0000000000000
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryRoot.php
+++ /dev/null
@@ -1,49 +0,0 @@
-getRootCategoryId = $getRootCategoryId;
- }
-
- /**
- * @inheritdoc
- */
- public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
- {
- $rootCategoryId = 0;
- try {
- $rootCategoryId = $this->getRootCategoryId->execute();
- } catch (LocalizedException $exception) {
- throw new GraphQlNoSuchEntityException(__('Store doesn\'t exist'));
- }
- return $rootCategoryId;
- }
-}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index f99e80eea493c..f72fb317ef3dc 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -7,10 +7,8 @@
namespace Magento\CatalogGraphQl\Model\Resolver;
-use Magento\Catalog\Model\Category;
use Magento\CatalogGraphQl\Model\Resolver\Category\CheckCategoryIsActive;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree;
-use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
@@ -40,45 +38,20 @@ class CategoryTree implements ResolverInterface
* @var CheckCategoryIsActive
*/
private $checkCategoryIsActive;
- /**
- * @var \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId
- */
- private $getRootCategoryId;
/**
* @param Products\DataProvider\CategoryTree $categoryTree
* @param ExtractDataFromCategoryTree $extractDataFromCategoryTree
* @param CheckCategoryIsActive $checkCategoryIsActive
- * @param \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId $getRootCategoryId
*/
public function __construct(
Products\DataProvider\CategoryTree $categoryTree,
ExtractDataFromCategoryTree $extractDataFromCategoryTree,
- CheckCategoryIsActive $checkCategoryIsActive,
- \Magento\CatalogGraphQl\Model\Category\GetRootCategoryId $getRootCategoryId
+ CheckCategoryIsActive $checkCategoryIsActive
) {
$this->categoryTree = $categoryTree;
$this->extractDataFromCategoryTree = $extractDataFromCategoryTree;
$this->checkCategoryIsActive = $checkCategoryIsActive;
- $this->getRootCategoryId = $getRootCategoryId;
- }
-
- /**
- * Get category id
- *
- * @param array $args
- * @return int
- * @throws GraphQlNoSuchEntityException
- */
- private function getCategoryId(array $args) : int
- {
- $rootCategoryId = 0;
- try {
- $rootCategoryId = isset($args['id']) ? (int)$args['id'] : $this->getRootCategoryId->execute();
- } catch (LocalizedException $exception) {
- throw new GraphQlNoSuchEntityException(__('Store doesn\'t exist'));
- }
- return $rootCategoryId;
}
/**
@@ -90,10 +63,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
return $value[$field->getName()];
}
- $rootCategoryId = $this->getCategoryId($args);
- if ($rootCategoryId !== $this->getRootCategoryId->execute() && $rootCategoryId > 0) {
- $this->checkCategoryIsActive->execute($rootCategoryId);
- }
+ $rootCategoryId = isset($args['id']) ? (int)$args['id'] :
+ $context->getExtensionAttributes()->getStore()->getRootCategoryId();
+ $this->checkCategoryIsActive->execute($rootCategoryId);
+
$categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId);
if (empty($categoriesTree) || ($categoriesTree->count() == 0)) {
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php
new file mode 100644
index 0000000000000..add47e3107ec9
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php
@@ -0,0 +1,26 @@
+getExtensionAttributes()->getStore()->getRootCategoryId();
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index 554ad4b45cf99..d8a3b5e744d17 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -416,7 +416,7 @@ type StoreConfig @doc(description: "The type contains information about a store
grid_per_page : Int @doc(description: "Products per Page on Grid Default Value.")
list_per_page : Int @doc(description: "Products per Page on List Default Value.")
catalog_default_sort_by : String @doc(description: "Default Sort By.")
- root_category_id: Int @doc(description: "The ID ot root category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryRoot")
+ root_category_id: Int @doc(description: "The ID of the root category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\RootCategoryId")
}
type ProductVideo @doc(description: "Contains information about a product video.") implements MediaGalleryInterface {
From 8f491d3dcc905b12b08b5327999ce51e658a34ac Mon Sep 17 00:00:00 2001
From: Rus0
Date: Tue, 27 Aug 2019 14:27:21 -0500
Subject: [PATCH 150/593] building errors
---
app/code/Magento/Wishlist/Model/Wishlist.php | 11 +-
.../Wishlist/Test/Unit/Model/WishlistTest.php | 173 ++++++++++++------
2 files changed, 122 insertions(+), 62 deletions(-)
diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php
index bf43fbd6f4363..0d9c74edb3d4c 100644
--- a/app/code/Magento/Wishlist/Model/Wishlist.php
+++ b/app/code/Magento/Wishlist/Model/Wishlist.php
@@ -176,6 +176,7 @@ class Wishlist extends AbstractModel implements IdentityInterface
* @param DateTime $dateTime
* @param ProductRepositoryInterface $productRepository
* @param StockItemRepository $stockItemRepository
+ * @param ScopeConfigInterface|null $scopeConfig
* @param bool $useCurrentWebsite
* @param array $data
* @param Json|null $serializer
@@ -400,7 +401,7 @@ public function getItem($itemId)
* Adding item to wishlist
*
* @param Item $item
- * @return $this
+ * @return $this
* @throws Exception
*/
public function addItem(Item $item)
@@ -424,7 +425,8 @@ public function addItem(Item $item)
* @return Item|string
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
- *@throws LocalizedException
+ * @throws LocalizedException
+ * @throws InvalidArgumentException
*/
public function addNewItem($product, $buyRequest = null, $forciblySetQty = false)
{
@@ -452,7 +454,8 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
throw new LocalizedException(__('Cannot specify product.'));
}
- $stockItem = $this->stockItemRepository->get($product->getId());
+ /** @var \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem */
+ $stockItem = $this->stockItemRepository->get($productId);
$showOutOfStock = $this->scopeConfig->isSetFlag(
Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
ScopeInterface::SCOPE_STORE
@@ -470,7 +473,7 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
if (!is_array($buyRequestData)) {
$isInvalidItemConfiguration = true;
}
- } catch (InvalidArgumentException $exception) {
+ } catch (Exception $exception) {
$isInvalidItemConfiguration = true;
}
if ($isInvalidItemConfiguration) {
diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
index ff8a3a3b87cec..cbbccf29e63b8 100644
--- a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
+++ b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
@@ -5,76 +5,103 @@
*/
namespace Magento\Wishlist\Test\Unit\Model;
+use ArrayIterator;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Helper\Product as HelperProduct;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Type\AbstractType;
+use Magento\Catalog\Model\ProductFactory;
+use Magento\CatalogInventory\Model\Stock\Item as StockItem;
+use Magento\CatalogInventory\Model\Stock\StockItemRepository;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\DataObject;
+use Magento\Framework\Event\ManagerInterface;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Math\Random;
+use Magento\Framework\Model\Context;
+use Magento\Framework\Registry;
+use Magento\Framework\Serialize\Serializer\Json;
+use Magento\Framework\Stdlib\DateTime;
+use Magento\Store\Model\StoreManagerInterface;
+use Magento\Wishlist\Helper\Data;
+use Magento\Wishlist\Model\Item;
+use Magento\Wishlist\Model\ItemFactory;
+use Magento\Wishlist\Model\ResourceModel\Item\Collection;
+use Magento\Wishlist\Model\ResourceModel\Item\CollectionFactory;
+use Magento\Wishlist\Model\ResourceModel\Wishlist as WishlistResource;
+use Magento\Wishlist\Model\ResourceModel\Wishlist\Collection as WishlistCollection;
use Magento\Wishlist\Model\Wishlist;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyFields)
*/
-class WishlistTest extends \PHPUnit\Framework\TestCase
+class WishlistTest extends TestCase
{
/**
- * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject
+ * @var Registry|PHPUnit_Framework_MockObject_MockObject
*/
protected $registry;
/**
- * @var \Magento\Catalog\Helper\Product|\PHPUnit_Framework_MockObject_MockObject
+ * @var HelperProduct|PHPUnit_Framework_MockObject_MockObject
*/
protected $productHelper;
/**
- * @var \Magento\Wishlist\Helper\Data|\PHPUnit_Framework_MockObject_MockObject
+ * @var Data|PHPUnit_Framework_MockObject_MockObject
*/
protected $helper;
/**
- * @var \Magento\Wishlist\Model\ResourceModel\Wishlist|\PHPUnit_Framework_MockObject_MockObject
+ * @var WishlistResource|PHPUnit_Framework_MockObject_MockObject
*/
protected $resource;
/**
- * @var \Magento\Wishlist\Model\ResourceModel\Wishlist\Collection|\PHPUnit_Framework_MockObject_MockObject
+ * @var WishlistCollection|PHPUnit_Framework_MockObject_MockObject
*/
protected $collection;
/**
- * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var StoreManagerInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $storeManager;
/**
- * @var \Magento\Framework\Stdlib\DateTime\DateTime|\PHPUnit_Framework_MockObject_MockObject
+ * @var DateTime\DateTime|PHPUnit_Framework_MockObject_MockObject
*/
protected $date;
/**
- * @var \Magento\Wishlist\Model\ItemFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var ItemFactory|PHPUnit_Framework_MockObject_MockObject
*/
protected $itemFactory;
/**
- * @var \Magento\Wishlist\Model\ResourceModel\Item\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var CollectionFactory|PHPUnit_Framework_MockObject_MockObject
*/
protected $itemsFactory;
/**
- * @var \Magento\Catalog\Model\ProductFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var ProductFactory|PHPUnit_Framework_MockObject_MockObject
*/
protected $productFactory;
/**
- * @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject
+ * @var Random|PHPUnit_Framework_MockObject_MockObject
*/
protected $mathRandom;
/**
- * @var \Magento\Framework\Stdlib\DateTime|\PHPUnit_Framework_MockObject_MockObject
+ * @var DateTime|PHPUnit_Framework_MockObject_MockObject
*/
protected $dateTime;
/**
- * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var ManagerInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $eventDispatcher;
@@ -84,63 +111,79 @@ class WishlistTest extends \PHPUnit\Framework\TestCase
protected $wishlist;
/**
- * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var ProductRepositoryInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $productRepository;
/**
- * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject
+ * @var Json|PHPUnit_Framework_MockObject_MockObject
*/
protected $serializer;
+ /**
+ * @var StockItemRepository|PHPUnit_Framework_MockObject_MockObject
+ */
+ private $stockItemRepository;
+
+ /**
+ * @var StockItemRepository|PHPUnit_Framework_MockObject_MockObject
+ */
+ private $scopeConfig;
+
protected function setUp()
{
- $context = $this->getMockBuilder(\Magento\Framework\Model\Context::class)
+ $context = $this->getMockBuilder(Context::class)
->disableOriginalConstructor()
->getMock();
- $this->eventDispatcher = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class)
+ $this->eventDispatcher = $this->getMockBuilder(ManagerInterface::class)
->getMock();
- $this->registry = $this->getMockBuilder(\Magento\Framework\Registry::class)
+ $this->registry = $this->getMockBuilder(Registry::class)
->disableOriginalConstructor()
->getMock();
- $this->productHelper = $this->getMockBuilder(\Magento\Catalog\Helper\Product::class)
+ $this->productHelper = $this->getMockBuilder(HelperProduct::class)
->disableOriginalConstructor()
->getMock();
- $this->helper = $this->getMockBuilder(\Magento\Wishlist\Helper\Data::class)
+ $this->helper = $this->getMockBuilder(Data::class)
->disableOriginalConstructor()
->getMock();
- $this->resource = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Wishlist::class)
+ $this->resource = $this->getMockBuilder(WishlistResource::class)
->disableOriginalConstructor()
->getMock();
- $this->collection = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Wishlist\Collection::class)
+ $this->collection = $this->getMockBuilder(WishlistCollection::class)
->disableOriginalConstructor()
->getMock();
- $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class)
+ $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
->getMock();
- $this->date = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\DateTime::class)
+ $this->date = $this->getMockBuilder(DateTime\DateTime::class)
->disableOriginalConstructor()
->getMock();
- $this->itemFactory = $this->getMockBuilder(\Magento\Wishlist\Model\ItemFactory::class)
+ $this->itemFactory = $this->getMockBuilder(ItemFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
- $this->itemsFactory = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Item\CollectionFactory::class)
+ $this->itemsFactory = $this->getMockBuilder(CollectionFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
- $this->productFactory = $this->getMockBuilder(\Magento\Catalog\Model\ProductFactory::class)
+ $this->productFactory = $this->getMockBuilder(ProductFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
- $this->mathRandom = $this->getMockBuilder(\Magento\Framework\Math\Random::class)
+ $this->mathRandom = $this->getMockBuilder(Random::class)
->disableOriginalConstructor()
->getMock();
- $this->dateTime = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime::class)
+ $this->dateTime = $this->getMockBuilder(DateTime::class)
->disableOriginalConstructor()
->getMock();
- $this->productRepository = $this->createMock(\Magento\Catalog\Api\ProductRepositoryInterface::class);
- $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class)
+ $this->productRepository = $this->createMock(ProductRepositoryInterface::class);
+ $this->stockItemRepository = $this->createMock(StockItemRepository::class);
+ $this->scopeConfig = $this->createMock(ScopeConfigInterface::class);
+
+ $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->serializer = $this->getMockBuilder(Json::class)
->disableOriginalConstructor()
->getMock();
@@ -163,6 +206,8 @@ protected function setUp()
$this->mathRandom,
$this->dateTime,
$this->productRepository,
+ $this->stockItemRepository,
+ $this->scopeConfig,
false,
[],
$this->serializer
@@ -186,7 +231,7 @@ public function testLoadByCustomerId()
->will($this->returnValue($sharingCode));
$this->assertInstanceOf(
- \Magento\Wishlist\Model\Wishlist::class,
+ Wishlist::class,
$this->wishlist->loadByCustomerId($customerId, true)
);
$this->assertEquals($customerId, $this->wishlist->getCustomerId());
@@ -194,10 +239,10 @@ public function testLoadByCustomerId()
}
/**
- * @param int|\Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject $itemId
- * @param \Magento\Framework\DataObject $buyRequest
- * @param null|array|\Magento\Framework\DataObject $param
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @param int|Item|PHPUnit_Framework_MockObject_MockObject $itemId
+ * @param DataObject $buyRequest
+ * @param null|array|DataObject $param
+ * @throws LocalizedException
*
* @dataProvider updateItemDataProvider
*/
@@ -205,9 +250,9 @@ public function testUpdateItem($itemId, $buyRequest, $param)
{
$storeId = 1;
$productId = 1;
- $stores = [(new \Magento\Framework\DataObject())->setId($storeId)];
+ $stores = [(new DataObject())->setId($storeId)];
- $newItem = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class)
+ $newItem = $this->getMockBuilder(Item::class)
->setMethods(
['setProductId', 'setWishlistId', 'setStoreId', 'setOptions', 'setProduct', 'setQty', 'getItem', 'save']
)
@@ -228,12 +273,16 @@ public function testUpdateItem($itemId, $buyRequest, $param)
$this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($stores[0]));
$product = $this->getMockBuilder(
- \Magento\Catalog\Model\Product::class
+ Product::class
)->disableOriginalConstructor()->getMock();
$product->expects($this->any())->method('getId')->will($this->returnValue($productId));
$product->expects($this->any())->method('getStoreId')->will($this->returnValue($storeId));
- $instanceType = $this->getMockBuilder(\Magento\Catalog\Model\Product\Type\AbstractType::class)
+ $stockItem = $this->getMockBuilder(StockItem::class)->disableOriginalConstructor()->getMock();
+ $stockItem->expects($this->any())->method('getIsInStock')->will($this->returnValue(true));
+ $this->stockItemRepository->expects($this->any())->method('get')->will($this->returnValue($stockItem));
+
+ $instanceType = $this->getMockBuilder(AbstractType::class)
->disableOriginalConstructor()
->getMock();
$instanceType->expects($this->once())
@@ -241,13 +290,13 @@ public function testUpdateItem($itemId, $buyRequest, $param)
->will(
$this->returnValue(
$this->getMockBuilder(
- \Magento\Catalog\Model\Product::class
+ Product::class
)->disableOriginalConstructor()->getMock()
)
);
$newProduct = $this->getMockBuilder(
- \Magento\Catalog\Model\Product::class
+ Product::class
)->disableOriginalConstructor()->getMock();
$newProduct->expects($this->any())
->method('setStoreId')
@@ -257,12 +306,12 @@ public function testUpdateItem($itemId, $buyRequest, $param)
->method('getTypeInstance')
->will($this->returnValue($instanceType));
- $item = $this->getMockBuilder(\Magento\Wishlist\Model\Item::class)->disableOriginalConstructor()->getMock();
+ $item = $this->getMockBuilder(Item::class)->disableOriginalConstructor()->getMock();
$item->expects($this->once())
->method('getProduct')
->will($this->returnValue($product));
- $items = $this->getMockBuilder(\Magento\Wishlist\Model\ResourceModel\Item\Collection::class)
+ $items = $this->getMockBuilder(Collection::class)
->disableOriginalConstructor()
->getMock();
@@ -280,7 +329,7 @@ public function testUpdateItem($itemId, $buyRequest, $param)
->will($this->returnValue($item));
$items->expects($this->any())
->method('getIterator')
- ->will($this->returnValue(new \ArrayIterator([$item])));
+ ->will($this->returnValue(new ArrayIterator([$item])));
$this->itemsFactory->expects($this->any())
->method('create')
@@ -292,7 +341,7 @@ public function testUpdateItem($itemId, $buyRequest, $param)
->will($this->returnValue($newProduct));
$this->assertInstanceOf(
- \Magento\Wishlist\Model\Wishlist::class,
+ Wishlist::class,
$this->wishlist->updateItem($itemId, $buyRequest, $param)
);
}
@@ -303,7 +352,7 @@ public function testUpdateItem($itemId, $buyRequest, $param)
public function updateItemDataProvider()
{
return [
- '0' => [1, new \Magento\Framework\DataObject(), null]
+ '0' => [1, new DataObject(), null]
];
}
@@ -311,24 +360,26 @@ public function testAddNewItem()
{
$productId = 1;
$storeId = 1;
- $buyRequest = json_encode([
- 'number' => 42,
- 'string' => 'string_value',
- 'boolean' => true,
- 'collection' => [1, 2, 3],
- 'product' => 1,
- 'form_key' => 'abc'
- ]);
+ $buyRequest = json_encode(
+ [
+ 'number' => 42,
+ 'string' => 'string_value',
+ 'boolean' => true,
+ 'collection' => [1, 2, 3],
+ 'product' => 1,
+ 'form_key' => 'abc'
+ ]
+ );
$result = 'product';
- $instanceType = $this->getMockBuilder(\Magento\Catalog\Model\Product\Type\AbstractType::class)
+ $instanceType = $this->getMockBuilder(AbstractType::class)
->disableOriginalConstructor()
->getMock();
$instanceType->expects($this->once())
->method('processConfiguration')
->willReturn('product');
- $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
+ $productMock = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
->setMethods(['getId', 'hasWishlistStoreId', 'getStoreId', 'getTypeInstance'])
->getMock();
@@ -358,6 +409,12 @@ function ($value) {
}
);
+ $stockItem = $this->getMockBuilder(
+ StockItem::class
+ )->disableOriginalConstructor()->getMock();
+ $stockItem->expects($this->any())->method('getIsInStock')->will($this->returnValue(true));
+ $this->stockItemRepository->expects($this->any())->method('get')->will($this->returnValue($stockItem));
+
$this->assertEquals($result, $this->wishlist->addNewItem($productMock, $buyRequest));
}
}
From 96da3e34504de6f5ba16c7ab0ec0a8932a7aed51 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Tue, 27 Aug 2019 15:53:08 -0500
Subject: [PATCH 151/593] getRootCategoryId returns a string, and getTree
expected an int
---
.../Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index f72fb317ef3dc..00d6c00cd32c3 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -13,6 +13,7 @@
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+use Magento\GraphQl\Model\Query\ContextInterface;
/**
* Category tree field resolver, used for GraphQL request processing.
@@ -64,7 +65,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
}
$rootCategoryId = isset($args['id']) ? (int)$args['id'] :
- $context->getExtensionAttributes()->getStore()->getRootCategoryId();
+ (int)$context->getExtensionAttributes()->getStore()->getRootCategoryId();
$this->checkCategoryIsActive->execute($rootCategoryId);
$categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId);
From fe6dd4bb4b471cffc5cc3a93b2ea70baab5428db Mon Sep 17 00:00:00 2001
From: Lena Orobei
Date: Tue, 27 Aug 2019 16:52:17 -0500
Subject: [PATCH 152/593] magento/graphql-ce#816: Root category ID
---
.../Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index 00d6c00cd32c3..b726615b2f4d7 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -13,7 +13,6 @@
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-use Magento\GraphQl\Model\Query\ContextInterface;
/**
* Category tree field resolver, used for GraphQL request processing.
@@ -64,7 +63,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
return $value[$field->getName()];
}
- $rootCategoryId = isset($args['id']) ? (int)$args['id'] :
+ $rootCategoryId = !empty($args['id']) ? (int)$args['id'] :
(int)$context->getExtensionAttributes()->getStore()->getRootCategoryId();
$this->checkCategoryIsActive->execute($rootCategoryId);
From 3df7743cd932887db6d6093b875fe49ba1159073 Mon Sep 17 00:00:00 2001
From: Lena Orobei
Date: Tue, 27 Aug 2019 16:58:50 -0500
Subject: [PATCH 153/593] magento/graphql-ce#816: Root category ID
---
.../Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php | 7 ++++---
.../CatalogGraphQl/Model/Resolver/RootCategoryId.php | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index b726615b2f4d7..74bc1a459aa50 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -13,6 +13,7 @@
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree as CategoryTreeDataProvider;
/**
* Category tree field resolver, used for GraphQL request processing.
@@ -25,7 +26,7 @@ class CategoryTree implements ResolverInterface
const CATEGORY_INTERFACE = 'CategoryInterface';
/**
- * @var Products\DataProvider\CategoryTree
+ * @var CategoryTreeDataProvider
*/
private $categoryTree;
@@ -40,12 +41,12 @@ class CategoryTree implements ResolverInterface
private $checkCategoryIsActive;
/**
- * @param Products\DataProvider\CategoryTree $categoryTree
+ * @param CategoryTreeDataProvider $categoryTree
* @param ExtractDataFromCategoryTree $extractDataFromCategoryTree
* @param CheckCategoryIsActive $checkCategoryIsActive
*/
public function __construct(
- Products\DataProvider\CategoryTree $categoryTree,
+ CategoryTreeDataProvider $categoryTree,
ExtractDataFromCategoryTree $extractDataFromCategoryTree,
CheckCategoryIsActive $checkCategoryIsActive
) {
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php
index add47e3107ec9..4b3e0a1a58dfd 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/RootCategoryId.php
@@ -21,6 +21,6 @@ class RootCategoryId implements ResolverInterface
*/
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
- return $context->getExtensionAttributes()->getStore()->getRootCategoryId();
+ return (int)$context->getExtensionAttributes()->getStore()->getRootCategoryId();
}
}
From f98027b62c79394dbb1e5aa0cd44ec26e637c09a Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Tue, 27 Aug 2019 23:24:30 -0500
Subject: [PATCH 154/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- fix static failures tests and added fixture for sort by relevance
---
.../GraphQl/Catalog/ProductSearchTest.php | 104 +++++++++++++++---
.../GraphQl/Catalog/ProductViewTest.php | 3 +-
.../_files/products_for_relevance_sorting.php | 75 +++++++++++++
...th_layered_navigation_custom_attribute.php | 3 +-
4 files changed, 169 insertions(+), 16 deletions(-)
create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_for_relevance_sorting.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 169aba7c15734..b6c02f0161ae2 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -116,7 +116,7 @@ public function testLayeredNavigationWithConfigurableChildrenOutOfStock()
$query = $this->getQueryProductsWithCustomAttribute($attributeCode, $firstOption);
$response = $this->graphQlQuery($query);
- // 1 product is returned since only one child product with attribute option1 from 1st Configurable product is OOS
+ // Out of two children, only one child product of 1st Configurable product with option1 is OOS
$this->assertEquals(1, $response['products']['total_count']);
// Custom attribute filter layer data
@@ -269,7 +269,8 @@ public function testFilterProductsByMultiSelectCustomAttribute()
/** @var AttributeOptionInterface[] $options */
$options = $attribute->getOptions();
array_shift($options);
- $optionValues = array();
+ $optionValues = [];
+ // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall
for ($i = 0; $i < count($options); $i++) {
$optionValues[] = $options[$i]->getValue();
}
@@ -411,7 +412,7 @@ public function testFullTextSearchForProductAndFilterByCustomAttribute()
$layers[$layerIndex][0]['request_var'],
$response['products']['filters'][$layerIndex]['request_var'],
'request_var does not match'
- ) ;
+ );
}
// Validate the price filter layer data from the response
@@ -490,7 +491,7 @@ public function testFilterByCategoryIdAndCustomAttribute()
$this->assertEquals(2, $response['products']['total_count']);
$actualCategoryFilterItems = $response['products']['filters'][1]['filter_items'];
//Validate the number of categories/sub-categories that contain the products with the custom attribute
- $this->assertCount(6,$actualCategoryFilterItems);
+ $this->assertCount(6, $actualCategoryFilterItems);
$expectedCategoryFilterItems =
[
@@ -527,7 +528,7 @@ public function testFilterByCategoryIdAndCustomAttribute()
$categoryFilterItems[$index][0]['items_count'],
$actualCategoryFilterItems[$index]['items_count'],
'Products count in the category is incorrect'
- ) ;
+ );
}
}
/**
@@ -972,7 +973,7 @@ public function testFilteringForProductsFromMultipleCategories()
}
/**
- * Filter products by category only
+ * Filter products by single category
*
* @magentoApiDataFixture Magento/Catalog/_files/product_in_multiple_categories.php
* @return void
@@ -1056,6 +1057,58 @@ public function testFilterProductsBySingleCategoryId()
}
}
+ /**
+ * Sorting the search results by relevance (DESC => most relevant)
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/product_in_multiple_categories.php
+ * @return void
+ */
+ public function testFilterProductsAndSortByRelevance()
+ {
+ $search_term ="red white blue grey socks";
+ $query
+ = <<graphQlQuery($query);
+ $this->assertEquals(2, $response['products']['total_count']);
+ }
+
/**
* Sorting by price in the DESC order from the filtered items with default pageSize
*
@@ -1064,7 +1117,6 @@ public function testFilterProductsBySingleCategoryId()
*/
public function testQuerySortByPriceDESCWithDefaultPageSize()
{
-
$query
= <<get(ProductRepositoryInterface::class);
+
+ $prod1 = $productRepository->get('simple2');
+ $prod2 = $productRepository->get('simple1');
+ $filteredProducts = [$prod1, $prod2];
+ /** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */
+ $categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class);
+ foreach ($filteredProducts as $product) {
+ $categoryLinkManagement->assignProductToCategories(
+ $product->getSku(),
+ [333]
+ );
+ }
+
$query
=<<graphQlQuery($query);
$this->assertEquals(2, $response['products']['total_count']);
- /** @var ProductRepositoryInterface $productRepository */
- $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
-
- $prod1 = $productRepository->get('simple2');
- $prod2 = $productRepository->get('simple1');
- $filteredProducts = [$prod1, $prod2];
$this->assertProductItemsWithPriceCheck($filteredProducts, $response);
+ //verify that by default Price and category are the only layers available
+ $filterNames = ['Price', 'Category'];
+ $this->assertCount(2, $response['products']['filters'], 'Filter count does not match');
+ for ($i = 0; $i < count($response['products']['filters']); $i++) {
+ $this->assertEquals($filterNames[$i], $response['products']['filters'][$i]['name']);
+ }
}
/**
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
index 5990211f1e47d..378b87fb9591f 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
@@ -795,6 +795,7 @@ private function assertOptions($product, $actualResponse)
];
$this->assertResponseFields($value, $assertionMapValues);
} else {
+ // phpcs:ignore Magento2.Performance.ForeachArrayMerge
$assertionMap = array_merge(
$assertionMap,
[
@@ -823,7 +824,7 @@ private function assertOptions($product, $actualResponse)
$valueKeyName = 'date_option';
$valueAssertionMap = [];
}
-
+ // phpcs:ignore Magento2.Performance.ForeachArrayMerge
$valueAssertionMap = array_merge(
$valueAssertionMap,
[
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_for_relevance_sorting.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_for_relevance_sorting.php
new file mode 100644
index 0000000000000..e25bd21b7683d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_for_relevance_sorting.php
@@ -0,0 +1,75 @@
+create(\Magento\Catalog\Model\Category::class);
+$category->isObjectNew(true);
+$category->setId(
+ 333
+)->setCreatedAt(
+ '2019-08-27 11:05:07'
+)->setName(
+ 'Colorful Category'
+)->setParentId(
+ 2
+)->setPath(
+ '1/2/300'
+)->setLevel(
+ 2
+)->setAvailableSortBy(
+ ['position', 'name']
+)->setDefaultSortBy(
+ 'name'
+)->setIsActive(
+ true
+)->setPosition(
+ 1
+)->save();
+
+$defaultAttributeSet = $objectManager->get(Magento\Eav\Model\Config::class)
+ ->getEntityType('catalog_product')
+ ->getDefaultAttributeSetId();
+
+/** @var $product \Magento\Catalog\Model\Product */
+$product = $objectManager->create(\Magento\Catalog\Model\Product::class);
+$product->isObjectNew(true);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setAttributeSetId($defaultAttributeSet)
+ ->setStoreId(1)
+ ->setWebsiteIds([1])
+ ->setName('Red White and Blue striped Shoes')
+ ->setSku('red white and blue striped shoes')
+ ->setPrice(40)
+ ->setWeight(8)
+ ->setDescription('Red white and blue flip flops at one ')
+ ->setMetaTitle('Multi colored shoes meta title')
+ ->setMetaKeyword('red, white,flip-flops, women, kids')
+ ->setMetaDescription('flip flops women kids meta description')
+ ->setStockData(['use_config_manage_stock' => 0])
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->save();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$skus = ['green_socks', 'white_shorts','red_trousers','blue_briefs','grey_shorts', 'red white and blue striped shoes' ];
+$products = [];
+foreach ($skus as $sku) {
+ $products = $productRepository->get($sku);
+}
+/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */
+ $categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class);
+foreach ($products as $product) {
+ $categoryLinkManagement->assignProductToCategories(
+ $product->getSku(),
+ [300]
+ );
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
index 6cd2819c2d296..864a931359bdd 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_custom_attribute.php
@@ -115,7 +115,8 @@
'catalog_product',
$attributeSet->getId(),
$attributeSet->getDefaultGroupId(),
- $attribute1->getId());
+ $attribute1->getId()
+ );
}
$eavConfig->clear();
From 06d9d2dc5db67791e6eadcd73881d915a034329f Mon Sep 17 00:00:00 2001
From: Volodymyr Vygovskyi
Date: Wed, 28 Aug 2019 09:47:30 +0300
Subject: [PATCH 155/593] added quote mask generation to fixture, refactoring
---
.../UpdateConfigurableCartItemsTest.php | 19 ++-----------------
.../quote_with_configurable_product.php | 8 ++++++++
2 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
index b13a75fa8c4e7..4f4e7ecab6fe3 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php
@@ -49,7 +49,8 @@ class UpdateConfigurableCartItemsTest extends GraphQlAbstract
public function testUpdateConfigurableCartItemQuantity()
{
$reservedOrderId = 'test_cart_with_configurable';
- $maskedQuoteId = $this->getMaskedQuoteId($reservedOrderId);
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+
$productSku = 'simple_10';
$newQuantity = 123;
$quoteItem = $this->getQuoteItemBySku($productSku, $reservedOrderId);
@@ -125,20 +126,4 @@ private function getQuoteItemBySku(string $sku, string $reservedOrderId)
return $item;
}
-
- /**
- * @param $reservedOrderId
- * @return string
- * @throws NoSuchEntityException
- */
- private function getMaskedQuoteId(string $reservedOrderId): string
- {
- $quote = $this->quoteFactory->create();
- $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id');
- $quoteIdMask = $this->quoteIdMaskFactory->create();
- $quoteIdMask->setQuoteId($quote->getId())
- ->save();
-
- return $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
- }
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
index 150ce3b3108e5..7f108623f02f2 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
@@ -40,3 +40,11 @@
/** @var $objectManager \Magento\TestFramework\ObjectManager */
$objectManager = Bootstrap::getObjectManager();
$objectManager->removeSharedInstance(\Magento\Checkout\Model\Session::class);
+
+/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
+$quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
+ ->create();
+$quoteIdMask->setQuoteId($cart->getQuote()->getId());
+$quoteIdMask->setDataChanges(true);
+$quoteIdMask->save();
From 9a8cf3a5affede83010fa31879a59f7f482b1db3 Mon Sep 17 00:00:00 2001
From: Vitaliy Boyko
Date: Wed, 28 Aug 2019 11:10:25 +0300
Subject: [PATCH 156/593] graphQl-812: test Add Variation From Another
Configurable ProductToCart
---
.../AddConfigurableProductToCartTest.php | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php
index 0e334999599b4..36120c0535d49 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php
@@ -67,6 +67,48 @@ public function testAddConfigurableProductToCart()
self::assertArrayHasKey('value_label', $option);
}
+ /**
+ * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products.php
+ * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
+ */
+ public function testAddVariationFromAnotherConfigurableProductToCart()
+ {
+ $searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable_12345'));
+ $product = current($searchResponse['products']['items']);
+ $attributeId = (int) $product['configurable_options'][0]['attribute_id'];
+ $optionId = $product['configurable_options'][0]['values'][1]['value_index'];
+
+ $quantity = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1');
+ $parentSku = $product['sku'];
+
+ //'configurable' -> ['simple_10', 'simple_20']
+ //'configurable_12345' -> ['simple_30', 'simple_40']
+ //'simple_20' has same configurable option value index (super attribute) as 'simple_40'
+ //therefore 'simple_40' should be added to cart
+ $sku = 'simple_20';
+
+ $query = $this->getQuery(
+ $maskedQuoteId,
+ $parentSku,
+ $sku,
+ $quantity
+ );
+
+ $response = $this->graphQlMutation($query);
+
+ $cartItem = current($response['addConfigurableProductsToCart']['cart']['items']);
+ self::assertEquals($quantity, $cartItem['quantity']);
+ self::assertEquals($parentSku, $cartItem['product']['sku']);
+ self::assertArrayHasKey('configurable_options', $cartItem);
+
+ $option = current($cartItem['configurable_options']);
+ self::assertEquals($attributeId, $option['id']);
+ self::assertEquals($optionId, $option['value_id']);
+ self::assertArrayHasKey('option_label', $option);
+ self::assertArrayHasKey('value_label', $option);
+ }
+
/**
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php
* @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
From 5a782f7d54ebaa992802122b09cc14b2aa11f441 Mon Sep 17 00:00:00 2001
From: Raul E Watson
Date: Wed, 28 Aug 2019 14:50:53 +0100
Subject: [PATCH 157/593] Update Magento_Authorizenet module description
---
app/code/Magento/Authorizenet/README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/Authorizenet/README.md b/app/code/Magento/Authorizenet/README.md
index 6d157860e42d6..62598837bee6d 100644
--- a/app/code/Magento/Authorizenet/README.md
+++ b/app/code/Magento/Authorizenet/README.md
@@ -1,6 +1,7 @@
# Magento_Authorizenet module
-The Magento_Authorizenet module is a part of the staging functionality in Magento Commerce. The module adds the “Configurations” tab and the configuration wizard to the Schedule Update form of a product. You can change the Configurable Product attributes in campaigns. These updates are shown on the campaign dashboard.
+The Magento_Authorizenet module implements the integration with the Authorize.Net payment gateway and makes the latter available as a payment method in Magento.
+
## Extensibility
Extension developers can interact with the Magento_Authorizenet module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html).
From 6971c763d544013902b6f12961ddf0e557718e1f Mon Sep 17 00:00:00 2001
From: Prabhu Ram
Date: Wed, 28 Aug 2019 10:07:19 -0500
Subject: [PATCH 158/593] MC-19618: Update schema for layered navigation output
- review fixes
---
.../Product/LayeredNavigation/Builder/Attribute.php | 2 +-
.../Product/LayeredNavigation/Builder/Category.php | 2 +-
.../Product/LayeredNavigation/Builder/Price.php | 2 +-
.../{Builder => }/Formatter/LayerFormatter.php | 2 +-
app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 8 ++++----
5 files changed, 8 insertions(+), 8 deletions(-)
rename app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/{Builder => }/Formatter/LayerFormatter.php (97%)
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
index 89df1be8d59f2..b70c9f6165fc6 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php
@@ -12,7 +12,7 @@
use Magento\Framework\Api\Search\AggregationInterface;
use Magento\Framework\Api\Search\AggregationValueInterface;
use Magento\Framework\Api\Search\BucketInterface;
-use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Formatter\LayerFormatter;
+use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Formatter\LayerFormatter;
/**
* @inheritdoc
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
index 97644328abc2a..cebafe31385ba 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Category.php
@@ -15,7 +15,7 @@
use Magento\Framework\Api\Search\AggregationValueInterface;
use Magento\Framework\Api\Search\BucketInterface;
use Magento\Framework\App\ResourceConnection;
-use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Formatter\LayerFormatter;
+use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Formatter\LayerFormatter;
/**
* @inheritdoc
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
index e84a8f1b11078..02b638edbdce8 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Price.php
@@ -10,7 +10,7 @@
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilderInterface;
use Magento\Framework\Api\Search\AggregationInterface;
use Magento\Framework\Api\Search\BucketInterface;
-use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Formatter\LayerFormatter;
+use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Formatter\LayerFormatter;
/**
* @inheritdoc
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Formatter/LayerFormatter.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Formatter/LayerFormatter.php
similarity index 97%
rename from app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Formatter/LayerFormatter.php
rename to app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Formatter/LayerFormatter.php
index 72495f8ef8524..48a1265b10fc3 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Formatter/LayerFormatter.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Formatter/LayerFormatter.php
@@ -5,7 +5,7 @@
*/
declare(strict_types=1);
-namespace Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Formatter;
+namespace Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Formatter;
/**
* Format Layered Navigation Items
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index 7fe5374611a70..20bc5ef9949a6 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -391,10 +391,10 @@ type MediaGalleryEntry @doc(description: "MediaGalleryEntry defines characterist
}
type LayerFilter {
- name: String @doc(description: "Layered navigation filter name.")
- request_var: String @doc(description: "Request variable name for filter query.")
- filter_items_count: Int @doc(description: "Count of filter items in filter group.")
- filter_items: [LayerFilterItemInterface] @doc(description: "Array of filter items.")
+ name: String @doc(description: "Layered navigation filter name.") @deprecated(reason: "Use Aggregation.label instead")
+ request_var: String @doc(description: "Request variable name for filter query.") @deprecated(reason: "Use Aggregation.attribute_code instead")
+ filter_items_count: Int @doc(description: "Count of filter items in filter group.") @deprecated(reason: "Use Aggregation.count instead")
+ filter_items: [LayerFilterItemInterface] @doc(description: "Array of filter items.") @deprecated(reason: "Use Aggregation.options instead")
}
interface LayerFilterItemInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\LayerFilterItemTypeResolverComposite") {
From 9553775b6294b9149a6b5bbe35ce4f2791e36073 Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Wed, 28 Aug 2019 18:51:43 +0300
Subject: [PATCH 159/593] GraphQl-220: Implement exception logging. Removed
client errors logging.
---
app/etc/graphql/di.xml | 22 ++++-------
.../Framework/GraphQl/Query/ErrorHandler.php | 17 ++++----
.../GraphQl/Query/Resolver/LoggerFactory.php | 39 -------------------
.../Query/Resolver/LoggerFactoryInterface.php | 28 -------------
4 files changed, 14 insertions(+), 92 deletions(-)
delete mode 100644 lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php
delete mode 100644 lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactoryInterface.php
diff --git a/app/etc/graphql/di.xml b/app/etc/graphql/di.xml
index 474266bb9eff0..4370c3b445f65 100644
--- a/app/etc/graphql/di.xml
+++ b/app/etc/graphql/di.xml
@@ -7,29 +7,21 @@
-->
-
-
+
- - GraphQLClientErrorHandler
+ - GraphQlErrorHandler
-
+
- Magento\Framework\GraphQl\Query\ErrorHandler::CLIENT_LOG_FILE
+ var/log/graphql/exception.log
-
+
-
- - GraphQLServerErrorHandler
-
-
-
-
-
- Magento\Framework\GraphQl\Query\ErrorHandler::SERVER_LOG_FILE
+ GraphQlLogger
-
+
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
index 874f714284936..2661034116f9d 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/ErrorHandler.php
@@ -8,7 +8,7 @@
namespace Magento\Framework\GraphQl\Query;
use GraphQL\Error\ClientAware;
-use Magento\Framework\GraphQl\Query\Resolver\LoggerFactoryInterface;
+use Psr\Log\LoggerInterface;
/**
* @inheritDoc
@@ -17,21 +17,18 @@
*/
class ErrorHandler implements ErrorHandlerInterface
{
- const SERVER_LOG_FILE = 'var/log/graphql/server/exception.log';
- const CLIENT_LOG_FILE = 'var/log/graphql/client/exception.log';
-
/**
- * @var LoggerFactoryInterface
+ * @var LoggerInterface
*/
- private $loggerFactory;
+ private $logger;
/**
- * @param LoggerFactoryInterface $loggerFactory
+ * @param LoggerInterface $logger
*/
public function __construct(
- LoggerFactoryInterface $loggerFactory
+ LoggerInterface $logger
) {
- $this->loggerFactory = $loggerFactory;
+ $this->logger = $logger;
}
/**
@@ -41,7 +38,7 @@ public function handle(array $errors, callable $formatter): array
{
return array_map(
function (ClientAware $error) use ($formatter) {
- $this->loggerFactory->getLogger($error)->error($error);
+ $this->logger->error($error);
return $formatter($error);
},
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php
deleted file mode 100644
index 2b35b53981100..0000000000000
--- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactory.php
+++ /dev/null
@@ -1,39 +0,0 @@
-objectManager = $objectManager;
- }
-
- /**
- * @inheritDoc
- */
- public function getLogger(ClientAware $clientAware): LoggerInterface
- {
- return $clientAware->isClientSafe() ?
- $this->objectManager->get('GraphQLClientLogger') :
- $this->objectManager->get('GraphQLServerLogger');
- }
-}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactoryInterface.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactoryInterface.php
deleted file mode 100644
index 778fb61db994d..0000000000000
--- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/LoggerFactoryInterface.php
+++ /dev/null
@@ -1,28 +0,0 @@
-
Date: Wed, 28 Aug 2019 10:59:15 -0500
Subject: [PATCH 160/593] MC-19274: Bank Transfer Payment instructons are
copied from default store view when order invoce is created
---
.../BeforeOrderPaymentSaveObserver.php | 33 ++++---
.../BeforeOrderPaymentSaveObserverTest.php | 14 +--
.../Magento/OfflinePayments/composer.json | 3 +-
.../Payment/Block/Info/Instructions.php | 12 +++
.../Test/Unit/Block/Info/InstructionsTest.php | 86 +++++++++++++++++--
.../templates/info/instructions.phtml | 2 +-
.../templates/info/instructions.phtml | 2 +-
7 files changed, 124 insertions(+), 28 deletions(-)
diff --git a/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php b/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php
index b177b115d02ab..16ef7ecdf5011 100644
--- a/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php
+++ b/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php
@@ -3,38 +3,38 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
-/**
- * OfflinePayments Observer
- */
namespace Magento\OfflinePayments\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\OfflinePayments\Model\Banktransfer;
use Magento\OfflinePayments\Model\Cashondelivery;
use Magento\OfflinePayments\Model\Checkmo;
+use Magento\Sales\Model\Order\Payment;
+/**
+ * Sets payment additional information.
+ */
class BeforeOrderPaymentSaveObserver implements ObserverInterface
{
/**
- * Sets current instructions for bank transfer account
+ * Sets current instructions for bank transfer account.
*
* @param \Magento\Framework\Event\Observer $observer
* @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
- public function execute(\Magento\Framework\Event\Observer $observer)
+ public function execute(\Magento\Framework\Event\Observer $observer): void
{
- /** @var \Magento\Sales\Model\Order\Payment $payment */
+ /** @var Payment $payment */
$payment = $observer->getEvent()->getPayment();
$instructionMethods = [
Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE,
Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE
];
if (in_array($payment->getMethod(), $instructionMethods)) {
- $payment->setAdditionalInformation(
- 'instructions',
- $payment->getMethodInstance()->getInstructions()
- );
+ $payment->setAdditionalInformation('instructions', $this->getInstructions($payment));
} elseif ($payment->getMethod() === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) {
$methodInstance = $payment->getMethodInstance();
if (!empty($methodInstance->getPayableTo())) {
@@ -45,4 +45,17 @@ public function execute(\Magento\Framework\Event\Observer $observer)
}
}
}
+
+ /**
+ * Retrieve store-specific payment method instructions, or already saved if exists.
+ *
+ * @param Payment $payment
+ * @return string|null
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ private function getInstructions(Payment $payment): ?string
+ {
+ return $payment->getAdditionalInformation('instructions')
+ ?: $payment->getMethodInstance()->getConfigData('instructions', $payment->getOrder()->getStoreId());
+ }
}
diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php
index 51edaea0e659c..57c5ec533dc64 100644
--- a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php
+++ b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php
@@ -11,6 +11,7 @@
use Magento\OfflinePayments\Model\Banktransfer;
use Magento\OfflinePayments\Model\Cashondelivery;
use Magento\OfflinePayments\Observer\BeforeOrderPaymentSaveObserver;
+use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Payment;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
use Magento\OfflinePayments\Model\Checkmo;
@@ -76,19 +77,12 @@ public function testBeforeOrderPaymentSaveWithInstructions($methodCode)
$this->payment->expects(self::once())
->method('getMethod')
->willReturn($methodCode);
+ $this->payment->method('getAdditionalInformation')
+ ->with('instructions')
+ ->willReturn('payment configuration');
$this->payment->expects(self::once())
->method('setAdditionalInformation')
->with('instructions', 'payment configuration');
- $method = $this->getMockBuilder(Banktransfer::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $method->expects(self::once())
- ->method('getInstructions')
- ->willReturn('payment configuration');
- $this->payment->expects(self::once())
- ->method('getMethodInstance')
- ->willReturn($method);
$this->_model->execute($this->observer);
}
diff --git a/app/code/Magento/OfflinePayments/composer.json b/app/code/Magento/OfflinePayments/composer.json
index 4de112ac72152..7bf4b78628a70 100644
--- a/app/code/Magento/OfflinePayments/composer.json
+++ b/app/code/Magento/OfflinePayments/composer.json
@@ -8,7 +8,8 @@
"php": "~7.1.3||~7.2.0||~7.3.0",
"magento/framework": "*",
"magento/module-checkout": "*",
- "magento/module-payment": "*"
+ "magento/module-payment": "*",
+ "magento/module-sales": "*"
},
"suggest": {
"magento/module-config": "*"
diff --git a/app/code/Magento/Payment/Block/Info/Instructions.php b/app/code/Magento/Payment/Block/Info/Instructions.php
index 687c6b54a2f4f..f670fa6925bfb 100644
--- a/app/code/Magento/Payment/Block/Info/Instructions.php
+++ b/app/code/Magento/Payment/Block/Info/Instructions.php
@@ -25,6 +25,18 @@ class Instructions extends \Magento\Payment\Block\Info
*/
protected $_template = 'Magento_Payment::info/instructions.phtml';
+ /**
+ * Gets payment method title for appropriate store.
+ *
+ * @return string
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function getTitle()
+ {
+ return $this->getInfo()->getAdditionalInformation('method_title')
+ ?: $this->getMethod()->getConfigData('title', $this->getInfo()->getOrder()->getStoreId());
+ }
+
/**
* Get instructions text from order payment
* (or from config, if instructions are missed in payment)
diff --git a/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php b/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php
index 68c76d94e02ae..88144b6e05c62 100644
--- a/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php
+++ b/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php
@@ -4,11 +4,12 @@
* See COPYING.txt for license details.
*/
-/**
- * Test class for \Magento\Payment\Block\Info\Instructions
- */
namespace Magento\Payment\Test\Unit\Block\Info;
+use Magento\Payment\Model\MethodInterface;
+use Magento\Sales\Model\Order;
+use PHPUnit\Framework\MockObject\MockObject;
+
class InstructionsTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -25,10 +26,59 @@ protected function setUp()
{
$context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class);
$this->_instructions = new \Magento\Payment\Block\Info\Instructions($context);
- $this->_info = $this->createMock(\Magento\Payment\Model\Info::class);
+ $this->_info = $this->getMockBuilder(\Magento\Payment\Model\Info::class)
+ ->setMethods(
+ [
+ 'getOrder',
+ 'getAdditionalInformation',
+ 'getMethodInstance'
+ ]
+ )
+ ->disableOriginalConstructor()
+ ->getMock();
$this->_instructions->setData('info', $this->_info);
}
+ /**
+ * @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function testGetTitleFromPaymentAdditionalData()
+ {
+ $this->_info->method('getAdditionalInformation')
+ ->with('method_title')
+ ->willReturn('payment_method_title');
+
+ $this->getMethod()->expects($this->never())
+ ->method('getConfigData');
+
+ $this->assertEquals($this->_instructions->getTitle(), 'payment_method_title');
+ }
+
+ /**
+ * @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function testGetTitleFromPaymentMethodConfig()
+ {
+ $this->_info->method('getAdditionalInformation')
+ ->with('method_title')
+ ->willReturn(null);
+
+ $this->getMethod()->expects($this->once())
+ ->method('getConfigData')
+ ->with('title', null)
+ ->willReturn('payment_method_title');
+
+ $order = $this->getOrder();
+ $this->_info->method('getOrder')->willReturn($order);
+
+ $this->assertEquals($this->_instructions->getTitle(), 'payment_method_title');
+ }
+
+ /**
+ * @return void
+ */
public function testGetInstructionAdditionalInformation()
{
$this->_info->expects($this->once())
@@ -41,10 +91,13 @@ public function testGetInstructionAdditionalInformation()
$this->assertEquals('get the instruction here', $this->_instructions->getInstructions());
}
+ /**
+ * @return void
+ */
public function testGetInstruction()
{
$methodInstance = $this->getMockBuilder(
- \Magento\Payment\Model\MethodInterface::class
+ MethodInterface::class
)->getMockForAbstractClass();
$methodInstance->expects($this->once())
->method('getConfigData')
@@ -59,4 +112,27 @@ public function testGetInstruction()
->willReturn($methodInstance);
$this->assertEquals('get the instruction here', $this->_instructions->getInstructions());
}
+
+ /**
+ * @return MethodInterface|MockObject
+ */
+ private function getMethod()
+ {
+ $method = $this->getMockBuilder(MethodInterface::class)
+ ->getMockForAbstractClass();
+ $this->_info->method('getMethodInstance')
+ ->willReturn($method);
+
+ return $method;
+ }
+
+ /**
+ * @return Order|MockObject
+ */
+ private function getOrder()
+ {
+ return $this->getMockBuilder(Order::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
}
diff --git a/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml b/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml
index f60c1d063addf..904f0bd2e2cdc 100644
--- a/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml
+++ b/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml
@@ -9,7 +9,7 @@
* @see \Magento\Payment\Block\Info
*/
?>
-= $block->escapeHtml($block->getMethod()->getTitle()) ?>
+= $block->escapeHtml($block->getTitle()) ?>
getInstructions()) : ?>
diff --git a/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml b/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml
index 60efae16b1711..a8d2b15c3ea31 100644
--- a/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml
+++ b/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml
@@ -10,7 +10,7 @@
*/
?>
- = $block->escapeHtml($block->getMethod()->getTitle()) ?>
+ = $block->escapeHtml($block->getTitle()) ?>
getInstructions()) : ?>
= /* @noEscape */ nl2br($block->escapeHtml($block->getInstructions())) ?>
From 08de5cfce0fb0a27f41d6991b3fdc16a3840b6b2 Mon Sep 17 00:00:00 2001
From: Prabhu Ram
Date: Wed, 28 Aug 2019 11:29:41 -0500
Subject: [PATCH 161/593] MC-19618: Update schema for layered navigation output
- review fixes
---
app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index 20bc5ef9949a6..d29a24428ae12 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -404,14 +404,14 @@ interface LayerFilterItemInterface @typeResolver(class: "Magento\\CatalogGraphQl
}
type Aggregation {
- count: Int @doc(description: "Count of filter items in filter group.")
- label: String @doc(description: "Layered navigation filter name.")
+ count: Int @doc(description: "The number of filter items in the filter group.")
+ label: String @doc(description: "The filter named displayed in layered navigation.")
attribute_code: String! @doc(description: "Attribute code of the filter item.")
- options: [AggregationOption] @doc(description: "Array of aggregation options.")
+ options: [AggregationOption] @doc(description: "Describes each aggregated filter option.")
}
type AggregationOption {
- count: Int @doc(description: "Count of items by filter.")
+ count: Int @doc(description: "The number of items returned by the filter.")
label: String! @doc(description: "Filter label.")
value: String! @doc(description: "Value for filter request variable to be used in query.")
}
From 8a2fbffbad2da4c9b00e84cfc40d2d59c846379d Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Wed, 28 Aug 2019 12:14:33 -0500
Subject: [PATCH 162/593] MC-19702: Add input_type to customAttributeMetadata
query
---
.../Resolver/CustomAttributeMetadata.php | 14 ++++-
.../Model/Resolver/Query/FrontendType.php | 61 +++++++++++++++++++
.../Magento/EavGraphQl/etc/schema.graphqls | 1 +
3 files changed, 74 insertions(+), 2 deletions(-)
create mode 100644 app/code/Magento/EavGraphQl/Model/Resolver/Query/FrontendType.php
diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/CustomAttributeMetadata.php b/app/code/Magento/EavGraphQl/Model/Resolver/CustomAttributeMetadata.php
index 62e3f01836619..85445580bb1fb 100644
--- a/app/code/Magento/EavGraphQl/Model/Resolver/CustomAttributeMetadata.php
+++ b/app/code/Magento/EavGraphQl/Model/Resolver/CustomAttributeMetadata.php
@@ -9,6 +9,7 @@
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\EavGraphQl\Model\Resolver\Query\Type;
+use Magento\EavGraphQl\Model\Resolver\Query\FrontendType;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
@@ -26,12 +27,19 @@ class CustomAttributeMetadata implements ResolverInterface
*/
private $type;
+ /**
+ * @var FrontendType
+ */
+ private $frontendType;
+
/**
* @param Type $type
+ * @param FrontendType $frontendType
*/
- public function __construct(Type $type)
+ public function __construct(Type $type, FrontendType $frontendType)
{
$this->type = $type;
+ $this->frontendType = $frontendType;
}
/**
@@ -52,6 +60,7 @@ public function resolve(
continue;
}
try {
+ $frontendType = $this->frontendType->getType($attribute['attribute_code'], $attribute['entity_type']);
$type = $this->type->getType($attribute['attribute_code'], $attribute['entity_type']);
} catch (InputException $exception) {
$attributes['items'][] = new GraphQlNoSuchEntityException(
@@ -78,7 +87,8 @@ public function resolve(
$attributes['items'][] = [
'attribute_code' => $attribute['attribute_code'],
'entity_type' => $attribute['entity_type'],
- 'attribute_type' => ucfirst($type)
+ 'attribute_type' => ucfirst($type),
+ 'input_type' => $frontendType
];
}
diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/Query/FrontendType.php b/app/code/Magento/EavGraphQl/Model/Resolver/Query/FrontendType.php
new file mode 100644
index 0000000000000..c76f19e6dfeb4
--- /dev/null
+++ b/app/code/Magento/EavGraphQl/Model/Resolver/Query/FrontendType.php
@@ -0,0 +1,61 @@
+attributeRepository = $attributeRepository;
+ $this->serviceTypeMap = $serviceTypeMap;
+ }
+
+ /**
+ * Return frontend type for attribute
+ *
+ * @param string $attributeCode
+ * @param string $entityType
+ * @return null|string
+ */
+ public function getType(string $attributeCode, string $entityType): ?string
+ {
+ $mappedEntityType = $this->serviceTypeMap->getEntityType($entityType);
+ if ($mappedEntityType) {
+ $entityType = $mappedEntityType;
+ }
+ try {
+ $attribute = $this->attributeRepository->get($entityType, $attributeCode);
+ } catch (NoSuchEntityException $e) {
+ return null;
+ }
+ return $attribute->getFrontendInput();
+ }
+}
diff --git a/app/code/Magento/EavGraphQl/etc/schema.graphqls b/app/code/Magento/EavGraphQl/etc/schema.graphqls
index 0b174fbc4d84d..21aa7001fab2b 100644
--- a/app/code/Magento/EavGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/EavGraphQl/etc/schema.graphqls
@@ -13,6 +13,7 @@ type Attribute @doc(description: "Attribute contains the attribute_type of the s
attribute_code: String @doc(description: "The unique identifier for an attribute code. This value should be in lowercase letters without spaces.")
entity_type: String @doc(description: "The type of entity that defines the attribute")
attribute_type: String @doc(description: "The data type of the attribute")
+ input_type: String @doc(description: "The frontend input type of the attribute")
attribute_options: [AttributeOption] @resolver(class: "Magento\\EavGraphQl\\Model\\Resolver\\AttributeOptions") @doc(description: "Attribute options list.")
}
From 4800a57e287aa946a796a06ae113f5372ea0b24b Mon Sep 17 00:00:00 2001
From: Max Lesechko
Date: Wed, 28 Aug 2019 10:45:22 -0500
Subject: [PATCH 163/593] MC-19684: Some minute values cannot be set for
Analytics data collection
---
.../AdminConfigurationTimeToSendDataTest.xml | 6 +--
.../Block/Agreement/Edit/AgreementsForm.xml | 2 +-
.../Customer/Test/Block/Form/CustomerForm.php | 2 +-
.../Block/Adminhtml/Queue/Edit/QueueForm.xml | 2 +-
.../Block/Adminhtml/Sales/Coupons/Filter.xml | 2 +-
.../Block/Adminhtml/Sales/TaxRule/Filter.xml | 2 +-
.../Adminhtml/Rating/Edit/RatingForm.xml | 2 +-
.../Block/Adminhtml/Report/Filter/Form.xml | 2 +-
lib/web/mage/validation.js | 45 -------------------
lib/web/mage/validation/validation.js | 22 ++++-----
10 files changed, 18 insertions(+), 69 deletions(-)
diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml
index 58e62500b8203..8ebd8cb594bee 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml
@@ -25,9 +25,9 @@
-
-
-
+
+
+
diff --git a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml
index 95d99f9fa76cd..f98f9ca7cfe24 100644
--- a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml
+++ b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml
@@ -18,7 +18,7 @@
select
- [name="stores[0]"]
+ [name="stores[]"]
multiselectgrouplist
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php
index 61166339475b7..dc1e901a3feae 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php
+++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php
@@ -29,7 +29,7 @@ class CustomerForm extends Form
*
* @var string
*/
- protected $customerAttribute = "[orig-name='%s[]']";
+ protected $customerAttribute = "[name='%s[]']";
/**
* Validation text message for a field.
diff --git a/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml b/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml
index 4d2acc76c8703..c1970955013e8 100644
--- a/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml
+++ b/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml
@@ -11,7 +11,7 @@
input[name='start_at']
- select[name="stores[0]"]
+ select[name="stores[]"]
multiselectgrouplist
diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml
index 51809448e4edb..d66c3b702f076 100644
--- a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml
+++ b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml
@@ -29,7 +29,7 @@
select
- [name="order_statuses[0]"]
+ [name="order_statuses[]"]
multiselect
diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml
index 5820de6772e1c..08e783e1329a4 100644
--- a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml
+++ b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml
@@ -23,7 +23,7 @@
select
- [name="order_statuses[0]"]
+ [name="order_statuses[]"]
multiselect
diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml
index 504ce64bf2a73..3e1a1c727c668 100644
--- a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml
+++ b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml
@@ -12,7 +12,7 @@
css selector
- [name="stores[0]"]
+ [name="stores[]"]
multiselectgrouplist
diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml
index 294f64966bde9..d868798eba79d 100644
--- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml
+++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml
@@ -26,7 +26,7 @@
select
- [name="order_statuses[0]"]
+ [name="order_statuses[]"]
multiselect
diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js
index b284f0002bc66..55921c054e61a 100644
--- a/lib/web/mage/validation.js
+++ b/lib/web/mage/validation.js
@@ -1925,7 +1925,6 @@
* @protected
*/
_create: function () {
- this._prepareArrayInputs();
this.validate = this.element.validate(this.options);
// ARIA (adding aria-required attribute)
@@ -1938,50 +1937,6 @@
this._listenFormValidate();
},
- /**
- * Validation creation.
- *
- * @protected
- */
- _prepareArrayInputs: function () {
- /* Store original names for array inputs */
- var originalElements = [],
- originalSubmitHandler = this.options.submitHandler;
-
- /* For all array inputs, assign index so that validation is proper */
- this.element.find('[name$="[]"]').each(function (key, input) {
- var originalName, name;
-
- input = $(input);
- originalName = input.attr('name');
- name = originalName.replace('[]', '[' + key + ']');
- $(input).attr('name', name);
- $(input).attr('orig-name', originalName);
- originalElements.push({
- element: $(input),
- name: originalName
- });
- });
-
- if (originalElements.length) {
- /**
- * Before submitting the actual form, remove the previously assigned indices
- * @param {Object} form
- */
- this.options.submitHandler = function (form) {
- originalElements.forEach(function (element) {
- element.element.attr('name', element.name);
- element.element.removeAttr('orig-name');
- });
-
- console.error(this.submit);
-
- /* Call the originalSubmitHandler if it's a function */
- typeof originalSubmitHandler === 'function' ? originalSubmitHandler(form) : form.submit();
- };
- }
- },
-
/**
* Validation listening.
*
diff --git a/lib/web/mage/validation/validation.js b/lib/web/mage/validation/validation.js
index 69cb984b0d82d..74b00e34e9160 100644
--- a/lib/web/mage/validation/validation.js
+++ b/lib/web/mage/validation/validation.js
@@ -49,23 +49,17 @@
'validate-one-checkbox-required-by-name': [
function (value, element, params) {
var checkedCount = 0,
- selector,
- container,
- origNameSelector,
- nameSelector;
+ container;
if (element.type === 'checkbox') {
- /* If orig-name attribute is present, use it for validation. Else use name */
- origNameSelector = '[orig-name="' + element.getAttribute('orig-name') + '"]';
- nameSelector = '[name="' + element.name + '"]';
- selector = element.getAttribute('orig-name') ? origNameSelector : nameSelector;
- $(selector).each(function () {
- if ($(this).is(':checked')) {
- checkedCount += 1;
-
- return false;
+ $('[name="' + element.name + '"]').each(
+ function () {
+ if ($(this).is(':checked')) {
+ checkedCount += 1;
+ return false;
+ }
}
- });
+ );
}
container = '#' + params;
From 6993da4ebf8d30a5ef278e5a3c233985ebdbb02d Mon Sep 17 00:00:00 2001
From: Max Lesechko
Date: Wed, 28 Aug 2019 11:41:53 -0500
Subject: [PATCH 164/593] MC-19684: Some minute values cannot be set for
Analytics data collection
---
lib/web/mage/validation/validation.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/web/mage/validation/validation.js b/lib/web/mage/validation/validation.js
index 74b00e34e9160..0f2c4c06b119b 100644
--- a/lib/web/mage/validation/validation.js
+++ b/lib/web/mage/validation/validation.js
@@ -56,6 +56,7 @@
function () {
if ($(this).is(':checked')) {
checkedCount += 1;
+
return false;
}
}
From 53ffcd761360d5d2fc3840e3227b744d8e3b318b Mon Sep 17 00:00:00 2001
From: Raoul Rego
Date: Wed, 28 Aug 2019 14:38:21 -0500
Subject: [PATCH 165/593] MC-17627: Dependency static test does not analyze
content of phtml files
- Fix handling of comment sanitation
- Fix incorrect urls
- Added checking route by frontName
---
.../catalog/product/edit/super/config.phtml | 2 +-
.../TestFramework/Dependency/PhpRule.php | 31 ++++++++++++++-----
.../Dependency/Route/RouteMapper.php | 9 ++++++
.../Magento/Test/Integrity/DependencyTest.php | 26 +++++++---------
.../dependency_test/whitelist/routes_ce.php | 2 +-
5 files changed, 45 insertions(+), 25 deletions(-)
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml
index c11a1adc19896..240c5e65c79c3 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml
@@ -64,7 +64,7 @@
"productsProvider": "configurable_associated_product_listing.data_source",
"productsMassAction": "configurable_associated_product_listing.configurable_associated_product_listing.product_columns.ids",
"productsColumns": "configurable_associated_product_listing.configurable_associated_product_listing.product_columns",
- "productsGridUrl": "= /* @noEscape */ $block->getUrl('catalog/product/associated_grid', ['componentJson' => true]) ?>",
+ "productsGridUrl": "= /* @noEscape */ $block->getUrl('catalog/product_associated/grid', ['componentJson' => true]) ?>",
"configurableVariations": "configurableVariations"
}
}
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
index 4958795412681..990df68a95cf3 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
@@ -288,8 +288,8 @@ private function isPluginDependency($dependent, $dependency)
*/
protected function _caseGetUrl(string $currentModule, string &$contents): array
{
- $pattern = '#(\->|:)(?getUrl\(([\'"])(?[a-z0-9\-_]{3,})'
- .'(/(?[a-z0-9\-_]+))?(/(?[a-z0-9\-_]+))?\3)#i';
+ $pattern = '#(\->|:)(?getUrl\(([\'"])(?[a-z0-9\-_]{3,}|\*)'
+ .'(/(?[a-z0-9\-_]+|\*))?(/(?[a-z0-9\-_]+))?\3|\*)#i';
$dependencies = [];
if (!preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)) {
@@ -298,10 +298,27 @@ protected function _caseGetUrl(string $currentModule, string &$contents): array
try {
foreach ($matches as $item) {
+ $routeId = $item['route_id'];
+ $controllerName = $item['controller_name'] ?? UrlInterface::DEFAULT_CONTROLLER_NAME;
+ $actionName = $item['action_name'] ?? UrlInterface::DEFAULT_ACTION_NAME;
+ if (
+ in_array(
+ implode('/', [$routeId, $controllerName, $actionName]),
+ $this->getRoutesWhitelist())) {
+ continue;
+ }
+ // skip rest
+ if($routeId == "rest") { //MC-17627
+ continue;
+ }
+ // skip wildcards
+ if($routeId == "*" || $controllerName == "*" || $actionName == "*" ) { //MC-17627
+ continue;
+ }
$modules = $this->routeMapper->getDependencyByRoutePath(
- $item['route_id'],
- $item['controller_name'] ?? UrlInterface::DEFAULT_CONTROLLER_NAME,
- $item['action_name'] ?? UrlInterface::DEFAULT_ACTION_NAME
+ $routeId,
+ $controllerName,
+ $actionName
);
if (!in_array($currentModule, $modules)) {
if (count($modules) === 1) {
@@ -315,9 +332,7 @@ protected function _caseGetUrl(string $currentModule, string &$contents): array
}
}
} catch (NoSuchActionException $e) {
- if (array_search($e->getMessage(), $this->getRoutesWhitelist()) === false) {
- throw new LocalizedException(__('Invalid URL path: %1', $e->getMessage()), $e);
- }
+ throw new LocalizedException(__('Invalid URL path: %1', $e->getMessage()), $e);
}
return $dependencies;
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php
index 87cc0985a053b..a3d6fbffa8c7e 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php
@@ -243,6 +243,15 @@ private function processConfigFile(string $module, string $configFile)
if (!in_array($module, $this->routers[$routerId][$routeId])) {
$this->routers[$routerId][$routeId][] = $module;
}
+ if(isset($route['frontName'])) {
+ $frontName = (string)$route['frontName'];
+ if (!isset($this->routers[$routerId][$frontName])) {
+ $this->routers[$routerId][$frontName] = [];
+ }
+ if (!in_array($module, $this->routers[$routerId][$frontName])) {
+ $this->routers[$routerId][$frontName][] = $module;
+ }
+ }
}
}
}
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
index 540fcc76349d1..e52c31723725b 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
@@ -284,21 +284,20 @@ private static function getRoutesWhitelist(): array
*/
protected function _getCleanedFileContents($fileType, $file)
{
- $contents = (string)file_get_contents($file);
switch ($fileType) {
case 'php':
- //Removing php comments
- $contents = preg_replace('~/\*.*?\*/~m', '', $contents);
- $contents = preg_replace('~^\s*/\*.*?\*/~sm', '', $contents);
- $contents = preg_replace('~^\s*//.*$~m', '', $contents);
- break;
+ return php_strip_whitespace($file);
case 'layout':
case 'config':
//Removing xml comments
- $contents = preg_replace('~\~s', '', $contents);
+ return preg_replace(
+ '~\~s',
+ '',
+ (string)file_get_contents($file)
+ );
break;
case 'template':
- //Removing html
+ $contents = php_strip_whitespace($file);
$contentsWithoutHtml = '';
preg_replace_callback(
'~(<\?(php|=)\s+.*\?>)~sU',
@@ -308,15 +307,13 @@ function ($matches) use ($contents, &$contentsWithoutHtml) {
},
$contents
);
- $contents = $contentsWithoutHtml;
- //Removing php comments
- $contents = preg_replace('~/\*.*?\*/~s', '', $contents);
- $contents = preg_replace('~^\s*//.*$~s', '', $contents);
- break;
}
- return $contents;
+
+ return (string)file_get_contents($file);
}
+
+
/**
* @inheritdoc
* @throws \Exception
@@ -395,7 +392,6 @@ protected function getDependenciesFromFiles($module, $fileType, $file, $contents
$newDependencies = $rule->getDependencyInfo($module, $fileType, $file, $contents);
$dependencies = array_merge($dependencies, $newDependencies);
}
-
foreach ($dependencies as $key => $dependency) {
foreach (self::$whiteList as $namespace) {
if (strpos($dependency['source'], $namespace) !== false) {
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/whitelist/routes_ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/whitelist/routes_ce.php
index 9ebc951a3a3a1..1aef3ffdf104a 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/whitelist/routes_ce.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/whitelist/routes_ce.php
@@ -6,5 +6,5 @@
declare(strict_types=1);
return [
- 'privacy-policy-cookie-restriction-mode/index/index',
+ 'privacy-policy-cookie-restriction-mode/index/index'
];
From efd076e6f9417ee1799c69383d8d914f9c40d240 Mon Sep 17 00:00:00 2001
From: Krissy Hiserote
Date: Wed, 28 Aug 2019 15:08:41 -0500
Subject: [PATCH 166/593] MC-16650: Product Attribute Type Price Not Displaying
- fix test failures
---
.../Test/Unit/Model/Layer/FilterListTest.php | 10 ++++--
.../LayerNavigationOfCatalogSearchTest.xml | 2 +-
.../attribute_special_price_filterable.php | 2 +-
.../_files/multiple_visible_products.php | 32 ++++++++++++++++---
.../multiple_visible_products_rollback.php | 7 ++++
.../Model/Layer/Filter/DecimalTest.php | 2 +-
6 files changed, 45 insertions(+), 10 deletions(-)
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
index 6943bc27fd77c..731c5efd99746 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/FilterListTest.php
@@ -73,9 +73,13 @@ public function testGetFilters($method, $value, $expectedClass)
$this->objectManagerMock->expects($this->at(1))
->method('create')
- ->with($expectedClass, [
- 'data' => ['attribute_model' => $this->attributeMock],
- 'layer' => $this->layerMock])
+ ->with(
+ $expectedClass,
+ [
+ 'data' => ['attribute_model' => $this->attributeMock],
+ 'layer' => $this->layerMock
+ ]
+ )
->will($this->returnValue('filter'));
$this->attributeMock->expects($this->once())
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
index e91fb9b7a55cc..210b474af2e02 100644
--- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml
@@ -14,7 +14,7 @@
-
+
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
index 3c92db99cd4f1..eedff7aaefb1d 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
@@ -9,4 +9,4 @@
\Magento\Catalog\Setup\CategorySetup::class
);
-$installer->updateAttribute('catalog_product', 'special_price', 'is_filterable', 1);
\ No newline at end of file
+$installer->updateAttribute('catalog_product', 'special_price', 'is_filterable', 1);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
index 4ace41d23c872..f5c22c570911f 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
@@ -4,6 +4,30 @@
* See COPYING.txt for license details.
*/
+$category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Category::class);
+$category->isObjectNew(true);
+$category->setId(
+ 100
+)->setCreatedAt(
+ '2014-06-23 09:50:07'
+)->setName(
+ 'Category 100'
+)->setParentId(
+ 2
+)->setPath(
+ '1/2/100'
+)->setLevel(
+ 2
+)->setAvailableSortBy(
+ ['position', 'name']
+)->setDefaultSortBy(
+ 'name'
+)->setIsActive(
+ true
+)->setPosition(
+ 1
+)->save();
+
/** @var $product \Magento\Catalog\Model\Product */
$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
$product->isObjectNew(true);
@@ -25,7 +49,7 @@
->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
->setWebsiteIds([1])
- ->setCategoryIds([2])
+ ->setCategoryIds([100])
->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
->setSpecialPrice('10')
->save();
@@ -50,7 +74,7 @@
->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
->setWebsiteIds([1])
- ->setCategoryIds([2])
+ ->setCategoryIds([100])
->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
->setSpecialPrice('20')
->save();
@@ -70,7 +94,7 @@
->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
->setWebsiteIds([1])
- ->setCategoryIds([2])
+ ->setCategoryIds([100])
->setStockData(['use_config_manage_stock' => 1, 'qty' => 140, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
->setSpecialPrice('30')
- ->save();
+ ->save();
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
index a9155d3fadf0b..76876c39b7d72 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
@@ -12,6 +12,13 @@
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', true);
+/** @var $category \Magento\Catalog\Model\Category */
+$category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Category::class);
+$category->load(100);
+if ($category->getId()) {
+ $category->delete();
+}
+
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
index 33dea3ea37179..63973ac0fbe20 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
@@ -61,7 +61,7 @@ public function testApplyProductCollection()
/** @var $objectManager \Magento\TestFramework\ObjectManager */
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$category = $objectManager->create(\Magento\Catalog\Model\Category::class);
- $category->load(2);
+ $category->load(100);
$this->_model->getLayer()->setCurrentCategory($category);
/** @var $attribute \Magento\Catalog\Model\Entity\Attribute */
From 9cecea1826cd3eaf45b09cfd462bc8d013d47a71 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 23:53:07 +0300
Subject: [PATCH 167/593] Update
app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
Co-Authored-By: Lena Orobei
---
.../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index 8997ff06b5c6d..86ba3df0eafed 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -13,7 +13,6 @@
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-use Magento\Store\Model\ScopeInterface;
use Magento\Newsletter\Model\Config;
/**
From 68940ec051915c416c89cccd0e57df857d6d0fee Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 23:55:20 +0300
Subject: [PATCH 168/593] Renamed $config to $newsletterConfig
Co-Authored-By: Lena Orobei
---
.../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index 86ba3df0eafed..8133aae93f5a9 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -33,7 +33,7 @@ class CreateCustomer implements ResolverInterface
/**
* @var Config
*/
- private $config;
+ private $newsletterConfig;
/**
* CreateCustomer constructor.
From b88a3db488a1ea083da7e9db363550ff6363f444 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 23:56:48 +0300
Subject: [PATCH 169/593] Renamed config to newsletterConfig
Co-Authored-By: Lena Orobei
---
.../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index 8133aae93f5a9..8a6163494d822 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -45,7 +45,7 @@ class CreateCustomer implements ResolverInterface
public function __construct(
ExtractCustomerData $extractCustomerData,
CreateCustomerAccount $createCustomerAccount,
- Config $config
+ Config $newsletterConfig
) {
$this->config = $config;
$this->extractCustomerData = $extractCustomerData;
From 010536d6b152d8826e2c6c1b60e8c38e4f59f6a4 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 23:57:31 +0300
Subject: [PATCH 170/593] Renamed config to newsletterConfig
Co-Authored-By: Lena Orobei
---
.../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index 8a6163494d822..caf1436ae3eee 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -47,7 +47,7 @@ public function __construct(
CreateCustomerAccount $createCustomerAccount,
Config $newsletterConfig
) {
- $this->config = $config;
+ $this->newsLetterConfig = $newsLetterConfig;
$this->extractCustomerData = $extractCustomerData;
$this->createCustomerAccount = $createCustomerAccount;
}
From bf5d62286f1c88b71f7837ca24315c0e032c3fd5 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 23:57:48 +0300
Subject: [PATCH 171/593] Renamed config to newsletterConfig
Co-Authored-By: Lena Orobei
---
.../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index caf1436ae3eee..b7ac4d5ea5fdd 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -66,7 +66,7 @@ public function resolve(
throw new GraphQlInputException(__('"input" value should be specified'));
}
- if (!$this->config->isActive()) {
+ if (!$this->newsLetterConfig->isActive()) {
$args['input']['is_subscribed'] = false;
}
From 75e240865563979af8c9876e8530cb3818d51ed7 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 23:58:21 +0300
Subject: [PATCH 172/593] Deleted empty line
Co-Authored-By: Lena Orobei
---
.../Test/Unit/Observer/PredispatchNewsletterObserverTest.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php b/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
index d0cfa507eb42f..6846231319d69 100644
--- a/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
+++ b/app/code/Magento/Newsletter/Test/Unit/Observer/PredispatchNewsletterObserverTest.php
@@ -93,7 +93,6 @@ public function testNewsletterEnabled() : void
->method('isActive')
->with(ScopeInterface::SCOPE_STORE)
->willReturn(true);
-
$observerMock->expects($this->never())
->method('getData')
->with('controller_action')
From c0caee9c5d36323979ec35aa46cc7ef36eccdbd6 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 23:58:49 +0300
Subject: [PATCH 173/593] Removed Exception
Co-Authored-By: Lena Orobei
---
.../testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
index 9654be4f5e3cc..c5714012f38c9 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
@@ -277,7 +277,6 @@ public function testCreateCustomerIfNameEmpty()
/**
* @magentoConfigFixture default_store newsletter/general/active 0
- * @throws \Exception
*/
public function testCreateCustomerSubscribed()
{
From a2a2e16e000eed074d7219a2519cab9a5d740a95 Mon Sep 17 00:00:00 2001
From: Raoul Rego
Date: Wed, 28 Aug 2019 16:20:14 -0500
Subject: [PATCH 174/593] MC-17627: Dependency static test does not analyze
content of phtml files
- Fixed depedencies of modules
---
app/code/Magento/Braintree/composer.json | 6 +++---
app/code/Magento/Catalog/composer.json | 3 +--
app/code/Magento/CatalogWidget/composer.json | 3 ++-
app/code/Magento/SendFriend/composer.json | 3 ++-
app/code/Magento/Translation/composer.json | 3 ++-
app/code/Magento/Vault/composer.json | 3 ++-
.../testsuite/Magento/Test/Integrity/DependencyTest.php | 2 +-
7 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json
index 5b5eeaf2b3dd7..58049f7bf0f93 100644
--- a/app/code/Magento/Braintree/composer.json
+++ b/app/code/Magento/Braintree/composer.json
@@ -22,11 +22,11 @@
"magento/module-sales": "*",
"magento/module-ui": "*",
"magento/module-vault": "*",
- "magento/module-multishipping": "*"
+ "magento/module-multishipping": "*",
+ "magento/module-theme": "*"
},
"suggest": {
- "magento/module-checkout-agreements": "*",
- "magento/module-theme": "*"
+ "magento/module-checkout-agreements": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json
index fa8daaabe5710..8023634fa074d 100644
--- a/app/code/Magento/Catalog/composer.json
+++ b/app/code/Magento/Catalog/composer.json
@@ -31,8 +31,7 @@
"magento/module-ui": "*",
"magento/module-url-rewrite": "*",
"magento/module-widget": "*",
- "magento/module-wishlist": "*",
- "magento/module-authorization": "*"
+ "magento/module-wishlist": "*"
},
"suggest": {
"magento/module-cookie": "*",
diff --git a/app/code/Magento/CatalogWidget/composer.json b/app/code/Magento/CatalogWidget/composer.json
index 6722d0df93752..8c1bd220a0f32 100644
--- a/app/code/Magento/CatalogWidget/composer.json
+++ b/app/code/Magento/CatalogWidget/composer.json
@@ -14,7 +14,8 @@
"magento/module-rule": "*",
"magento/module-store": "*",
"magento/module-widget": "*",
- "magento/module-wishlist": "*"
+ "magento/module-wishlist": "*",
+ "magento/module-theme": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/SendFriend/composer.json b/app/code/Magento/SendFriend/composer.json
index f06f1b4a9e3e3..064b45e97d6c5 100644
--- a/app/code/Magento/SendFriend/composer.json
+++ b/app/code/Magento/SendFriend/composer.json
@@ -11,7 +11,8 @@
"magento/module-customer": "*",
"magento/module-store": "*",
"magento/module-captcha": "*",
- "magento/module-authorization": "*"
+ "magento/module-authorization": "*",
+ "magento/module-theme": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/Translation/composer.json b/app/code/Magento/Translation/composer.json
index c01791c88f99f..511238aefe7f3 100644
--- a/app/code/Magento/Translation/composer.json
+++ b/app/code/Magento/Translation/composer.json
@@ -9,7 +9,8 @@
"magento/framework": "*",
"magento/module-backend": "*",
"magento/module-developer": "*",
- "magento/module-store": "*"
+ "magento/module-store": "*",
+ "magento/module-theme" : "*"
},
"suggest": {
"magento/module-deploy": "*"
diff --git a/app/code/Magento/Vault/composer.json b/app/code/Magento/Vault/composer.json
index 7dc2e0be78640..c37bc51f9d432 100644
--- a/app/code/Magento/Vault/composer.json
+++ b/app/code/Magento/Vault/composer.json
@@ -12,7 +12,8 @@
"magento/module-payment": "*",
"magento/module-quote": "*",
"magento/module-sales": "*",
- "magento/module-store": "*"
+ "magento/module-store": "*",
+ "magento/module-theme": "*"
},
"type": "magento2-module",
"license": [
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
index e52c31723725b..c890cee6f1dda 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
@@ -307,8 +307,8 @@ function ($matches) use ($contents, &$contentsWithoutHtml) {
},
$contents
);
+ return $contentsWithoutHtml;
}
-
return (string)file_get_contents($file);
}
From e99ddb823ac358249389693c7be0d66c295a507d Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Wed, 28 Aug 2019 21:46:54 +0000
Subject: [PATCH 175/593] Set magentoApiDataFixture
---
.../Magento/GraphQl/Customer/CreateCustomerTest.php | 2 +-
.../Magento/Customer/_files/customer_subscribe.php | 13 +++++++++++++
.../Customer/_files/customer_subscribe_rollback.php | 13 +++++++++++++
3 files changed, 27 insertions(+), 1 deletion(-)
create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
index c5714012f38c9..5ebf76f3b6cc0 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
@@ -276,7 +276,7 @@ public function testCreateCustomerIfNameEmpty()
}
/**
- * @magentoConfigFixture default_store newsletter/general/active 0
+ * @magentoApiDataFixture Magento/Customer/_files/customer_subscribe.php
*/
public function testCreateCustomerSubscribed()
{
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
new file mode 100644
index 0000000000000..ab45a28a795bf
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
@@ -0,0 +1,13 @@
+create(\Magento\Config\Model\ResourceModel\Config::class);
+$resourceConfig->saveConfig(
+ 'newsletter/general/active',
+ false,
+ 'default',
+ 0
+);
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
new file mode 100644
index 0000000000000..35e10e52b6e67
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
@@ -0,0 +1,13 @@
+create(WriterInterface::class);
+$configWriter->delete('newsletter/general/active');
\ No newline at end of file
From 3bc203941125294fd3fabc9b2bacfd74da468a90 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Wed, 28 Aug 2019 16:52:19 -0500
Subject: [PATCH 176/593] MC-18512: Dynamically inject all searchable custom
attributes for product filtering
- Use FilterMatchTypeInput
---
.../Model/Config/FilterAttributeReader.php | 19 ++++++++++++-------
.../Model/Resolver/Products.php | 4 ++--
app/code/Magento/GraphQl/etc/schema.graphqls | 5 ++---
3 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
index b2cb4dca28bde..3238a1b6564ad 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Config/FilterAttributeReader.php
@@ -11,6 +11,7 @@
use Magento\Framework\GraphQl\Schema\Type\Entity\MapperInterface;
use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory;
use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection;
+use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
/**
* Adds custom/eav attributes to product filter type in the GraphQL config.
@@ -32,7 +33,7 @@ class FilterAttributeReader implements ReaderInterface
*/
private const FILTER_EQUAL_TYPE = 'FilterEqualTypeInput';
private const FILTER_RANGE_TYPE = 'FilterRangeTypeInput';
- private const FILTER_LIKE_TYPE = 'FilterLikeTypeInput';
+ private const FILTER_MATCH_TYPE = 'FilterMatchTypeInput';
/**
* @var MapperInterface
@@ -74,7 +75,7 @@ public function read($scope = null) : array
foreach ($typeNames as $typeName) {
$config[$typeName]['fields'][$attributeCode] = [
'name' => $attributeCode,
- 'type' => $this->getFilterType($attribute->getFrontendInput()),
+ 'type' => $this->getFilterType($attribute),
'arguments' => [],
'required' => false,
'description' => sprintf('Attribute label: %s', $attribute->getDefaultFrontendLabel())
@@ -88,22 +89,26 @@ public function read($scope = null) : array
/**
* Map attribute type to filter type
*
- * @param string $attributeType
+ * @param Attribute $attribute
* @return string
*/
- private function getFilterType($attributeType): string
+ private function getFilterType(Attribute $attribute): string
{
+ if ($attribute->getAttributeCode() === 'sku') {
+ return self::FILTER_EQUAL_TYPE;
+ }
+
$filterTypeMap = [
'price' => self::FILTER_RANGE_TYPE,
'date' => self::FILTER_RANGE_TYPE,
'select' => self::FILTER_EQUAL_TYPE,
'multiselect' => self::FILTER_EQUAL_TYPE,
'boolean' => self::FILTER_EQUAL_TYPE,
- 'text' => self::FILTER_LIKE_TYPE,
- 'textarea' => self::FILTER_LIKE_TYPE,
+ 'text' => self::FILTER_MATCH_TYPE,
+ 'textarea' => self::FILTER_MATCH_TYPE,
];
- return $filterTypeMap[$attributeType] ?? self::FILTER_LIKE_TYPE;
+ return $filterTypeMap[$attribute->getFrontendInput()] ?? self::FILTER_MATCH_TYPE;
}
/**
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index 3bc61a0fb3f2c..691f93e4148bc 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -97,8 +97,8 @@ public function resolve(
//get product children fields queried
$productFields = (array)$info->getFieldSelection(1);
-
- $searchCriteria = $this->searchApiCriteriaBuilder->build($args, isset($productFields['filters']));
+ $includeAggregations = isset($productFields['filters']) || isset($productFields['aggregations']);
+ $searchCriteria = $this->searchApiCriteriaBuilder->build($args, $includeAggregations);
$searchResult = $this->searchQuery->getResult($searchCriteria, $info, $args);
if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) {
diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls
index 997572471122f..69b822d4285b8 100644
--- a/app/code/Magento/GraphQl/etc/schema.graphqls
+++ b/app/code/Magento/GraphQl/etc/schema.graphqls
@@ -75,9 +75,8 @@ input FilterRangeTypeInput @doc(description: "Specifies which action will be per
to: String
}
-input FilterLikeTypeInput @doc(description: "Specifies which action will be performed in a query ") {
- like: String
- eq: String
+input FilterMatchTypeInput @doc(description: "Specifies which action will be performed in a query ") {
+ match: String
}
type SearchResultPageInfo @doc(description: "SearchResultPageInfo provides navigation for the query response") {
From 26653c9bf1b8eede1f58fbe471fdaf34e20a5cca Mon Sep 17 00:00:00 2001
From: Veronika Kurochkina
Date: Wed, 28 Aug 2019 15:52:11 +0300
Subject: [PATCH 177/593] MAGETWO-44170: Not pass function test
\Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest
- Fix modularity issue
---
...AdminProductTypeSwitchingOnEditingTest.xml | 52 +-----------
...AdminProductTypeSwitchingOnEditingTest.xml | 76 ++----------------
...AdminProductTypeSwitchingOnEditingTest.xml | 79 +++++++++++++++++++
3 files changed, 88 insertions(+), 119 deletions(-)
create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
index 45f404478809a..42623f43d4e82 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -11,6 +11,7 @@
+
@@ -61,6 +62,7 @@
+
@@ -91,54 +93,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
index 071ee8d18920f..8253dcf58f9fd 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -11,6 +11,7 @@
+
@@ -60,8 +61,8 @@
-
-
+
+
@@ -75,6 +76,7 @@
+
@@ -113,6 +115,7 @@
+
@@ -163,8 +166,8 @@
-
-
+
+
@@ -175,69 +178,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
new file mode 100644
index 0000000000000..80e86ab4d747c
--- /dev/null
+++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From c4ea11883671af962804a233e8dc1c08360034a9 Mon Sep 17 00:00:00 2001
From: Krissy Hiserote
Date: Thu, 29 Aug 2019 09:26:13 -0500
Subject: [PATCH 178/593] MC-16650: Product Attribute Type Price Not Displaying
- remove decimal filter integration test
---
.../attribute_special_price_filterable.php | 12 ---
.../_files/multiple_visible_products.php | 100 ------------------
.../multiple_visible_products_rollback.php | 35 ------
.../Model/Layer/Filter/DecimalTest.php | 36 +------
4 files changed, 1 insertion(+), 182 deletions(-)
delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
deleted file mode 100644
index eedff7aaefb1d..0000000000000
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
+++ /dev/null
@@ -1,12 +0,0 @@
-create(
- \Magento\Catalog\Setup\CategorySetup::class
-);
-
-$installer->updateAttribute('catalog_product', 'special_price', 'is_filterable', 1);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
deleted file mode 100644
index f5c22c570911f..0000000000000
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products.php
+++ /dev/null
@@ -1,100 +0,0 @@
-create(\Magento\Catalog\Model\Category::class);
-$category->isObjectNew(true);
-$category->setId(
- 100
-)->setCreatedAt(
- '2014-06-23 09:50:07'
-)->setName(
- 'Category 100'
-)->setParentId(
- 2
-)->setPath(
- '1/2/100'
-)->setLevel(
- 2
-)->setAvailableSortBy(
- ['position', 'name']
-)->setDefaultSortBy(
- 'name'
-)->setIsActive(
- true
-)->setPosition(
- 1
-)->save();
-
-/** @var $product \Magento\Catalog\Model\Product */
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
- ->setId(10)
- ->setAttributeSetId(4)
- ->setName('Simple Product1')
- ->setSku('simple1')
- ->setTaxClassId('none')
- ->setDescription('description')
- ->setShortDescription('short description')
- ->setOptionsContainer('container1')
- ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART)
- ->setPrice(15)
- ->setWeight(10)
- ->setMetaTitle('meta title')
- ->setMetaKeyword('meta keyword')
- ->setMetaDescription('meta description')
- ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
- ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
- ->setWebsiteIds([1])
- ->setCategoryIds([100])
- ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
- ->setSpecialPrice('10')
- ->save();
-
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
- ->setId(11)
- ->setAttributeSetId(4)
- ->setName('Simple Product2')
- ->setSku('simple2')
- ->setTaxClassId('none')
- ->setDescription('description')
- ->setShortDescription('short description')
- ->setOptionsContainer('container1')
- ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_ON_GESTURE)
- ->setPrice(25)
- ->setWeight(20)
- ->setMetaTitle('meta title')
- ->setMetaKeyword('meta keyword')
- ->setMetaDescription('meta description')
- ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
- ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
- ->setWebsiteIds([1])
- ->setCategoryIds([100])
- ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
- ->setSpecialPrice('20')
- ->save();
-
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
- ->setId(12)
- ->setAttributeSetId(4)
- ->setName('Simple Product3')
- ->setSku('simple3')
- ->setTaxClassId('none')
- ->setDescription('description')
- ->setShortDescription('short description')
- ->setPrice(35)
- ->setWeight(30)
- ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
- ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
- ->setWebsiteIds([1])
- ->setCategoryIds([100])
- ->setStockData(['use_config_manage_stock' => 1, 'qty' => 140, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
- ->setSpecialPrice('30')
- ->save();
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
deleted file mode 100644
index 76876c39b7d72..0000000000000
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_visible_products_rollback.php
+++ /dev/null
@@ -1,35 +0,0 @@
-get(\Magento\Framework\Registry::class);
-
-$registry->unregister('isSecureArea');
-$registry->register('isSecureArea', true);
-
-/** @var $category \Magento\Catalog\Model\Category */
-$category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Category::class);
-$category->load(100);
-if ($category->getId()) {
- $category->delete();
-}
-
-/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
-$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
-
-foreach (['simple1', 'simple2', 'simple3'] as $sku) {
- try {
- $product = $productRepository->get($sku, false, null, true);
- $productRepository->delete($product);
- } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
- //Product already removed
- }
-}
-
-$registry->unregister('isSecureArea');
-$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
index 63973ac0fbe20..b75a984178f24 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Layer/Filter/DecimalTest.php
@@ -48,41 +48,7 @@ protected function setUp()
->create(\Magento\CatalogSearch\Model\Layer\Filter\Decimal::class, ['layer' => $layer]);
$this->_model->setAttributeModel($attribute);
}
-
- /**
- * Test the product collection returns the correct number of items after the filter is applied.
- *
- * @magentoDataFixture Magento/Catalog/Model/Layer/Filter/_files/attribute_special_price_filterable.php
- * @magentoDataFixture Magento/Catalog/_files/multiple_visible_products.php
- * @magentoDbIsolation disabled
- */
- public function testApplyProductCollection()
- {
- /** @var $objectManager \Magento\TestFramework\ObjectManager */
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $category = $objectManager->create(\Magento\Catalog\Model\Category::class);
- $category->load(100);
- $this->_model->getLayer()->setCurrentCategory($category);
-
- /** @var $attribute \Magento\Catalog\Model\Entity\Attribute */
- $attribute = $objectManager->create(\Magento\Catalog\Model\Entity\Attribute::class);
- $attribute->loadByCode('catalog_product', 'special_price');
- $this->_model->setAttributeModel($attribute);
-
- /** @var $objectManager \Magento\TestFramework\ObjectManager */
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- /** @var $request \Magento\TestFramework\Request */
- $request = $objectManager->get(\Magento\TestFramework\Request::class);
- $request->setParam('special_price', '10-20');
- $result = $this->_model->apply($request);
- $collection = $this->_model->getLayer()->getProductCollection();
- $size = $collection->getSize();
- $this->assertEquals(
- 1,
- $size
- );
- }
-
+
/**
* Test the filter label is correct
*/
From bfb57ec3bc426a9605682eab61dd12a8ae21da23 Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Thu, 29 Aug 2019 14:34:41 +0000
Subject: [PATCH 179/593] Corrected newsLetterConfig
---
.../CustomerGraphQl/Model/Resolver/CreateCustomer.php | 6 +++---
.../Magento/Customer/_files/customer_subscribe.php | 2 +-
.../Magento/Customer/_files/customer_subscribe_rollback.php | 4 +++-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index b7ac4d5ea5fdd..18e417bb5edfe 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -33,19 +33,19 @@ class CreateCustomer implements ResolverInterface
/**
* @var Config
*/
- private $newsletterConfig;
+ private $newsLetterConfig;
/**
* CreateCustomer constructor.
*
* @param ExtractCustomerData $extractCustomerData
* @param CreateCustomerAccount $createCustomerAccount
- * @param Config $config
+ * @param Config $newsLetterConfig
*/
public function __construct(
ExtractCustomerData $extractCustomerData,
CreateCustomerAccount $createCustomerAccount,
- Config $newsletterConfig
+ Config $newsLetterConfig
) {
$this->newsLetterConfig = $newsLetterConfig;
$this->extractCustomerData = $extractCustomerData;
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
index ab45a28a795bf..58eb11fb00e8d 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe.php
@@ -10,4 +10,4 @@
false,
'default',
0
-);
\ No newline at end of file
+);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
index 35e10e52b6e67..2a84fd5556eb7 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
@@ -5,9 +5,11 @@
*/
// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167
+declare(strict_types=1);
+
use Magento\Framework\App\Config\Storage\WriterInterface;
use Magento\TestFramework\Helper\Bootstrap;
$objectManager = Bootstrap::getObjectManager();
/** @var Writer $configWriter */
$configWriter = $objectManager->create(WriterInterface::class);
-$configWriter->delete('newsletter/general/active');
\ No newline at end of file
+$configWriter->delete('newsletter/general/active');
From c35ac36f888de3aba4c755c617d3c58a7cd9da4b Mon Sep 17 00:00:00 2001
From: Sergey Dovbenko
Date: Thu, 29 Aug 2019 19:46:54 +0000
Subject: [PATCH 180/593] Corrected code styles
---
.../Customer/_files/customer_subscribe_rollback.php | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
index 2a84fd5556eb7..26823c7534b99 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_subscribe_rollback.php
@@ -1,14 +1,15 @@
create(WriterInterface::class);
From 87cd878883728613f2fa11f592990e4858141363 Mon Sep 17 00:00:00 2001
From: Rus0
Date: Thu, 29 Aug 2019 15:35:34 -0500
Subject: [PATCH 181/593] Added functionality for throwing error when zero is
set as category id
---
.../Model/Resolver/CategoryTree.php | 2 +-
.../Magento/GraphQl/Catalog/CategoryTest.php | 28 +++++++++++++++----
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index 74bc1a459aa50..aefe7b97b8a3f 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -64,7 +64,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
return $value[$field->getName()];
}
- $rootCategoryId = !empty($args['id']) ? (int)$args['id'] :
+ $rootCategoryId = isset($args['id']) ? (int)$args['id'] :
(int)$context->getExtensionAttributes()->getStore()->getRootCategoryId();
$this->checkCategoryIsActive->execute($rootCategoryId);
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
index fdd19cdfbb04a..bc55d81b455ff 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
@@ -8,14 +8,14 @@
namespace Magento\GraphQl\Catalog;
use Magento\Catalog\Api\Data\CategoryInterface;
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\CategoryRepository;
use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
use Magento\Framework\DataObject;
+use Magento\TestFramework\ObjectManager;
use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException;
use Magento\TestFramework\TestCase\GraphQlAbstract;
-use Magento\Catalog\Api\Data\ProductInterface;
-use Magento\Catalog\Api\ProductRepositoryInterface;
-use Magento\TestFramework\ObjectManager;
/**
* Test loading of category tree
@@ -261,6 +261,25 @@ public function testGetDisabledCategory()
$this->graphQlQuery($query);
}
+ /**
+ * @magentoApiDataFixture Magento/Catalog/_files/categories.php
+ * @expectedException \Exception
+ * @expectedExceptionMessage Category doesn't exist
+ */
+ public function testGetCategoryIdZero()
+ {
+ $categoryId = 0;
+ $query = <<graphQlQuery($query);
+ }
+
public function testNonExistentCategoryWithProductCount()
{
$query = << 'attribute_set_id', 'expected_value' => $product->getAttributeSetId()],
['response_field' => 'created_at', 'expected_value' => $product->getCreatedAt()],
['response_field' => 'name', 'expected_value' => $product->getName()],
- ['response_field' => 'price', 'expected_value' =>
- [
+ ['response_field' => 'price', 'expected_value' => [
'minimalPrice' => [
'amount' => [
'value' => $product->getPrice(),
From 4760f86ba4c8ad2f24f6db077a7998359574e5eb Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Thu, 29 Aug 2019 15:56:49 -0500
Subject: [PATCH 182/593] MC-18512: Dynamically inject all searchable custom
attributes for product filtering
---
app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index d29a24428ae12..8e9471c77dc6b 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -7,7 +7,7 @@ type Query {
filter: ProductAttributeFilterInput @doc(description: "Identifies which product attributes to search for and return."),
pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."),
currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."),
- sort: ProductAttributeSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.")
+ sort: ProductAttributeSortInput @doc(description: "Specifies which attributes to sort on, and whether to return the results in ascending or descending order.")
): Products
@resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity")
category (
@@ -221,7 +221,7 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model
products(
pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."),
currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."),
- sort: ProductAttributeSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.")
+ sort: ProductAttributeSortInput @doc(description: "Specifies which attributes to sort on, and whether to return the results in ascending or descending order.")
): CategoryProducts @doc(description: "The list of products assigned to the category.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products")
breadcrumbs: [Breadcrumb] @doc(description: "Breadcrumbs, parent categories info.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Breadcrumbs")
}
@@ -338,7 +338,7 @@ type ProductMediaGalleryEntriesVideoContent @doc(description: "ProductMediaGalle
video_metadata: String @doc(description: "Optional data about the video.")
}
-input ProductSortInput @deprecated(reason: "Attributes used in this input are hardcoded and some of them are not searcheable. Use @ProductAttributeSortInput instead") @doc(description: "ProductSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order.") {
+input ProductSortInput @deprecated(reason: "The attributes used in this input are hard-coded, and some of them are not sortable. Use @ProductAttributeSortInput instead") @doc(description: "ProductSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order.") {
name: SortEnum @doc(description: "The product name. Customers use this name to identify the product.")
sku: SortEnum @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.")
description: SortEnum @doc(description: "Detailed information about the product. The value can include simple HTML tags.")
From ab3b7d8d4ea35e5728a20a6916d00b25093c937a Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Thu, 29 Aug 2019 16:36:54 -0500
Subject: [PATCH 183/593] MC-19702: Add input_type to customAttributeMetadata
query
- update test
---
.../Catalog/ProductAttributeTypeTest.php | 35 ++++++++++++++-----
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php
index 063da7c11bf7f..a34d5e21704af 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeTypeTest.php
@@ -50,7 +50,8 @@ public function testAttributeTypeResolver()
{
attribute_code
attribute_type
- entity_type
+ entity_type
+ input_type
}
}
}
@@ -71,7 +72,8 @@ public function testAttributeTypeResolver()
\Magento\Catalog\Api\Data\ProductInterface::class
];
$attributeTypes = ['String', 'Int', 'Float','Boolean', 'Float'];
- $this->assertAttributeType($attributeTypes, $expectedAttributeCodes, $entityType, $response);
+ $inputTypes = ['textarea', 'select', 'price', 'boolean', 'price'];
+ $this->assertAttributeType($attributeTypes, $expectedAttributeCodes, $entityType, $inputTypes, $response);
}
/**
@@ -121,7 +123,8 @@ public function testComplexAttributeTypeResolver()
{
attribute_code
attribute_type
- entity_type
+ entity_type
+ input_type
}
}
}
@@ -154,7 +157,16 @@ public function testComplexAttributeTypeResolver()
'CustomerDataRegionInterface',
'ProductMediaGallery'
];
- $this->assertAttributeType($attributeTypes, $expectedAttributeCodes, $entityTypes, $response);
+ $inputTypes = [
+ 'select',
+ 'multiselect',
+ 'select',
+ 'select',
+ 'text',
+ 'text',
+ 'gallery'
+ ];
+ $this->assertAttributeType($attributeTypes, $expectedAttributeCodes, $entityTypes, $inputTypes, $response);
}
/**
@@ -213,11 +225,17 @@ public function testUnDefinedAttributeType()
* @param array $attributeTypes
* @param array $expectedAttributeCodes
* @param array $entityTypes
+ * @param array $inputTypes
* @param array $actualResponse
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
- private function assertAttributeType($attributeTypes, $expectedAttributeCodes, $entityTypes, $actualResponse)
- {
+ private function assertAttributeType(
+ $attributeTypes,
+ $expectedAttributeCodes,
+ $entityTypes,
+ $inputTypes,
+ $actualResponse
+ ) {
$attributeMetaDataItems = array_map(null, $actualResponse['customAttributeMetadata']['items'], $attributeTypes);
foreach ($attributeMetaDataItems as $itemIndex => $itemArray) {
@@ -225,8 +243,9 @@ private function assertAttributeType($attributeTypes, $expectedAttributeCodes, $
$attributeMetaDataItems[$itemIndex][0],
[
"attribute_code" => $expectedAttributeCodes[$itemIndex],
- "attribute_type" =>$attributeTypes[$itemIndex],
- "entity_type" => $entityTypes[$itemIndex]
+ "attribute_type" => $attributeTypes[$itemIndex],
+ "entity_type" => $entityTypes[$itemIndex],
+ "input_type" => $inputTypes[$itemIndex]
]
);
}
From 91f8f886b47578956a6059c8b4273e5800fc213c Mon Sep 17 00:00:00 2001
From: Rus0
Date: Thu, 29 Aug 2019 16:43:06 -0500
Subject: [PATCH 184/593] adding backward compatibility in constructor
---
app/code/Magento/Wishlist/Model/Wishlist.php | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php
index 0d9c74edb3d4c..827607e72f807 100644
--- a/app/code/Magento/Wishlist/Model/Wishlist.php
+++ b/app/code/Magento/Wishlist/Model/Wishlist.php
@@ -175,11 +175,11 @@ class Wishlist extends AbstractModel implements IdentityInterface
* @param Random $mathRandom
* @param DateTime $dateTime
* @param ProductRepositoryInterface $productRepository
- * @param StockItemRepository $stockItemRepository
- * @param ScopeConfigInterface|null $scopeConfig
* @param bool $useCurrentWebsite
* @param array $data
* @param Json|null $serializer
+ * @param StockItemRepository|null $stockItemRepository
+ * @param ScopeConfigInterface|null $scopeConfig
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -197,11 +197,11 @@ public function __construct(
Random $mathRandom,
DateTime $dateTime,
ProductRepositoryInterface $productRepository,
- StockItemRepository $stockItemRepository,
- ScopeConfigInterface $scopeConfig = null,
$useCurrentWebsite = true,
array $data = [],
- Json $serializer = null
+ Json $serializer = null,
+ StockItemRepository $stockItemRepository = null,
+ ScopeConfigInterface $scopeConfig = null
) {
$this->_useCurrentWebsite = $useCurrentWebsite;
$this->_catalogProduct = $catalogProduct;
@@ -216,7 +216,9 @@ public function __construct(
$this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
$this->productRepository = $productRepository;
- $this->stockItemRepository = $stockItemRepository;
+ $this->stockItemRepository = $stockItemRepository ?: ObjectManager::getInstance()->get(
+ StockItemRepository::class
+ );
$this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
}
From 239ef1494e2c25d7cdd84e82072cdf71a9f6a5b5 Mon Sep 17 00:00:00 2001
From: Dmytro Horytskyi
Date: Thu, 29 Aug 2019 18:22:31 -0500
Subject: [PATCH 185/593] MC-19689: Simple product disappearing in the
configurable grid after qty set to 0
---
.../Model/Product/Type/Configurable.php | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
index a849d964eaed5..77450748f7eba 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
@@ -1304,15 +1304,11 @@ private function loadUsedProducts(\Magento\Catalog\Model\Product $product, $cach
{
$dataFieldName = $salableOnly ? $this->usedSalableProducts : $this->_usedProducts;
if (!$product->hasData($dataFieldName)) {
- $usedProducts = $this->readUsedProductsCacheData($cacheKey);
- if ($usedProducts === null) {
- $collection = $this->getConfiguredUsedProductCollection($product, false);
- if ($salableOnly) {
- $collection = $this->salableProcessor->process($collection);
- }
- $usedProducts = array_values($collection->getItems());
- $this->saveUsedProductsCacheData($product, $usedProducts, $cacheKey);
+ $collection = $this->getConfiguredUsedProductCollection($product, false);
+ if ($salableOnly) {
+ $collection = $this->salableProcessor->process($collection);
}
+ $usedProducts = array_values($collection->getItems());
$product->setData($dataFieldName, $usedProducts);
}
From 6753604a30cb29fcc1772bc90e639e0a2f459c3c Mon Sep 17 00:00:00 2001
From: Rus0
Date: Thu, 29 Aug 2019 21:53:18 -0500
Subject: [PATCH 186/593] wrong order of constructor
---
app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
index cbbccf29e63b8..4d4d1a235de1d 100644
--- a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
+++ b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
@@ -206,11 +206,11 @@ protected function setUp()
$this->mathRandom,
$this->dateTime,
$this->productRepository,
- $this->stockItemRepository,
- $this->scopeConfig,
false,
[],
- $this->serializer
+ $this->serializer,
+ $this->stockItemRepository,
+ $this->scopeConfig
);
}
From 2a8291985b4b0d5a79ca8df0fe1c27d4040d934d Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Fri, 30 Aug 2019 11:55:57 -0500
Subject: [PATCH 187/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
- Allow exact filtering by sku
---
.../Plugin/Search/Request/ConfigReader.php | 207 ++++++++++++------
.../FieldMapper/Product/AttributeAdapter.php | 13 ++
.../Product/FieldProvider/StaticField.php | 12 +
3 files changed, 166 insertions(+), 66 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php b/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
index 151be89177744..02f23e59f15d7 100644
--- a/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
+++ b/app/code/Magento/CatalogGraphQl/Plugin/Search/Request/ConfigReader.php
@@ -8,9 +8,12 @@
use Magento\Catalog\Api\Data\EavAttributeInterface;
use Magento\CatalogSearch\Model\Search\RequestGenerator;
use Magento\CatalogSearch\Model\Search\RequestGenerator\GeneratorResolver;
+use Magento\Eav\Model\Entity\Attribute;
+use Magento\Framework\Search\EngineResolverInterface;
use Magento\Framework\Search\Request\FilterInterface;
use Magento\Framework\Search\Request\QueryInterface;
use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory;
+use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection;
/**
* Add search request configuration to config for give ability filter and search products during GraphQL request
@@ -42,19 +45,27 @@ class ConfigReader
*/
private $productAttributeCollectionFactory;
+ /**
+ * @var EngineResolverInterface
+ */
+ private $searchEngineResolver;
+
/** Bucket name suffix */
private const BUCKET_SUFFIX = '_bucket';
/**
* @param GeneratorResolver $generatorResolver
* @param CollectionFactory $productAttributeCollectionFactory
+ * @param EngineResolverInterface $searchEngineResolver
*/
public function __construct(
GeneratorResolver $generatorResolver,
- CollectionFactory $productAttributeCollectionFactory
+ CollectionFactory $productAttributeCollectionFactory,
+ EngineResolverInterface $searchEngineResolver
) {
$this->generatorResolver = $generatorResolver;
$this->productAttributeCollectionFactory = $productAttributeCollectionFactory;
+ $this->searchEngineResolver = $searchEngineResolver;
}
/**
@@ -86,19 +97,19 @@ public function afterRead(
/**
* Retrieve searchable attributes
*
- * @return \Magento\Eav\Model\Entity\Attribute[]
+ * @return Attribute[]
*/
private function getSearchableAttributes(): array
{
$attributes = [];
- /** @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection $productAttributes */
+ /** @var Collection $productAttributes */
$productAttributes = $this->productAttributeCollectionFactory->create();
$productAttributes->addFieldToFilter(
['is_searchable', 'is_visible_in_advanced_search', 'is_filterable', 'is_filterable_in_search'],
[1, 1, [1, 2], 1]
);
- /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
+ /** @var Attribute $attribute */
foreach ($productAttributes->getItems() as $attribute) {
$attributes[$attribute->getAttributeCode()] = $attribute;
}
@@ -121,83 +132,35 @@ private function generateRequest()
continue;
}
$queryName = $attribute->getAttributeCode() . '_query';
+ $filterName = $attribute->getAttributeCode() . RequestGenerator::FILTER_SUFFIX;
$request['queries'][$this->requestNameWithAggregation]['queryReference'][] = [
'clause' => 'must',
'ref' => $queryName,
];
+
switch ($attribute->getBackendType()) {
case 'static':
case 'text':
case 'varchar':
if ($attribute->getFrontendInput() === 'multiselect') {
- $filterName = $attribute->getAttributeCode() . RequestGenerator::FILTER_SUFFIX;
- $request['queries'][$queryName] = [
- 'name' => $queryName,
- 'type' => QueryInterface::TYPE_FILTER,
- 'filterReference' => [
- [
- 'ref' => $filterName,
- ],
- ],
- ];
- $request['filters'][$filterName] = [
- 'type' => FilterInterface::TYPE_TERM,
- 'name' => $filterName,
- 'field' => $attribute->getAttributeCode(),
- 'value' => '$' . $attribute->getAttributeCode() . '$',
- ];
+ $request['queries'][$queryName] = $this->generateFilterQuery($queryName, $filterName);
+ $request['filters'][$filterName] = $this->generateTermFilter($filterName, $attribute);
+ } elseif ($attribute->getAttributeCode() === 'sku') {
+ $request['queries'][$queryName] = $this->generateFilterQuery($queryName, $filterName);
+ $request['filters'][$filterName] = $this->generateSkuTermFilter($filterName, $attribute);
} else {
- $request['queries'][$queryName] = [
- 'name' => $queryName,
- 'type' => 'matchQuery',
- 'value' => '$' . $attribute->getAttributeCode() . '$',
- 'match' => [
- [
- 'field' => $attribute->getAttributeCode(),
- 'boost' => $attribute->getSearchWeight() ?: 1,
- ],
- ],
- ];
+ $request['queries'][$queryName] = $this->generateMatchQuery($queryName, $attribute);
}
break;
case 'decimal':
case 'datetime':
case 'date':
- $filterName = $attribute->getAttributeCode() . RequestGenerator::FILTER_SUFFIX;
- $request['queries'][$queryName] = [
- 'name' => $queryName,
- 'type' => QueryInterface::TYPE_FILTER,
- 'filterReference' => [
- [
- 'ref' => $filterName,
- ],
- ],
- ];
- $request['filters'][$filterName] = [
- 'field' => $attribute->getAttributeCode(),
- 'name' => $filterName,
- 'type' => FilterInterface::TYPE_RANGE,
- 'from' => '$' . $attribute->getAttributeCode() . '.from$',
- 'to' => '$' . $attribute->getAttributeCode() . '.to$',
- ];
+ $request['queries'][$queryName] = $this->generateFilterQuery($queryName, $filterName);
+ $request['filters'][$filterName] = $this->generateRangeFilter($filterName, $attribute);
break;
default:
- $filterName = $attribute->getAttributeCode() . RequestGenerator::FILTER_SUFFIX;
- $request['queries'][$queryName] = [
- 'name' => $queryName,
- 'type' => QueryInterface::TYPE_FILTER,
- 'filterReference' => [
- [
- 'ref' => $filterName,
- ],
- ],
- ];
- $request['filters'][$filterName] = [
- 'type' => FilterInterface::TYPE_TERM,
- 'name' => $filterName,
- 'field' => $attribute->getAttributeCode(),
- 'value' => '$' . $attribute->getAttributeCode() . '$',
- ];
+ $request['queries'][$queryName] = $this->generateFilterQuery($queryName, $filterName);
+ $request['filters'][$filterName] = $this->generateTermFilter($filterName, $attribute);
}
$generator = $this->generatorResolver->getGeneratorForType($attribute->getBackendType());
@@ -215,11 +178,11 @@ private function generateRequest()
/**
* Add attribute with specified boost to "search" query used in full text search
*
- * @param \Magento\Eav\Model\Entity\Attribute $attribute
+ * @param Attribute $attribute
* @param array $request
* @return void
*/
- private function addSearchAttributeToFullTextSearch(\Magento\Eav\Model\Entity\Attribute $attribute, &$request): void
+ private function addSearchAttributeToFullTextSearch(Attribute $attribute, &$request): void
{
// Match search by custom price attribute isn't supported
if ($attribute->getFrontendInput() !== 'price') {
@@ -229,4 +192,116 @@ private function addSearchAttributeToFullTextSearch(\Magento\Eav\Model\Entity\At
];
}
}
+
+ /**
+ * Return array representation of range filter
+ *
+ * @param string $filterName
+ * @param Attribute $attribute
+ * @return array
+ */
+ private function generateRangeFilter(string $filterName, Attribute $attribute)
+ {
+ return [
+ 'field' => $attribute->getAttributeCode(),
+ 'name' => $filterName,
+ 'type' => FilterInterface::TYPE_RANGE,
+ 'from' => '$' . $attribute->getAttributeCode() . '.from$',
+ 'to' => '$' . $attribute->getAttributeCode() . '.to$',
+ ];
+ }
+
+ /**
+ * Return array representation of term filter
+ *
+ * @param string $filterName
+ * @param Attribute $attribute
+ * @return array
+ */
+ private function generateTermFilter(string $filterName, Attribute $attribute)
+ {
+ return [
+ 'type' => FilterInterface::TYPE_TERM,
+ 'name' => $filterName,
+ 'field' => $attribute->getAttributeCode(),
+ 'value' => '$' . $attribute->getAttributeCode() . '$',
+ ];
+ }
+
+ /**
+ * Generate term filter for sku field
+ *
+ * Sku needs to be treated specially to allow for exact match
+ *
+ * @param string $filterName
+ * @param Attribute $attribute
+ * @return array
+ */
+ private function generateSkuTermFilter(string $filterName, Attribute $attribute)
+ {
+ $field = $this->isElasticSearch() ? 'sku.filter_sku' : 'sku';
+
+ return [
+ 'type' => FilterInterface::TYPE_TERM,
+ 'name' => $filterName,
+ 'field' => $field,
+ 'value' => '$' . $attribute->getAttributeCode() . '$',
+ ];
+ }
+
+ /**
+ * Return array representation of query based on filter
+ *
+ * @param string $queryName
+ * @param string $filterName
+ * @return array
+ */
+ private function generateFilterQuery(string $queryName, string $filterName)
+ {
+ return [
+ 'name' => $queryName,
+ 'type' => QueryInterface::TYPE_FILTER,
+ 'filterReference' => [
+ [
+ 'ref' => $filterName,
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Return array representation of match query
+ *
+ * @param string $queryName
+ * @param Attribute $attribute
+ * @return array
+ */
+ private function generateMatchQuery(string $queryName, Attribute $attribute)
+ {
+ return [
+ 'name' => $queryName,
+ 'type' => 'matchQuery',
+ 'value' => '$' . $attribute->getAttributeCode() . '$',
+ 'match' => [
+ [
+ 'field' => $attribute->getAttributeCode(),
+ 'boost' => $attribute->getSearchWeight() ?: 1,
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Check if the current search engine is elasticsearch
+ *
+ * @return bool
+ */
+ private function isElasticSearch()
+ {
+ $searchEngine = $this->searchEngineResolver->getCurrentSearchEngine();
+ if (strpos($searchEngine, 'elasticsearch') === 0) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/AttributeAdapter.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/AttributeAdapter.php
index 165f7e78eb65f..c3952b494f2e9 100644
--- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/AttributeAdapter.php
+++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/AttributeAdapter.php
@@ -176,6 +176,19 @@ public function getFrontendInput()
return $this->getAttribute()->getFrontendInput();
}
+ /**
+ * Check if product should always be filterable
+ *
+ * @return bool
+ */
+ public function isAlwaysFilterable(): bool
+ {
+ // List of attributes which are required to be filterable
+ $alwaysFilterableAttributes = ['sku'];
+
+ return in_array($this->getAttributeCode(), $alwaysFilterableAttributes, true);
+ }
+
/**
* Get product attribute instance.
*
diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php
index 6876b23bbb156..466bf5d2d09eb 100644
--- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php
+++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php
@@ -130,6 +130,18 @@ public function getFields(array $context = []): array
];
}
+ if ($attributeAdapter->isAlwaysFilterable()) {
+ $filterFieldName = 'filter_' . $this->fieldNameResolver->getFieldName(
+ $attributeAdapter,
+ ['type' => FieldMapperInterface::TYPE_FILTER]
+ );
+ $allAttributes[$fieldName]['fields'][$filterFieldName] = [
+ 'type' => $this->fieldTypeConverter->convert(
+ FieldTypeConverterInterface::INTERNAL_DATA_TYPE_KEYWORD
+ )
+ ];
+ }
+
if ($attributeAdapter->isComplexType()) {
$childFieldName = $this->fieldNameResolver->getFieldName(
$attributeAdapter,
From 2487db1b537c9d14f534d8628acf60581f5cbb1b Mon Sep 17 00:00:00 2001
From: Dmytro Horytskyi
Date: Fri, 30 Aug 2019 12:15:50 -0500
Subject: [PATCH 188/593] MC-19689: Simple product disappearing in the
configurable grid after qty set to 0
---
.../Plugin/Frontend/UsedProductsCache.php | 185 ++++++++++++++++++
.../Model/Product/Type/Configurable.php | 36 ++--
.../Magento/ConfigurableProduct/etc/di.xml | 8 +
.../ConfigurableProduct/etc/frontend/di.xml | 3 +
4 files changed, 213 insertions(+), 19 deletions(-)
create mode 100644 app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php
new file mode 100644
index 0000000000000..69c0a5f6000f0
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php
@@ -0,0 +1,185 @@
+metadataPool = $metadataPool;
+ $this->cache = $cache;
+ $this->serializer = $serializer;
+ $this->productFactory = $productFactory;
+ $this->customerSession = $customerSession;
+ }
+
+ /**
+ * Retrieve used products for configurable product
+ *
+ * @param Configurable $subject
+ * @param callable $proceed
+ * @param Product $product
+ * @param array|null $requiredAttributeIds
+ * @return ProductInterface[]
+ */
+ public function aroundGetUsedProducts(
+ Configurable $subject,
+ callable $proceed,
+ $product,
+ $requiredAttributeIds = null
+ ) {
+ $cacheKey = $this->getCacheKey($product, $requiredAttributeIds);
+ $usedProducts = $this->readUsedProductsCacheData($cacheKey);
+ if ($usedProducts === null) {
+ $usedProducts = $proceed($product, $requiredAttributeIds);
+ $this->saveUsedProductsCacheData($product, $usedProducts, $cacheKey);
+ }
+
+ return $usedProducts;
+ }
+
+ /**
+ * Generate cache key for product
+ *
+ * @param Product $product
+ * @param array|null $requiredAttributeIds
+ * @return string
+ */
+ private function getCacheKey($product, $requiredAttributeIds = null): string
+ {
+ $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+ $keyParts = [
+ 'getUsedProducts',
+ $product->getData($metadata->getLinkField()),
+ $product->getStoreId(),
+ $this->customerSession->getCustomerGroupId(),
+ ];
+ if ($requiredAttributeIds !== null) {
+ sort($requiredAttributeIds);
+ $keyParts[] = implode('', $requiredAttributeIds);
+ }
+ $cacheKey = sha1(implode('_', $keyParts));
+
+ return $cacheKey;
+ }
+
+ /**
+ * Read used products data from cache
+ *
+ * Looking for cache record stored under provided $cacheKey
+ * In case data exists turns it into array of products
+ *
+ * @param string $cacheKey
+ * @return ProductInterface[]|null
+ */
+ private function readUsedProductsCacheData(string $cacheKey): ?array
+ {
+ $data = $this->cache->load($cacheKey);
+ if (!$data) {
+ return null;
+ }
+
+ $items = $this->serializer->unserialize($data);
+ if (!$items) {
+ return null;
+ }
+
+ $usedProducts = [];
+ foreach ($items as $item) {
+ /** @var Product $productItem */
+ $productItem = $this->productFactory->create();
+ $productItem->setData($item);
+ $usedProducts[] = $productItem;
+ }
+
+ return $usedProducts;
+ }
+
+ /**
+ * Save $subProducts to cache record identified with provided $cacheKey
+ *
+ * Cached data will be tagged with combined list of product tags and data specific tags i.e. 'price' etc.
+ *
+ * @param Product $product
+ * @param ProductInterface[] $subProducts
+ * @param string $cacheKey
+ * @return bool
+ */
+ private function saveUsedProductsCacheData(Product $product, array $subProducts, string $cacheKey): bool
+ {
+ $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+ $data = $this->serializer->serialize(array_map(
+ function ($item) {
+ return $item->getData();
+ },
+ $subProducts
+ ));
+ $tags = array_merge(
+ $product->getIdentities(),
+ [
+ Category::CACHE_TAG,
+ Product::CACHE_TAG,
+ 'price',
+ Configurable::TYPE_CODE . '_' . $product->getData($metadata->getLinkField())
+ ]
+ );
+ $result = $this->cache->save($data, $cacheKey, $tags);
+
+ return (bool) $result;
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
index 77450748f7eba..c60953e33e9eb 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
@@ -1233,28 +1233,22 @@ public function isPossibleBuyFromList($product)
* Returns array of sub-products for specified configurable product
*
* $requiredAttributeIds - one dimensional array, if provided
- *
* Result array contains all children for specified configurable product
*
- * @param \Magento\Catalog\Model\Product $product
- * @param array $requiredAttributeIds
+ * @param \Magento\Catalog\Model\Product $product
+ * @param array $requiredAttributeIds
* @return ProductInterface[]
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function getUsedProducts($product, $requiredAttributeIds = null)
{
- $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
- $keyParts = [
- __METHOD__,
- $product->getData($metadata->getLinkField()),
- $product->getStoreId(),
- $this->getCustomerSession()->getCustomerGroupId()
- ];
- if ($requiredAttributeIds !== null) {
- sort($requiredAttributeIds);
- $keyParts[] = implode('', $requiredAttributeIds);
+ if (!$product->hasData($this->_usedProducts)) {
+ $collection = $this->getConfiguredUsedProductCollection($product, false);
+ $usedProducts = array_values($collection->getItems());
+ $product->setData($this->_usedProducts, $usedProducts);
}
- $cacheKey = $this->getUsedProductsCacheKey($keyParts);
- return $this->loadUsedProducts($product, $cacheKey);
+
+ return $product->getData($this->_usedProducts);
}
/**
@@ -1304,11 +1298,15 @@ private function loadUsedProducts(\Magento\Catalog\Model\Product $product, $cach
{
$dataFieldName = $salableOnly ? $this->usedSalableProducts : $this->_usedProducts;
if (!$product->hasData($dataFieldName)) {
- $collection = $this->getConfiguredUsedProductCollection($product, false);
- if ($salableOnly) {
- $collection = $this->salableProcessor->process($collection);
+ $usedProducts = $this->readUsedProductsCacheData($cacheKey);
+ if ($usedProducts === null) {
+ $collection = $this->getConfiguredUsedProductCollection($product, false);
+ if ($salableOnly) {
+ $collection = $this->salableProcessor->process($collection);
+ }
+ $usedProducts = array_values($collection->getItems());
+ $this->saveUsedProductsCacheData($product, $usedProducts, $cacheKey);
}
- $usedProducts = array_values($collection->getItems());
$product->setData($dataFieldName, $usedProducts);
}
diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml
index b8f7ed67a9868..c8a278df92dc6 100644
--- a/app/code/Magento/ConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/di.xml
@@ -256,4 +256,12 @@
+
+
+ Magento\Framework\App\Cache\Type\Collection
+
+
+ Magento\Framework\Serialize\Serializer\Json
+
+
diff --git a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml
index df96829b354c8..b2d50f54f5334 100644
--- a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml
@@ -13,4 +13,7 @@
+
+
+
From 3051ff6096a6b1eb58e67ac7ce00fce333730bbc Mon Sep 17 00:00:00 2001
From: Dmytro Horytskyi
Date: Fri, 30 Aug 2019 15:49:20 -0500
Subject: [PATCH 189/593] MC-19689: Simple product disappearing in the
configurable grid after qty set to 0
---
.../Plugin/Frontend/UsedProductsCache.php | 19 +++--
.../Model/Product/Type/ConfigurableTest.php | 77 ++-----------------
2 files changed, 20 insertions(+), 76 deletions(-)
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php
index 69c0a5f6000f0..19a1b8d3ca17f 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/UsedProductsCache.php
@@ -19,6 +19,8 @@
/**
* Cache of used products for configurable product
+ *
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class UsedProductsCache
{
@@ -76,6 +78,7 @@ public function __construct(
* @param Product $product
* @param array|null $requiredAttributeIds
* @return ProductInterface[]
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundGetUsedProducts(
Configurable $subject,
@@ -163,19 +166,21 @@ private function readUsedProductsCacheData(string $cacheKey): ?array
private function saveUsedProductsCacheData(Product $product, array $subProducts, string $cacheKey): bool
{
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
- $data = $this->serializer->serialize(array_map(
- function ($item) {
- return $item->getData();
- },
- $subProducts
- ));
+ $data = $this->serializer->serialize(
+ array_map(
+ function ($item) {
+ return $item->getData();
+ },
+ $subProducts
+ )
+ );
$tags = array_merge(
$product->getIdentities(),
[
Category::CACHE_TAG,
Product::CACHE_TAG,
'price',
- Configurable::TYPE_CODE . '_' . $product->getData($metadata->getLinkField())
+ Configurable::TYPE_CODE . '_' . $product->getData($metadata->getLinkField()),
]
);
$result = $this->cache->save($data, $cacheKey, $tags);
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
index c351d12fa813d..bf8357b5181d7 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
@@ -344,25 +344,13 @@ public function testCanUseAttribute()
public function testGetUsedProducts()
{
- $productCollectionItemData = ['array'];
+ $productCollectionItem = $this->createMock(\Magento\Catalog\Model\Product::class);
+ $attributeCollection = $this->createMock(Collection::class);
+ $product = $this->createMock(\Magento\Catalog\Model\Product::class);
+ $productCollection = $this->createMock(ProductCollection::class);
- $productCollectionItem = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
- ->disableOriginalConstructor()
- ->getMock();
- $attributeCollection = $this->getMockBuilder(Collection::class)
- ->disableOriginalConstructor()
- ->getMock();
- $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
- ->disableOriginalConstructor()
- ->getMock();
- $productCollection = $this->getMockBuilder(ProductCollection::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $productCollectionItem->expects($this->once())->method('getData')->willReturn($productCollectionItemData);
$attributeCollection->expects($this->any())->method('setProductFilter')->willReturnSelf();
$product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(5);
- $product->expects($this->once())->method('getIdentities')->willReturn(['123']);
$product->expects($this->exactly(2))
->method('hasData')
@@ -388,59 +376,10 @@ public function testGetUsedProducts()
$productCollection->expects($this->once())->method('setStoreId')->with(5)->willReturn([]);
$productCollection->expects($this->once())->method('getItems')->willReturn([$productCollectionItem]);
- $this->serializer->expects($this->once())
- ->method('serialize')
- ->with([$productCollectionItemData])
- ->willReturn('result');
-
$this->productCollectionFactory->expects($this->any())->method('create')->willReturn($productCollection);
$this->model->getUsedProducts($product);
}
- public function testGetUsedProductsWithDataInCache()
- {
- $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
- ->disableOriginalConstructor()
- ->getMock();
- $childProduct = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $dataKey = '_cache_instance_products';
- $usedProductsData = [['first']];
- $usedProducts = [$childProduct];
-
- $product->expects($this->once())
- ->method('hasData')
- ->with($dataKey)
- ->willReturn(false);
- $product->expects($this->once())
- ->method('setData')
- ->with($dataKey, $usedProducts);
- $product->expects($this->any())
- ->method('getData')
- ->willReturnOnConsecutiveCalls(1, $usedProducts);
-
- $childProduct->expects($this->once())
- ->method('setData')
- ->with($usedProductsData[0]);
-
- $this->productFactory->expects($this->once())
- ->method('create')
- ->willReturn($childProduct);
-
- $this->cache->expects($this->once())
- ->method('load')
- ->willReturn($usedProductsData);
-
- $this->serializer->expects($this->once())
- ->method('unserialize')
- ->with($usedProductsData)
- ->willReturn($usedProductsData);
-
- $this->assertEquals($usedProducts, $this->model->getUsedProducts($product));
- }
-
/**
* @param int $productStore
*
@@ -878,12 +817,12 @@ public function testSetImageFromChildProduct()
->method('getLinkField')
->willReturn('link');
$productMock->expects($this->any())->method('hasData')
- ->withConsecutive(['store_id'], ['_cache_instance_products'])
- ->willReturnOnConsecutiveCalls(true, true);
+ ->withConsecutive(['_cache_instance_products'])
+ ->willReturnOnConsecutiveCalls(true);
$productMock->expects($this->any())->method('getData')
- ->withConsecutive(['image'], ['image'], ['link'], ['store_id'], ['_cache_instance_products'])
- ->willReturnOnConsecutiveCalls('no_selection', 'no_selection', 1, 1, [$childProductMock]);
+ ->withConsecutive(['image'], ['image'], ['_cache_instance_products'])
+ ->willReturnOnConsecutiveCalls('no_selection', 'no_selection', [$childProductMock]);
$childProductMock->expects($this->any())->method('getData')->with('image')->willReturn('image_data');
$productMock->expects($this->once())->method('setImage')->with('image_data')->willReturnSelf();
From ef68feda5d4b289dfb7e3355e23323c5b3101438 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Fri, 30 Aug 2019 15:59:58 -0500
Subject: [PATCH 190/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
- Always apply relevance sort order
---
.../Product/SearchCriteriaBuilder.php | 22 +++++++++++++------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
index 1e646b3c0b74b..af6ed85196cf8 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -96,11 +96,9 @@ public function build(array $args, bool $includeAggregation): SearchCriteriaInte
if (!empty($args['search'])) {
$this->addFilter($searchCriteria, 'search_term', $args['search']);
- if (!$searchCriteria->getSortOrders()) {
- $this->addDefaultSortOrder($searchCriteria);
- }
}
+ $this->addDefaultSortOrder($searchCriteria);
$this->addVisibilityFilter($searchCriteria, !empty($args['search']), !empty($args['filter']));
$searchCriteria->setCurrentPage($args['currentPage']);
@@ -166,17 +164,27 @@ private function addFilter(SearchCriteriaInterface $searchCriteria, string $fiel
}
/**
- * Sort by _score DESC if no sort order is set
+ * Sort by relevance DESC by default
*
* @param SearchCriteriaInterface $searchCriteria
*/
private function addDefaultSortOrder(SearchCriteriaInterface $searchCriteria): void
{
- $sortOrder = $this->sortOrderBuilder
- ->setField('_score')
+ $sortOrders = $searchCriteria->getSortOrders() ?? [];
+ foreach ($sortOrders as $sortOrder) {
+ // Relevance order is already specified
+ if ($sortOrder->getField() === 'relevance') {
+ return;
+ }
+ }
+ $defaultSortOrder = $this->sortOrderBuilder
+ ->setField('relevance')
->setDirection(SortOrder::SORT_DESC)
->create();
- $searchCriteria->setSortOrders([$sortOrder]);
+
+ $sortOrders[] = $defaultSortOrder;
+
+ $searchCriteria->setSortOrders($sortOrders);
}
/**
From 689a0b9719aa286e57858020077e6bb270ef42d9 Mon Sep 17 00:00:00 2001
From: Dmytro Horytskyi
Date: Fri, 30 Aug 2019 16:21:47 -0500
Subject: [PATCH 191/593] MC-19689: Simple product disappearing in the
configurable grid after qty set to 0
---
.../Test/Unit/Model/Product/Type/ConfigurableTest.php | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
index bf8357b5181d7..165e479d99348 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
@@ -266,10 +266,12 @@ public function testSave()
->with('_cache_instance_used_product_attribute_ids')
->willReturn(true);
$extensionAttributes = $this->getMockBuilder(ProductExtensionInterface::class)
- ->setMethods([
- 'getConfigurableProductOptions',
- 'getConfigurableProductLinks'
- ])
+ ->setMethods(
+ [
+ 'getConfigurableProductOptions',
+ 'getConfigurableProductLinks'
+ ]
+ )
->getMockForAbstractClass();
$this->entityMetadata->expects($this->any())
->method('getLinkField')
From 48ae1fcf80d4b39df2e0699fcc3442f35bf3b299 Mon Sep 17 00:00:00 2001
From: Gustavo Dauer
Date: Fri, 30 Aug 2019 20:39:15 -0300
Subject: [PATCH 192/593] bugfix/MAGE24366
---
app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php b/app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php
index ac4a93e6066a4..77adbb4a2b67c 100644
--- a/app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php
+++ b/app/code/Magento/Checkout/Controller/Cart/UpdateItemQty.php
@@ -115,6 +115,8 @@ public function execute()
*/
private function updateItemQuantity(Item $item, float $qty)
{
+ $item->clearMessage();
+
if ($qty > 0) {
$item->setQty($qty);
From 2f7b1d964b95ec89bcc80318103573c4378cdf3b Mon Sep 17 00:00:00 2001
From: Vitaliy Boyko
Date: Sat, 31 Aug 2019 11:57:01 +0300
Subject: [PATCH 193/593] graphQl-812: Added test case for configurable
products with different super attributes
---
.../AddConfigurableProductToCartTest.php | 37 +++-
.../_files/configurable_attribute_2.php | 62 +++++++
.../configurable_attribute_2_rollback.php | 28 +++
...roducts_with_different_super_attribute.php | 162 ++++++++++++++++++
...ith_different_super_attribute_rollback.php | 14 ++
5 files changed, 302 insertions(+), 1 deletion(-)
create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2.php
create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2_rollback.php
create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute.php
create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute_rollback.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php
index 36120c0535d49..9145f06542fdd 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php
@@ -7,6 +7,7 @@
namespace Magento\GraphQl\ConfigurableProduct;
+use Exception;
use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -68,10 +69,12 @@ public function testAddConfigurableProductToCart()
}
/**
+ * TODO: Verify whether exception should be thrown in this scenario
+ *
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products.php
* @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
*/
- public function testAddVariationFromAnotherConfigurableProductToCart()
+ public function testAddVariationFromAnotherConfigurableProductWithTheSameSuperAttributeToCart()
{
$searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable_12345'));
$product = current($searchResponse['products']['items']);
@@ -109,6 +112,38 @@ public function testAddVariationFromAnotherConfigurableProductToCart()
self::assertArrayHasKey('value_label', $option);
}
+ /**
+ * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute.php
+ * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
+ *
+ * @expectedException Exception
+ * @expectedExceptionMessage You need to choose options for your item.
+ */
+ public function testAddVariationFromAnotherConfigurableProductWithDifferentSuperAttributeToCart()
+ {
+ $searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable_12345'));
+ $product = current($searchResponse['products']['items']);
+
+ $quantity = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1');
+ $parentSku = $product['sku'];
+
+ //'configurable' -> ['simple_10', 'simple_20']
+ //'configurable_12345' -> ['simple_30', 'simple_40']
+ //'simple_20' hasn't any common configurable option with 'configurable_12345' children
+ //therefore exception is thrown
+ $sku = 'simple_20';
+
+ $query = $this->getQuery(
+ $maskedQuoteId,
+ $parentSku,
+ $sku,
+ $quantity
+ );
+
+ $this->graphQlMutation($query);
+ }
+
/**
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php
* @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2.php
new file mode 100644
index 0000000000000..4de079ba3b6ee
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2.php
@@ -0,0 +1,62 @@
+get(\Magento\Eav\Model\Config::class);
+$attribute2 = $eavConfig->getAttribute('catalog_product', 'test_configurable_2');
+
+$eavConfig->clear();
+
+/** @var $installer \Magento\Catalog\Setup\CategorySetup */
+$installer = Bootstrap::getObjectManager()->create(\Magento\Catalog\Setup\CategorySetup::class);
+
+if (!$attribute2->getId()) {
+
+ /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
+ $attribute2 = Bootstrap::getObjectManager()->create(
+ \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
+ );
+
+ /** @var AttributeRepositoryInterface $attributeRepository */
+ $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class);
+
+ $attribute2->setData(
+ [
+ 'attribute_code' => 'test_configurable_2',
+ 'entity_type_id' => $installer->getEntityTypeId('catalog_product'),
+ 'is_global' => 1,
+ 'is_user_defined' => 1,
+ 'frontend_input' => 'select',
+ 'is_unique' => 0,
+ 'is_required' => 0,
+ 'is_searchable' => 0,
+ 'is_visible_in_advanced_search' => 0,
+ 'is_comparable' => 0,
+ 'is_filterable' => 0,
+ 'is_filterable_in_search' => 0,
+ 'is_used_for_promo_rules' => 0,
+ 'is_html_allowed_on_front' => 1,
+ 'is_visible_on_front' => 0,
+ 'used_in_product_listing' => 0,
+ 'used_for_sort_by' => 0,
+ 'frontend_label' => ['Test Configurable 2'],
+ 'backend_type' => 'int',
+ 'option' => [
+ 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']],
+ 'order' => ['option_0' => 1, 'option_1' => 2],
+ ],
+ ]
+ );
+
+ $attributeRepository->save($attribute2);
+
+ /* Assign attribute to attribute set */
+ $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute2->getId());
+}
+
+$eavConfig->clear();
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2_rollback.php
new file mode 100644
index 0000000000000..84f6ec58d3e4f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_2_rollback.php
@@ -0,0 +1,28 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+$productCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->get(\Magento\Catalog\Model\ResourceModel\Product\Collection::class);
+foreach ($productCollection as $product) {
+ $product->delete();
+}
+
+$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable_2');
+if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute
+ && $attribute->getId()
+) {
+ $attribute->delete();
+}
+$eavConfig->clear();
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute.php
new file mode 100644
index 0000000000000..1bf816425a9c9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute.php
@@ -0,0 +1,162 @@
+get(ProductRepositoryInterface::class);
+
+/** @var $installer CategorySetup */
+$installer = Bootstrap::getObjectManager()->create(CategorySetup::class);
+
+/* Create simple products per each option value*/
+/** @var AttributeOptionInterface[] $options */
+$options = $attribute->getOptions();
+
+$attributeValues = [];
+$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default');
+$associatedProductIds = [];
+$productIds = [10, 20];
+array_shift($options); //remove the first option which is empty
+
+foreach ($options as $option) {
+ /** @var $product Product */
+ $product = Bootstrap::getObjectManager()->create(Product::class);
+ $productId = array_shift($productIds);
+ $product->setTypeId(Type::TYPE_SIMPLE)
+ ->setId($productId)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds([1])
+ ->setName('Configurable Option' . $option->getLabel())
+ ->setSku('simple_' . $productId)
+ ->setPrice($productId)
+ ->setTestConfigurable($option->getValue())
+ ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+ $product = $productRepository->save($product);
+
+ $attributeValues[] = [
+ 'label' => 'test',
+ 'attribute_id' => $attribute->getId(),
+ 'value_index' => $option->getValue(),
+ ];
+ $associatedProductIds[] = $product->getId();
+}
+
+/** @var $product Product */
+$product = Bootstrap::getObjectManager()->create(Product::class);
+/** @var Factory $optionsFactory */
+$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class);
+$configurableAttributesData = [
+ [
+ 'attribute_id' => $attribute->getId(),
+ 'code' => $attribute->getAttributeCode(),
+ 'label' => $attribute->getStoreLabel(),
+ 'position' => '0',
+ 'values' => $attributeValues,
+ ],
+];
+$configurableOptions = $optionsFactory->create($configurableAttributesData);
+$extensionConfigurableAttributes = $product->getExtensionAttributes();
+$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions);
+$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds);
+$product->setExtensionAttributes($extensionConfigurableAttributes);
+
+$product->setTypeId(Configurable::TYPE_CODE)
+ ->setId(1)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds([1])
+ ->setName('Configurable Product')
+ ->setSku('configurable')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]);
+$productRepository->cleanCache();
+$productRepository->save($product);
+
+/* Create simple products per each option value*/
+/** @var AttributeOptionInterface[] $options */
+$options = $attribute2->getOptions();
+
+$attributeValues = [];
+$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default');
+$associatedProductIds = [];
+$productIds = [30, 40];
+array_shift($options); //remove the first option which is empty
+
+foreach ($options as $option) {
+ /** @var $product Product */
+ $product = Bootstrap::getObjectManager()->create(Product::class);
+ $productId = array_shift($productIds);
+ $product->setTypeId(Type::TYPE_SIMPLE)
+ ->setId($productId)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds([1])
+ ->setName('Configurable Option' . $option->getLabel())
+ ->setSku('simple_' . $productId)
+ ->setPrice($productId)
+ ->setTestConfigurable2($option->getValue())
+ ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+ $product = $productRepository->save($product);
+
+ $attributeValues[] = [
+ 'label' => 'test',
+ 'attribute_id' => $attribute2->getId(),
+ 'value_index' => $option->getValue(),
+ ];
+ $associatedProductIds[] = $product->getId();
+}
+
+/** @var $product Product */
+$product = Bootstrap::getObjectManager()->create(Product::class);
+
+/** @var Factory $optionsFactory */
+$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class);
+
+$configurableAttributesData = [
+ [
+ 'attribute_id' => $attribute2->getId(),
+ 'code' => $attribute2->getAttributeCode(),
+ 'label' => $attribute2->getStoreLabel(),
+ 'position' => '1',
+ 'values' => $attributeValues,
+ ],
+];
+
+$configurableOptions = $optionsFactory->create($configurableAttributesData);
+
+$extensionConfigurableAttributes = $product->getExtensionAttributes();
+$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions);
+$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds);
+
+$product->setExtensionAttributes($extensionConfigurableAttributes);
+
+$product->setTypeId(Configurable::TYPE_CODE)
+ ->setId(11)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds([1])
+ ->setName('Configurable Product 12345')
+ ->setSku('configurable_12345')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]);
+$productRepository->cleanCache();
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute_rollback.php
new file mode 100644
index 0000000000000..d4fa2a97c4934
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products_with_different_super_attribute_rollback.php
@@ -0,0 +1,14 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+require __DIR__ . '/configurable_attribute_2_rollback.php';
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
From f02e79155499978aebe17d9419dcd712346aa912 Mon Sep 17 00:00:00 2001
From: Denys Soloviov
Date: Sat, 31 Aug 2019 14:58:26 +0300
Subject: [PATCH 194/593] MAGETWO-24349: Added br as allowed tag
---
.../Sales/view/adminhtml/templates/items/column/name.phtml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml
index 64600f31b47de..c9b2f7c8de254 100644
--- a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml
+++ b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml
@@ -29,7 +29,7 @@
getFormattedOption($_option['value']); ?>
- = $block->escapeHtml($_option['value'], ['a']) ?> ... = $block->escapeHtml($_option['remainder'], ['a']) ?>
+ = $block->escapeHtml($_option['value'], ['a', 'br']) ?> ... = $block->escapeHtml($_option['remainder'], ['a']) ?>
From bc1657b298d55d024c3104cb94976019032f4f1f Mon Sep 17 00:00:00 2001
From: Sunil Patel
Date: Fri, 6 Sep 2019 15:16:25 +0530
Subject: [PATCH 278/593] change word shopping cart to mini cart
---
...namicProductToShoppingCartWithDisableMiniCartSidebarTest.xml | 2 +-
app/design/frontend/Magento/blank/web/css/source/_extends.less | 2 +-
app/design/frontend/Magento/luma/web/css/source/_extends.less | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml
index 9c00f2be1d60b..d67800e21afc2 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml
@@ -109,7 +109,7 @@
-
+
diff --git a/app/design/frontend/Magento/blank/web/css/source/_extends.less b/app/design/frontend/Magento/blank/web/css/source/_extends.less
index a969d3499b51c..5bdaa4c3c35a3 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_extends.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_extends.less
@@ -1290,7 +1290,7 @@
}
//
-// Shopping cart sidebar and checkout sidebar totals
+// Mini Cart and checkout sidebar totals
// ---------------------------------------------
& when (@media-common = true) {
diff --git a/app/design/frontend/Magento/luma/web/css/source/_extends.less b/app/design/frontend/Magento/luma/web/css/source/_extends.less
index 81b7716d61ab7..ce86b690f6252 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_extends.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_extends.less
@@ -1713,7 +1713,7 @@
}
//
-// Shopping cart sidebar and checkout sidebar totals
+// Mini Cart and checkout sidebar totals
// ---------------------------------------------
& when (@media-common = true) {
From f463caf1b77575c89ddc15320043f12d97284499 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torben=20Ho=CC=88hn?=
Date: Fri, 6 Sep 2019 14:20:54 +0200
Subject: [PATCH 279/593] rework
---
.../Block/Product/Renderer/Configurable.php | 60 +++++++++----------
1 file changed, 27 insertions(+), 33 deletions(-)
diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
index 35bf199681720..19d9340c4049c 100644
--- a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
+++ b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php
@@ -61,12 +61,12 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
/**
* Config path which contains number of swatches per product
*/
- const XML_PATH_SWATCHES_PER_PRODUCT = 'catalog/frontend/swatches_per_product';
+ private const XML_PATH_SWATCHES_PER_PRODUCT = 'catalog/frontend/swatches_per_product';
/**
* Config path if swatch tooltips are enabled
*/
- const XML_PATH_SHOW_SWATCH_TOOLTIP = 'catalog/frontend/show_swatch_tooltip';
+ private const XML_PATH_SHOW_SWATCH_TOOLTIP = 'catalog/frontend/show_swatch_tooltip';
/**
* @var Product
@@ -104,19 +104,19 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
- * @param Context $context
- * @param ArrayUtils $arrayUtils
- * @param EncoderInterface $jsonEncoder
- * @param Data $helper
- * @param CatalogProduct $catalogProduct
- * @param CurrentCustomer $currentCustomer
- * @param PriceCurrencyInterface $priceCurrency
- * @param ConfigurableAttributeData $configurableAttributeData
- * @param SwatchData $swatchHelper
- * @param Media $swatchMediaHelper
- * @param array $data
+ * @param Context $context
+ * @param ArrayUtils $arrayUtils
+ * @param EncoderInterface $jsonEncoder
+ * @param Data $helper
+ * @param CatalogProduct $catalogProduct
+ * @param CurrentCustomer $currentCustomer
+ * @param PriceCurrencyInterface $priceCurrency
+ * @param ConfigurableAttributeData $configurableAttributeData
+ * @param SwatchData $swatchHelper
+ * @param Media $swatchMediaHelper
+ * @param array $data
* @param SwatchAttributesProvider|null $swatchAttributesProvider
- * @param UrlBuilder|null $imageUrlBuilder
+ * @param UrlBuilder|null $imageUrlBuilder
*/
public function __construct(
Context $context,
@@ -183,6 +183,7 @@ public function getJsonSwatchConfig()
$attributesData = $this->getSwatchAttributesData();
$allOptionIds = $this->getConfigurableOptionsIds($attributesData);
$swatchesData = $this->swatchHelper->getSwatchesByOptionsId($allOptionIds);
+
$config = [];
foreach ($attributesData as $attributeId => $attributeDataArray) {
if (isset($attributeDataArray['options'])) {
@@ -232,13 +233,11 @@ public function getShowSwatchTooltip()
* Set product to block
*
* @param Product $product
- *
* @return $this
*/
public function setProduct(Product $product)
{
$this->product = $product;
-
return $this;
}
@@ -269,10 +268,10 @@ protected function getSwatchAttributesData()
/**
* Init isProductHasSwatchAttribute.
*
- * @return void
* @deprecated 100.1.5 Method isProductHasSwatchAttribute() is used instead of this.
*
* @codeCoverageIgnore
+ * @return void
*/
protected function initIsProductHasSwatchAttribute()
{
@@ -288,7 +287,6 @@ protected function initIsProductHasSwatchAttribute()
protected function isProductHasSwatchAttribute()
{
$swatchAttributes = $this->swatchAttributesProvider->provide($this->getProduct());
-
return count($swatchAttributes) > 0;
}
@@ -298,7 +296,6 @@ protected function isProductHasSwatchAttribute()
* @param array $options
* @param array $swatchesCollectionArray
* @param array $attributeDataArray
- *
* @return array
*/
protected function addSwatchDataForAttribute(
@@ -321,10 +318,9 @@ protected function addSwatchDataForAttribute(
/**
* Add media from variation
*
- * @param array $swatch
+ * @param array $swatch
* @param integer $optionId
- * @param array $attributeDataArray
- *
+ * @param array $attributeDataArray
* @return array
*/
protected function addAdditionalMediaData(array $swatch, $optionId, array $attributeDataArray)
@@ -333,12 +329,11 @@ protected function addAdditionalMediaData(array $swatch, $optionId, array $attri
&& $attributeDataArray['use_product_image_for_swatch']
) {
$variationMedia = $this->getVariationMedia($attributeDataArray['attribute_code'], $optionId);
- if (!empty($variationMedia)) {
+ if (! empty($variationMedia)) {
$swatch['type'] = Swatch::SWATCH_TYPE_VISUAL_IMAGE;
$swatch = array_merge($swatch, $variationMedia);
}
}
-
return $swatch;
}
@@ -346,12 +341,12 @@ protected function addAdditionalMediaData(array $swatch, $optionId, array $attri
* Retrieve Swatch data for config
*
* @param array $swatchDataArray
- *
* @return array
*/
protected function extractNecessarySwatchData(array $swatchDataArray)
{
$result['type'] = $swatchDataArray['type'];
+
if ($result['type'] == Swatch::SWATCH_TYPE_VISUAL_IMAGE && !empty($swatchDataArray['value'])) {
$result['value'] = $this->swatchMediaHelper->getSwatchAttributeImage(
Swatch::SWATCH_IMAGE_NAME,
@@ -371,9 +366,8 @@ protected function extractNecessarySwatchData(array $swatchDataArray)
/**
* Generate Product Media array
*
- * @param string $attributeCode
+ * @param string $attributeCode
* @param integer $optionId
- *
* @return array
*/
protected function getVariationMedia($attributeCode, $optionId)
@@ -382,12 +376,14 @@ protected function getVariationMedia($attributeCode, $optionId)
$this->getProduct(),
[$attributeCode => $optionId]
);
+
if (!$variationProduct) {
$variationProduct = $this->swatchHelper->loadFirstVariationWithImage(
$this->getProduct(),
[$attributeCode => $optionId]
);
}
+
$variationMediaArray = [];
if ($variationProduct) {
$variationMediaArray = [
@@ -403,8 +399,7 @@ protected function getVariationMedia($attributeCode, $optionId)
* Get swatch product image.
*
* @param Product $childProduct
- * @param string $imageType
- *
+ * @param string $imageType
* @return string
*/
protected function getSwatchProductImage(Product $childProduct, $imageType)
@@ -416,6 +411,7 @@ protected function getSwatchProductImage(Product $childProduct, $imageType)
$swatchImageId = $imageType == Swatch::SWATCH_IMAGE_NAME ? 'swatch_image_base' : 'swatch_thumb_base';
$imageAttributes = ['type' => 'image'];
}
+
if (!empty($swatchImageId) && !empty($imageAttributes['type'])) {
return $this->imageUrlBuilder->getUrl($childProduct->getData($imageAttributes['type']), $swatchImageId);
}
@@ -425,8 +421,7 @@ protected function getSwatchProductImage(Product $childProduct, $imageType)
* Check if product have image.
*
* @param Product $product
- * @param string $imageType
- *
+ * @param string $imageType
* @return bool
*/
protected function isProductHasImage(Product $product, $imageType)
@@ -438,7 +433,6 @@ protected function isProductHasImage(Product $product, $imageType)
* Get configurable options ids.
*
* @param array $attributeData
- *
* @return array
* @since 100.0.3
*/
@@ -455,7 +449,6 @@ protected function getConfigurableOptionsIds(array $attributeData)
}
}
}
-
return array_keys($ids);
}
@@ -540,6 +533,7 @@ public function getJsonSwatchSizeConfig()
{
$imageConfig = $this->swatchMediaHelper->getImageConfig();
$sizeConfig = [];
+
$sizeConfig[self::SWATCH_IMAGE_NAME]['width'] = $imageConfig[Swatch::SWATCH_IMAGE_NAME]['width'];
$sizeConfig[self::SWATCH_IMAGE_NAME]['height'] = $imageConfig[Swatch::SWATCH_IMAGE_NAME]['height'];
$sizeConfig[self::SWATCH_THUMBNAIL_NAME]['height'] = $imageConfig[Swatch::SWATCH_THUMBNAIL_NAME]['height'];
From 2ea7a99f9684597d36cc7422d5d0f6ebf3ea4f4f Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Fri, 6 Sep 2019 11:47:56 +0300
Subject: [PATCH 280/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix CR comments.
---
app/code/Magento/Catalog/Model/ResourceModel/Category.php | 6 +++++-
.../Catalog/Model/ResourceModel/Product/Collection.php | 4 ++++
.../Catalog/Model/ResourceModel/Product/CollectionTest.php | 7 +++++--
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
index fb8c089ed9f9a..797ce72ae9b7a 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
@@ -1155,6 +1155,10 @@ public function getCategoryWithChildren(int $categoryId): array
)->where('entity_type_id = ?', CategorySetup::CATEGORY_ENTITY_TYPE_ID)
->where('attribute_code = ?', 'is_anchor')
->limit(1);
+ $isAnchorAttributeCode = $connection->fetchOne($selectAttributeCode);
+ if (empty($isAnchorAttributeCode) || (int)$isAnchorAttributeCode <= 0) {
+ return [];
+ }
$select = $connection->select()
->from(
@@ -1166,7 +1170,7 @@ public function getCategoryWithChildren(int $categoryId): array
['is_anchor' => 'cce_int.value']
)->where(
'cce_int.attribute_id = ?',
- $connection->fetchOne($selectAttributeCode)
+ $isAnchorAttributeCode
)->where(
"cce.path LIKE '%/{$categoryId}' OR cce.path LIKE '%/{$categoryId}/%'"
)->order('path');
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 71b03620b342b..42d55892b6ec6 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -2107,6 +2107,10 @@ private function getChildrenCategories(int $categoryId): array
$anchorCategory = [];
$categories = $this->categoryResourceModel->getCategoryWithChildren($categoryId);
+ if (empty($categories)) {
+ return $categoryIds;
+ }
+
$firstCategory = array_shift($categories);
if ($firstCategory['is_anchor'] == 1) {
$anchorCategory[] = (int)$firstCategory['entity_id'];
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
index 80d700c7e54ff..d3970ad403ffd 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
@@ -99,6 +99,7 @@ public function testAddPriceDataOnSchedule()
/**
* @magentoDataFixture Magento/Catalog/_files/products.php
+ * @magentoAppIsolation enabled
* @magentoDbIsolation disabled
*/
public function testSetVisibility()
@@ -116,6 +117,7 @@ public function testSetVisibility()
/**
* @magentoDataFixture Magento/Catalog/_files/category_product.php
+ * @magentoAppIsolation enabled
* @magentoDbIsolation disabled
*/
public function testSetCategoryWithStoreFilter()
@@ -131,8 +133,8 @@ public function testSetCategoryWithStoreFilter()
$this->collection->load();
$collectionStoreFilterAfter = Bootstrap::getObjectManager()->create(
- \Magento\Catalog\Model\ResourceModel\Product\Collection::class
- );
+ \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class
+ )->create();
$collectionStoreFilterAfter->addStoreFilter(1)->addCategoryFilter($category);
$collectionStoreFilterAfter->load();
$this->assertEquals($this->collection->getItems(), $collectionStoreFilterAfter->getItems());
@@ -141,6 +143,7 @@ public function testSetCategoryWithStoreFilter()
/**
* @magentoDataFixture Magento/Catalog/_files/categories.php
+ * @magentoAppIsolation enabled
* @magentoDbIsolation disabled
*/
public function testSetCategoryFilter()
From 17f291b465b5481ffa0ca221593114db01ff8ae2 Mon Sep 17 00:00:00 2001
From: Buba Suma
Date: Thu, 5 Sep 2019 14:21:27 -0500
Subject: [PATCH 281/593] MC-19749: Store creation ('app:config:import' with
pre-defined stores) fails during upgrade
- Fix object manager provider reset causes multiple connection to database
---
.../Setup/Model/ObjectManagerProviderTest.php | 31 +++++++++++++---
.../Magento/Framework/Console/Cli.php | 1 +
.../Setup/Model/ObjectManagerProvider.php | 5 ++-
.../Unit/Model/ObjectManagerProviderTest.php | 35 ++++++++++---------
4 files changed, 47 insertions(+), 25 deletions(-)
diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php
index c4fc0fa7c5854..a80da16be67eb 100644
--- a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php
@@ -6,9 +6,17 @@
namespace Magento\Setup\Model;
+use Magento\Framework\ObjectManagerInterface;
use Magento\Setup\Mvc\Bootstrap\InitParamListener;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject;
+use Symfony\Component\Console\Application;
+use Zend\ServiceManager\ServiceLocatorInterface;
-class ObjectManagerProviderTest extends \PHPUnit\Framework\TestCase
+/**
+ * Tests ObjectManagerProvider
+ */
+class ObjectManagerProviderTest extends TestCase
{
/**
* @var ObjectManagerProvider
@@ -16,21 +24,34 @@ class ObjectManagerProviderTest extends \PHPUnit\Framework\TestCase
private $object;
/**
- * @var \Zend\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var ServiceLocatorInterface|PHPUnit_Framework_MockObject_MockObject
*/
private $locator;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
- $this->locator = $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class);
+ $this->locator = $this->getMockForAbstractClass(ServiceLocatorInterface::class);
$this->object = new ObjectManagerProvider($this->locator, new Bootstrap());
+ $this->locator->expects($this->any())
+ ->method('get')
+ ->willReturnMap(
+ [
+ [InitParamListener::BOOTSTRAP_PARAM, []],
+ [Application::class, $this->getMockForAbstractClass(Application::class)],
+ ]
+ );
}
+ /**
+ * Tests the same instance of ObjectManagerInterface should be provided by the ObjectManagerProvider
+ */
public function testGet()
{
- $this->locator->expects($this->once())->method('get')->with(InitParamListener::BOOTSTRAP_PARAM)->willReturn([]);
$objectManager = $this->object->get();
- $this->assertInstanceOf(\Magento\Framework\ObjectManagerInterface::class, $objectManager);
+ $this->assertInstanceOf(ObjectManagerInterface::class, $objectManager);
$this->assertSame($objectManager, $this->object->get());
}
}
diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php
index 2ef41f361027e..34fd6316ce454 100644
--- a/lib/internal/Magento/Framework/Console/Cli.php
+++ b/lib/internal/Magento/Framework/Console/Cli.php
@@ -93,6 +93,7 @@ public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
}
parent::__construct($name, $version);
+ $this->serviceManager->setService(\Symfony\Component\Console\Application::class, $this);
}
/**
diff --git a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php
index e25b976e9207f..79216c8ec89b5 100644
--- a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php
+++ b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php
@@ -76,10 +76,9 @@ private function createCliCommands()
{
/** @var CommandListInterface $commandList */
$commandList = $this->objectManager->create(CommandListInterface::class);
+ $application = $this->serviceLocator->get(Application::class);
foreach ($commandList->getCommands() as $command) {
- $command->setApplication(
- $this->serviceLocator->get(Application::class)
- );
+ $application->add($command);
}
}
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php
index 9d40b053e394e..552453c4a185c 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php
@@ -47,6 +47,14 @@ public function setUp()
public function testGet()
{
$initParams = ['param' => 'value'];
+ $commands = [
+ new Command('setup:install'),
+ new Command('setup:upgrade'),
+ ];
+
+ $application = $this->getMockBuilder(Application::class)
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
$this->serviceLocatorMock
->expects($this->atLeastOnce())
@@ -56,16 +64,21 @@ public function testGet()
[InitParamListener::BOOTSTRAP_PARAM, $initParams],
[
Application::class,
- $this->getMockBuilder(Application::class)->disableOriginalConstructor()->getMock(),
+ $application,
],
]
);
+ $commandListMock = $this->createMock(CommandListInterface::class);
+ $commandListMock->expects($this->once())
+ ->method('getCommands')
+ ->willReturn($commands);
+
$objectManagerMock = $this->createMock(ObjectManagerInterface::class);
$objectManagerMock->expects($this->once())
->method('create')
->with(CommandListInterface::class)
- ->willReturn($this->getCommandListMock());
+ ->willReturn($commandListMock);
$objectManagerFactoryMock = $this->getMockBuilder(ObjectManagerFactory::class)
->disableOriginalConstructor()
@@ -81,21 +94,9 @@ public function testGet()
->willReturn($objectManagerFactoryMock);
$this->assertInstanceOf(ObjectManagerInterface::class, $this->model->get());
- }
-
- /**
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- private function getCommandListMock()
- {
- $commandMock = $this->getMockBuilder(Command::class)->disableOriginalConstructor()->getMock();
- $commandMock->expects($this->once())->method('setApplication');
-
- $commandListMock = $this->createMock(CommandListInterface::class);
- $commandListMock->expects($this->once())
- ->method('getCommands')
- ->willReturn([$commandMock]);
- return $commandListMock;
+ foreach ($commands as $command) {
+ $this->assertSame($application, $command->getApplication());
+ }
}
}
From 4dad2e5b010b26308cce4ea0c4465a3e50d330d7 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Fri, 6 Sep 2019 09:33:19 -0500
Subject: [PATCH 282/593] MC-19746: The shipping address contains `null` values
when there's no shipping address set on a cart
- fix changes to also apply to 2.3.2
---
.../Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php
index 5968576e3a8dc..ae97f7efda45f 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php
@@ -54,10 +54,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
/** @var Quote $cart */
$cart = $value['model'];
+ $addressesData = [];
$shippingAddresses = $cart->getAllShippingAddresses();
- $addressesData = [];
- if (!empty($shippingAddresses)) {
+ if (count($shippingAddresses)) {
foreach ($shippingAddresses as $shippingAddress) {
$address = $this->extractQuoteAddressData->execute($shippingAddress);
From 860e239812ca605229e107f65a967f28a33e032a Mon Sep 17 00:00:00 2001
From: Maksym Novik
Date: Fri, 6 Sep 2019 18:46:07 +0300
Subject: [PATCH 283/593] GraphQl-220: Implement exception logging. Removed
duplicated logging to var/log/graphql.
---
app/etc/graphql/di.xml | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/app/etc/graphql/di.xml b/app/etc/graphql/di.xml
index 4370c3b445f65..aba60d00080ff 100644
--- a/app/etc/graphql/di.xml
+++ b/app/etc/graphql/di.xml
@@ -7,21 +7,4 @@
-->
-
-
-
- - GraphQlErrorHandler
-
-
-
-
-
- var/log/graphql/exception.log
-
-
-
-
- GraphQlLogger
-
-
From 2f04e666aceba9bcbd863b78613714922a1f0f35 Mon Sep 17 00:00:00 2001
From: Oleksandr Dubovyk
Date: Fri, 6 Sep 2019 12:42:04 -0500
Subject: [PATCH 284/593] MC-19798: 'Quote Lifetime (days)' setting does not
work
- fixed
---
app/code/Magento/Sales/Cron/CleanExpiredQuotes.php | 1 -
.../Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php b/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php
index 021e7b66cd13f..a5c7f71df66c5 100644
--- a/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php
+++ b/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php
@@ -57,7 +57,6 @@ public function execute()
$quotes->addFieldToFilter('store_id', $storeId);
$quotes->addFieldToFilter('updated_at', ['to' => date("Y-m-d", time() - $lifetime)]);
- $quotes->addFieldToFilter('is_active', 0);
foreach ($this->getExpireQuotesAdditionalFilterFields() as $field => $condition) {
$quotes->addFieldToFilter($field, $condition);
diff --git a/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php b/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php
index e424cae85f223..ad6a3e03ba679 100644
--- a/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php
@@ -59,7 +59,7 @@ public function testExecute($lifetimes, $additionalFilterFields)
$this->quoteFactoryMock->expects($this->exactly(count($lifetimes)))
->method('create')
->will($this->returnValue($quotesMock));
- $quotesMock->expects($this->exactly((3 + count($additionalFilterFields)) * count($lifetimes)))
+ $quotesMock->expects($this->exactly((2 + count($additionalFilterFields)) * count($lifetimes)))
->method('addFieldToFilter');
if (!empty($lifetimes)) {
$quotesMock->expects($this->exactly(count($lifetimes)))
From 2b5a39b6088e34a11ac14e0f19f3b8bcdd4ca866 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Fri, 6 Sep 2019 13:39:23 -0500
Subject: [PATCH 285/593] MC-19746: The shipping address contains `null` values
when there's no shipping address set on a cart
- fix test
---
.../GraphQl/Quote/Customer/GetSpecifiedShippingAddressTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedShippingAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedShippingAddressTest.php
index d843748263ce4..2023603a21eed 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedShippingAddressTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedShippingAddressTest.php
@@ -92,7 +92,7 @@ public function testGetSpecifiedShippingAddressIfShippingAddressIsNotSet()
self::assertArrayHasKey('cart', $response);
self::assertArrayHasKey('shipping_addresses', $response['cart']);
- self::assertEquals(0, $response['cart']['shipping_addresses']);
+ self::assertEquals([], $response['cart']['shipping_addresses']);
}
/**
From 7852b00a993ff176aedaba68f96646dedd5c583d Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Fri, 6 Sep 2019 14:00:21 -0500
Subject: [PATCH 286/593] MC-19746: The shipping address contains `null` values
when there's no shipping address set on a cart
- fix test
---
.../GetSelectedShippingMethodTest.php | 21 ++-----------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php
index 9bb36bf8f0929..c8a80c493e282 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php
@@ -108,18 +108,7 @@ public function testGetSelectedShippingMethodBeforeSet()
$shippingAddress = current($response['cart']['shipping_addresses']);
self::assertArrayHasKey('selected_shipping_method', $shippingAddress);
-
- self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['carrier_code']);
-
- self::assertArrayHasKey('method_code', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['method_code']);
-
- self::assertArrayHasKey('carrier_title', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['carrier_title']);
-
- self::assertArrayHasKey('method_title', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['method_title']);
+ self::assertNull($shippingAddress['selected_shipping_method']);
}
/**
@@ -182,13 +171,7 @@ public function testGetGetSelectedShippingMethodIfShippingMethodIsNotSet()
$shippingAddress = current($response['cart']['shipping_addresses']);
self::assertArrayHasKey('selected_shipping_method', $shippingAddress);
-
- self::assertNull($shippingAddress['selected_shipping_method']['carrier_code']);
- self::assertNull($shippingAddress['selected_shipping_method']['method_code']);
- self::assertNull($shippingAddress['selected_shipping_method']['carrier_title']);
- self::assertNull($shippingAddress['selected_shipping_method']['method_title']);
- self::assertNull($shippingAddress['selected_shipping_method']['amount']);
- self::assertNull($shippingAddress['selected_shipping_method']['base_amount']);
+ self::assertNull('selected_shipping_method', $shippingAddress['selected_shipping_method']);
}
/**
From af414b085fa7c13267169aaafc79675448872c88 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Fri, 6 Sep 2019 14:57:54 -0500
Subject: [PATCH 287/593] MC-19746: The shipping address contains `null` values
when there's no shipping address set on a cart
- fix test
---
.../Customer/GetSelectedShippingMethodTest.php | 2 +-
.../Quote/Guest/GetSelectedShippingMethodTest.php | 13 +------------
2 files changed, 2 insertions(+), 13 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php
index c8a80c493e282..90107ad2220da 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php
@@ -171,7 +171,7 @@ public function testGetGetSelectedShippingMethodIfShippingMethodIsNotSet()
$shippingAddress = current($response['cart']['shipping_addresses']);
self::assertArrayHasKey('selected_shipping_method', $shippingAddress);
- self::assertNull('selected_shipping_method', $shippingAddress['selected_shipping_method']);
+ self::assertNull($shippingAddress['selected_shipping_method']);
}
/**
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php
index 5d1033b39819e..fb694508a643f 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php
@@ -99,18 +99,7 @@ public function testGetSelectedShippingMethodBeforeSet()
$shippingAddress = current($response['cart']['shipping_addresses']);
self::assertArrayHasKey('selected_shipping_method', $shippingAddress);
-
- self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['carrier_code']);
-
- self::assertArrayHasKey('method_code', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['method_code']);
-
- self::assertArrayHasKey('carrier_title', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['carrier_title']);
-
- self::assertArrayHasKey('method_title', $shippingAddress['selected_shipping_method']);
- self::assertNull($shippingAddress['selected_shipping_method']['method_title']);
+ self::assertNull($shippingAddress['selected_shipping_method']);
}
/**
From 3f87c97ac9ba14fb93656150db22a0609b743bd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?=
Date: Fri, 6 Sep 2019 22:13:35 +0200
Subject: [PATCH 288/593] Fix #21610 - replace inline js with x-magento-init
---
.../Sitemap/view/adminhtml/templates/js.phtml | 15 +++++----------
.../adminhtml/web/js/form-submit-loader.js | 19 +++++++++++++++++++
2 files changed, 24 insertions(+), 10 deletions(-)
create mode 100644 app/code/Magento/Sitemap/view/adminhtml/web/js/form-submit-loader.js
diff --git a/app/code/Magento/Sitemap/view/adminhtml/templates/js.phtml b/app/code/Magento/Sitemap/view/adminhtml/templates/js.phtml
index 4e42a7f7b779a..480615cba67de 100644
--- a/app/code/Magento/Sitemap/view/adminhtml/templates/js.phtml
+++ b/app/code/Magento/Sitemap/view/adminhtml/templates/js.phtml
@@ -1,12 +1,7 @@
-
diff --git a/app/code/Magento/Sitemap/view/adminhtml/web/js/form-submit-loader.js b/app/code/Magento/Sitemap/view/adminhtml/web/js/form-submit-loader.js
new file mode 100644
index 0000000000000..6b7ba6be65b22
--- /dev/null
+++ b/app/code/Magento/Sitemap/view/adminhtml/web/js/form-submit-loader.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'jquery'
+], function ($) {
+ 'use strict';
+
+ return function (data, element) {
+
+ $(element).on('save', function () {
+ if ($(this).valid()) {
+ $('body').trigger('processStart');
+ }
+ });
+ };
+});
From a579c208029c5352d09038665ef7e0d03f3d9d55 Mon Sep 17 00:00:00 2001
From: mahesh
Date: Sat, 7 Sep 2019 16:44:54 +0530
Subject: [PATCH 289/593] Fixed issue:24031 Layer navigation displaying
whenever select Static Block only
---
.../LayeredNavigation/Block/Navigation.php | 3 +-
.../Test/Unit/Block/NavigationTest.php | 54 +++++++++++++++++++
2 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/LayeredNavigation/Block/Navigation.php b/app/code/Magento/LayeredNavigation/Block/Navigation.php
index 4173469da8e42..d4709ac05426f 100644
--- a/app/code/Magento/LayeredNavigation/Block/Navigation.php
+++ b/app/code/Magento/LayeredNavigation/Block/Navigation.php
@@ -107,7 +107,8 @@ public function getFilters()
*/
public function canShowBlock()
{
- return $this->visibilityFlag->isEnabled($this->getLayer(), $this->getFilters());
+ return $this->getLayer()->getCurrentCategory()->getDisplayMode() !== \Magento\Catalog\Model\Category::DM_PAGE
+ && $this->visibilityFlag->isEnabled($this->getLayer(), $this->getFilters());
}
/**
diff --git a/app/code/Magento/LayeredNavigation/Test/Unit/Block/NavigationTest.php b/app/code/Magento/LayeredNavigation/Test/Unit/Block/NavigationTest.php
index e37e58b14f027..f0243784dd618 100644
--- a/app/code/Magento/LayeredNavigation/Test/Unit/Block/NavigationTest.php
+++ b/app/code/Magento/LayeredNavigation/Test/Unit/Block/NavigationTest.php
@@ -6,6 +6,8 @@
namespace Magento\LayeredNavigation\Test\Unit\Block;
+use Magento\Catalog\Model\Category;
+
class NavigationTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -98,9 +100,61 @@ public function testCanShowBlock()
->method('isEnabled')
->with($this->catalogLayerMock, $filters)
->will($this->returnValue($enabled));
+
+ $category = $this->createMock(Category::class);
+ $this->catalogLayerMock->expects($this->atLeastOnce())->method('getCurrentCategory')->willReturn($category);
+ $category->expects($this->once())->method('getDisplayMode')->willReturn(Category::DM_PRODUCT);
+
$this->assertEquals($enabled, $this->model->canShowBlock());
}
+ /**
+ * Test canShowBlock() with different category display types.
+ *
+ * @param string $mode
+ * @param bool $result
+ *
+ * @dataProvider canShowBlockDataProvider
+ */
+ public function testCanShowBlockWithDifferentDisplayModes(string $mode, bool $result)
+ {
+ $filters = ['To' => 'be', 'or' => 'not', 'to' => 'be'];
+
+ $this->filterListMock->expects($this->atLeastOnce())->method('getFilters')
+ ->with($this->catalogLayerMock)
+ ->will($this->returnValue($filters));
+ $this->assertEquals($filters, $this->model->getFilters());
+
+ $this->visibilityFlagMock
+ ->expects($this->any())
+ ->method('isEnabled')
+ ->with($this->catalogLayerMock, $filters)
+ ->will($this->returnValue(true));
+
+ $category = $this->createMock(Category::class);
+ $this->catalogLayerMock->expects($this->atLeastOnce())->method('getCurrentCategory')->willReturn($category);
+ $category->expects($this->once())->method('getDisplayMode')->willReturn($mode);
+ $this->assertEquals($result, $this->model->canShowBlock());
+ }
+
+ public function canShowBlockDataProvider()
+ {
+ return [
+ [
+ Category::DM_PRODUCT,
+ true,
+ ],
+ [
+ Category::DM_PAGE,
+ false,
+ ],
+ [
+ Category::DM_MIXED,
+ true,
+ ],
+ ];
+ }
+
public function testGetClearUrl()
{
$this->filterListMock->expects($this->any())->method('getFilters')->will($this->returnValue([]));
From 7005d99ae4af1f3ffdf35ba8370c4295c0bfbfe6 Mon Sep 17 00:00:00 2001
From: Eden
Date: Sat, 7 Sep 2019 18:21:11 +0700
Subject: [PATCH 290/593] Resolve "Clear Shopping Cart" button not working in
Internet Explorer issue24491
---
.../Checkout/view/frontend/templates/cart/form.phtml | 2 +-
.../Magento/Checkout/view/frontend/web/js/shopping-cart.js | 7 +++----
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml
index e1ab036c7d889..370d70c44d886 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml
@@ -56,7 +56,7 @@
= $block->escapeHtml(__('Continue Shopping')) ?>
- 0) {
+ $(this.options.emptyCartButton).parents('form').submit();
+ }
}, this));
items = $.find('[data-role="cart-item-qty"]');
From 1f597119af94c46d1a5d6a052393f91edca46904 Mon Sep 17 00:00:00 2001
From: Prabhat Rawat
Date: Sat, 7 Sep 2019 17:14:26 +0530
Subject: [PATCH 291/593] issue fix 23205
---
app/code/Magento/Vault/view/frontend/layout/customer_account.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/code/Magento/Vault/view/frontend/layout/customer_account.xml b/app/code/Magento/Vault/view/frontend/layout/customer_account.xml
index 73ce9247fef0a..05044da272e6d 100644
--- a/app/code/Magento/Vault/view/frontend/layout/customer_account.xml
+++ b/app/code/Magento/Vault/view/frontend/layout/customer_account.xml
@@ -11,7 +11,6 @@
vault/cards/listaction
From 5847d9f64dad423d83bf0c742d810783ca213639 Mon Sep 17 00:00:00 2001
From: Eden
Date: Sat, 7 Sep 2019 19:13:14 +0700
Subject: [PATCH 292/593] Fix static test issue 24491
---
.../Magento/Checkout/view/frontend/web/js/shopping-cart.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js
index 69a2cec47181b..b15599673095f 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js
@@ -14,10 +14,11 @@ define([
_create: function () {
var items, i, reload;
- $(this.options.emptyCartButton).on('click', $.proxy(function (event) {
+ $(this.options.emptyCartButton).on('click', $.proxy(function () {
$(this.options.emptyCartButton).attr('name', 'update_cart_action_temp');
$(this.options.updateCartActionContainer)
.attr('name', 'update_cart_action').attr('value', 'empty_cart');
+
if ($(this.options.emptyCartButton).parents('form').length > 0) {
$(this.options.emptyCartButton).parents('form').submit();
}
From 114ac4b39a5818df2f1c91c07fd9b27cc97de7b1 Mon Sep 17 00:00:00 2001
From: ashutosh
Date: Sat, 7 Sep 2019 17:52:20 +0530
Subject: [PATCH 293/593] fixed issue #24410
---
.../Model/Product/TierPriceManagement.php | 23 +++++++++++--------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php b/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php
index f2da1e770279e..f078349c2a8f4 100644
--- a/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php
+++ b/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php
@@ -182,16 +182,19 @@ public function getList($sku, $customerGroupId)
: $customerGroupId);
$prices = [];
- foreach ($product->getData('tier_price') as $price) {
- if ((is_numeric($customerGroupId) && (int) $price['cust_group'] === (int) $customerGroupId)
- || ($customerGroupId === 'all' && $price['all_groups'])
- ) {
- /** @var \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice */
- $tierPrice = $this->priceFactory->create();
- $tierPrice->setValue($price[$priceKey])
- ->setQty($price['price_qty'])
- ->setCustomerGroupId($cgi);
- $prices[] = $tierPrice;
+ $tierPrices = $product->getData('tier_price');
+ if ($tierPrices !== null) {
+ foreach ($tierPrices as $price) {
+ if ((is_numeric($customerGroupId) && (int) $price['cust_group'] === (int) $customerGroupId)
+ || ($customerGroupId === 'all' && $price['all_groups'])
+ ) {
+ /** @var \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice */
+ $tierPrice = $this->priceFactory->create();
+ $tierPrice->setValue($price[$priceKey])
+ ->setQty($price['price_qty'])
+ ->setCustomerGroupId($cgi);
+ $prices[] = $tierPrice;
+ }
}
}
return $prices;
From 41ef34feca73eea48f3dd1744bce529d36909c91 Mon Sep 17 00:00:00 2001
From: Pieter Hoste
Date: Sat, 7 Sep 2019 14:34:35 +0200
Subject: [PATCH 294/593] Fixes excluding JS files from bundles when minifying
is enabled.
---
app/code/Magento/Deploy/Service/Bundle.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Deploy/Service/Bundle.php b/app/code/Magento/Deploy/Service/Bundle.php
index f16b93a185595..26e61624c219e 100644
--- a/app/code/Magento/Deploy/Service/Bundle.php
+++ b/app/code/Magento/Deploy/Service/Bundle.php
@@ -216,7 +216,7 @@ private function isExcluded($filePath, $area, $theme)
$excludedFiles = $this->bundleConfig->getExcludedFiles($area, $theme);
foreach ($excludedFiles as $excludedFileId) {
$excludedFilePath = $this->prepareExcludePath($excludedFileId);
- if ($excludedFilePath === $filePath) {
+ if ($excludedFilePath === $filePath || $excludedFilePath === str_replace('.min.js', '.js', $filePath)) {
return true;
}
}
From 47512517325f7d33b4f7f111d9d357b2d8961680 Mon Sep 17 00:00:00 2001
From: Pieter Hoste
Date: Thu, 5 Sep 2019 22:29:03 +0200
Subject: [PATCH 295/593] Excludes Magento_Tinymce3 scripts from the bundling,
saves about 3.7 MB in the generated JS bundles.
---
app/design/adminhtml/Magento/backend/etc/view.xml | 1 +
app/design/frontend/Magento/blank/etc/view.xml | 1 +
app/design/frontend/Magento/luma/etc/view.xml | 1 +
3 files changed, 3 insertions(+)
diff --git a/app/design/adminhtml/Magento/backend/etc/view.xml b/app/design/adminhtml/Magento/backend/etc/view.xml
index 18c2d8f1b1722..05a5e1978c751 100644
--- a/app/design/adminhtml/Magento/backend/etc/view.xml
+++ b/app/design/adminhtml/Magento/backend/etc/view.xml
@@ -63,6 +63,7 @@
- Lib::requirejs/text.js
- Lib::date-format-normalizer.js
- Lib::varien/js.js
+ - Magento_Tinymce3::tiny_mce
- Lib::css
- Lib::lib
- Lib::prototype
diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml
index e742ce0a21cd1..0081a254eb3b6 100644
--- a/app/design/frontend/Magento/blank/etc/view.xml
+++ b/app/design/frontend/Magento/blank/etc/view.xml
@@ -292,6 +292,7 @@
- Magento_Ui::templates/grid
- Magento_Ui::templates/dynamic-rows
- Magento_Swagger::swagger-ui
+ - Magento_Tinymce3::tiny_mce
- Lib::modernizr
- Lib::tiny_mce
- Lib::varien
diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml
index 7aa2e51481bd9..ff1d06204e51e 100644
--- a/app/design/frontend/Magento/luma/etc/view.xml
+++ b/app/design/frontend/Magento/luma/etc/view.xml
@@ -303,6 +303,7 @@
- Magento_Ui::templates/grid
- Magento_Ui::templates/dynamic-rows
- Magento_Swagger::swagger-ui
+ - Magento_Tinymce3::tiny_mce
- Lib::modernizr
- Lib::tiny_mce
- Lib::varien
From 06a1aa1b1b5830ccc17c2ebb2fdc713892d22d59 Mon Sep 17 00:00:00 2001
From: rani-priya
Date: Sat, 7 Sep 2019 20:03:40 +0530
Subject: [PATCH 296/593] fixed Not able to update decimal quantity from cart
details page. #24509
---
.../Checkout/view/frontend/templates/cart/item/default.phtml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml
index 86c9a357af23c..d07f349265dfe 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml
@@ -50,7 +50,7 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima
= $block->escapeHtml($_formatedOptionValue['full_view']) ?>
- = $block->escapeHtml($_formatedOptionValue['value'], ['span', 'a']) ?>
+ = $block->escapeHtml($_formatedOptionValue['value'], ['span']) ?>
@@ -104,6 +104,7 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima
value="= $block->escapeHtmlAttr($block->getQty()) ?>"
type="number"
size="4"
+ step="any"
title="= $block->escapeHtmlAttr(__('Qty')) ?>"
class="input-text qty"
data-validate="{required:true,'validate-greater-than-zero':true}"
From 2b3a44074286aa4ecf1eaf142a5dd23c22b5fa1e Mon Sep 17 00:00:00 2001
From: Patrick McLain
Date: Sat, 7 Sep 2019 14:57:15 -0400
Subject: [PATCH 297/593] Add input validation for authorizenet_acceptjs
payment method
Fixes magento/graphql-ce#774
---
.../Model/AuthorizenetDataProvider.php | 25 +++-
.../Customer/SetPaymentMethodTest.php | 107 ++++++++++++++++++
2 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php b/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php
index 207d21994308f..704f0af85da06 100644
--- a/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php
+++ b/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php
@@ -9,6 +9,7 @@
use Magento\QuoteGraphQl\Model\Cart\Payment\AdditionalDataProviderInterface;
use Magento\Framework\Stdlib\ArrayManager;
+use Magento\Framework\GraphQl\Exception\GraphQlInputException;
/**
* SetPaymentMethod additional data provider model for Authorizenet payment method
@@ -36,10 +37,32 @@ public function __construct(
*
* @param array $data
* @return array
+ * @throws GraphQlInputException
*/
public function getData(array $data): array
{
- $additionalData = $this->arrayManager->get(static::PATH_ADDITIONAL_DATA, $data) ?? [];
+ if (!isset($data[self::PATH_ADDITIONAL_DATA])) {
+ throw new GraphQlInputException(
+ __('Required parameter "authorizenet_acceptjs" for "payment_method" is missing.')
+ );
+ }
+ if (!isset($data[self::PATH_ADDITIONAL_DATA]['opaque_data_descriptor'])) {
+ throw new GraphQlInputException(
+ __('Required parameter "opaque_data_descriptor" for "authorizenet_acceptjs" is missing.')
+ );
+ }
+ if (!isset($data[self::PATH_ADDITIONAL_DATA]['opaque_data_value'])) {
+ throw new GraphQlInputException(
+ __('Required parameter "opaque_data_value" for "authorizenet_acceptjs" is missing.')
+ );
+ }
+ if (!isset($data[self::PATH_ADDITIONAL_DATA]['cc_last_4'])) {
+ throw new GraphQlInputException(
+ __('Required parameter "cc_last_4" for "authorizenet_acceptjs" is missing.')
+ );
+ }
+
+ $additionalData = $this->arrayManager->get(static::PATH_ADDITIONAL_DATA, $data);
foreach ($additionalData as $key => $value) {
$additionalData[$this->convertSnakeCaseToCamelCase($key)] = $value;
unset($additionalData[$key]);
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php
index ad454c67080e9..5f70cf4fd6687 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php
@@ -109,6 +109,113 @@ public function dataProviderTestPlaceOrder(): array
];
}
+ /**
+ * @magentoConfigFixture default_store carriers/flatrate/active 1
+ * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1
+ * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox
+ * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login def_login
+ * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key def_trans_key
+ * @magentoConfigFixture default_store payment/authorizenet_acceptjs/public_client_key def_public_client_key
+ * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key def_trans_signature_key
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ * @dataProvider dataProviderSetPaymentInvalidInput
+ * @param \Closure $getMutationClosure
+ * @param string $expectedMessage
+ * @expectedException \Exception
+ */
+ public function testSetPaymentInvalidInput(\Closure $getMutationClosure, string $expectedMessage)
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+
+ $setPaymentMutation = $getMutationClosure($maskedQuoteId);
+
+ $this->expectExceptionMessage($expectedMessage);
+ $this->graphQlMutation($setPaymentMutation, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * Data provider for testSetPaymentInvalidInput
+ *
+ * @return array
+ */
+ public function dataProviderSetPaymentInvalidInput(): array
+ {
+ return [
+ [
+ function (string $maskedQuoteId) {
+ return $this->getInvalidSetPaymentMutation($maskedQuoteId);
+ },
+ 'Required parameter "authorizenet_acceptjs" for "payment_method" is missing.',
+ ],
+ [
+ function (string $maskedQuoteId) {
+ return $this->getInvalidAcceptJsInput($maskedQuoteId);
+ },
+ 'for "authorizenet_acceptjs" is missing.'
+ ],
+ ];
+ }
+
+ /**
+ * Get setPaymentMethodOnCart missing additional data property
+ *
+ * @param string $maskedQuoteId
+ * @return string
+ */
+ private function getInvalidSetPaymentMutation(string $maskedQuoteId): string
+ {
+ return <<
Date: Sun, 8 Sep 2019 11:34:38 +0700
Subject: [PATCH 298/593] Cover Magento Version Unit Test
---
.../Test/Unit/Controller/Index/IndexTest.php | 97 +++++++++++++++++++
1 file changed, 97 insertions(+)
create mode 100644 app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php
diff --git a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php
new file mode 100644
index 0000000000000..2ade1cd927afd
--- /dev/null
+++ b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php
@@ -0,0 +1,97 @@
+context = $this->getMockBuilder(Context::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->productMetadata = $this->getMockBuilder(ProductMetadataInterface::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getName', 'getEdition', 'getVersion'])
+ ->getMock();
+
+ $this->response = $this->getMockBuilder(ResponseInterface::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setBody', 'sendResponse'])
+ ->getMock();
+
+ $this->context->expects($this->any())
+ ->method('getResponse')
+ ->willReturn($this->response);
+
+ $helper = new ObjectManager($this);
+
+ $this->model = $helper->getObject(
+ 'Magento\Version\Controller\Index\Index',
+ [
+ 'context' => $this->context,
+ 'productMetadata' => $this->productMetadata
+ ]
+ );
+ }
+
+ /**
+ * Test with Git Base version
+ */
+ public function testExecuteWithGitBase()
+ {
+ $this->productMetadata->expects($this->any())->method('getVersion')->willReturn('dev-2.3');
+ $this->assertNull($this->model->execute());
+ }
+
+ /**
+ * Test with Community Version
+ */
+ public function testExecuteWithCommunityVersion()
+ {
+ $this->productMetadata->expects($this->any())->method('getVersion')->willReturn('2.3.3');
+ $this->productMetadata->expects($this->any())->method('getEdition')->willReturn('Community');
+ $this->productMetadata->expects($this->any())->method('getName')->willReturn('Magento');
+ $this->response->expects($this->once())->method('setBody')
+ ->with('Magento/2.3 (Community)')
+ ->will($this->returnSelf());
+ $this->model->execute();
+ }
+}
From 4bf8bfdb7e148847c4c74c90bc90c5407b3ab8ae Mon Sep 17 00:00:00 2001
From: Rani Priya
Date: Sun, 8 Sep 2019 12:26:19 +0530
Subject: [PATCH 299/593] fixed custom option file type issue
---
.../Checkout/view/frontend/templates/cart/item/default.phtml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml
index d07f349265dfe..0267f68c5f81d 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml
@@ -50,7 +50,7 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima
= $block->escapeHtml($_formatedOptionValue['full_view']) ?>
- = $block->escapeHtml($_formatedOptionValue['value'], ['span']) ?>
+ = $block->escapeHtml($_formatedOptionValue['value'], ['span', 'a']) ?>
From d3e9fd4ce80b7fd7341c069f101bc298204d4430 Mon Sep 17 00:00:00 2001
From: eduard13
Date: Sun, 8 Sep 2019 10:37:38 +0300
Subject: [PATCH 300/593] Covering the CouponCodeValidation class by Unit Tests
---
.../Observer/CouponCodeValidationTest.php | 174 ++++++++++++++++++
1 file changed, 174 insertions(+)
create mode 100644 app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
diff --git a/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php b/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
new file mode 100644
index 0000000000000..6ad9db2d0ca7a
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
@@ -0,0 +1,174 @@
+codeLimitManagerMock = $this->createMock(CodeLimitManagerInterface::class);
+ $this->observerMock = $this->createMock(Observer::class);
+ $this->searchCriteriaMock = $this->getMockBuilder(SearchCriteria::class)
+ ->disableOriginalConstructor()->getMockForAbstractClass();
+ $this->cartRepositoryMock = $this->getMockBuilder(CartRepositoryInterface::class)
+ ->setMethods(['getItems'])
+ ->disableOriginalConstructor()->getMockForAbstractClass();
+ $this->searchCriteriaBuilderMock = $this->getMockBuilder(SearchCriteriaBuilder::class)
+ ->setMethods(['addFilter', 'create'])
+ ->disableOriginalConstructor()->getMockForAbstractClass();
+ $this->quoteMock = $this->createPartialMock(Quote::class, [
+ 'getCouponCode', 'setCouponCode', 'getId'
+ ]);
+
+ $this->couponCodeValidation = new CouponCodeValidation(
+ $this->codeLimitManagerMock,
+ $this->cartRepositoryMock,
+ $this->searchCriteriaBuilderMock
+ );
+ }
+
+ /**
+ * Testing the coupon code that haven't reached the request limit
+ */
+ public function testCouponCodeNotReachedTheLimit()
+ {
+ $couponCode = 'AB123';
+ $this->observerMock->expects($this->once())->method('getData')->with('quote')
+ ->willReturn($this->quoteMock);
+ $this->quoteMock->expects($this->once())->method('getCouponCode')->willReturn($couponCode);
+ $this->searchCriteriaBuilderMock->expects($this->once())->method('addFilter')->willReturnSelf();
+ $this->searchCriteriaBuilderMock->expects($this->once())->method('create')
+ ->willReturn($this->searchCriteriaMock);
+ $this->quoteMock->expects($this->once())->method('getId')->willReturn(123);
+ $this->cartRepositoryMock->expects($this->any())->method('getList')->willReturnSelf();
+ $this->cartRepositoryMock->expects($this->any())->method('getItems')->willReturn([]);
+ $this->codeLimitManagerMock->expects($this->once())->method('checkRequest')->with($couponCode);
+ $this->quoteMock->expects($this->never())->method('setCouponCode')->with('');
+
+ $this->couponCodeValidation->execute($this->observerMock);
+ }
+
+ /**
+ * Testing with the changed coupon code
+ */
+ public function testCouponCodeNotReachedTheLimitWithNewCouponCode()
+ {
+ $couponCode = 'AB123';
+ $newCouponCode = 'AB234';
+
+ $this->observerMock->expects($this->once())->method('getData')->with('quote')
+ ->willReturn($this->quoteMock);
+ $this->quoteMock->expects($this->once())->method('getCouponCode')->willReturn($couponCode);
+ $this->searchCriteriaBuilderMock->expects($this->once())->method('addFilter')->willReturnSelf();
+ $this->searchCriteriaBuilderMock->expects($this->once())->method('create')
+ ->willReturn($this->searchCriteriaMock);
+ $this->quoteMock->expects($this->once())->method('getId')->willReturn(123);
+ $this->cartRepositoryMock->expects($this->any())->method('getList')->willReturnSelf();
+ $this->cartRepositoryMock->expects($this->any())->method('getItems')
+ ->willReturn([new DataObject(['coupon_code' => $newCouponCode])]);
+ $this->codeLimitManagerMock->expects($this->once())->method('checkRequest')->with($couponCode);
+ $this->quoteMock->expects($this->never())->method('setCouponCode')->with('');
+
+ $this->couponCodeValidation->execute($this->observerMock);
+ }
+
+ /**
+ * Testing the coupon code that reached the request limit
+ *
+ * @expectedException \Magento\SalesRule\Api\Exception\CodeRequestLimitException
+ * @expectedExceptionMessage Too many coupon code requests, please try again later.
+ */
+ public function testReachingLimitForCouponCode()
+ {
+ $couponCode = 'AB123';
+ $this->observerMock->expects($this->once())->method('getData')->with('quote')
+ ->willReturn($this->quoteMock);
+ $this->quoteMock->expects($this->once())->method('getCouponCode')->willReturn($couponCode);
+ $this->searchCriteriaBuilderMock->expects($this->once())->method('addFilter')->willReturnSelf();
+ $this->searchCriteriaBuilderMock->expects($this->once())->method('create')
+ ->willReturn($this->searchCriteriaMock);
+ $this->quoteMock->expects($this->once())->method('getId')->willReturn(123);
+ $this->cartRepositoryMock->expects($this->any())->method('getList')->willReturnSelf();
+ $this->cartRepositoryMock->expects($this->any())->method('getItems')->willReturn([]);
+ $this->codeLimitManagerMock->expects($this->once())->method('checkRequest')->with($couponCode)
+ ->willThrowException(
+ new CodeRequestLimitException(__('Too many coupon code requests, please try again later.'))
+ );
+ $this->quoteMock->expects($this->once())->method('setCouponCode')->with('');
+
+ $this->couponCodeValidation->execute($this->observerMock);
+ }
+
+ /**
+ * Testing the quote that doesn't have a coupon code set
+ */
+ public function testQuoteWithNoCouponCode()
+ {
+ $couponCode = null;
+ $this->observerMock->expects($this->once())->method('getData')->with('quote')
+ ->willReturn($this->quoteMock);
+ $this->quoteMock->expects($this->once())->method('getCouponCode')->willReturn($couponCode);
+ $this->quoteMock->expects($this->never())->method('getId')->willReturn(123);
+ $this->quoteMock->expects($this->never())->method('setCouponCode')->with('');
+
+ $this->couponCodeValidation->execute($this->observerMock);
+ }
+}
From 80fc9b82e8fe2cd93208303095d005b30d2e9921 Mon Sep 17 00:00:00 2001
From: Yaroslav Rogoza
Date: Sun, 8 Sep 2019 10:12:35 +0200
Subject: [PATCH 301/593] Comments adjustments
---
.../Magento/Version/Test/Unit/Controller/Index/IndexTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php
index 2ade1cd927afd..6f8daa9d8008d 100644
--- a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php
+++ b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php
@@ -39,7 +39,7 @@ class IndexTest extends \PHPUnit\Framework\TestCase
private $response;
/**
- * Prepare Mock Object
+ * Prepare test preconditions
*/
protected function setUp()
{
From bebbc6b7b619261d6b1c1f450f30915464e850d2 Mon Sep 17 00:00:00 2001
From: eduard13
Date: Sun, 8 Sep 2019 11:46:50 +0300
Subject: [PATCH 302/593] Fixing static tests
---
.../Test/Unit/Observer/CouponCodeValidationTest.php | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php b/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
index 6ad9db2d0ca7a..e9ff0324f0187 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
@@ -74,9 +74,13 @@ protected function setUp()
$this->searchCriteriaBuilderMock = $this->getMockBuilder(SearchCriteriaBuilder::class)
->setMethods(['addFilter', 'create'])
->disableOriginalConstructor()->getMockForAbstractClass();
- $this->quoteMock = $this->createPartialMock(Quote::class, [
- 'getCouponCode', 'setCouponCode', 'getId'
- ]);
+ $this->quoteMock = $this->createPartialMock(Quote::class,
+ [
+ 'getCouponCode',
+ 'setCouponCode',
+ 'getId'
+ ]
+ );
$this->couponCodeValidation = new CouponCodeValidation(
$this->codeLimitManagerMock,
From 809a0c026ad21fb8bf7cf2f118dd8697bb6aab57 Mon Sep 17 00:00:00 2001
From: eduard13
Date: Sun, 8 Sep 2019 12:12:20 +0300
Subject: [PATCH 303/593] Fixing Static Tests
---
.../Test/Unit/Observer/CouponCodeValidationTest.php | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php b/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
index e9ff0324f0187..2fe2069b0c8b1 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Observer/CouponCodeValidationTest.php
@@ -74,12 +74,9 @@ protected function setUp()
$this->searchCriteriaBuilderMock = $this->getMockBuilder(SearchCriteriaBuilder::class)
->setMethods(['addFilter', 'create'])
->disableOriginalConstructor()->getMockForAbstractClass();
- $this->quoteMock = $this->createPartialMock(Quote::class,
- [
- 'getCouponCode',
- 'setCouponCode',
- 'getId'
- ]
+ $this->quoteMock = $this->createPartialMock(
+ Quote::class,
+ ['getCouponCode', 'setCouponCode', 'getId']
);
$this->couponCodeValidation = new CouponCodeValidation(
From 28596decdd54359e90a905b5976a77209d225456 Mon Sep 17 00:00:00 2001
From: Patrick McLain
Date: Sat, 7 Sep 2019 16:27:03 -0400
Subject: [PATCH 304/593] Deprecate setPaymentMethodAndPlaceOrderMutation
Fixes magento/graphql-ce#875
---
.../Resolver/SetPaymentAndPlaceOrder.php | 6 ++-
.../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +-
.../SetPaymentMethodAndPlaceOrderTest.php | 43 ++++++++++++++-----
3 files changed, 39 insertions(+), 12 deletions(-)
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php
index 0efbde5d6b218..03e1e6ffe822d 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php
@@ -20,7 +20,11 @@
use Magento\Sales\Api\OrderRepositoryInterface;
/**
- * @inheritdoc
+ * Resolver for setting payment method and placing order
+ *
+ * @deprecated Should use setPaymentMethodOnCart and placeOrder mutations in single request.
+ * @see \Magento\QuoteGraphQl\Model\Resolver\SetPaymentMethodOnCart
+ * @see \Magento\QuoteGraphQl\Model\Resolver\PlaceOrder
*/
class SetPaymentAndPlaceOrder implements ResolverInterface
{
diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
index 08bb78ba776c4..816a1f40d953a 100644
--- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
@@ -18,7 +18,7 @@ type Mutation {
setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart")
setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart")
setGuestEmailOnCart(input: SetGuestEmailOnCartInput): SetGuestEmailOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetGuestEmailOnCart")
- setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder")
+ setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @deprecated(reason: "Should use setPaymentMethodOnCart and placeOrder mutations in single request.") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder")
placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder")
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php
index 12fb356904224..192c10a67aa6b 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php
@@ -78,9 +78,14 @@ public function testSetPaymentOnCartWithSimpleProduct()
$query = $this->getQuery($maskedQuoteId, $methodCode);
$response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
- self::assertArrayHasKey('setPaymentMethodAndPlaceOrder', $response);
- self::assertArrayHasKey('order', $response['setPaymentMethodAndPlaceOrder']);
- self::assertArrayHasKey('order_id', $response['setPaymentMethodAndPlaceOrder']['order']);
+ self::assertArrayHasKey('setPaymentMethodOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']);
+ self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']);
+ self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']);
+ self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']);
+
+ self::assertArrayHasKey('order', $response['placeOrder']);
+ self::assertArrayHasKey('order_id', $response['placeOrder']['order']);
}
/**
@@ -116,9 +121,14 @@ public function testSetPaymentOnCartWithVirtualProduct()
$query = $this->getQuery($maskedQuoteId, $methodCode);
$response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
- self::assertArrayHasKey('setPaymentMethodAndPlaceOrder', $response);
- self::assertArrayHasKey('order', $response['setPaymentMethodAndPlaceOrder']);
- self::assertArrayHasKey('order_id', $response['setPaymentMethodAndPlaceOrder']['order']);
+ self::assertArrayHasKey('setPaymentMethodOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']);
+ self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']);
+ self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']);
+ self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']);
+
+ self::assertArrayHasKey('order', $response['placeOrder']);
+ self::assertArrayHasKey('order_id', $response['placeOrder']['order']);
}
/**
@@ -224,12 +234,25 @@ private function getQuery(
) : string {
return <<
Date: Sun, 8 Sep 2019 19:14:44 -0500
Subject: [PATCH 305/593] MC-19904: Start/End Date time is changed after event
saving in event edit page
---
.../Framework/Stdlib/DateTime/Timezone.php | 26 +++---
.../Test/Unit/DateTime/TimezoneTest.php | 87 +++++++++++++++----
2 files changed, 88 insertions(+), 25 deletions(-)
diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php
index 014854ddd584f..118a3e053bd79 100644
--- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php
+++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php
@@ -195,16 +195,16 @@ public function date($date = null, $locale = null, $useTimezone = true, $include
*/
public function scopeDate($scope = null, $date = null, $includeTime = false)
{
- $timezone = $this->_scopeConfig->getValue($this->getDefaultTimezonePath(), $this->_scopeType, $scope);
+ $timezone = new \DateTimeZone(
+ $this->_scopeConfig->getValue($this->getDefaultTimezonePath(), $this->_scopeType, $scope)
+ );
switch (true) {
case (empty($date)):
- $date = new \DateTime('now', new \DateTimeZone($timezone));
+ $date = new \DateTime('now', $timezone);
break;
case ($date instanceof \DateTime):
- $date = $date->setTimezone(new \DateTimeZone($timezone));
- break;
case ($date instanceof \DateTimeImmutable):
- $date = new \DateTime($date->format('Y-m-d H:i:s'), $date->getTimezone());
+ $date = $date->setTimezone($timezone);
break;
case (!is_numeric($date)):
$timeType = $includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE;
@@ -212,14 +212,20 @@ public function scopeDate($scope = null, $date = null, $includeTime = false)
$this->_localeResolver->getLocale(),
\IntlDateFormatter::SHORT,
$timeType,
- new \DateTimeZone($timezone)
+ $timezone
);
- $date = $formatter->parse($date) ?: (new \DateTime($date))->getTimestamp();
- $date = (new \DateTime(null, new \DateTimeZone($timezone)))->setTimestamp($date);
+ $timestamp = $formatter->parse($date);
+ $date = $timestamp
+ ? (new \DateTime('@' . $timestamp))->setTimezone($timezone)
+ : new \DateTime($date, $timezone);
+ break;
+ case (is_numeric($date)):
+ $date = new \DateTime('@' . $date);
+ $date = $date->setTimezone($timezone);
break;
default:
- $date = new \DateTime(is_numeric($date) ? '@' . $date : $date);
- $date->setTimezone(new \DateTimeZone($timezone));
+ $date = new \DateTime($date, $timezone);
+ break;
}
if (!$includeTime) {
diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php
index 3d7d14a394629..53980e574c267 100644
--- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php
+++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php
@@ -22,6 +22,16 @@ class TimezoneTest extends \PHPUnit\Framework\TestCase
*/
private $defaultTimeZone;
+ /**
+ * @var string
+ */
+ private $scopeType;
+
+ /**
+ * @var string
+ */
+ private $defaultTimezonePath;
+
/**
* @var ObjectManager
*/
@@ -49,6 +59,8 @@ protected function setUp()
{
$this->defaultTimeZone = date_default_timezone_get();
date_default_timezone_set('UTC');
+ $this->scopeType = 'store';
+ $this->defaultTimezonePath = 'default/timezone/path';
$this->objectManager = new ObjectManager($this);
$this->scopeResolver = $this->getMockBuilder(ScopeResolverInterface::class)->getMock();
@@ -86,9 +98,10 @@ public function testDateIncludeTime($date, $locale, $includeTime, $expectedTimes
/**
* DataProvider for testDateIncludeTime
+ *
* @return array
*/
- public function dateIncludeTimeDataProvider()
+ public function dateIncludeTimeDataProvider(): array
{
return [
'Parse d/m/y date without time' => [
@@ -133,9 +146,10 @@ public function testConvertConfigTimeToUtc($date, $configuredTimezone, $expected
/**
* Data provider for testConvertConfigTimeToUtc
+ *
* @return array
*/
- public function getConvertConfigTimeToUtcFixtures()
+ public function getConvertConfigTimeToUtcFixtures(): array
{
return [
'string' => [
@@ -181,9 +195,10 @@ public function testDate()
/**
* DataProvider for testDate
+ *
* @return array
*/
- private function getDateFixtures()
+ private function getDateFixtures(): array
{
return [
'now_datetime_utc' => [
@@ -239,29 +254,71 @@ private function getTimezone()
return new Timezone(
$this->scopeResolver,
$this->localeResolver,
- $this->getMockBuilder(DateTime::class)->getMock(),
+ $this->createMock(DateTime::class),
$this->scopeConfig,
- '',
- ''
+ $this->scopeType,
+ $this->defaultTimezonePath
);
}
/**
* @param string $configuredTimezone
+ * @param string|null $scope
*/
- private function scopeConfigWillReturnConfiguredTimezone($configuredTimezone)
+ private function scopeConfigWillReturnConfiguredTimezone(string $configuredTimezone, string $scope = null)
{
- $this->scopeConfig->method('getValue')->with('', '', null)->willReturn($configuredTimezone);
+ $this->scopeConfig->expects($this->atLeastOnce())
+ ->method('getValue')
+ ->with($this->defaultTimezonePath, $this->scopeType, $scope)
+ ->willReturn($configuredTimezone);
}
- public function testCheckIfScopeDateSetsTimeZone()
+ /**
+ * @dataProvider scopeDateDataProvider
+ * @param \DateTimeInterface|string|int $date
+ * @param string $timezone
+ * @param string $locale
+ * @param string $expectedDate
+ */
+ public function testScopeDate($date, string $timezone, string $locale, string $expectedDate)
{
- $scopeDate = new \DateTime('now', new \DateTimeZone('America/Vancouver'));
- $this->scopeConfig->method('getValue')->willReturn('America/Vancouver');
+ $scopeCode = 'test';
- $this->assertEquals(
- $scopeDate->getTimezone(),
- $this->getTimezone()->scopeDate(0, $scopeDate->getTimestamp())->getTimezone()
- );
+ $this->scopeConfigWillReturnConfiguredTimezone($timezone, $scopeCode);
+ $this->localeResolver->method('getLocale')
+ ->willReturn($locale);
+
+ $scopeDate = $this->getTimezone()->scopeDate($scopeCode, $date, true);
+ $this->assertEquals($expectedDate, $scopeDate->format('Y-m-d H:i:s'));
+ $this->assertEquals($timezone, $scopeDate->getTimezone()->getName());
+ }
+
+ /**
+ * @return array
+ */
+ public function scopeDateDataProvider(): array
+ {
+ $utcTz = new \DateTimeZone('UTC');
+
+ return [
+ ['2018-10-20 00:00:00', 'UTC', 'en_US', '2018-10-20 00:00:00'],
+ ['2018-10-20 00:00:00', 'America/Los_Angeles', 'en_US', '2018-10-20 00:00:00'],
+ ['2018-10-20 00:00:00', 'Asia/Qatar', 'en_US', '2018-10-20 00:00:00'],
+ ['10/20/18 00:00', 'UTC', 'en_US', '2018-10-20 00:00:00'],
+ ['10/20/18 00:00', 'America/Los_Angeles', 'en_US', '2018-10-20 00:00:00'],
+ ['10/20/18 00:00', 'Asia/Qatar', 'en_US', '2018-10-20 00:00:00'],
+ ['20/10/18 00:00', 'UTC', 'fr_FR', '2018-10-20 00:00:00'],
+ ['20/10/18 00:00', 'America/Los_Angeles', 'fr_FR', '2018-10-20 00:00:00'],
+ ['20/10/18 00:00', 'Asia/Qatar', 'fr_FR', '2018-10-20 00:00:00'],
+ [1539993600, 'UTC', 'en_US', '2018-10-20 00:00:00'],
+ [1539993600, 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'],
+ [1539993600, 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'],
+ [new \DateTime('2018-10-20', $utcTz), 'UTC', 'en_US', '2018-10-20 00:00:00'],
+ [new \DateTime('2018-10-20', $utcTz), 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'],
+ [new \DateTime('2018-10-20', $utcTz), 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'],
+ [new \DateTimeImmutable('2018-10-20', $utcTz), 'UTC', 'en_US', '2018-10-20 00:00:00'],
+ [new \DateTimeImmutable('2018-10-20', $utcTz), 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'],
+ [new \DateTimeImmutable('2018-10-20', $utcTz), 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'],
+ ];
}
}
From 1da4f2ac4985ab6d1d348e93d6fa8670888a2f5c Mon Sep 17 00:00:00 2001
From: Eden
Date: Mon, 9 Sep 2019 13:20:47 +0700
Subject: [PATCH 306/593] Resolve API
"V1/attributeMetadata/customerAddress/attribute/prefix" and
"V1/attributeMetadata/customerAddress/attribute/suffix" can not get options
issue24518
---
.../Model/AttributeMetadataConverter.php | 92 +++++++++++++++----
1 file changed, 73 insertions(+), 19 deletions(-)
diff --git a/app/code/Magento/Customer/Model/AttributeMetadataConverter.php b/app/code/Magento/Customer/Model/AttributeMetadataConverter.php
index 44d104659e069..0407aebf9d670 100644
--- a/app/code/Magento/Customer/Model/AttributeMetadataConverter.php
+++ b/app/code/Magento/Customer/Model/AttributeMetadataConverter.php
@@ -3,18 +3,36 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Customer\Model;
use Magento\Customer\Api\Data\OptionInterfaceFactory;
use Magento\Customer\Api\Data\ValidationRuleInterfaceFactory;
use Magento\Customer\Api\Data\AttributeMetadataInterfaceFactory;
use Magento\Eav\Api\Data\AttributeDefaultValueInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\ObjectManager;
/**
* Converter for AttributeMetadata
*/
class AttributeMetadataConverter
{
+ /**
+ * Attribute Code get options from system config
+ *
+ * @var array
+ */
+ private const ATTRIBUTE_CODE_LIST_FROM_SYSTEM_CONFIG = ['prefix', 'suffix'];
+
+ /**
+ * XML Path to get address config
+ *
+ * @var string
+ */
+ private const XML_CUSTOMER_ADDRESS = 'customer/address/';
+
/**
* @var OptionInterfaceFactory
*/
@@ -35,6 +53,11 @@ class AttributeMetadataConverter
*/
protected $dataObjectHelper;
+ /**
+ * @var ScopeConfigInterface
+ */
+ private $scopeConfig;
+
/**
* Initialize the Converter
*
@@ -42,17 +65,20 @@ class AttributeMetadataConverter
* @param ValidationRuleInterfaceFactory $validationRuleFactory
* @param AttributeMetadataInterfaceFactory $attributeMetadataFactory
* @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
+ * @param ScopeConfigInterface $scopeConfig
*/
public function __construct(
OptionInterfaceFactory $optionFactory,
ValidationRuleInterfaceFactory $validationRuleFactory,
AttributeMetadataInterfaceFactory $attributeMetadataFactory,
- \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
+ \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
+ ScopeConfigInterface $scopeConfig = null
) {
$this->optionFactory = $optionFactory;
$this->validationRuleFactory = $validationRuleFactory;
$this->attributeMetadataFactory = $attributeMetadataFactory;
$this->dataObjectHelper = $dataObjectHelper;
+ $this->scopeConfig = $scopeConfig ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class);
}
/**
@@ -64,28 +90,34 @@ public function __construct(
public function createMetadataAttribute($attribute)
{
$options = [];
- if ($attribute->usesSource()) {
- foreach ($attribute->getSource()->getAllOptions() as $option) {
- $optionDataObject = $this->optionFactory->create();
- if (!is_array($option['value'])) {
- $optionDataObject->setValue($option['value']);
- } else {
- $optionArray = [];
- foreach ($option['value'] as $optionArrayValues) {
- $optionObject = $this->optionFactory->create();
- $this->dataObjectHelper->populateWithArray(
- $optionObject,
- $optionArrayValues,
- \Magento\Customer\Api\Data\OptionInterface::class
- );
- $optionArray[] = $optionObject;
+
+ if (in_array($attribute->getAttributeCode(), self::ATTRIBUTE_CODE_LIST_FROM_SYSTEM_CONFIG)) {
+ $options = $this->getOptionFromConfig($attribute->getAttributeCode());
+ } else {
+ if ($attribute->usesSource()) {
+ foreach ($attribute->getSource()->getAllOptions() as $option) {
+ $optionDataObject = $this->optionFactory->create();
+ if (!is_array($option['value'])) {
+ $optionDataObject->setValue($option['value']);
+ } else {
+ $optionArray = [];
+ foreach ($option['value'] as $optionArrayValues) {
+ $optionObject = $this->optionFactory->create();
+ $this->dataObjectHelper->populateWithArray(
+ $optionObject,
+ $optionArrayValues,
+ \Magento\Customer\Api\Data\OptionInterface::class
+ );
+ $optionArray[] = $optionObject;
+ }
+ $optionDataObject->setOptions($optionArray);
}
- $optionDataObject->setOptions($optionArray);
+ $optionDataObject->setLabel($option['label']);
+ $options[] = $optionDataObject;
}
- $optionDataObject->setLabel($option['label']);
- $options[] = $optionDataObject;
}
}
+
$validationRules = [];
foreach ((array)$attribute->getValidateRules() as $name => $value) {
$validationRule = $this->validationRuleFactory->create()
@@ -122,4 +154,26 @@ public function createMetadataAttribute($attribute)
->setIsFilterableInGrid($attribute->getIsFilterableInGrid())
->setIsSearchableInGrid($attribute->getIsSearchableInGrid());
}
+
+ /**
+ * Get option from System Config instead of Use Source (Prefix, Suffix)
+ *
+ * @param string $attributeCode
+ * @return \Magento\Customer\Api\Data\OptionInterface[]
+ */
+ private function getOptionFromConfig($attributeCode)
+ {
+ $result = [];
+ $value = $this->scopeConfig->getValue(self::XML_CUSTOMER_ADDRESS . $attributeCode . '_options');
+ if ($value) {
+ $optionArray = explode(';', $value);
+ foreach ($optionArray as $value) {
+ $optionObject = $this->optionFactory->create();
+ $optionObject->setLabel($value);
+ $optionObject->setValue($value);
+ $result[] = $optionObject;
+ }
+ }
+ return $result;
+ }
}
From 08e446e28f4f9949eb9a907e74c4d544578ab787 Mon Sep 17 00:00:00 2001
From: Yuliya Labudova
Date: Mon, 9 Sep 2019 10:31:40 +0300
Subject: [PATCH 307/593] MAGETWO-94565: Catalog product collection filters
produce errors and cause inconsistent behaviour
- Fix static tests.
---
.../Catalog/Model/ResourceModel/Product/CollectionTest.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
index d3970ad403ffd..de0e881474cf0 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
@@ -15,6 +15,8 @@
/**
* Collection test
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CollectionTest extends \PHPUnit\Framework\TestCase
{
From 650c215872275e6ff392a988f5b19e8aae616928 Mon Sep 17 00:00:00 2001
From: Pavel Bystritsky <51681487+engcom-Foxtrot@users.noreply.github.com>
Date: Mon, 9 Sep 2019 12:14:59 +0300
Subject: [PATCH 308/593] magento/magento2#24380: Integration tests fix.
---
.../Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php
index 4c653ab9ae33f..8d6c2625daadc 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemQtyTest.php
@@ -10,6 +10,7 @@
use Magento\Catalog\Model\Product;
use Magento\Checkout\Model\Session;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\Framework\Data\Form\FormKey;
use Magento\Framework\Serialize\Serializer\Json;
@@ -81,6 +82,7 @@ public function testExecute($requestQuantity, $expectedResponse)
];
}
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($request);
$this->dispatch('checkout/cart/updateItemQty');
$response = $this->getResponse()->getBody();
From a356f94dd8004db75b7b57319097cb857fe2c2b9 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Mon, 9 Sep 2019 08:26:37 -0500
Subject: [PATCH 309/593] MC-19702: Add input_type to customAttributeMetadata
query
- fix test to avoid using hard coded values for attribute options
---
.../Catalog/ProductAttributeOptionsTest.php | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeOptionsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeOptionsTest.php
index 53e09bf590e3c..517a1c966b04d 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeOptionsTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductAttributeOptionsTest.php
@@ -8,6 +8,7 @@
namespace Magento\GraphQl\Catalog;
use Magento\TestFramework\TestCase\GraphQlAbstract;
+use Magento\Eav\Api\Data\AttributeOptionInterface;
class ProductAttributeOptionsTest extends GraphQlAbstract
{
@@ -18,6 +19,17 @@ class ProductAttributeOptionsTest extends GraphQlAbstract
*/
public function testCustomAttributeMetadataOptions()
{
+ /** @var \Magento\Eav\Model\Config $eavConfig */
+ $eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+ $attribute = $eavConfig->getAttribute('catalog_product', 'dropdown_attribute');
+ /** @var AttributeOptionInterface[] $options */
+ $options = $attribute->getOptions();
+ array_shift($options);
+ $optionValues = [];
+ // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall
+ for ($i = 0; $i < count($options); $i++) {
+ $optionValues[] = $options[$i]->getValue();
+ }
$query
= << 'Option 1',
- 'value' => '10'
+ 'value' => $optionValues[0]
],
[
'label' => 'Option 2',
- 'value' => '11'
+ 'value' => $optionValues[1]
],
[
'label' => 'Option 3',
- 'value' => '12'
+ 'value' => $optionValues[2]
]
]
];
From f006daad2981d49026fd392ecce861664d2b7aa3 Mon Sep 17 00:00:00 2001
From: Deepty Thampy
Date: Mon, 9 Sep 2019 08:28:19 -0500
Subject: [PATCH 310/593] MC-18514: API functional test to cover filterable
custom attributes in layered navigation
- fix failing tests
---
.../GraphQl/Catalog/ProductSearchTest.php | 62 ++-----------------
.../GraphQl/Catalog/ProductViewTest.php | 10 ++-
.../GraphQl/Swatches/ProductSearchTest.php | 2 +-
3 files changed, 13 insertions(+), 61 deletions(-)
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index cef036a31e6b7..2e3e67c65ca46 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -106,7 +106,7 @@ public function testFilterLn()
* @magentoApiDataFixture Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
- public function testLayeredNavigationWithConfigurableChildrenOutOfStock()
+ public function testLayeredNavigationForConfigurableProducts()
{
CacheCleaner::cleanAll();
$attributeCode = 'test_configurable';
@@ -704,59 +704,6 @@ public function testFilterByCategoryIdAndCustomAttribute()
);
}
}
- /**
- *
- * @return string
- */
- private function getQueryProductsWithCustomAttribute($attributeCode, $optionValue) : string
- {
- return <<assertEquals(4, $response['products']['total_count']);
$this->assertNotEmpty($response['products']['filters'],'Filters should have the Category layer');
$this->assertEquals('Colorful Category', $response['products']['filters'][0]['filter_items'][0]['label']);
- $productsInResponse = ['Blue briefs', 'ocean blue Shoes', 'Navy Striped Shoes','Grey shorts'];
+ $productsInResponse = ['ocean blue Shoes', 'Blue briefs', 'Navy Striped Shoes','Grey shorts'];
for ($i = 0; $i < count($response['products']['items']); $i++) {
$this->assertEquals($productsInResponse[$i], $response['products']['items'][$i]['name']);
}
+ $this->assertCount(2, $response['products']['aggregations']);
}
/**
@@ -1521,7 +1469,7 @@ public function testProductBasicFullTextSearchQuery()
* @magentoApiDataFixture Magento/Catalog/_files/category.php
* @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products_2.php
*/
- public function testFilterProductsWithinASpecificPriceRangeSortedByPriceDESC()
+ public function testFilterWithinASpecificPriceRangeSortedByPriceDESC()
{
/** @var ProductRepositoryInterface $productRepository */
$productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
index 378b87fb9591f..5685fcdb25877 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php
@@ -981,7 +981,7 @@ public function testProductInAllAnchoredCategories()
{
$query = <<
Date: Mon, 9 Sep 2019 09:39:20 -0500
Subject: [PATCH 311/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
- Fix sorting issue
- Use interface for aggregation options
---
.../Product/SearchCriteriaBuilder.php | 16 ++-----
.../Model/AggregationOptionTypeResolver.php | 29 ++++++++++++
...AggregationOptionTypeResolverComposite.php | 45 +++++++++++++++++++
.../Products/DataProvider/ProductSearch.php | 12 ++---
.../Magento/CatalogGraphQl/etc/graphql/di.xml | 7 +++
.../CatalogGraphQl/etc/schema.graphqls | 8 +++-
.../Adapter/Mysql/Filter/Preprocessor.php | 5 ++-
.../Search/Adapter/Mysql/TemporaryStorage.php | 2 +-
8 files changed, 101 insertions(+), 23 deletions(-)
create mode 100644 app/code/Magento/CatalogGraphQl/Model/AggregationOptionTypeResolver.php
create mode 100644 app/code/Magento/CatalogGraphQl/Model/AggregationOptionTypeResolverComposite.php
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
index af6ed85196cf8..810e9172d0973 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -97,8 +97,9 @@ public function build(array $args, bool $includeAggregation): SearchCriteriaInte
if (!empty($args['search'])) {
$this->addFilter($searchCriteria, 'search_term', $args['search']);
}
-
- $this->addDefaultSortOrder($searchCriteria);
+ if (!$searchCriteria->getSortOrders()) {
+ $this->addDefaultSortOrder($searchCriteria);
+ }
$this->addVisibilityFilter($searchCriteria, !empty($args['search']), !empty($args['filter']));
$searchCriteria->setCurrentPage($args['currentPage']);
@@ -170,21 +171,12 @@ private function addFilter(SearchCriteriaInterface $searchCriteria, string $fiel
*/
private function addDefaultSortOrder(SearchCriteriaInterface $searchCriteria): void
{
- $sortOrders = $searchCriteria->getSortOrders() ?? [];
- foreach ($sortOrders as $sortOrder) {
- // Relevance order is already specified
- if ($sortOrder->getField() === 'relevance') {
- return;
- }
- }
$defaultSortOrder = $this->sortOrderBuilder
->setField('relevance')
->setDirection(SortOrder::SORT_DESC)
->create();
- $sortOrders[] = $defaultSortOrder;
-
- $searchCriteria->setSortOrders($sortOrders);
+ $searchCriteria->setSortOrders([$defaultSortOrder]);
}
/**
diff --git a/app/code/Magento/CatalogGraphQl/Model/AggregationOptionTypeResolver.php b/app/code/Magento/CatalogGraphQl/Model/AggregationOptionTypeResolver.php
new file mode 100644
index 0000000000000..3a532a1a6c760
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/AggregationOptionTypeResolver.php
@@ -0,0 +1,29 @@
+typeResolvers = $typeResolvers;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolveType(array $data) : string
+ {
+ /** @var TypeResolverInterface $typeResolver */
+ foreach ($this->typeResolvers as $typeResolver) {
+ $resolvedType = $typeResolver->resolveType($data);
+ if ($resolvedType) {
+ return $resolvedType;
+ }
+ }
+ throw new GraphQlInputException(__('Cannot resolve aggregation option type'));
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
index 8c5fe42c730f6..661c5874614b5 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
@@ -79,7 +79,7 @@ public function __construct(
*/
public function getList(
SearchCriteriaInterface $searchCriteria,
- SearchResultsInterface $searchResult,
+ SearchResultInterface $searchResult,
array $attributes = []
): SearchResultsInterface {
/** @var Collection $collection */
@@ -92,11 +92,11 @@ public function getList(
$collection->load();
$this->collectionPostProcessor->process($collection, $attributes);
- $searchResult = $this->searchResultsFactory->create();
- $searchResult->setSearchCriteria($searchCriteria);
- $searchResult->setItems($collection->getItems());
- $searchResult->setTotalCount($collection->getSize());
- return $searchResult;
+ $searchResults = $this->searchResultsFactory->create();
+ $searchResults->setSearchCriteria($searchCriteria);
+ $searchResults->setItems($collection->getItems());
+ $searchResults->setTotalCount($collection->getSize());
+ return $searchResults;
}
/**
diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
index ea2704f1a4ee5..fe3413dc3b218 100644
--- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
@@ -28,6 +28,13 @@
+
+
+
+ - Magento\CatalogGraphQl\Model\AggregationOptionTypeResolver
+
+
+
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index b509865f0e82b..69c13a9e81923 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -403,6 +403,10 @@ interface LayerFilterItemInterface @typeResolver(class: "Magento\\CatalogGraphQl
items_count: Int @doc(description: "Count of items by filter.") @deprecated(reason: "Use AggregationOption.count instead.")
}
+type LayerFilterItem implements LayerFilterItemInterface {
+
+}
+
type Aggregation @doc(description: "A bucket that contains information for each filterable option (such as price, category ID, and custom attributes).") {
count: Int @doc(description: "The number of options in the aggregation group.")
label: String @doc(description: "The aggregation display name.")
@@ -410,13 +414,13 @@ type Aggregation @doc(description: "A bucket that contains information for each
options: [AggregationOption] @doc(description: "Array of options for the aggregation.")
}
-type AggregationOption {
+interface AggregationOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\AggregationOptionTypeResolverComposite") {
count: Int @doc(description: "The number of items that match the aggregation option.")
label: String @doc(description: "Aggregation option display label.")
value: String! @doc(description: "The internal ID that represents the value of the option.")
}
-type LayerFilterItem implements LayerFilterItemInterface {
+type AggregationOption implements AggregationOptionInterface {
}
diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
index 37d2dda886259..f164da99292ad 100644
--- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
+++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
@@ -201,8 +201,9 @@ private function processQueryWithField(FilterInterface $filter, $isNegation, $qu
)
->joinLeft(
['current_store' => $table],
- 'current_store.attribute_id = main_table.attribute_id AND current_store.store_id = '
- . $currentStoreId,
+ 'current_store.entity_id = main_table.entity_id AND '
+ . 'current_store.attribute_id = main_table.attribute_id AND current_store.store_id = '
+ . $currentStoreId,
null
)
->columns([$filter->getField() => $ifNullCondition])
diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php
index 60ee2d5706067..7f8ef8c422b92 100644
--- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php
+++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php
@@ -152,7 +152,7 @@ private function createTemporaryTable()
self::FIELD_SCORE,
Table::TYPE_DECIMAL,
[32, 16],
- ['unsigned' => true, 'nullable' => false],
+ ['unsigned' => true, 'nullable' => true],
'Score'
);
$table->setOption('type', 'memory');
From 0768d89e8f2a81fb18539edaa889de94e6003818 Mon Sep 17 00:00:00 2001
From: Lena Orobei
Date: Mon, 9 Sep 2019 10:10:30 -0500
Subject: [PATCH 312/593] magento/graphql-ce#762: Subscribe for newsletter even
when not allowed
---
.../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
index 18e417bb5edfe..6d33dea35835f 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php
@@ -14,6 +14,7 @@
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Newsletter\Model\Config;
+use Magento\Store\Model\ScopeInterface;
/**
* Create customer account resolver
@@ -66,7 +67,7 @@ public function resolve(
throw new GraphQlInputException(__('"input" value should be specified'));
}
- if (!$this->newsLetterConfig->isActive()) {
+ if (!$this->newsLetterConfig->isActive(ScopeInterface::SCOPE_STORE)) {
$args['input']['is_subscribed'] = false;
}
From cbe25f4960b59feed344eebee25e3dbe2f3efaaa Mon Sep 17 00:00:00 2001
From: Pieter Hoste
Date: Sat, 7 Sep 2019 14:51:53 +0200
Subject: [PATCH 313/593] Cleaned up non-existing files from being excluded
from bundling.
---
app/design/adminhtml/Magento/backend/etc/view.xml | 9 ---------
app/design/frontend/Magento/blank/etc/view.xml | 2 --
app/design/frontend/Magento/luma/etc/view.xml | 2 --
3 files changed, 13 deletions(-)
diff --git a/app/design/adminhtml/Magento/backend/etc/view.xml b/app/design/adminhtml/Magento/backend/etc/view.xml
index 18c2d8f1b1722..6539e3330e98b 100644
--- a/app/design/adminhtml/Magento/backend/etc/view.xml
+++ b/app/design/adminhtml/Magento/backend/etc/view.xml
@@ -24,7 +24,6 @@
- Lib::mage/captcha.js
- - Lib::mage/captcha.min.js
- Lib::mage/common.js
- Lib::mage/cookies.js
- Lib::mage/dataPost.js
@@ -46,7 +45,6 @@
- Lib::mage/translate-inline-vde.js
- Lib::mage/webapi.js
- Lib::mage/zoom.js
- - Lib::mage/validation/dob-rule.js
- Lib::mage/validation/validation.js
- Lib::mage/adminhtml/varienLoader.js
- Lib::mage/adminhtml/tools.js
@@ -57,11 +55,9 @@
- Lib::jquery/jquery.parsequery.js
- Lib::jquery/jquery.mobile.custom.js
- Lib::jquery/jquery-ui.js
- - Lib::jquery/jquery-ui.min.js
- Lib::matchMedia.js
- Lib::requirejs/require.js
- Lib::requirejs/text.js
- - Lib::date-format-normalizer.js
- Lib::varien/js.js
- Lib::css
- Lib::lib
@@ -72,10 +68,5 @@
- Lib::fotorama
- Lib::magnifier
- Lib::tiny_mce
- - Lib::tiny_mce/classes
- - Lib::tiny_mce/langs
- - Lib::tiny_mce/plugins
- - Lib::tiny_mce/themes
- - Lib::tiny_mce/utils
diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml
index e742ce0a21cd1..dfc5ad39af557 100644
--- a/app/design/frontend/Magento/blank/etc/view.xml
+++ b/app/design/frontend/Magento/blank/etc/view.xml
@@ -262,12 +262,10 @@
- Lib::jquery/jquery.min.js
- Lib::jquery/jquery-ui-1.9.2.js
- Lib::jquery/jquery.details.js
- - Lib::jquery/jquery.details.min.js
- Lib::jquery/jquery.hoverIntent.js
- Lib::jquery/colorpicker/js/colorpicker.js
- Lib::requirejs/require.js
- Lib::requirejs/text.js
- - Lib::date-format-normalizer.js
- Lib::legacy-build.min.js
- Lib::mage/captcha.js
- Lib::mage/dropdown_old.js
diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml
index 7aa2e51481bd9..c04c0d73cb3cd 100644
--- a/app/design/frontend/Magento/luma/etc/view.xml
+++ b/app/design/frontend/Magento/luma/etc/view.xml
@@ -273,12 +273,10 @@
- Lib::jquery/jquery.min.js
- Lib::jquery/jquery-ui-1.9.2.js
- Lib::jquery/jquery.details.js
- - Lib::jquery/jquery.details.min.js
- Lib::jquery/jquery.hoverIntent.js
- Lib::jquery/colorpicker/js/colorpicker.js
- Lib::requirejs/require.js
- Lib::requirejs/text.js
- - Lib::date-format-normalizer.js
- Lib::legacy-build.min.js
- Lib::mage/captcha.js
- Lib::mage/dropdown_old.js
From 09ef8d2a905c52dc057b8e6776b1a2e9dc529e65 Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Mon, 9 Sep 2019 12:59:46 -0500
Subject: [PATCH 314/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
- fix tests affected by making url_key searchable
- fix unit and integration tests
---
.../Adapter/Mysql/Filter/Preprocessor.php | 4 +-
.../Product/FieldProvider/StaticFieldTest.php | 94 ++++++++++++-------
.../Indexer/Fulltext/Action/FullTest.php | 18 +++-
.../CatalogSearch/_files/indexer_fulltext.php | 5 +
.../Controller/GraphQlControllerTest.php | 2 +-
.../Adapter/Mysql/TemporaryStorageTest.php | 6 +-
6 files changed, 83 insertions(+), 46 deletions(-)
diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
index f164da99292ad..a97d362c5de7f 100644
--- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
+++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php
@@ -201,8 +201,8 @@ private function processQueryWithField(FilterInterface $filter, $isNegation, $qu
)
->joinLeft(
['current_store' => $table],
- 'current_store.entity_id = main_table.entity_id AND '
- . 'current_store.attribute_id = main_table.attribute_id AND current_store.store_id = '
+ "current_store.{$linkIdField} = main_table.{$linkIdField} AND "
+ . "current_store.attribute_id = main_table.attribute_id AND current_store.store_id = "
. $currentStoreId,
null
)
diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php
index de85b8b6602b8..f90c13c9bfb65 100644
--- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php
+++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php
@@ -139,6 +139,7 @@ public function testGetAllAttributesTypes(
$isComplexType,
$complexType,
$isSortable,
+ $isTextType,
$fieldName,
$compositeFieldName,
$sortFieldName,
@@ -153,29 +154,33 @@ public function testGetAllAttributesTypes(
$this->indexTypeConverter->expects($this->any())
->method('convert')
->with($this->anything())
- ->will($this->returnCallback(
- function ($type) {
- if ($type === 'no_index') {
- return 'no';
- } elseif ($type === 'no_analyze') {
- return 'not_analyzed';
+ ->will(
+ $this->returnCallback(
+ function ($type) {
+ if ($type === 'no_index') {
+ return 'no';
+ } elseif ($type === 'no_analyze') {
+ return 'not_analyzed';
+ }
}
- }
- ));
+ )
+ );
$this->fieldNameResolver->expects($this->any())
->method('getFieldName')
->with($this->anything())
- ->will($this->returnCallback(
- function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortFieldName) {
- if (empty($context)) {
- return $fieldName;
- } elseif ($context['type'] === 'sort') {
- return $sortFieldName;
- } elseif ($context['type'] === 'text') {
- return $compositeFieldName;
+ ->will(
+ $this->returnCallback(
+ function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortFieldName) {
+ if (empty($context)) {
+ return $fieldName;
+ } elseif ($context['type'] === 'sort') {
+ return $sortFieldName;
+ } elseif ($context['type'] === 'text') {
+ return $compositeFieldName;
+ }
}
- }
- ));
+ )
+ );
$productAttributeMock = $this->getMockBuilder(AbstractAttribute::class)
->setMethods(['getAttributeCode'])
@@ -189,7 +194,7 @@ function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortF
$attributeMock = $this->getMockBuilder(AttributeAdapter::class)
->disableOriginalConstructor()
- ->setMethods(['isComplexType', 'getAttributeCode', 'isSortable'])
+ ->setMethods(['isComplexType', 'getAttributeCode', 'isSortable', 'isTextType'])
->getMock();
$attributeMock->expects($this->any())
->method('isComplexType')
@@ -197,6 +202,9 @@ function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortF
$attributeMock->expects($this->any())
->method('isSortable')
->willReturn($isSortable);
+ $attributeMock->expects($this->any())
+ ->method('isTextType')
+ ->willReturn($isTextType);
$attributeMock->expects($this->any())
->method('getAttributeCode')
->willReturn($attributeCode);
@@ -207,22 +215,24 @@ function ($attributeMock, $context) use ($fieldName, $compositeFieldName, $sortF
$this->fieldTypeConverter->expects($this->any())
->method('convert')
->with($this->anything())
- ->will($this->returnCallback(
- function ($type) use ($complexType) {
- static $callCount = [];
- $callCount[$type] = !isset($callCount[$type]) ? 1 : ++$callCount[$type];
+ ->will(
+ $this->returnCallback(
+ function ($type) use ($complexType) {
+ static $callCount = [];
+ $callCount[$type] = !isset($callCount[$type]) ? 1 : ++$callCount[$type];
- if ($type === 'string') {
- return 'string';
- } elseif ($type === 'float') {
- return 'float';
- } elseif ($type === 'keyword') {
- return 'string';
- } else {
- return $complexType;
+ if ($type === 'string') {
+ return 'string';
+ } elseif ($type === 'float') {
+ return 'float';
+ } elseif ($type === 'keyword') {
+ return 'string';
+ } else {
+ return $complexType;
+ }
}
- }
- ));
+ )
+ );
$this->assertEquals(
$expected,
@@ -243,13 +253,19 @@ public function attributeProvider()
true,
'text',
false,
+ true,
'category_ids',
'category_ids_value',
'',
[
'category_ids' => [
'type' => 'select',
- 'index' => true
+ 'index' => true,
+ 'fields' => [
+ 'keyword' => [
+ 'type' => 'string',
+ ]
+ ]
],
'category_ids_value' => [
'type' => 'string'
@@ -267,13 +283,19 @@ public function attributeProvider()
false,
null,
false,
+ true,
'attr_code',
'',
'',
[
'attr_code' => [
'type' => 'text',
- 'index' => 'no'
+ 'index' => 'no',
+ 'fields' => [
+ 'keyword' => [
+ 'type' => 'string',
+ ]
+ ]
],
'store_id' => [
'type' => 'string',
@@ -288,6 +310,7 @@ public function attributeProvider()
false,
null,
false,
+ false,
'attr_code',
'',
'',
@@ -308,6 +331,7 @@ public function attributeProvider()
false,
null,
true,
+ false,
'attr_code',
'',
'sort_attr_code',
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php
index 137a3845b1efa..916af235edbd8 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php
@@ -82,37 +82,45 @@ private function getExpectedIndexData()
$taxClassId = $attributeRepository
->get(\Magento\Customer\Api\Data\GroupInterface::TAX_CLASS_ID)
->getAttributeId();
+ $urlKeyId = $attributeRepository
+ ->get(\Magento\Catalog\Api\Data\ProductAttributeInterface::CODE_SEO_FIELD_URL_KEY)
+ ->getAttributeId();
return [
'configurable' => [
$skuId => 'configurable',
$configurableId => 'Option 2',
$nameId => 'Configurable Product | Configurable OptionOption 2',
$taxClassId => 'Taxable Goods | Taxable Goods',
- $statusId => 'Enabled | Enabled'
+ $statusId => 'Enabled | Enabled',
+ $urlKeyId => 'configurable-product | configurable-optionoption-2'
],
'index_enabled' => [
$skuId => 'index_enabled',
$nameId => 'index enabled',
$taxClassId => 'Taxable Goods',
- $statusId => 'Enabled'
+ $statusId => 'Enabled',
+ $urlKeyId => 'index-enabled'
],
'index_visible_search' => [
$skuId => 'index_visible_search',
$nameId => 'index visible search',
$taxClassId => 'Taxable Goods',
- $statusId => 'Enabled'
+ $statusId => 'Enabled',
+ $urlKeyId => 'index-visible-search'
],
'index_visible_category' => [
$skuId => 'index_visible_category',
$nameId => 'index visible category',
$taxClassId => 'Taxable Goods',
- $statusId => 'Enabled'
+ $statusId => 'Enabled',
+ $urlKeyId => 'index-visible-category'
],
'index_visible_both' => [
$skuId => 'index_visible_both',
$nameId => 'index visible both',
$taxClassId => 'Taxable Goods',
- $statusId => 'Enabled'
+ $statusId => 'Enabled',
+ $urlKeyId => 'index-visible-both'
]
];
}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/indexer_fulltext.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/indexer_fulltext.php
index 2ed7c1a45360d..0e5987f8326a5 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/indexer_fulltext.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/indexer_fulltext.php
@@ -14,6 +14,7 @@
->setWebsiteIds([1])
->setName('Simple Product Apple')
->setSku('fulltext-1')
+ ->setUrlKey('fulltext-1')
->setPrice(10)
->setMetaTitle('first meta title')
->setMetaKeyword('first meta keyword')
@@ -30,6 +31,7 @@
->setWebsiteIds([1])
->setName('Simple Product Banana')
->setSku('fulltext-2')
+ ->setUrlKey('fulltext-2')
->setPrice(20)
->setMetaTitle('second meta title')
->setMetaKeyword('second meta keyword')
@@ -46,6 +48,7 @@
->setWebsiteIds([1])
->setName('Simple Product Orange')
->setSku('fulltext-3')
+ ->setUrlKey('fulltext-3')
->setPrice(20)
->setMetaTitle('third meta title')
->setMetaKeyword('third meta keyword')
@@ -62,6 +65,7 @@
->setWebsiteIds([1])
->setName('Simple Product Papaya')
->setSku('fulltext-4')
+ ->setUrlKey('fulltext-4')
->setPrice(20)
->setMetaTitle('fourth meta title')
->setMetaKeyword('fourth meta keyword')
@@ -78,6 +82,7 @@
->setWebsiteIds([1])
->setName('Simple Product Cherry')
->setSku('fulltext-5')
+ ->setUrlKey('fulltext-5')
->setPrice(20)
->setMetaTitle('fifth meta title')
->setMetaKeyword('fifth meta keyword')
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php
index d0d746812ec44..b47cea971811e 100644
--- a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php
@@ -167,7 +167,7 @@ public function testDispatchGetWithParameterizedVariables() : void
/** @var ProductInterface $product */
$product = $productRepository->get('simple1');
$query = <<adapter->expects($this->once())
->method('dropTemporaryTable');
- $tableInteractionCount += 1;
+ $tableInteractionCount++;
}
$table->expects($this->at($tableInteractionCount))
->method('addColumn')
@@ -187,14 +187,14 @@ private function createTemporaryTable($persistentConnection = true)
['unsigned' => true, 'nullable' => false, 'primary' => true],
'Entity ID'
);
- $tableInteractionCount += 1;
+ $tableInteractionCount++;
$table->expects($this->at($tableInteractionCount))
->method('addColumn')
->with(
'score',
Table::TYPE_DECIMAL,
[32, 16],
- ['unsigned' => true, 'nullable' => false],
+ ['unsigned' => true, 'nullable' => true],
'Score'
);
$table->expects($this->once())
From 5830d5d9cd06e2c8de361b5b11e2a2c6dee7a88f Mon Sep 17 00:00:00 2001
From: William Johnston
Date: Mon, 9 Sep 2019 13:24:57 -0500
Subject: [PATCH 315/593] Refactor subscriber isSubscribed method to return the
output of the boolean expression
---
app/code/Magento/Newsletter/Model/Subscriber.php | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php
index 85d512afaf262..8aeb8684343ab 100644
--- a/app/code/Magento/Newsletter/Model/Subscriber.php
+++ b/app/code/Magento/Newsletter/Model/Subscriber.php
@@ -353,11 +353,7 @@ public function isStatusChanged()
*/
public function isSubscribed()
{
- if ($this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED) {
- return true;
- }
-
- return false;
+ return $this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED;
}
/**
From e3ecfb8e29ed74421075c3099d95ac286a091e02 Mon Sep 17 00:00:00 2001
From: Raoul Rego
Date: Mon, 9 Sep 2019 13:30:34 -0500
Subject: [PATCH 316/593] MC-17627: Dependency static test does not analyze
content of phtml files
- Fixed codestyle
---
.../Magento/TestFramework/Dependency/PhpRule.php | 6 +++---
.../testsuite/Magento/Test/Integrity/DependencyTest.php | 8 +++-----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
index 33f213454aaa4..913cc9448b978 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
@@ -290,7 +290,7 @@ private function isPluginDependency($dependent, $dependency)
protected function _caseGetUrl(string $currentModule, string &$contents): array
{
$pattern = '#(\->|:)(?getUrl\(([\'"])(?[a-z0-9\-_]{3,}|\*)'
- .'(\/(?[a-z0-9\-_]+|\*))?(\/(?[a-z0-9\-_]+|\*))?\3)#i';
+ .'(/(?[a-z0-9\-_]+|\*))?(/(?[a-z0-9\-_]+|\*))?\3)#i';
$dependencies = [];
if (!preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER)) {
@@ -304,11 +304,11 @@ protected function _caseGetUrl(string $currentModule, string &$contents): array
$actionName = $item['action_name'] ?? UrlInterface::DEFAULT_ACTION_NAME;
// skip rest
- if ($routeId === "rest") { //MC-17627
+ if ($routeId === "rest") { //MC-19890
continue;
}
// skip wildcards
- if ($routeId === "*" || $controllerName === "*" || $actionName === "*") { //MC-17627
+ if ($routeId === "*" || $controllerName === "*" || $actionName === "*") { //MC-19890
continue;
}
$modules = $this->routeMapper->getDependencyByRoutePath(
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
index 144386df55e3a..a644f8894d08f 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
@@ -265,14 +265,12 @@ private static function getRoutesWhitelist(): array
{
if (is_null(self::$routesWhitelist)) {
$routesWhitelistFilePattern = realpath(__DIR__) . '/_files/dependency_test/whitelist/routes_*.php';
- self::$routesWhitelist = [];
+ $routesWhitelist = [];
foreach (glob($routesWhitelistFilePattern) as $fileName) {
//phpcs:ignore Magento2.Performance.ForeachArrayMerge
- self::$routesWhitelist = array_merge(
- self::$routesWhitelist,
- include $fileName
- );
+ $routesWhitelist = array_merge($routesWhitelist, include $fileName);
}
+ self::$routesWhitelist = $routesWhitelist;
}
return self::$routesWhitelist;
}
From 7c3106a5c8f47d64fb5d18cbc7365e1fc93cf29b Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Mon, 9 Sep 2019 13:50:16 -0500
Subject: [PATCH 317/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
- Sort by position if only filtering
---
.../Product/SearchCriteriaBuilder.php | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
index 810e9172d0973..0e92bbbab4259 100644
--- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
+++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php
@@ -7,6 +7,7 @@
namespace Magento\CatalogGraphQl\DataProvider\Product;
+use Magento\Catalog\Api\Data\EavAttributeInterface;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\Search\FilterGroupBuilder;
use Magento\Framework\Api\Search\SearchCriteriaInterface;
@@ -84,6 +85,7 @@ public function __construct(
public function build(array $args, bool $includeAggregation): SearchCriteriaInterface
{
$searchCriteria = $this->builder->build('products', $args);
+ $isSearch = !empty($args['search']);
$this->updateRangeFilters($searchCriteria);
if ($includeAggregation) {
@@ -94,13 +96,15 @@ public function build(array $args, bool $includeAggregation): SearchCriteriaInte
}
$searchCriteria->setRequestName($requestName);
- if (!empty($args['search'])) {
+ if ($isSearch) {
$this->addFilter($searchCriteria, 'search_term', $args['search']);
}
+
if (!$searchCriteria->getSortOrders()) {
- $this->addDefaultSortOrder($searchCriteria);
+ $this->addDefaultSortOrder($searchCriteria, $isSearch);
}
- $this->addVisibilityFilter($searchCriteria, !empty($args['search']), !empty($args['filter']));
+
+ $this->addVisibilityFilter($searchCriteria, $isSearch, !empty($args['filter']));
$searchCriteria->setCurrentPage($args['currentPage']);
$searchCriteria->setPageSize($args['pageSize']);
@@ -168,12 +172,15 @@ private function addFilter(SearchCriteriaInterface $searchCriteria, string $fiel
* Sort by relevance DESC by default
*
* @param SearchCriteriaInterface $searchCriteria
+ * @param bool $isSearch
*/
- private function addDefaultSortOrder(SearchCriteriaInterface $searchCriteria): void
+ private function addDefaultSortOrder(SearchCriteriaInterface $searchCriteria, $isSearch = false): void
{
+ $sortField = $isSearch ? 'relevance' : EavAttributeInterface::POSITION;
+ $sortDirection = $isSearch ? SortOrder::SORT_DESC : SortOrder::SORT_ASC;
$defaultSortOrder = $this->sortOrderBuilder
- ->setField('relevance')
- ->setDirection(SortOrder::SORT_DESC)
+ ->setField($sortField)
+ ->setDirection($sortDirection)
->create();
$searchCriteria->setSortOrders([$defaultSortOrder]);
From 5962be16b90d0dc32439898293d666e934709e75 Mon Sep 17 00:00:00 2001
From: Lena Orobei
Date: Mon, 9 Sep 2019 14:13:17 -0500
Subject: [PATCH 318/593] magento/graphql-ce#777: Upgrade graphql-php library
to the latest version
---
composer.json | 2 +-
composer.lock | 34 +++++++++++++++++-----------------
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/composer.json b/composer.json
index 6b261b92ce309..4a179f480c9b0 100644
--- a/composer.json
+++ b/composer.json
@@ -52,7 +52,7 @@
"symfony/process": "~4.1.0|~4.2.0|~4.3.0",
"tedivm/jshrink": "~1.3.0",
"tubalmartin/cssmin": "4.1.1",
- "webonyx/graphql-php": "^0.13.6",
+ "webonyx/graphql-php": "^0.13.8",
"zendframework/zend-captcha": "^2.7.1",
"zendframework/zend-code": "~3.3.0",
"zendframework/zend-config": "^2.6.0",
diff --git a/composer.lock b/composer.lock
index 840ffd704c5d2..cb4f029182219 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "485287ce3a33ce9873eb328c8b42f3f1",
+ "content-hash": "fe4a8dce06cfede9180e774e43149550",
"packages": [
{
"name": "braintree/braintree_php",
@@ -1552,28 +1552,28 @@
"authors": [
{
"name": "Jim Wigginton",
- "email": "terrafrost@php.net",
- "role": "Lead Developer"
+ "role": "Lead Developer",
+ "email": "terrafrost@php.net"
},
{
"name": "Patrick Monnerat",
- "email": "pm@datasphere.ch",
- "role": "Developer"
+ "role": "Developer",
+ "email": "pm@datasphere.ch"
},
{
"name": "Andreas Fischer",
- "email": "bantu@phpbb.com",
- "role": "Developer"
+ "role": "Developer",
+ "email": "bantu@phpbb.com"
},
{
"name": "Hans-Jürgen Petrich",
- "email": "petrich@tronic-media.com",
- "role": "Developer"
+ "role": "Developer",
+ "email": "petrich@tronic-media.com"
},
{
"name": "Graham Campbell",
- "email": "graham@alt-three.com",
- "role": "Developer"
+ "role": "Developer",
+ "email": "graham@alt-three.com"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
@@ -2402,7 +2402,7 @@
},
{
"name": "Gert de Pagter",
- "email": "backendtea@gmail.com"
+ "email": "BackEndTea@gmail.com"
}
],
"description": "Symfony polyfill for ctype functions",
@@ -2670,16 +2670,16 @@
},
{
"name": "webonyx/graphql-php",
- "version": "v0.13.6",
+ "version": "v0.13.8",
"source": {
"type": "git",
"url": "https://github.com/webonyx/graphql-php.git",
- "reference": "123af49e46d26b0cd2e7a71a387253aa01ea9a6b"
+ "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/123af49e46d26b0cd2e7a71a387253aa01ea9a6b",
- "reference": "123af49e46d26b0cd2e7a71a387253aa01ea9a6b",
+ "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6829ae58f4c59121df1f86915fb9917a2ec595e8",
+ "reference": "6829ae58f4c59121df1f86915fb9917a2ec595e8",
"shasum": ""
},
"require": {
@@ -2718,7 +2718,7 @@
"api",
"graphql"
],
- "time": "2019-08-07T08:16:55+00:00"
+ "time": "2019-08-25T10:32:47+00:00"
},
{
"name": "wikimedia/less.php",
From a2f41c7455588d631f20e1cb7ffec879312abfd1 Mon Sep 17 00:00:00 2001
From: Oleksandr Dubovyk
Date: Mon, 9 Sep 2019 14:44:12 -0500
Subject: [PATCH 319/593] MC-17948: Newsletter template preview show small
section with scroll when image added
---
.../adminhtml/Magento/backend/web/css/styles-old.less | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less
index 53af8933343f1..44fca79c31be5 100644
--- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less
+++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less
@@ -4060,6 +4060,16 @@
}
}
+.newsletter-template-preview {
+ height: 100%;
+ .cms-revision-preview {
+ height: 100%;
+ .preview_iframe {
+ height: calc(~'100% - 50px');
+ }
+ }
+}
+
.admin__scope-old {
.buttons-set {
margin: 0 0 15px;
From 9c413b9c746cb99044d3a57dd65ebb81255a67dd Mon Sep 17 00:00:00 2001
From: Daniel Renaud
Date: Mon, 9 Sep 2019 14:46:32 -0500
Subject: [PATCH 320/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
---
.../CatalogGraphQl/Model/Resolver/Products/Query/Search.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index a6d7f8641bd86..ef83cc6132ecc 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -110,6 +110,8 @@ public function getResult(
} else {
$maxPages = 0;
}
+ $searchCriteria->setPageSize($realPageSize);
+ $searchCriteria->setCurrentPage($realCurrentPage);
$productArray = [];
/** @var \Magento\Catalog\Model\Product $product */
From 650a4a1b70d9ca531b8a3093eeeb2cfabe0a0e0c Mon Sep 17 00:00:00 2001
From: Oleksandr Iegorov
Date: Mon, 9 Sep 2019 14:56:50 -0500
Subject: [PATCH 321/593] MC-19791: Poor performance on sales order update -
string to integer
---
.../Framework/Model/ResourceModel/Db/AbstractDb.php | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
index 3fd44db548149..62d2edcc89d19 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
@@ -793,10 +793,15 @@ protected function saveNewObject(\Magento\Framework\Model\AbstractModel $object)
*/
protected function updateObject(\Magento\Framework\Model\AbstractModel $object)
{
- //quoting numeric values as strings may decrease query performance on some environments
- $condition = is_numeric($object->getId())
- ? $this->getIdFieldName() . '=' . (int) $object->getId()
- : $this->getConnection()->quoteInto($this->getIdFieldName() . '=?', $object->getId());
+ $tableDescription = $this->getConnection()
+ ->describeTable($this->getMainTable());
+ $preparedValue = $this->getConnection()
+ ->prepareColumnValue(
+ $tableDescription[$this->getIdFieldName()],
+ $object->getId()
+ );
+ $condition = $this->getIdFieldName() . '=' . $preparedValue;
+
/**
* Not auto increment primary key support
*/
From c9ed703b2d2a650c18d4e906e02c1b7be6013ee8 Mon Sep 17 00:00:00 2001
From: Cristian Partica
Date: Mon, 9 Sep 2019 14:57:11 -0500
Subject: [PATCH 322/593] MC-18450: Allow custom attributes to be filterable
and also returned in the layered navigation the product filter section in
GraphQL
- Update performance tests to use url keys
---
setup/performance-toolkit/benchmark.jmx | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx
index 8ffec6aff0422..a4128576e8e15 100644
--- a/setup/performance-toolkit/benchmark.jmx
+++ b/setup/performance-toolkit/benchmark.jmx
@@ -39784,7 +39784,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetail($name: String, $onServer: Boolean!) {\n productDetail: products(filter: { name: { eq: $name } }) {\n items {\n sku\n name\n price {\n regularPrice {\n amount {\n currency\n value\n }\n }\n }\n description {html}\n media_gallery_entries {\n label\n position\n disabled\n file\n }\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n # Yes, Products have `meta_keyword` and\n # everything else has `meta_keywords`.\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetail"}
+ {"query":"query productDetail($url_key: String, $onServer: Boolean!) {\n productDetail: products(filter: { url_key: { eq: $url_key } }) {\n items {\n sku\n name\n price {\n regularPrice {\n amount {\n currency\n value\n }\n }\n }\n description {html}\n media_gallery_entries {\n label\n position\n disabled\n file\n }\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n # Yes, Products have `meta_keyword` and\n # everything else has `meta_keywords`.\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetail"}
=
@@ -40032,7 +40032,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ {"query":"query productDetailByName($url_key: String, $onServer: Boolean!) {\n products(filter: { url_key: { eq: $url_key } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetailByName"}
=
@@ -41674,7 +41674,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ {"query":"query productDetailByName($url_key: String, $onServer: Boolean!) {\n products(filter: { url_key: { eq: $url_key } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetailByName"}
=
@@ -42152,7 +42152,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ {"query":"query productDetailByName($url_key: String, $onServer: Boolean!) {\n products(filter: { url_key: { eq: $url_key } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetailByName"}
=
@@ -42716,7 +42716,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ {"query":"query productDetailByName($url_key: String, $onServer: Boolean!) {\n products(filter: { url_key: { eq: $url_key } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetailByName"}
=
@@ -43664,7 +43664,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ {"query":"query productDetailByName($url_key: String, $onServer: Boolean!) {\n products(filter: { url_key: { eq: $url_key } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetailByName"}
=
@@ -43762,7 +43762,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetail($name: String, $onServer: Boolean!) {\n productDetail: products(filter: { name: { eq: $name } }) {\n items {\n sku\n name\n price {\n regularPrice {\n amount {\n currency\n value\n }\n }\n }\n description {html}\n media_gallery_entries {\n label\n position\n disabled\n file\n }\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n # Yes, Products have `meta_keyword` and\n # everything else has `meta_keywords`.\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetail"}
+ {"query":"query productDetail($url_key: String, $onServer: Boolean!) {\n productDetail: products(filter: { url_key: { eq: $url_key } }) {\n items {\n sku\n name\n price {\n regularPrice {\n amount {\n currency\n value\n }\n }\n }\n description {html}\n media_gallery_entries {\n label\n position\n disabled\n file\n }\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n # Yes, Products have `meta_keyword` and\n # everything else has `meta_keywords`.\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetail"}
=
@@ -44066,7 +44066,7 @@ vars.put("product_sku", product.get("sku"));
false
- {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ {"query":"query productDetailByName($url_key: String, $onServer: Boolean!) {\n products(filter: { url_key: { eq: $url_key } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"url_key":"${product_url_key}","onServer":false},"operationName":"productDetailByName"}
=
From 19067dc082c32d7e6cd0add30d6e35dc514553a7 Mon Sep 17 00:00:00 2001
From: Prabhu Ram
Date: Mon, 9 Sep 2019 14:58:48 -0500
Subject: [PATCH 323/593] MC-19253: Update schema - Updated schema as per
https://github.com/magento/architecture/pull/259
---
app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
index ce23283f95bfb..99bbcf881bf08 100644
--- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
@@ -149,10 +149,10 @@ type CartPrices {
grand_total: Money
subtotal_including_tax: Money
subtotal_excluding_tax: Money
- discount: CartDiscount
+ discount: CartDiscount @deprecated(reason: "Use discounts instead ")
subtotal_with_discount_excluding_tax: Money
applied_taxes: [CartTaxItem]
- discounts: CartPromotions
+ discounts: [Discount]
}
type CartTaxItem {
@@ -191,7 +191,8 @@ type PlaceOrderOutput {
type Cart {
items: [CartItemInterface] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartItems")
- applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon")
+ applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon") @deprecated(reason: "Use applied_coupons instead ")
+ applied_coupons: [AppliedCoupon]
email: String @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartEmail")
shipping_addresses: [ShippingCartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses")
billing_address: BillingCartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress")
@@ -332,10 +333,6 @@ type CartItemPrices {
discount: Money
}
-type CartPromotions {
- discounts: [Discount] @doc(description: "Shows discount breakdown at Cart level")
-}
-
type SelectedCustomizableOption {
id: Int!
label: String!
From 92adff00b6062eae519b59b761416319114f4618 Mon Sep 17 00:00:00 2001
From: Raoul Rego
Date: Mon, 9 Sep 2019 15:17:27 -0500
Subject: [PATCH 324/593] MC-17627: Dependency static test does not analyze
content of phtml files
- Fixed codestyle
---
.../static/testsuite/Magento/Test/Integrity/DependencyTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
index a644f8894d08f..fa0d365061858 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
@@ -235,7 +235,7 @@ protected static function _initRules()
$dbRuleTables = [];
foreach (glob($replaceFilePattern) as $fileName) {
//phpcs:ignore Magento2.Performance.ForeachArrayMerge
- $dbRuleTables = array_merge($dbRuleTables, @include $fileName);
+ $dbRuleTables = array_merge($dbRuleTables, include $fileName);
}
self::$_rulesInstances = [
new PhpRule(
From 29f99e690035f43d49062b521058658288d42d82 Mon Sep 17 00:00:00 2001
From: Oleksandr Iegorov
Date: Mon, 9 Sep 2019 15:41:52 -0500
Subject: [PATCH 325/593] MC-19791: Poor performance on sales order update -
string to integer
---
.../Model/ResourceModel/Db/AbstractDb.php | 26 +++++++++----------
.../Unit/ResourceModel/Db/AbstractDbTest.php | 9 +++++++
2 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
index 62d2edcc89d19..5db1d999a0cfc 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
@@ -11,6 +11,7 @@
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\DB\Adapter\DuplicateException;
use Magento\Framework\Phrase;
+use Magento\Framework\DB\Adapter\AdapterInterface;
/**
* Abstract resource model
@@ -301,7 +302,7 @@ public function getTable($tableName)
* Get connection by resource name
*
* @param string $resourceName
- * @return \Magento\Framework\DB\Adapter\AdapterInterface|false
+ * @return AdapterInterface|false
*/
protected function _getConnection($resourceName)
{
@@ -320,7 +321,7 @@ protected function _getConnection($resourceName)
/**
* Get connection
*
- * @return \Magento\Framework\DB\Adapter\AdapterInterface|false
+ * @return AdapterInterface|false
*/
public function getConnection()
{
@@ -793,13 +794,10 @@ protected function saveNewObject(\Magento\Framework\Model\AbstractModel $object)
*/
protected function updateObject(\Magento\Framework\Model\AbstractModel $object)
{
- $tableDescription = $this->getConnection()
- ->describeTable($this->getMainTable());
- $preparedValue = $this->getConnection()
- ->prepareColumnValue(
- $tableDescription[$this->getIdFieldName()],
- $object->getId()
- );
+ /** @var AdapterInterface $connection */
+ $connection = $this->getConnection();
+ $tableDescription = $connection->describeTable($this->getMainTable());
+ $preparedValue = $connection->prepareColumnValue($tableDescription[$this->getIdFieldName()], $object->getId());
$condition = $this->getIdFieldName() . '=' . $preparedValue;
/**
@@ -808,22 +806,22 @@ protected function updateObject(\Magento\Framework\Model\AbstractModel $object)
if ($this->_isPkAutoIncrement) {
$data = $this->prepareDataForUpdate($object);
if (!empty($data)) {
- $this->getConnection()->update($this->getMainTable(), $data, $condition);
+ $connection->update($this->getMainTable(), $data, $condition);
}
} else {
- $select = $this->getConnection()->select()->from(
+ $select = $connection->select()->from(
$this->getMainTable(),
[$this->getIdFieldName()]
)->where(
$condition
);
- if ($this->getConnection()->fetchOne($select) !== false) {
+ if ($connection->fetchOne($select) !== false) {
$data = $this->prepareDataForUpdate($object);
if (!empty($data)) {
- $this->getConnection()->update($this->getMainTable(), $data, $condition);
+ $connection->update($this->getMainTable(), $data, $condition);
}
} else {
- $this->getConnection()->insert(
+ $connection->insert(
$this->getMainTable(),
$this->_prepareDataForSave($object)
);
diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
index 4236c4f076dcb..7fb5ec4b506e2 100644
--- a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
+++ b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
@@ -425,6 +425,7 @@ public function testPrepareDataForUpdate()
$connectionMock = $this->getMockBuilder(AdapterInterface::class)
->setMethods(['save'])
->getMockForAbstractClass();
+
$context = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
\Magento\Framework\Model\Context::class
);
@@ -451,6 +452,7 @@ public function testPrepareDataForUpdate()
$this->_resourcesMock->expects($this->any())->method('getTableName')->with($data)->will(
$this->returnValue('tableName')
);
+
$mainTableReflection = new \ReflectionProperty(
AbstractDb::class,
'_mainTable'
@@ -465,6 +467,13 @@ public function testPrepareDataForUpdate()
$idFieldNameReflection->setValue($this->_model, 'idFieldName');
$connectionMock->expects($this->any())->method('save')->with('tableName', 'idFieldName');
$connectionMock->expects($this->any())->method('quoteInto')->will($this->returnValue('idFieldName'));
+ $connectionMock->expects($this->any())
+ ->method('describeTable')
+ ->with('tableName')
+ ->willReturn(['idFieldName' => []]);
+ $connectionMock->expects($this->any())
+ ->method('prepareColumnValue')
+ ->willReturn(0);
$abstractModelMock->setIdFieldName('id');
$abstractModelMock->setData(
[
From 0d49eeb8e8f85dca5b0ebfb8e884b988f8fcbd9f Mon Sep 17 00:00:00 2001
From: Viktor Tymchynskyi
Date: Mon, 9 Sep 2019 15:43:15 -0500
Subject: [PATCH 326/593] MC-19917: 2 Place order buttons appear on the PayPal
Review page with PayPal Express only
---
.../Paypal/view/frontend/templates/express/review.phtml | 4 ----
app/code/Magento/Paypal/view/frontend/web/js/order-review.js | 4 +---
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml b/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml
index 7a94ac56232bc..8e222ca7eb04d 100644
--- a/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml
+++ b/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml
@@ -136,10 +136,6 @@
value="= $block->escapeHtml(__('Place Order')) ?>">
= $block->escapeHtml(__('Place Order')) ?>
-
- = $block->escapeHtml(__('Place Order')) ?>
-
diff --git a/app/code/Magento/Paypal/view/frontend/web/js/order-review.js b/app/code/Magento/Paypal/view/frontend/web/js/order-review.js
index 1deee1bd76593..e3db1010693ee 100644
--- a/app/code/Magento/Paypal/view/frontend/web/js/order-review.js
+++ b/app/code/Magento/Paypal/view/frontend/web/js/order-review.js
@@ -26,7 +26,6 @@ define([
agreementSelector: 'div.checkout-agreements input',
isAjax: false,
updateShippingMethodSubmitSelector: '#update-shipping-method-submit',
- reviewSubmitSelector: '#review-submit',
shippingMethodUpdateUrl: null,
updateOrderSubmitUrl: null,
canEditShippingMethod: false
@@ -57,8 +56,7 @@ define([
this.options.updateContainerSelector
)
).find(this.options.updateOrderSelector).on('click', $.proxy(this._updateOrderHandler, this)).end()
- .find(this.options.updateShippingMethodSubmitSelector).hide().end()
- .find(this.options.reviewSubmitSelector).hide();
+ .find(this.options.updateShippingMethodSubmitSelector).hide().end();
this._shippingTobilling();
if ($(this.options.shippingSubmitFormSelector).length && this.options.canEditShippingMethod) {
From aefa7d98fe89b92e389d5ba8a266d2d5807179ef Mon Sep 17 00:00:00 2001
From: Oleksandr Iegorov
Date: Mon, 9 Sep 2019 15:57:11 -0500
Subject: [PATCH 327/593] MC-19791: Poor performance on sales order update -
string to integer
---
.../Model/ResourceModel/Db/AbstractDb.php | 22 ++++++++++++++++++-
.../Unit/ResourceModel/Db/AbstractDbTest.php | 2 +-
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
index 5db1d999a0cfc..5d9f05c3eb8c9 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
@@ -785,6 +785,24 @@ protected function saveNewObject(\Magento\Framework\Model\AbstractModel $object)
}
}
+ /**
+ * Check in column value should be quoted
+ *
+ * Based on column description
+ *
+ * @param array $columnDescription
+ * @return bool
+ */
+ private function isNeedToQuoteValue(array $columnDescription): bool
+ {
+ $result = true;
+ if (!empty($columnDescription['DATA_TYPE'])
+ && in_array($columnDescription['DATA_TYPE'], ['smallint', 'int'])) {
+ $result = false;
+ }
+ return $result;
+ }
+
/**
* Update existing object
*
@@ -798,7 +816,9 @@ protected function updateObject(\Magento\Framework\Model\AbstractModel $object)
$connection = $this->getConnection();
$tableDescription = $connection->describeTable($this->getMainTable());
$preparedValue = $connection->prepareColumnValue($tableDescription[$this->getIdFieldName()], $object->getId());
- $condition = $this->getIdFieldName() . '=' . $preparedValue;
+ $condition = (!$this->isNeedToQuoteValue($tableDescription[$this->getIdFieldName()]))
+ ? $this->getIdFieldName() . '=' . $preparedValue
+ : $connection->quoteInto($this->getIdFieldName() . '=?', $preparedValue);
/**
* Not auto increment primary key support
diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
index 7fb5ec4b506e2..2a87cd774332a 100644
--- a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
+++ b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
@@ -496,7 +496,7 @@ public function testPrepareDataForUpdate()
->with(
'tableName',
$newData,
- 'idFieldName=0'
+ 'idFieldName'
);
$select = $this->getMockBuilder(\Magento\Framework\DB\Select::class)
->disableOriginalConstructor()
From e3bedcf838dac3a965b11c75d5f6e1688e1426be Mon Sep 17 00:00:00 2001
From: Oleksandr Dubovyk
Date: Mon, 9 Sep 2019 16:12:23 -0500
Subject: [PATCH 328/593] MC-17948: Newsletter template preview show small
section with scroll when image added
---
.../view/adminhtml/templates/preview/iframeswitcher.phtml | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml b/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml
index 5175080add914..20ff63a60a263 100644
--- a/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml
+++ b/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml
@@ -17,6 +17,7 @@