Skip to content

Commit

Permalink
Implementing getting the results from a specific location for GraphQL
Browse files Browse the repository at this point in the history
Requires ezsystem:ezplatform-graphql:^3.0
  • Loading branch information
Bertrand Dunogier committed Nov 24, 2020
1 parent abd1f4f commit de9c945
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 41 deletions.
61 changes: 26 additions & 35 deletions src/API/QueryFieldService.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
use eZ\Publish\API\Repository\SearchService;
use eZ\Publish\API\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\Values\Content\Location;
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
use eZ\Publish\API\Repository\Values\Content\Query;
use eZ\Publish\API\Repository\Values\Content\Search\SearchHit;
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
use eZ\Publish\Core\QueryType\QueryTypeRegistry;
Expand All @@ -31,9 +33,8 @@ final class QueryFieldService implements QueryFieldServiceInterface, QueryFieldL

/** @var \eZ\Publish\API\Repository\ContentTypeService */
private $contentTypeService;
/**
* @var \eZ\Publish\API\Repository\LocationService
*/

/** @var \eZ\Publish\API\Repository\LocationService */
private $locationService;

public function __construct(
Expand Down Expand Up @@ -98,7 +99,7 @@ public function loadContentItemsSliceForLocation(Location $location, string $fie

public function getPaginationConfiguration($content, string $fieldDefinitionIdentifier): int
{
$fieldDefinition = $this->loadFieldDefinition($content, $fieldDefinitionIdentifier);
$fieldDefinition = $content->getContentType()->getFieldDefinition($fieldDefinitionIdentifier);

if ($fieldDefinition->fieldSettings['EnablePagination'] === false) {
return false;
Expand Down Expand Up @@ -137,12 +138,9 @@ private function resolveExpression(string $expression, array $variables)

private function prepareQuery(Content $content, Location $location, string $fieldDefinitionIdentifier, array $extraParameters = []): Query
{
// @todo change to actual main location
$mainLocation = $location;

$fieldDefinition = $this->loadFieldDefinition($content, $fieldDefinitionIdentifier);

$fieldDefinition = $content->getContentType()->getFieldDefinition($fieldDefinitionIdentifier);
$queryType = $this->queryTypeRegistry->getQueryType($fieldDefinition->fieldSettings['QueryType']);

$parameters = $this->resolveParameters(
$fieldDefinition->fieldSettings['Parameters'],
array_merge(
Expand All @@ -151,7 +149,7 @@ private function prepareQuery(Content $content, Location $location, string $fiel
'content' => $content,
'contentInfo' => $content->contentInfo,
'location' => $location,
'mainLocation' => $mainLocation,
'mainLocation' => $mainLocation = $content->contentInfo->getMainLocation(),
'returnedType' => $fieldDefinition->fieldSettings['ReturnedType'],
]
)
Expand All @@ -161,42 +159,35 @@ private function prepareQuery(Content $content, Location $location, string $fiel
}

/**
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* @param string $fieldDefinitionIdentifier
* @param \eZ\Publish\API\Repository\Values\Content\Query $query
*
* @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|null
* @return \eZ\Publish\API\Repository\Values\Content\Location[]
*
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
private function loadFieldDefinition(Content $content, string $fieldDefinitionIdentifier): FieldDefinition
private function executeQueryAndMapResult(Query $query): array
{
$contentType = $this->contentTypeService->loadContentType($content->contentInfo->contentTypeId);
$fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier);

if ($fieldDefinition === null) {
throw new NotFoundException(
'Query field definition',
$contentType->identifier . '/' . $fieldDefinitionIdentifier
);
}
return array_map(
function (SearchHit $searchHit) {
$valueObject = $searchHit->valueObject;
if ($valueObject instanceof Location) {
$valueObject = $valueObject->getContent();
}

return $fieldDefinition;
return $valueObject;
},
$this->executeQuery($query)->searchHits
);
}

/**
* @param \eZ\Publish\API\Repository\Values\Content\Query $query
*
* @return array
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
* @return string
*/
private function executeQueryAndMapResult(Query $query): array
private function executeQuery(Query $query): SearchResult
{
return array_map(
function (SearchHit $searchHit) {
return $searchHit->valueObject;
},
$this->searchService->findContent($query)->searchHits
);
$method = ($query instanceof LocationQuery) ? 'findLocations' : 'findContent';
return $this->searchService->$method($query);
}
}
22 changes: 18 additions & 4 deletions src/GraphQL/ContentQueryFieldDefinitionMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ final class ContentQueryFieldDefinitionMapper extends DecoratingFieldDefinitionM
/** @var string */
private $fieldTypeIdentifier;

/**
* Used for backward compatibility in regards to Content vs Item in GraphQL.
* @var bool
*/
private $resolveWithItem = false;

public function __construct(
FieldDefinitionMapper $innerMapper,
NameHelper $nameHelper,
Expand All @@ -35,6 +41,11 @@ public function __construct(
$this->fieldTypeIdentifier = $fieldTypeIdentifier;
}

public function setResolveWithItem(bool $resolveWithItem = false)
{
$this->resolveWithItem = $resolveWithItem;
}

public function mapToFieldValueType(FieldDefinition $fieldDefinition): ?string
{
if (!$this->canMap($fieldDefinition)) {
Expand All @@ -58,10 +69,12 @@ public function mapToFieldValueResolver(FieldDefinition $fieldDefinition): ?stri

$fieldSettings = $fieldDefinition->getFieldSettings();

$valueVariable = $this->resolveWithItem ? 'item' : 'content';

if ($fieldSettings['EnablePagination']) {
return '@=resolver("QueryFieldValueConnection", [args, field, content])';
return sprintf('@=resolver("QueryFieldValueConnection", [args, field, %s])', $valueVariable);
} else {
return '@=resolver("QueryFieldValue", [field, content])';
return sprintf('@=resolver("QueryFieldValue", [field, %s])', $valueVariable);
}
}

Expand Down Expand Up @@ -94,14 +107,15 @@ protected function getFieldTypeIdentifier(): string

private function nameValueType($typeIdentifier): string
{
return $this->nameHelper->domainContentName(
return $this->nameHelper->itemName(
$this->contentTypeService->loadContentTypeByIdentifier($typeIdentifier)
);
}

private function nameValueConnectionType($typeIdentifier): string
{
return $this->nameHelper->domainContentConnection(
// @todo depends on the GraphQL version. Unless the method is modified instead of adding a new one. BC ?
return $this->nameHelper->itemConnection(
$this->contentTypeService->loadContentTypeByIdentifier($typeIdentifier)
);
}
Expand Down
69 changes: 69 additions & 0 deletions src/GraphQL/QueryFieldItemResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace EzSystems\EzPlatformQueryFieldType\GraphQL;

use EzSystems\EzPlatformGraphQL\GraphQL\ItemFactory;
use EzSystems\EzPlatformGraphQL\GraphQL\Value\Item;
use EzSystems\EzPlatformQueryFieldType\API\QueryFieldServiceInterface;
use eZ\Publish\API\Repository\Values\Content\Content;
use EzSystems\EzPlatformGraphQL\GraphQL\Value\Field;
use Overblog\GraphQLBundle\Definition\Argument;
use Overblog\GraphQLBundle\Relay\Connection\Paginator;

final class QueryFieldItemResolver
{
/** @var \EzSystems\EzPlatformQueryFieldType\API\QueryFieldLocationService */
private $queryFieldService;

/** @var \EzSystems\EzPlatformGraphQL\GraphQL\ItemFactory */
private $itemFactory;

public function __construct(QueryFieldServiceInterface $queryFieldService, ItemFactory $itemFactory)
{
$this->queryFieldService = $queryFieldService;
$this->itemFactory = $itemFactory;
}

public function resolveQueryField(Field $field, Item $item)
{
return $this->queryFieldService->loadContentItems($item->getContent(), $field->fieldDefIdentifier);
}

public function resolveQueryFieldConnection(Argument $args, Field $field, Item $item)
{
if (!isset($args['first'])) {
$args['first'] = $this->queryFieldService->getPaginationConfiguration($item->getContent(), $field->fieldDefIdentifier);
}

$paginator = new Paginator(function ($offset, $limit) use ($item, $field) {
return array_map(
function (Content $content) {
return $this->itemFactory->fromContent($content);
},
$this->queryFieldService->loadContentItemsSliceForLocation($item->getLocation(), $field->fieldDefIdentifier, $offset, $limit)
);
});

return $paginator->auto(
$args,
function () use ($item, $field) {
return $this->queryFieldService->countContentItemsForLocation($item->getLocation(), $field->fieldDefIdentifier);
}
);
}

public function resolveQueryFieldDefinitionParameters(array $parameters): array
{
$return = [];

foreach ($parameters as $name => $value) {
$return[] = ['name' => $name, 'value' => $value];
}

return $return;
}
}
44 changes: 44 additions & 0 deletions src/Symfony/DependencyInjection/Compiler/ItemFeaturePass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/

namespace EzSystems\EzPlatformQueryFieldType\Symfony\DependencyInjection\Compiler;

use EzSystems\EzPlatformQueryFieldType\GraphQL\ContentQueryFieldDefinitionMapper;
use EzSystems\EzPlatformQueryFieldType\GraphQL\QueryFieldItemResolver;
use EzSystems\EzPlatformQueryFieldType\GraphQL\QueryFieldResolver;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* Enables the Item API if it is available in GraphQL.
*/
class ItemFeaturePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!class_exists('EzSystems\\EzPlatformGraphQL\\GraphQL\\Value\\Item')) {
return;
}

$this->updateResolver($container);
$this->updateFieldDefinitionMapper($container);
}

private function updateResolver(ContainerBuilder $container)
{
$definition = $container->getDefinition(QueryFieldResolver::class);
$definition->setClass(QueryFieldItemResolver::class);
$container->setDefinition(QueryFieldResolver::class, $definition);
}

private function updateFieldDefinitionMapper(ContainerBuilder $container)
{
$definition = $container->getDefinition(ContentQueryFieldDefinitionMapper::class);
$definition->addMethodCall('setResolveWithItem', [true]);
$container->setDefinition(ContentQueryFieldDefinitionMapper::class, $definition);
}

}
1 change: 1 addition & 0 deletions src/Symfony/EzSystemsEzPlatformQueryFieldTypeBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new Compiler\QueryTypesListPass());
$container->addCompilerPass(new Compiler\ConfigurableFieldDefinitionMapperPass());
$container->addCompilerPass(new Compiler\FieldDefinitionIdentifierViewMatcherPass());
$container->addCompilerPass(new Compiler\ItemFeaturePass());
}
}
1 change: 0 additions & 1 deletion src/Symfony/Resources/config/services/graphql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ services:
- { name: overblog_graphql.resolver, alias: "QueryFieldValueConnection", method: "resolveQueryFieldConnection" }
- { name: overblog_graphql.resolver, alias: "QueryFieldDefinitionParameters", method: "resolveQueryFieldDefinitionParameters" }


EzSystems\EzPlatformQueryFieldType\GraphQL\ContentQueryFieldDefinitionMapper:
decorates: EzSystems\EzPlatformGraphQL\Schema\Domain\Content\Mapper\FieldDefinition\FieldDefinitionMapper
arguments:
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Resources/config/services/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ services:
autowire: true
public: true

EzSystems\EzPlatformQueryFieldType\Controller\QueryFieldRestController: ~
EzSystems\EzPlatformQueryFieldType\Controller\QueryFieldRestController:
$requestParser: '@ezpublish_rest.request_parser'

EzSystems\EzPlatformQueryFieldType\API\QueryFieldService:
arguments:
Expand Down

0 comments on commit de9c945

Please sign in to comment.