Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for getting the results for a location #38

Merged
merged 9 commits into from
Jan 13, 2021
Merged
70 changes: 54 additions & 16 deletions spec/API/QueryFieldServiceSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
class QueryFieldServiceSpec extends ObjectBehavior
{
const CONTENT_TYPE_ID = 1;
const CONTENT_TYPE_ID_WITHOUT_PAGINATION = 2;
const LOCATION_ID = 1;
const QUERY_TYPE_IDENTIFIER = 'query_type_identifier';
const FIELD_DEFINITION_IDENTIFIER = 'test';
Expand All @@ -46,24 +47,16 @@ function let(
'param2' => 'value2',
];

$contentType = new Values\ContentType\ContentType([
'fieldDefinitions' => new Values\ContentType\FieldDefinitionCollection([
new Values\ContentType\FieldDefinition([
'identifier' => self::FIELD_DEFINITION_IDENTIFIER,
'fieldTypeIdentifier' => 'ezcontentquery',
'fieldSettings' => [
'ReturnedType' => 'folder',
'QueryType' => self::QUERY_TYPE_IDENTIFIER,
'Parameters' => $parameters,
],
]),
]),
]);
$location = new Values\Content\Location([
'id' => self::LOCATION_ID,
]);

$contentTypeService->loadContentType(self::CONTENT_TYPE_ID)->willReturn($contentType);
$contentTypeWithPagination = $this->getContentType($parameters);
$contentTypeService->loadContentType(self::CONTENT_TYPE_ID)->willReturn($contentTypeWithPagination);

$contentTypeWithoutPagination = $this->getContentType($parameters, false, 10);
$contentTypeService->loadContentType(self::CONTENT_TYPE_ID_WITHOUT_PAGINATION)->willReturn($contentTypeWithoutPagination);

$locationService->loadLocation(self::LOCATION_ID)->willReturn($location);
$queryTypeRegistry->getQueryType(self::QUERY_TYPE_IDENTIFIER)->willReturn($queryType);
$queryType->getQuery(Argument::any())->willReturn(new ApiQuery());
Expand Down Expand Up @@ -113,18 +106,63 @@ function it_returns_zero_if_offset_is_bigger_than_count(QueryType $queryType, Se
$this->countContentItems($this->getContent(), self::FIELD_DEFINITION_IDENTIFIER)->shouldBe(0);
}

function it_returns_0_as_pagination_configuration_if_pagination_is_disabled()
{
$this->getPaginationConfiguration(
$this->getContent(self::CONTENT_TYPE_ID_WITHOUT_PAGINATION),
self::FIELD_DEFINITION_IDENTIFIER
)->shouldBe(0);
}

function it_returns_the_items_per_page_number_as_pagination_configuration_if_pagination_is_enabled()
{
$this->getPaginationConfiguration(
$this->getContent(),
self::FIELD_DEFINITION_IDENTIFIER
)->shouldBe(10);
}

/**
* @return \eZ\Publish\Core\Repository\Values\Content\Content
*/
private function getContent(): Values\Content\Content
private function getContent(int $contentTypeId = self::CONTENT_TYPE_ID): Values\Content\Content
{
return new Values\Content\Content([
'versionInfo' => new Values\Content\VersionInfo([
'contentInfo' => new ContentInfo([
'contentTypeId' => self::CONTENT_TYPE_ID,
'contentTypeId' => $contentTypeId,
'mainLocationId' => self::LOCATION_ID,
'mainLocation' => new Values\Content\Location([
'id' => self::LOCATION_ID,
]),
]),
]),
]);
}

/**
* @param array $parameters
*
* @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
*/
private function getContentType(array $parameters, bool $enablePagination = true, $itemsPerPage = 10): \eZ\Publish\API\Repository\Values\ContentType\ContentType
{
$contentType = new Values\ContentType\ContentType([
'fieldDefinitions' => new Values\ContentType\FieldDefinitionCollection([
new Values\ContentType\FieldDefinition([
'identifier' => self::FIELD_DEFINITION_IDENTIFIER,
'fieldTypeIdentifier' => 'ezcontentquery',
'fieldSettings' => [
'ReturnedType' => 'folder',
'QueryType' => self::QUERY_TYPE_IDENTIFIER,
'EnablePagination' => $enablePagination,
'ItemsPerPage' => $itemsPerPage,
'Parameters' => $parameters,
],
]),
]),
]);

return $contentType;
}
}
34 changes: 34 additions & 0 deletions src/API/QueryFieldLocationService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?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\API;

use eZ\Publish\API\Repository\Values\Content\Location;

/**
* Executes queries for a query field for a given a location.
*/
interface QueryFieldLocationService
{
/**
* Returns the query results for the given location.
*
* @return iterable An iterable that yields Content items.
*/
public function loadContentItemsForLocation(Location $location, string $fieldDefinitionIdentifier): iterable;

/**
* Returns a slice of the query results for the given location.
*
* @return iterable An iterable that yields Content items.
*/
public function loadContentItemsSliceForLocation(Location $location, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable;

/**
* Counts the results for the given location.
*/
public function countContentItemsForLocation(Location $location, string $fieldDefinitionIdentifier): int;
}
104 changes: 53 additions & 51 deletions src/API/QueryFieldService.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use eZ\Publish\API\Repository\LocationService;
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\Query;
use eZ\Publish\API\Repository\Values\Content\Search\SearchHit;
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
Expand All @@ -21,7 +22,7 @@
/**
* Executes a query and returns the results.
*/
final class QueryFieldService implements QueryFieldServiceInterface
final class QueryFieldService implements QueryFieldServiceInterface, QueryFieldLocationService
{
/** @var \eZ\Publish\Core\QueryType\QueryTypeRegistry */
private $queryTypeRegistry;
Expand All @@ -48,29 +49,33 @@ public function __construct(
$this->queryTypeRegistry = $queryTypeRegistry;
}

/**
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* @param string $fieldDefinitionIdentifier
*
* @return \eZ\Publish\API\Repository\Values\Content\Content[]
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
public function loadContentItems(Content $content, string $fieldDefinitionIdentifier): iterable
{
$query = $this->prepareQuery($content, $fieldDefinitionIdentifier);
$query = $this->prepareQuery($content, $content->contentInfo->getMainLocation(), $fieldDefinitionIdentifier);

return array_map(
function (SearchHit $searchHit) {
return $searchHit->valueObject;
},
$this->searchService->findContent($query)->searchHits
);
return $this->executeQueryAndMapResult($query);
}

public function loadContentItemsForLocation(Location $location, string $fieldDefinitionIdentifier): iterable
{
$query = $this->prepareQuery($location->getContent(), $location, $fieldDefinitionIdentifier);

return $this->executeQueryAndMapResult($query);
}

public function countContentItems(Content $content, string $fieldDefinitionIdentifier): int
{
$query = $this->prepareQuery($content, $fieldDefinitionIdentifier);
$query = $this->prepareQuery($content, $content->contentInfo->getMainLocation(), $fieldDefinitionIdentifier);
$query->limit = 0;

$count = $this->searchService->findContent($query)->totalCount - $query->offset;

return $count < 0 ? 0 : $count;
bdunogier marked this conversation as resolved.
Show resolved Hide resolved
}

public function countContentItemsForLocation(Location $location, string $fieldDefinitionIdentifier): int
{
$query = $this->prepareQuery($location->getContent(), $location, $fieldDefinitionIdentifier);
$query->limit = 0;

$count = $this->searchService->findContent($query)->totalCount - $query->offset;
Expand All @@ -80,34 +85,35 @@ public function countContentItems(Content $content, string $fieldDefinitionIdent

public function loadContentItemsSlice(Content $content, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable
{
$query = $this->prepareQuery($content, $fieldDefinitionIdentifier);
$query->offset += $offset;
$query = $this->prepareQuery($content, $content->contentInfo->getMainLocation(), $fieldDefinitionIdentifier);
$query->offset = $offset;
$query->limit = $limit;

return array_map(
function (SearchHit $searchHit) {
return $searchHit->valueObject;
},
$this->searchService->findContent($query)->searchHits
);
return $this->executeQueryAndMapResult($query);
}

public function loadContentItemsSliceForLocation(Location $location, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable
{
$query = $this->prepareQuery($location->getContent(), $location, $fieldDefinitionIdentifier);
$query->offset = $offset;
$query->limit = $limit;

return $this->executeQueryAndMapResult($query);
}

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

if ($fieldDefinition->fieldSettings['EnablePagination'] === false) {
return false;
return 0;
}

return $fieldDefinition->fieldSettings['ItemsPerPage'];
}

/**
* @param array $expressions parameters that may include expressions to be resolved
* @param array $variables
*
* @return array
*/
private function resolveParameters(array $expressions, array $variables): array
{
Expand All @@ -130,11 +136,6 @@ private function isExpression($expression): bool
}

/**
* @param string $expression
* @param array $variables
*
* @return mixed
*
* @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException if $expression is not an expression.
*/
private function resolveExpression(string $expression, array $variables)
Expand All @@ -146,20 +147,10 @@ private function resolveExpression(string $expression, array $variables)
return (new ExpressionLanguage())->evaluate(substr($expression, 2), $variables);
}

/**
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* @param string $fieldDefinitionIdentifier
*
* @return \eZ\Publish\API\Repository\Values\Content\Query
*
* @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
*/
private function prepareQuery(Content $content, string $fieldDefinitionIdentifier, array $extraParameters = []): Query
private function prepareQuery(Content $content, Location $location, string $fieldDefinitionIdentifier, array $extraParameters = []): Query
{
$fieldDefinition = $this->loadFieldDefinition($content, $fieldDefinitionIdentifier);

$location = $this->locationService->loadLocation($content->contentInfo->mainLocationId);
$queryType = $this->queryTypeRegistry->getQueryType($fieldDefinition->fieldSettings['QueryType']);
$parameters = $this->resolveParameters(
$fieldDefinition->fieldSettings['Parameters'],
Expand All @@ -168,7 +159,8 @@ private function prepareQuery(Content $content, string $fieldDefinitionIdentifie
[
'content' => $content,
'contentInfo' => $content->contentInfo,
'mainLocation' => $location,
'location' => $location,
'mainLocation' => $location->id === $content->contentInfo->mainLocationId ? $location : $content->contentInfo->getMainLocation(),
'returnedType' => $fieldDefinition->fieldSettings['ReturnedType'],
]
)
Expand All @@ -178,11 +170,6 @@ private function prepareQuery(Content $content, string $fieldDefinitionIdentifie
}

/**
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* @param string $fieldDefinitionIdentifier
*
* @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|null
*
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
*/
private function loadFieldDefinition(Content $content, string $fieldDefinitionIdentifier): FieldDefinition
Expand All @@ -199,4 +186,19 @@ private function loadFieldDefinition(Content $content, string $fieldDefinitionId

return $fieldDefinition;
}

/**
* @return \eZ\Publish\API\Repository\Values\Content\Content[]
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
private function executeQueryAndMapResult(Query $query): array
{
return array_map(
static function (SearchHit $searchHit): Content {
return $searchHit->valueObject;
},
$this->searchService->findContent($query)->searchHits
);
}
}
30 changes: 27 additions & 3 deletions src/API/QueryFieldServiceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
use eZ\Publish\API\Repository\Values\Content\Content;

/**
* Executes a query and returns the results.
* Executes queries for a query field.
*/
interface QueryFieldServiceInterface
{
/**
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* Executes the query without pagination and returns the content items.
*
* @param \eZ\Publish\API\Repository\Values\Content\Location $content
* @param string $fieldDefinitionIdentifier
*
* @return \eZ\Publish\API\Repository\Values\Content\Content[]
Expand All @@ -24,16 +26,38 @@ interface QueryFieldServiceInterface
public function loadContentItems(Content $content, string $fieldDefinitionIdentifier): iterable;

/**
* Counts the total results of a query.
*
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* @param string $fieldDefinitionIdentifier
*
* @return \eZ\Publish\API\Repository\Values\Content\Content[]
* @return int
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
public function countContentItems(Content $content, string $fieldDefinitionIdentifier): int;

/**
* Executes a paginated query and return the requested content items slice.
*
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* @param string $fieldDefinitionIdentifier
* @param int $offset
* @param int $limit
*
* @return \eZ\Publish\API\Repository\Values\Content\Content[]
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
public function loadContentItemsSlice(Content $content, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable;

/**
* @param \eZ\Publish\API\Repository\Values\Content\Content $content
* @param string $fieldDefinitionIdentifier
*
* @return int The page size, or 0 if pagination is disabled.
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
public function getPaginationConfiguration(Content $content, string $fieldDefinitionIdentifier): int;
}
Loading