Skip to content

Commit ac88ad9

Browse files
authored
Merge pull request #7131 from magento-l3/L3_PR_21-10-10
L3_PR_21-10-10
2 parents 8cdefb0 + 941f8de commit ac88ad9

File tree

17 files changed

+428
-151
lines changed

17 files changed

+428
-151
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogGraphQl\Model;
9+
10+
use Magento\Framework\Exception\NoSuchEntityException;
11+
use Magento\Framework\Stdlib\StringUtils as StdlibString;
12+
use Magento\GraphQl\Model\Query\ContextInterface;
13+
use Magento\Search\Model\Query;
14+
use Magento\Search\Model\QueryFactory;
15+
16+
/**
17+
* Prepares search query based on search text.
18+
*/
19+
class QueryProcessor
20+
{
21+
/**
22+
* @var QueryFactory
23+
*/
24+
private $queryFactory;
25+
26+
/**
27+
* @var StdlibString
28+
*/
29+
private $string;
30+
31+
/**
32+
* @param QueryFactory $queryFactory
33+
* @param StdlibString $string
34+
*/
35+
public function __construct(
36+
QueryFactory $queryFactory,
37+
StdlibString $string
38+
) {
39+
$this->queryFactory = $queryFactory;
40+
$this->string = $string;
41+
}
42+
43+
/**
44+
* Prepare Query object based on search text
45+
*
46+
* @param ContextInterface $context
47+
* @param string $queryText
48+
* @throws NoSuchEntityException
49+
* @return Query
50+
*/
51+
public function prepare(ContextInterface $context, string $queryText) : Query
52+
{
53+
$query = $this->queryFactory->create();
54+
$maxQueryLength = (int) $query->getMaxQueryLength();
55+
if ($maxQueryLength && $this->string->strlen($queryText) > $maxQueryLength) {
56+
$queryText = $this->string->substr($queryText, 0, $maxQueryLength);
57+
}
58+
$query->setQueryText($queryText);
59+
$store = $context->getExtensionAttributes()->getStore();
60+
$query->setStoreId($store->getId());
61+
return $query;
62+
}
63+
}

app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public function resolve(
7070
$data = [
7171
'total_count' => $searchResult->getTotalCount(),
7272
'items' => $searchResult->getProductsSearchResult(),
73+
'suggestions' => $searchResult->getSuggestions(),
7374
'page_info' => [
7475
'page_size' => $searchResult->getPageSize(),
7576
'current_page' => $searchResult->getCurrentPage(),

app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ class Search implements ProductQueryInterface
6363
*/
6464
private $searchCriteriaBuilder;
6565

66+
/**
67+
* @var Suggestions
68+
*/
69+
private $suggestions;
70+
6671
/**
6772
* @var QueryPopularity
6873
*/
@@ -76,6 +81,7 @@ class Search implements ProductQueryInterface
7681
* @param ProductSearch $productsProvider
7782
* @param SearchCriteriaBuilder $searchCriteriaBuilder
7883
* @param ArgumentsProcessorInterface|null $argsSelection
84+
* @param Suggestions|null $suggestions
7985
* @param QueryPopularity|null $queryPopularity
8086
*/
8187
public function __construct(
@@ -86,6 +92,7 @@ public function __construct(
8692
ProductSearch $productsProvider,
8793
SearchCriteriaBuilder $searchCriteriaBuilder,
8894
ArgumentsProcessorInterface $argsSelection = null,
95+
Suggestions $suggestions = null,
8996
QueryPopularity $queryPopularity = null
9097
) {
9198
$this->search = $search;
@@ -94,7 +101,10 @@ public function __construct(
94101
$this->fieldSelection = $fieldSelection;
95102
$this->productsProvider = $productsProvider;
96103
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
97-
$this->argsSelection = $argsSelection ?: ObjectManager::getInstance()->get(ArgumentsProcessorInterface::class);
104+
$this->argsSelection = $argsSelection ?: ObjectManager::getInstance()
105+
->get(ArgumentsProcessorInterface::class);
106+
$this->suggestions = $suggestions ?: ObjectManager::getInstance()
107+
->get(Suggestions::class);
98108
$this->queryPopularity = $queryPopularity ?: ObjectManager::getInstance()->get(QueryPopularity::class);
99109
}
100110

@@ -146,14 +156,21 @@ public function getResult(
146156
$productArray[$product->getId()]['model'] = $product;
147157
}
148158

159+
$suggestions = [];
160+
$totalCount = (int) $searchResults->getTotalCount();
161+
if ($totalCount === 0 && !empty($args['search'])) {
162+
$suggestions = $this->suggestions->execute($context, $args['search']);
163+
}
164+
149165
return $this->searchResultFactory->create(
150166
[
151-
'totalCount' => $searchResults->getTotalCount(),
167+
'totalCount' => $totalCount,
152168
'productsSearchResult' => $productArray,
153169
'searchAggregation' => $itemsResults->getAggregations(),
154170
'pageSize' => $realPageSize,
155171
'currentPage' => $realCurrentPage,
156172
'totalPages' => $totalPages,
173+
'suggestions' => $suggestions,
157174
]
158175
);
159176
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query;
9+
10+
use Magento\AdvancedSearch\Model\SuggestedQueries;
11+
use Magento\GraphQl\Model\Query\ContextInterface;
12+
use Magento\CatalogGraphQl\Model\QueryProcessor;
13+
14+
/**
15+
* Search suggestions implementations for GraphQL
16+
*/
17+
class Suggestions
18+
{
19+
/**
20+
* @var QueryProcessor
21+
*/
22+
private $queryProcessor;
23+
24+
/**
25+
* @var SuggestedQueries
26+
*/
27+
private $suggestedQueries;
28+
29+
/**
30+
* @param QueryProcessor $queryProcessor
31+
* @param SuggestedQueries $suggestedQueries
32+
*/
33+
public function __construct(
34+
QueryProcessor $queryProcessor,
35+
SuggestedQueries $suggestedQueries
36+
) {
37+
$this->queryProcessor = $queryProcessor;
38+
$this->suggestedQueries = $suggestedQueries;
39+
}
40+
41+
/**
42+
* Return search suggestions for the provided query text
43+
*
44+
* @param ContextInterface $context
45+
* @param string $queryText
46+
* @return array
47+
*/
48+
public function execute(ContextInterface $context, string $queryText) : array
49+
{
50+
$result = [];
51+
$query = $this->queryProcessor->prepare($context, $queryText);
52+
$suggestionItems = $this->suggestedQueries->getItems($query);
53+
foreach ($suggestionItems as $suggestion) {
54+
$result[] = ['search' => $suggestion->getQueryText()];
55+
}
56+
return $result;
57+
}
58+
}

app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchResult.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
*/
1515
class SearchResult
1616
{
17+
/**
18+
* @var array
19+
*/
1720
private $data;
1821

1922
/**
@@ -83,4 +86,14 @@ public function getTotalPages(): int
8386
{
8487
return $this->data['totalPages'] ?? 0;
8588
}
89+
90+
/**
91+
* Retrieve an array in the format of GraphQL-readable type containing search suggestions.
92+
*
93+
* @return array
94+
*/
95+
public function getSuggestions() : array
96+
{
97+
return $this->data['suggestions'] ?? [];
98+
}
8699
}

app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Products/Query/SearchTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search;
1414
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search\QueryPopularity;
1515
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory;
16+
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Suggestions;
1617
use Magento\Framework\Api\Search\SearchCriteriaInterface;
1718
use Magento\Framework\Api\Search\SearchResultInterface;
1819
use Magento\Framework\GraphQl\Query\Resolver\ArgumentsProcessorInterface;
@@ -21,7 +22,6 @@
2122
use Magento\GraphQl\Model\Query\ContextInterface;
2223
use Magento\Search\Api\SearchInterface;
2324
use Magento\Search\Model\Search\PageSizeProvider;
24-
use Magento\Store\Api\Data\StoreInterface;
2525
use PHPUnit\Framework\MockObject\MockObject;
2626
use PHPUnit\Framework\TestCase;
2727

@@ -67,6 +67,11 @@ class SearchTest extends TestCase
6767
*/
6868
private $searchCriteriaBuilder;
6969

70+
/**
71+
* @var Suggestions|MockObject
72+
*/
73+
private $suggestions;
74+
7075
/**
7176
* @var QueryPopularity|MockObject
7277
*/
@@ -104,6 +109,9 @@ protected function setUp(): void
104109
$this->searchCriteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class)
105110
->disableOriginalConstructor()
106111
->getMock();
112+
$this->suggestions = $this->getMockBuilder(Suggestions::class)
113+
->disableOriginalConstructor()
114+
->getMock();
107115
$this->queryPopularity = $this->getMockBuilder(QueryPopularity::class)
108116
->disableOriginalConstructor()
109117
->getMock();
@@ -115,6 +123,7 @@ protected function setUp(): void
115123
$this->productsProvider,
116124
$this->searchCriteriaBuilder,
117125
$this->argsSelection,
126+
$this->suggestions,
118127
$this->queryPopularity
119128
);
120129
}

app/code/Magento/CatalogGraphQl/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"magento/module-eav-graph-ql": "*",
1414
"magento/module-catalog-search": "*",
1515
"magento/framework": "*",
16-
"magento/module-graph-ql": "*"
16+
"magento/module-graph-ql": "*",
17+
"magento/module-advanced-search": "*"
1718
},
1819
"suggest": {
1920
"magento/module-graph-ql-cache": "*",

app/code/Magento/CatalogGraphQl/etc/schema.graphqls

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ type Products @doc(description: "The Products object is the top-level object ret
322322
filters: [LayerFilter] @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\LayerFilters") @doc(description: "Layered navigation filters array.") @deprecated(reason: "Use aggregations instead")
323323
aggregations: [Aggregation] @doc(description: "Layered navigation aggregations.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Aggregations")
324324
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")
325+
suggestions: [SearchSuggestion] @doc(description: "An array of search suggestions for case when search query have no results.")
325326
}
326327

327328
type CategoryProducts @doc(description: "The category products object returned in the Category query.") {
@@ -477,6 +478,10 @@ type Aggregation @doc(description: "A bucket that contains information for each
477478
position: Int @doc(description: "The relative position of the attribute in a layered navigation block")
478479
}
479480

481+
type SearchSuggestion @doc(description: "A string that contains search suggestion") {
482+
search: String! @doc(description: "The search suggestion of existing product.")
483+
}
484+
480485
interface AggregationOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\AggregationOptionTypeResolverComposite") {
481486
count: Int @doc(description: "The number of items that match the aggregation option.")
482487
label: String @doc(description: "Aggregation option display label.")

app/code/Magento/CatalogImportExport/Model/Import/Product.php

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,6 @@ class Product extends AbstractEntity
171171
const URL_KEY = 'url_key';
172172

173173
/**
174-
* Attribute cache
175-
*
176174
* @var array
177175
*/
178176
protected $_attributeCache = [];
@@ -238,8 +236,6 @@ class Product extends AbstractEntity
238236
];
239237

240238
/**
241-
* Attributes codes which shows as date
242-
*
243239
* @var array
244240
* @since 100.1.2
245241
*/
@@ -253,8 +249,6 @@ class Product extends AbstractEntity
253249
];
254250

255251
/**
256-
* Need to log in import history
257-
*
258252
* @var bool
259253
*/
260254
protected $logInHistory = true;
@@ -267,8 +261,6 @@ class Product extends AbstractEntity
267261
protected $_mediaGalleryAttributeId = null;
268262

269263
/**
270-
* Validation failure message template definitions
271-
*
272264
* @var array
273265
* @codingStandardsIgnoreStart
274266
*/
@@ -650,7 +642,7 @@ class Product extends AbstractEntity
650642
private $_logger;
651643

652644
/**
653-
* {@inheritdoc}
645+
* @var string
654646
*/
655647
protected $masterAttributeCode = 'sku';
656648

@@ -691,15 +683,11 @@ class Product extends AbstractEntity
691683
protected $rowNumbers = [];
692684

693685
/**
694-
* Product entity link field
695-
*
696686
* @var string
697687
*/
698688
private $productEntityLinkField;
699689

700690
/**
701-
* Product entity identifier field
702-
*
703691
* @var string
704692
*/
705693
private $productEntityIdentifierField;
@@ -726,8 +714,6 @@ class Product extends AbstractEntity
726714
private $catalogConfig;
727715

728716
/**
729-
* Stock Item Importer
730-
*
731717
* @var StockItemImporterInterface
732718
*/
733719
private $stockItemImporter;
@@ -3161,10 +3147,6 @@ private function formatStockDataForRow(array $rowData): array
31613147
$stockItemDo = $this->stockRegistry->getStockItem($row['product_id'], $row['website_id']);
31623148
$existStockData = $stockItemDo->getData();
31633149

3164-
if (isset($rowData['qty']) && $rowData['qty'] == 0 && !isset($rowData['is_in_stock'])) {
3165-
$rowData['is_in_stock'] = 0;
3166-
}
3167-
31683150
$row = array_merge(
31693151
$this->defaultStockData,
31703152
array_intersect_key($existStockData, $this->defaultStockData),
@@ -3173,11 +3155,10 @@ private function formatStockDataForRow(array $rowData): array
31733155
);
31743156

31753157
if ($this->stockConfiguration->isQty($this->skuProcessor->getNewSku($sku)['type_id'])) {
3176-
if (isset($rowData['qty']) && $rowData['qty'] == 0) {
3177-
$row['is_in_stock'] = 0;
3178-
}
31793158
$stockItemDo->setData($row);
3180-
$row['is_in_stock'] = $row['is_in_stock'] ?? $this->stockStateProvider->verifyStock($stockItemDo);
3159+
$row['is_in_stock'] = $this->stockStateProvider->verifyStock($stockItemDo)
3160+
? (int) $row['is_in_stock']
3161+
: 0;
31813162
if ($this->stockStateProvider->verifyNotification($stockItemDo)) {
31823163
$date = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC'));
31833164
$row['low_stock_date'] = $date->format(DateTime::DATETIME_PHP_FORMAT);

0 commit comments

Comments
 (0)