diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml
index be4bb9fcd701..e6e0f6cf7210 100644
--- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml
+++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml
@@ -6,5 +6,10 @@
*/
-->
-
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls
index b96cfcb03d41..f4ad2e930dda 100644
--- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls
@@ -4,6 +4,7 @@
interface ProductInterface {
url_key: String @doc(description: "The part of the URL that identifies the product")
url_path: String @doc(description: "The part of the URL that precedes the url_key")
+ url_rewrites: [UrlRewrite] @doc(description: "URL rewrites list") @resolver(class: "Magento\\UrlRewriteGraphQl\\Model\\Resolver\\UrlRewrite")
}
input ProductFilterInput {
diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/EntityUrl.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/EntityUrl.php
new file mode 100644
index 000000000000..1c25ffd1e9ff
--- /dev/null
+++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/EntityUrl.php
@@ -0,0 +1,148 @@
+urlFinder = $urlFinder;
+ $this->storeManager = $storeManager;
+ $this->customUrlLocator = $customUrlLocator;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!isset($args['url']) || empty(trim($args['url']))) {
+ throw new GraphQlInputException(__('"url" argument should be specified and not empty'));
+ }
+
+ $result = null;
+ $url = $args['url'];
+ if (substr($url, 0, 1) === '/' && $url !== '/') {
+ $url = ltrim($url, '/');
+ }
+ $customUrl = $this->customUrlLocator->locateUrl($url);
+ $url = $customUrl ?: $url;
+ $urlRewrite = $this->findCanonicalUrl($url);
+ if ($urlRewrite) {
+ $result = [
+ 'id' => $urlRewrite->getEntityId(),
+ 'canonical_url' => $urlRewrite->getTargetPath(),
+ 'type' => $this->sanitizeType($urlRewrite->getEntityType())
+ ];
+ }
+ return $result;
+ }
+
+ /**
+ * Find the canonical url passing through all redirects if any
+ *
+ * @param string $requestPath
+ * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ */
+ private function findCanonicalUrl(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ {
+ $urlRewrite = $this->findUrlFromRequestPath($requestPath);
+ if ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
+ while ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
+ $urlRewrite = $this->findUrlFromRequestPath($urlRewrite->getTargetPath());
+ }
+ }
+ if (!$urlRewrite) {
+ $urlRewrite = $this->findUrlFromTargetPath($requestPath);
+ }
+
+ return $urlRewrite;
+ }
+
+ /**
+ * Find a url from a request url on the current store
+ *
+ * @param string $requestPath
+ * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ */
+ private function findUrlFromRequestPath(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ {
+ return $this->urlFinder->findOneByData(
+ [
+ 'request_path' => $requestPath,
+ 'store_id' => $this->storeManager->getStore()->getId()
+ ]
+ );
+ }
+
+ /**
+ * Find a url from a target url on the current store
+ *
+ * @param string $targetPath
+ * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ */
+ private function findUrlFromTargetPath(string $targetPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ {
+ return $this->urlFinder->findOneByData(
+ [
+ 'target_path' => $targetPath,
+ 'store_id' => $this->storeManager->getStore()->getId()
+ ]
+ );
+ }
+
+ /**
+ * Sanitize the type to fit schema specifications
+ *
+ * @param string $type
+ * @return string
+ */
+ private function sanitizeType(string $type) : string
+ {
+ return strtoupper(str_replace('-', '_', $type));
+ }
+}
diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php
index 5bdaf3627253..0c4c78b58294 100644
--- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php
+++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php
@@ -11,12 +11,12 @@
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
-use Magento\Store\Model\StoreManagerInterface;
+use Magento\Framework\Model\AbstractModel;
use Magento\UrlRewrite\Model\UrlFinderInterface;
-use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface;
+use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteDTO;
/**
- * UrlRewrite field resolver, used for GraphQL request processing.
+ * Returns URL rewrites list for the specified product
*/
class UrlRewrite implements ResolverInterface
{
@@ -25,29 +25,13 @@ class UrlRewrite implements ResolverInterface
*/
private $urlFinder;
- /**
- * @var StoreManagerInterface
- */
- private $storeManager;
-
- /**
- * @var CustomUrlLocatorInterface
- */
- private $customUrlLocator;
-
/**
* @param UrlFinderInterface $urlFinder
- * @param StoreManagerInterface $storeManager
- * @param CustomUrlLocatorInterface $customUrlLocator
*/
public function __construct(
- UrlFinderInterface $urlFinder,
- StoreManagerInterface $storeManager,
- CustomUrlLocatorInterface $customUrlLocator
+ UrlFinderInterface $urlFinder
) {
$this->urlFinder = $urlFinder;
- $this->storeManager = $storeManager;
- $this->customUrlLocator = $customUrlLocator;
}
/**
@@ -59,90 +43,51 @@ public function resolve(
ResolveInfo $info,
array $value = null,
array $args = null
- ) {
- if (!isset($args['url']) || empty(trim($args['url']))) {
- throw new GraphQlInputException(__('"url" argument should be specified and not empty'));
+ ): array {
+ if (!isset($value['model'])) {
+ throw new GraphQlInputException(__('"model" value should be specified'));
}
- $result = null;
- $url = $args['url'];
- if (substr($url, 0, 1) === '/' && $url !== '/') {
- $url = ltrim($url, '/');
- }
- $customUrl = $this->customUrlLocator->locateUrl($url);
- $url = $customUrl ?: $url;
- $urlRewrite = $this->findCanonicalUrl($url);
- if ($urlRewrite) {
- $result = [
- 'id' => $urlRewrite->getEntityId(),
- 'relative_url' => $urlRewrite->getTargetPath(),
- 'type' => $this->sanitizeType($urlRewrite->getEntityType())
- ];
- }
- return $result;
- }
+ /** @var AbstractModel $entity */
+ $entity = $value['model'];
+ $entityId = $entity->getEntityId();
- /**
- * Find the canonical url passing through all redirects if any
- *
- * @param string $requestPath
- * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
- */
- private function findCanonicalUrl(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
- {
- $urlRewrite = $this->findUrlFromRequestPath($requestPath);
- if ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
- while ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
- $urlRewrite = $this->findUrlFromRequestPath($urlRewrite->getTargetPath());
+ $urlRewriteCollection = $this->urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $entityId]);
+ $urlRewrites = [];
+
+ /** @var UrlRewriteDTO $urlRewrite */
+ foreach ($urlRewriteCollection as $urlRewrite) {
+ if ($urlRewrite->getRedirectType() !== 0) {
+ continue;
}
+
+ $urlRewrites[] = [
+ 'url' => $urlRewrite->getRequestPath(),
+ 'parameters' => $this->getUrlParameters($urlRewrite->getTargetPath())
+ ];
}
- if (!$urlRewrite) {
- $urlRewrite = $this->findUrlFromTargetPath($requestPath);
- }
-
- return $urlRewrite;
- }
- /**
- * Find a url from a request url on the current store
- *
- * @param string $requestPath
- * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
- */
- private function findUrlFromRequestPath(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
- {
- return $this->urlFinder->findOneByData(
- [
- 'request_path' => $requestPath,
- 'store_id' => $this->storeManager->getStore()->getId()
- ]
- );
+ return $urlRewrites;
}
/**
- * Find a url from a target url on the current store
+ * Parses target path and extracts parameters
*
* @param string $targetPath
- * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ * @return array
*/
- private function findUrlFromTargetPath(string $targetPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ private function getUrlParameters(string $targetPath): array
{
- return $this->urlFinder->findOneByData(
- [
- 'target_path' => $targetPath,
- 'store_id' => $this->storeManager->getStore()->getId()
- ]
- );
- }
+ $urlParameters = [];
+ $targetPathParts = explode('/', trim($targetPath, '/'));
- /**
- * Sanitize the type to fit schema specifications
- *
- * @param string $type
- * @return string
- */
- private function sanitizeType(string $type) : string
- {
- return strtoupper(str_replace('-', '_', $type));
+ for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) {
+ $urlParameters[] = [
+ 'name' => $targetPathParts[$i],
+ 'value' => $targetPathParts[$i + 1]
+ ];
+ }
+
+ return $urlParameters;
}
}
diff --git a/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls b/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls
index e9a39617774c..dae695c69a33 100644
--- a/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls
@@ -1,15 +1,25 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.
-type EntityUrl @doc(description: "EntityUrl is an output object containing the `id`, `relative_url`, and `type` attributes") {
+type EntityUrl @doc(description: "EntityUrl is an output object containing the `id`, `canonical_url`, and `type` attributes") {
id: Int @doc(description: "The ID assigned to the object associated with the specified url. This could be a product ID, category ID, or page ID.")
- relative_url: String @doc(description: "The internal relative URL. If the specified url is a redirect, the query returns the redirected URL, not the original.")
+ canonical_url: String @doc(description: "The internal relative URL. If the specified url is a redirect, the query returns the redirected URL, not the original.")
type: UrlRewriteEntityTypeEnum @doc(description: "One of PRODUCT, CATEGORY, or CMS_PAGE.")
}
type Query {
- urlResolver(url: String!): EntityUrl @resolver(class: "Magento\\UrlRewriteGraphQl\\Model\\Resolver\\UrlRewrite") @doc(description: "The urlResolver query returns the relative URL for a specified product, category or CMS page")
+ urlResolver(url: String!): EntityUrl @resolver(class: "Magento\\UrlRewriteGraphQl\\Model\\Resolver\\EntityUrl") @doc(description: "The urlResolver query returns the relative URL for a specified product, category or CMS page")
}
enum UrlRewriteEntityTypeEnum {
}
+
+type UrlRewrite @doc(description: "The object contains URL rewrite details") {
+ url: String @doc(description: "Request URL")
+ parameters: [HttpQueryParameter] @doc(description: "Request parameters")
+}
+
+type HttpQueryParameter @doc(description: "The object details of target path parameters") {
+ name: String @doc(description: "Parameter name")
+ value: String @doc(description: "Parameter value")
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php
new file mode 100644
index 000000000000..c39b32e4bfa4
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php
@@ -0,0 +1,149 @@
+graphQlQuery($query);
+
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
+ $product = $productRepository->get('virtual-product', false, null, true);
+
+ $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class);
+
+ $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]);
+
+ /* There should be only one rewrite */
+ /** @var UrlRewriteDTO $urlRewrite */
+ $urlRewrite = current($rewritesCollection);
+
+ $this->assertArrayHasKey('url_rewrites', $response['products']['items'][0]);
+ $this->assertCount(1, $response['products']['items'][0]['url_rewrites']);
+
+ $this->assertResponseFields(
+ $response['products']['items'][0]['url_rewrites'][0],
+ [
+ "url" => $urlRewrite->getRequestPath(),
+ "parameters" => $this->getUrlParameters($urlRewrite->getTargetPath())
+ ]
+ );
+ }
+
+ /**
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function testProductWithOneCategoryAssigned()
+ {
+ $productSku = 'simple';
+ $query
+ = <<graphQlQuery($query);
+
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
+ $product = $productRepository->get('simple', false, null, true);
+
+ $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class);
+
+ $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]);
+ $rewritesCount = count($rewritesCollection);
+
+ $this->assertArrayHasKey('url_rewrites', $response['products']['items'][0]);
+ $this->assertCount($rewritesCount, $response['products']['items'][0]['url_rewrites']);
+
+ for ($i = 0; $i < $rewritesCount; $i++) {
+ $urlRewrite = $rewritesCollection[$i];
+ $this->assertResponseFields(
+ $response['products']['items'][0]['url_rewrites'][$i],
+ [
+ "url" => $urlRewrite->getRequestPath(),
+ "parameters" => $this->getUrlParameters($urlRewrite->getTargetPath())
+ ]
+ );
+ }
+ }
+
+ /**
+ * Parses target path and extracts parameters
+ *
+ * @param string $targetPath
+ * @return array
+ */
+ private function getUrlParameters(string $targetPath): array
+ {
+ $urlParameters = [];
+ $targetPathParts = explode('/', trim($targetPath, '/'));
+
+ for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) {
+ $urlParameters[] = [
+ 'name' => $targetPathParts[$i],
+ 'value' => $targetPathParts[$i + 1]
+ ];
+ }
+
+ return $urlParameters;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php
index 370121a1dad7..c70b1631e85c 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php
@@ -31,7 +31,7 @@ protected function setUp()
}
/**
- * Tests if target_path(relative_url) is resolved for Product entity
+ * Tests if target_path(canonical_url) is resolved for Product entity
*
* @magentoApiDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php
*/
@@ -60,7 +60,7 @@ public function testProductUrlResolver()
urlResolver(url:"{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -68,12 +68,12 @@ public function testProductUrlResolver()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($product->getEntityId(), $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
/**
- * Tests the use case where relative_url is provided as resolver input in the Query
+ * Tests the use case where canonical_url is provided as resolver input in the Query
*
* @magentoApiDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php
*/
@@ -104,7 +104,7 @@ public function testProductUrlWithCanonicalUrlInput()
urlResolver(url:"{$canonicalPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -112,7 +112,7 @@ public function testProductUrlWithCanonicalUrlInput()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($product->getEntityId(), $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -147,7 +147,7 @@ public function testCategoryUrlResolver()
urlResolver(url:"{$urlPath2}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -155,7 +155,7 @@ public function testCategoryUrlResolver()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($categoryId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -183,14 +183,14 @@ public function testCMSPageUrlResolver()
urlResolver(url:"{$requestPath}")
{
id
- relative_url
+ canonical_url
type
}
}
QUERY;
$response = $this->graphQlQuery($query);
$this->assertEquals($cmsPageId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper(str_replace('-', '_', $expectedEntityType)), $response['urlResolver']['type']);
}
@@ -226,7 +226,7 @@ public function testProductUrlRewriteResolver()
urlResolver(url:"{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -234,7 +234,7 @@ public function testProductUrlRewriteResolver()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($product->getEntityId(), $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -266,7 +266,7 @@ public function testInvalidUrlResolverInput()
urlResolver(url:"{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -307,7 +307,7 @@ public function testCategoryUrlWithLeadingSlash()
urlResolver(url:"/{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -315,7 +315,7 @@ public function testCategoryUrlWithLeadingSlash()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($categoryId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -344,7 +344,7 @@ public function testResolveSlash()
urlResolver(url:"/")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -352,7 +352,7 @@ public function testResolveSlash()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($homePageId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals('CMS_PAGE', $response['urlResolver']['type']);
}
}