Skip to content

Commit e96dc9f

Browse files
authored
Merge pull request #2622 from magento-engcom/ENGCOM-1663-magento-graphql-ce-48
[architects] MAGETWO-92260: GraphQL - Fix urlResolver query to support relative path #13
2 parents 81f6b3e + 18fe48c commit e96dc9f

File tree

13 files changed

+651
-22
lines changed

13 files changed

+651
-22
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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\Category;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Framework\GraphQl\Config\Element\Field;
12+
use Magento\Framework\GraphQl\Query\Resolver\Value;
13+
use Magento\Framework\GraphQl\Query\Resolver\ValueFactory;
14+
use Magento\Framework\GraphQl\Query\ResolverInterface;
15+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
16+
use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder;
17+
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter;
18+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
19+
20+
/**
21+
* Category products resolver, used by GraphQL endpoints to retrieve products assigned to a category
22+
*/
23+
class Products implements ResolverInterface
24+
{
25+
/** @var \Magento\Catalog\Api\ProductRepositoryInterface */
26+
private $productRepository;
27+
28+
/** @var Builder */
29+
private $searchCriteriaBuilder;
30+
31+
/** @var Filter */
32+
private $filterQuery;
33+
34+
/** @var ValueFactory */
35+
private $valueFactory;
36+
37+
/**
38+
* @param ProductRepositoryInterface $productRepository
39+
* @param Builder $searchCriteriaBuilder
40+
* @param Filter $filterQuery
41+
* @param ValueFactory $valueFactory
42+
*/
43+
public function __construct(
44+
ProductRepositoryInterface $productRepository,
45+
Builder $searchCriteriaBuilder,
46+
Filter $filterQuery,
47+
ValueFactory $valueFactory
48+
) {
49+
$this->productRepository = $productRepository;
50+
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
51+
$this->filterQuery = $filterQuery;
52+
$this->valueFactory = $valueFactory;
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function resolve(
59+
Field $field,
60+
$context,
61+
ResolveInfo $info,
62+
array $value = null,
63+
array $args = null
64+
): Value {
65+
$args['filter'] = [
66+
'category_ids' => [
67+
'eq' => $value['id']
68+
]
69+
];
70+
$searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args);
71+
$searchCriteria->setCurrentPage($args['currentPage']);
72+
$searchCriteria->setPageSize($args['pageSize']);
73+
$searchResult = $this->filterQuery->getResult($searchCriteria, $info);
74+
75+
//possible division by 0
76+
if ($searchCriteria->getPageSize()) {
77+
$maxPages = ceil($searchResult->getTotalCount() / $searchCriteria->getPageSize());
78+
} else {
79+
$maxPages = 0;
80+
}
81+
82+
$currentPage = $searchCriteria->getCurrentPage();
83+
if ($searchCriteria->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) {
84+
$currentPage = new GraphQlInputException(
85+
__(
86+
'currentPage value %1 specified is greater than the number of pages available.',
87+
[$maxPages]
88+
)
89+
);
90+
}
91+
92+
$data = [
93+
'total_count' => $searchResult->getTotalCount(),
94+
'items' => $searchResult->getProductsSearchResult(),
95+
'page_info' => [
96+
'page_size' => $searchCriteria->getPageSize(),
97+
'current_page' => $currentPage
98+
]
99+
];
100+
101+
$result = function () use ($data) {
102+
return $data;
103+
};
104+
105+
return $this->valueFactory->create($result);
106+
}
107+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,11 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model
376376
updated_at: String @doc(description: "Timestamp indicating when the category was updated")
377377
product_count: Int @doc(description: "The number of products in the category")
378378
default_sort_by: String @doc(description: "The attribute to use for sorting")
379+
products(
380+
pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."),
381+
currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."),
382+
sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.")
383+
): CategoryProducts @doc(description: "The list of products assigned to the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products")
379384
}
380385

381386
type CustomizableRadioOption implements CustomizableOptionInterface @doc(description: "CustomizableRadioOption contains information about a set of radio buttons that are defined as part of a customizable option") {
@@ -406,6 +411,12 @@ type Products @doc(description: "The Products object is the top-level object ret
406411
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")
407412
}
408413

414+
type CategoryProducts @doc(description: "The category products object returned in the Category query") {
415+
items: [ProductInterface] @doc(description: "An array of products that are assigned to the category")
416+
page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query")
417+
total_count: Int @doc(description: "The number of products returned")
418+
}
419+
409420
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.") {
410421
name: FilterTypeInput @doc(description: "The product name. Customers use this name to identify the product.")
411422
sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer")
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\CmsUrlRewriteGraphQl\Model\Resolver\UrlRewrite;
9+
10+
use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface;
11+
use Magento\Store\Model\ScopeInterface;
12+
use Magento\Cms\Helper\Page;
13+
14+
/**
15+
* Home page URL locator.
16+
*/
17+
class HomePageUrlLocator implements CustomUrlLocatorInterface
18+
{
19+
/**
20+
* @var \Magento\Framework\App\Config\ScopeConfigInterface
21+
*/
22+
private $scopeConfig;
23+
24+
/**
25+
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
26+
*/
27+
public function __construct(
28+
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
29+
) {
30+
$this->scopeConfig = $scopeConfig;
31+
}
32+
33+
/**
34+
* @inheritdoc
35+
*/
36+
public function locateUrl($urlKey): ?string
37+
{
38+
if ($urlKey === '/') {
39+
$homePageUrl = $this->scopeConfig->getValue(
40+
Page::XML_PATH_HOME_PAGE,
41+
ScopeInterface::SCOPE_STORE
42+
);
43+
return $homePageUrl;
44+
}
45+
return null;
46+
}
47+
}

app/code/Magento/CmsUrlRewriteGraphQl/composer.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
"type": "magento2-module",
55
"require": {
66
"php": "~7.1.3||~7.2.0",
7-
"magento/framework": "*"
8-
7+
"magento/framework": "*",
8+
"magento/module-url-rewrite-graph-ql": "*",
9+
"magento/module-store": "*",
10+
"magento/module-cms": "*"
911
},
1012
"suggest": {
1113
"magento/module-cms-url-rewrite": "*",
12-
"magento/module-catalog-graph-ql": "*",
13-
"magento/module-url-rewrite-graph-ql": "*"
14+
"magento/module-catalog-graph-ql": "*"
1415
},
1516
"license": [
1617
"OSL-3.0",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
9+
<type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocator">
10+
<arguments>
11+
<argument name="urlLocators" xsi:type="array">
12+
<item name="homePageUrlLocator" xsi:type="object">Magento\CmsUrlRewriteGraphQl\Model\Resolver\UrlRewrite\HomePageUrlLocator</item>
13+
</argument>
14+
</arguments>
15+
</type>
16+
</config>

app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Magento\Framework\GraphQl\Query\ResolverInterface;
1515
use Magento\Store\Model\StoreManagerInterface;
1616
use Magento\UrlRewrite\Model\UrlFinderInterface;
17+
use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface;
1718

1819
/**
1920
* UrlRewrite field resolver, used for GraphQL request processing.
@@ -34,20 +35,28 @@ class UrlRewrite implements ResolverInterface
3435
* @var ValueFactory
3536
*/
3637
private $valueFactory;
38+
39+
/**
40+
* @var CustomUrlLocatorInterface
41+
*/
42+
private $customUrlLocator;
3743

3844
/**
3945
* @param UrlFinderInterface $urlFinder
4046
* @param StoreManagerInterface $storeManager
4147
* @param ValueFactory $valueFactory
48+
* @param CustomUrlLocatorInterface $customUrlLocator
4249
*/
4350
public function __construct(
4451
UrlFinderInterface $urlFinder,
4552
StoreManagerInterface $storeManager,
46-
ValueFactory $valueFactory
53+
ValueFactory $valueFactory,
54+
CustomUrlLocatorInterface $customUrlLocator
4755
) {
4856
$this->urlFinder = $urlFinder;
4957
$this->storeManager = $storeManager;
5058
$this->valueFactory = $valueFactory;
59+
$this->customUrlLocator = $customUrlLocator;
5160
}
5261

5362
/**
@@ -63,8 +72,15 @@ public function resolve(
6372
$result = function () {
6473
return null;
6574
};
75+
6676
if (isset($args['url'])) {
67-
$urlRewrite = $this->findCanonicalUrl($args['url']);
77+
$url = $args['url'];
78+
if (substr($url, 0, 1) === '/' && $url !== '/') {
79+
$url = ltrim($url, '/');
80+
}
81+
$customUrl = $this->customUrlLocator->locateUrl($url);
82+
$url = $customUrl ?: $url;
83+
$urlRewrite = $this->findCanonicalUrl($url);
6884
if ($urlRewrite) {
6985
$urlRewriteReturnArray = [
7086
'id' => $urlRewrite->getEntityId(),
@@ -96,6 +112,7 @@ private function findCanonicalUrl(string $requestPath) : ?\Magento\UrlRewrite\Se
96112
if (!$urlRewrite) {
97113
$urlRewrite = $this->findUrlFromTargetPath($requestPath);
98114
}
115+
99116
return $urlRewrite;
100117
}
101118

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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\UrlRewriteGraphQl\Model\Resolver\UrlRewrite;
9+
10+
/**
11+
* Pool of custom URL locators.
12+
*/
13+
class CustomUrlLocator implements CustomUrlLocatorInterface
14+
{
15+
/**
16+
* @var CustomUrlLocatorInterface[]
17+
*/
18+
private $urlLocators;
19+
20+
/**
21+
* @param CustomUrlLocatorInterface[] $urlLocators
22+
*/
23+
public function __construct(array $urlLocators = [])
24+
{
25+
$this->urlLocators = $urlLocators;
26+
}
27+
28+
/**
29+
* @inheritdoc
30+
*/
31+
public function locateUrl($urlKey): ?string
32+
{
33+
foreach ($this->urlLocators as $urlLocator) {
34+
$url = $urlLocator->locateUrl($urlKey);
35+
if ($url !== null) {
36+
return $url;
37+
}
38+
}
39+
return null;
40+
}
41+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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\UrlRewriteGraphQl\Model\Resolver\UrlRewrite;
9+
10+
/**
11+
* Interface for resolution of custom URLs.
12+
*
13+
* It can be used, for example, to resolve '\' URL path to a 'Home' page.
14+
*/
15+
interface CustomUrlLocatorInterface
16+
{
17+
/**
18+
* Resolve URL based on custom rules.
19+
*
20+
* @param string $urlKey
21+
* @return string|null Return null if URL cannot be resolved
22+
*/
23+
public function locateUrl($urlKey): ?string;
24+
}

app/code/Magento/UrlRewriteGraphQl/composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"magento/framework": "*",
88
"magento/module-url-rewrite": "*",
99
"magento/module-store": "*"
10-
1110
},
1211
"suggest": {
1312
"magento/module-graph-ql": "*"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
9+
<preference for="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface" type="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocator"/>
10+
</config>

0 commit comments

Comments
 (0)