Skip to content

Commit

Permalink
Merge remote-tracking branch 'fredden/laminas-mail' into 2.4-develop-prs
Browse files Browse the repository at this point in the history
  • Loading branch information
Stanislav Idolov committed Oct 22, 2021
2 parents b36d46a + 9bb302d commit fa4685e
Show file tree
Hide file tree
Showing 32 changed files with 584 additions and 191 deletions.
1 change: 1 addition & 0 deletions app/code/Magento/Catalog/Pricing/Render/FinalPriceBox.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ public function getCacheKeyInfo()
$cacheKeys['display_minimal_price'] = $this->getDisplayMinimalPrice();
$cacheKeys['is_product_list'] = $this->isProductList();
$cacheKeys['customer_group_id'] = $this->getSaleableItem()->getCustomerGroupId();
$cacheKeys['zone'] = $this->getZone();
return $cacheKeys;
}

Expand Down
63 changes: 63 additions & 0 deletions app/code/Magento/CatalogGraphQl/Model/QueryProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model;

use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Stdlib\StringUtils as StdlibString;
use Magento\GraphQl\Model\Query\ContextInterface;
use Magento\Search\Model\Query;
use Magento\Search\Model\QueryFactory;

/**
* Prepares search query based on search text.
*/
class QueryProcessor
{
/**
* @var QueryFactory
*/
private $queryFactory;

/**
* @var StdlibString
*/
private $string;

/**
* @param QueryFactory $queryFactory
* @param StdlibString $string
*/
public function __construct(
QueryFactory $queryFactory,
StdlibString $string
) {
$this->queryFactory = $queryFactory;
$this->string = $string;
}

/**
* Prepare Query object based on search text
*
* @param ContextInterface $context
* @param string $queryText
* @throws NoSuchEntityException
* @return Query
*/
public function prepare(ContextInterface $context, string $queryText) : Query
{
$query = $this->queryFactory->create();
$maxQueryLength = (int) $query->getMaxQueryLength();
if ($maxQueryLength && $this->string->strlen($queryText) > $maxQueryLength) {
$queryText = $this->string->substr($queryText, 0, $maxQueryLength);
}
$query->setQueryText($queryText);
$store = $context->getExtensionAttributes()->getStore();
$query->setStoreId($store->getId());
return $query;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public function resolve(
$data = [
'total_count' => $searchResult->getTotalCount(),
'items' => $searchResult->getProductsSearchResult(),
'suggestions' => $searchResult->getSuggestions(),
'page_info' => [
'page_size' => $searchResult->getPageSize(),
'current_page' => $searchResult->getCurrentPage(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ class Search implements ProductQueryInterface
*/
private $searchCriteriaBuilder;

/**
* @var Suggestions
*/
private $suggestions;

/**
* @var QueryPopularity
*/
Expand All @@ -76,6 +81,7 @@ class Search implements ProductQueryInterface
* @param ProductSearch $productsProvider
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param ArgumentsProcessorInterface|null $argsSelection
* @param Suggestions|null $suggestions
* @param QueryPopularity|null $queryPopularity
*/
public function __construct(
Expand All @@ -86,6 +92,7 @@ public function __construct(
ProductSearch $productsProvider,
SearchCriteriaBuilder $searchCriteriaBuilder,
ArgumentsProcessorInterface $argsSelection = null,
Suggestions $suggestions = null,
QueryPopularity $queryPopularity = null
) {
$this->search = $search;
Expand All @@ -94,7 +101,10 @@ public function __construct(
$this->fieldSelection = $fieldSelection;
$this->productsProvider = $productsProvider;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->argsSelection = $argsSelection ?: ObjectManager::getInstance()->get(ArgumentsProcessorInterface::class);
$this->argsSelection = $argsSelection ?: ObjectManager::getInstance()
->get(ArgumentsProcessorInterface::class);
$this->suggestions = $suggestions ?: ObjectManager::getInstance()
->get(Suggestions::class);
$this->queryPopularity = $queryPopularity ?: ObjectManager::getInstance()->get(QueryPopularity::class);
}

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

$suggestions = [];
$totalCount = (int) $searchResults->getTotalCount();
if ($totalCount === 0 && !empty($args['search'])) {
$suggestions = $this->suggestions->execute($context, $args['search']);
}

return $this->searchResultFactory->create(
[
'totalCount' => $searchResults->getTotalCount(),
'totalCount' => $totalCount,
'productsSearchResult' => $productArray,
'searchAggregation' => $itemsResults->getAggregations(),
'pageSize' => $realPageSize,
'currentPage' => $realCurrentPage,
'totalPages' => $totalPages,
'suggestions' => $suggestions,
]
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query;

use Magento\AdvancedSearch\Model\SuggestedQueries;
use Magento\GraphQl\Model\Query\ContextInterface;
use Magento\CatalogGraphQl\Model\QueryProcessor;

/**
* Search suggestions implementations for GraphQL
*/
class Suggestions
{
/**
* @var QueryProcessor
*/
private $queryProcessor;

/**
* @var SuggestedQueries
*/
private $suggestedQueries;

/**
* @param QueryProcessor $queryProcessor
* @param SuggestedQueries $suggestedQueries
*/
public function __construct(
QueryProcessor $queryProcessor,
SuggestedQueries $suggestedQueries
) {
$this->queryProcessor = $queryProcessor;
$this->suggestedQueries = $suggestedQueries;
}

/**
* Return search suggestions for the provided query text
*
* @param ContextInterface $context
* @param string $queryText
* @return array
*/
public function execute(ContextInterface $context, string $queryText) : array
{
$result = [];
$query = $this->queryProcessor->prepare($context, $queryText);
$suggestionItems = $this->suggestedQueries->getItems($query);
foreach ($suggestionItems as $suggestion) {
$result[] = ['search' => $suggestion->getQueryText()];
}
return $result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
*/
class SearchResult
{
/**
* @var array
*/
private $data;

/**
Expand Down Expand Up @@ -83,4 +86,14 @@ public function getTotalPages(): int
{
return $this->data['totalPages'] ?? 0;
}

/**
* Retrieve an array in the format of GraphQL-readable type containing search suggestions.
*
* @return array
*/
public function getSuggestions() : array
{
return $this->data['suggestions'] ?? [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search;
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search\QueryPopularity;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory;
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Suggestions;
use Magento\Framework\Api\Search\SearchCriteriaInterface;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\GraphQl\Query\Resolver\ArgumentsProcessorInterface;
Expand All @@ -21,7 +22,6 @@
use Magento\GraphQl\Model\Query\ContextInterface;
use Magento\Search\Api\SearchInterface;
use Magento\Search\Model\Search\PageSizeProvider;
use Magento\Store\Api\Data\StoreInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -67,6 +67,11 @@ class SearchTest extends TestCase
*/
private $searchCriteriaBuilder;

/**
* @var Suggestions|MockObject
*/
private $suggestions;

/**
* @var QueryPopularity|MockObject
*/
Expand Down Expand Up @@ -104,6 +109,9 @@ protected function setUp(): void
$this->searchCriteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class)
->disableOriginalConstructor()
->getMock();
$this->suggestions = $this->getMockBuilder(Suggestions::class)
->disableOriginalConstructor()
->getMock();
$this->queryPopularity = $this->getMockBuilder(QueryPopularity::class)
->disableOriginalConstructor()
->getMock();
Expand All @@ -115,6 +123,7 @@ protected function setUp(): void
$this->productsProvider,
$this->searchCriteriaBuilder,
$this->argsSelection,
$this->suggestions,
$this->queryPopularity
);
}
Expand Down
3 changes: 2 additions & 1 deletion app/code/Magento/CatalogGraphQl/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"magento/module-eav-graph-ql": "*",
"magento/module-catalog-search": "*",
"magento/framework": "*",
"magento/module-graph-ql": "*"
"magento/module-graph-ql": "*",
"magento/module-advanced-search": "*"
},
"suggest": {
"magento/module-graph-ql-cache": "*",
Expand Down
5 changes: 5 additions & 0 deletions app/code/Magento/CatalogGraphQl/etc/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ type Products @doc(description: "The Products object is the top-level object ret
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")
suggestions: [SearchSuggestion] @doc(description: "An array of search suggestions for case when search query have no results.")
}

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

type SearchSuggestion @doc(description: "A string that contains search suggestion") {
search: String! @doc(description: "The search suggestion of existing product.")
}

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.")
Expand Down
Loading

0 comments on commit fa4685e

Please sign in to comment.