diff --git a/doc/howto/customize_rendering.md b/doc/howto/customize_rendering.md
index e728c70..0f95fe1 100644
--- a/doc/howto/customize_rendering.md
+++ b/doc/howto/customize_rendering.md
@@ -4,11 +4,17 @@ In this how-to, you will learn how to customize all the rendering phases.
## Rendering process
Content query fields are rendered using `ez_render_field()`.
-The query is executed, the items iterated on, and each is be displayed using the `line` content view.
+The query is executed, the items iterated on, and each is rendered using the `line` content view.
That template renders the content, the view controller, with a custom view type (`content_query_view`). A custom view
builder executes execute the query, and assigns the results to the view as `items`. The default template for that view (`query_field_view.html.twig`) iterates on each item resulting from the query, and renders each with the `line` view.
+The field renderer for a query field supports the following parameters:
+- `bool enablePagination`: force pagination enabled, even if it is disabled for that field definition
+- `bool disablePagination`: force pagination disabled, even if it is disabled for that field definition
+- `int itemsPerPage`: sets how many items are displayed per page with pagination. Required if `enablePagination` is
+ used and pagination is disabled in the field definition
+
### Summary
1. Your template: `ez_render_field(content, 'queryfield')`
2. Field view template: `fieldtype_ui.html.twig`:
@@ -41,6 +47,7 @@ As with any content view, a custom controller can also be defined to further cus
Use the `items` iterable to loop over the field's content items:
```
+
{% for item in items %}
{{ render(controller("ez_content:viewAction", {
"contentId": item.id,
@@ -48,9 +55,19 @@ Use the `items` iterable to loop over the field's content items:
"viewType": itemViewType
})) }}
{% endfor %}
+
+
+{% if isPaginationEnabled %}
+ {{ pagerfanta( items, 'ez', {'routeName': location, 'pageParameter': pageParameter } ) }}
+{% endif %}
```
-The usual [content view templates variables](https://doc.ezplatform.com/en/latest/api/field_type_form_and_template/#template-variables) are also available, including `content`, the item that contains the query field.
+In addition to the [usual content view templates variables](https://doc.ezplatform.com/en/latest/api/field_type_form_and_template/#template-variables), the following variables are available:
+- `Content content`: the item that contains the query field.
+- `bool isPaginationEnabled`: indicates if pagination is enabled. When it is, `items` is a `PagerFanta` instance.
+- `string pageParameter`: when pagination is enabled, contains the page parameter to use as the pager fanta
+ `pageParameter` argument (important, as it makes every pager unique, required if there are several query
+ fields in the same item)
## Customizing the line view template
The line view template, used to render each result, can be customized by creating `line` view configuration rules.
@@ -69,7 +86,7 @@ ezplatform:
template: "path/to/template.html.twig"
```
-The variables are the same as any view template ([documentation]((https://doc.ezplatform.com/en/latest/api/field_type_form_and_template/#template-variables))).
+The variables are the same than other view template ([documentation]((https://doc.ezplatform.com/en/latest/api/field_type_form_and_template/#template-variables))).
## Advanced
diff --git a/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php b/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php
index a9fd0e2..46a089c 100644
--- a/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php
+++ b/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php
@@ -79,24 +79,38 @@ function it_delegates_field_definition_type_to_the_parent_mapper_for_a_non_query
->shouldBe('FieldValue');
}
- function it_delegates_the_value_resolver_to_the_parent_mapper(FieldDefinitionMapper $innerMapper)
+ function it_maps_the_field_value_when_pagination_is_disabled(FieldDefinitionMapper $innerMapper)
{
$fieldDefinition = $this->fieldDefinition();
- $innerMapper->mapToFieldValueResolver($fieldDefinition)->willReturn('resolver');
+ $innerMapper->mapToFieldValueResolver($fieldDefinition)->shouldNotBeCalled();
$this
->mapToFieldValueResolver($fieldDefinition)
- ->shouldBe('resolver');
+ ->shouldBe('@=resolver("QueryFieldValue", [field, content])');
+ }
+
+ function it_maps_the_field_value_when_pagination_is_enabled(FieldDefinitionMapper $innerMapper)
+ {
+ $fieldDefinition = $this->fieldDefinition(true);
+ $innerMapper->mapToFieldValueResolver($fieldDefinition)->shouldNotBeCalled();
+ $this
+ ->mapToFieldValueResolver($fieldDefinition)
+ ->shouldBe('@=resolver("QueryFieldValueConnection", [args, field, content])');
}
/**
+ * @param bool $enablePagination
+ *
* @return FieldDefinition
*/
- private function fieldDefinition(): FieldDefinition
+ private function fieldDefinition(bool $enablePagination = false): FieldDefinition
{
return new FieldDefinition([
'identifier' => self::FIELD_IDENTIFIER,
'fieldTypeIdentifier' => self::FIELD_TYPE_IDENTIFIER,
- 'fieldSettings' => ['ReturnedType' => self::RETURNED_CONTENT_TYPE_IDENTIFIER]
+ 'fieldSettings' => [
+ 'ReturnedType' => self::RETURNED_CONTENT_TYPE_IDENTIFIER,
+ 'EnablePagination' => $enablePagination
+ ]
]);
}
diff --git a/src/API/QueryFieldPaginationService.php b/src/API/QueryFieldPaginationService.php
new file mode 100644
index 0000000..6f6910a
--- /dev/null
+++ b/src/API/QueryFieldPaginationService.php
@@ -0,0 +1,21 @@
+searchService->findContent($query)->totalCount;
}
+ public function loadContentItemsSlice(Content $content, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable
+ {
+ $query = $this->prepareQuery($content, $fieldDefinitionIdentifier);
+ $query->offset = $offset;
+ $query->limit = $limit;
+
+ return array_map(
+ function (SearchHit $searchHit) {
+ return $searchHit->valueObject;
+ },
+ $this->searchService->findContent($query)->searchHits
+ );
+ }
+
+ public function getPaginationConfiguration(Content $content, string $fieldDefinitionIdentifier): int
+ {
+ $fieldDefinition = $this->loadFieldDefinition($content, $fieldDefinitionIdentifier);
+
+ if ($fieldDefinition->fieldSettings['EnablePagination'] === false) {
+ return false;
+ }
+
+ return $fieldDefinition->fieldSettings['ItemsPerPage'];
+ }
+
/**
- * @param array $parameters parameters that may include expressions to be resolved
- * @param \eZ\Publish\API\Repository\Values\Content\Content $content
+ * @param array $expressions parameters that may include expressions to be resolved
+ * @param array $variables
*
* @return array
*/
- private function resolveParameters(array $parameters, array $variables): array
+ private function resolveParameters(array $expressions, array $variables): array
{
- foreach ($parameters as $key => $expression) {
+ foreach ($expressions as $key => $expression) {
if (is_array($expression)) {
- $parameters[$key] = $this->resolveParameters($expression, $variables);
+ $expressions[$key] = $this->resolveParameters($expression, $variables);
} else {
- $parameters[$key] = $this->resolveExpression($expression, $variables);
+ $expressions[$key] = $this->resolveExpression($expression, $variables);
}
}
- return $parameters;
+ return $expressions;
}
private function resolveExpression(string $expression, array $variables)
@@ -110,24 +136,40 @@ private function resolveExpression(string $expression, array $variables)
* @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
*/
- private function prepareQuery(Content $content, string $fieldDefinitionIdentifier): Query
+ private function prepareQuery(Content $content, string $fieldDefinitionIdentifier, array $extraParameters = []): Query
{
- $fieldDefinition = $this
- ->contentTypeService->loadContentType($content->contentInfo->contentTypeId)
- ->getFieldDefinition($fieldDefinitionIdentifier);
+ $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'],
- [
- 'content' => $content,
- 'contentInfo' => $content->contentInfo,
- 'mainLocation' => $location,
- 'returnedType' => $fieldDefinition->fieldSettings['ReturnedType'],
- ]
+ array_merge(
+ $extraParameters,
+ [
+ 'content' => $content,
+ 'contentInfo' => $content->contentInfo,
+ 'mainLocation' => $location,
+ 'returnedType' => $fieldDefinition->fieldSettings['ReturnedType'],
+ ]
+ )
);
return $queryType->getQuery($parameters);
}
+
+ /**
+ * @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
+ {
+ return $fieldDefinition = $this
+ ->contentTypeService->loadContentType($content->contentInfo->contentTypeId)
+ ->getFieldDefinition($fieldDefinitionIdentifier);
+ }
}
diff --git a/src/Controller/QueryFieldRestController.php b/src/Controller/QueryFieldRestController.php
index 6656569..d1cbc11 100644
--- a/src/Controller/QueryFieldRestController.php
+++ b/src/Controller/QueryFieldRestController.php
@@ -15,6 +15,7 @@
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
use eZ\Publish\Core\REST\Server\Values\ContentList;
use eZ\Publish\Core\REST\Server\Values\RestContent;
+use Symfony\Component\HttpFoundation\Request;
final class QueryFieldRestController
{
@@ -42,11 +43,19 @@ public function __construct(
$this->locationService = $locationService;
}
- public function getResults($contentId, $versionNumber, $fieldDefinitionIdentifier): ContentList
+ public function getResults(Request $request, $contentId, $versionNumber, $fieldDefinitionIdentifier): ContentList
{
+ $offset = (int)$request->query->get('offset', 0);
+ $limit = (int)$request->query->get('limit', -1);
+
$content = $this->contentService->loadContent($contentId, null, $versionNumber);
+ if ($limit === -1 || !method_exists($this->queryFieldService, 'loadContentItemsSlice')) {
+ $items = $this->queryFieldService->loadContentItems($content, $fieldDefinitionIdentifier);
+ } else {
+ $items = $this->queryFieldService->loadContentItemsSlice($content, $fieldDefinitionIdentifier, $offset, $limit);
+ }
- return new ContentList(
+ $list = new ContentList(
array_map(
function (Content $content) {
return new RestContent(
@@ -57,9 +66,15 @@ function (Content $content) {
$this->contentService->loadRelations($content->getVersionInfo())
);
},
- $this->queryFieldService->loadContentItems($content, $fieldDefinitionIdentifier)
+ $items
)
);
+
+ if (property_exists($list, 'totalCount')) {
+ $list->totalCount = $this->queryFieldService->countContentItems($content, $fieldDefinitionIdentifier);
+ }
+
+ return $list;
}
private function getContentType(ContentInfo $contentInfo): ContentType
diff --git a/src/GraphQL/ContentQueryFieldDefinitionMapper.php b/src/GraphQL/ContentQueryFieldDefinitionMapper.php
index 24621a1..53a4e10 100644
--- a/src/GraphQL/ContentQueryFieldDefinitionMapper.php
+++ b/src/GraphQL/ContentQueryFieldDefinitionMapper.php
@@ -9,10 +9,11 @@
use eZ\Publish\API\Repository\ContentTypeService;
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
use EzSystems\EzPlatformGraphQL\Schema\Domain\Content\Mapper\FieldDefinition\DecoratingFieldDefinitionMapper;
+use EzSystems\EzPlatformGraphQL\Schema\Domain\Content\Mapper\FieldDefinition\FieldDefinitionArgsBuilderMapper;
use EzSystems\EzPlatformGraphQL\Schema\Domain\Content\Mapper\FieldDefinition\FieldDefinitionMapper;
use EzSystems\EzPlatformGraphQL\Schema\Domain\Content\NameHelper;
-final class ContentQueryFieldDefinitionMapper extends DecoratingFieldDefinitionMapper implements FieldDefinitionMapper
+final class ContentQueryFieldDefinitionMapper extends DecoratingFieldDefinitionMapper implements FieldDefinitionMapper, FieldDefinitionArgsBuilderMapper
{
/** @var NameHelper */
private $nameHelper;
@@ -38,7 +39,26 @@ public function mapToFieldValueType(FieldDefinition $fieldDefinition): ?string
$fieldSettings = $fieldDefinition->getFieldSettings();
- return '[' . $this->getDomainTypeName($fieldSettings['ReturnedType']) . ']';
+ if ($fieldSettings['EnablePagination']) {
+ return $this->nameValueConnectionType($fieldSettings['ReturnedType']);
+ } else {
+ return '[' . $this->nameValueType($fieldSettings['ReturnedType']) . ']';
+ }
+ }
+
+ public function mapToFieldValueResolver(FieldDefinition $fieldDefinition): ?string
+ {
+ if (!$this->canMap($fieldDefinition)) {
+ return parent::mapToFieldValueType($fieldDefinition);
+ }
+
+ $fieldSettings = $fieldDefinition->getFieldSettings();
+
+ if ($fieldSettings['EnablePagination']) {
+ return '@=resolver("QueryFieldValueConnection", [args, field, content])';
+ } else {
+ return '@=resolver("QueryFieldValue", [field, content])';
+ }
}
public function mapToFieldDefinitionType(FieldDefinition $fieldDefinition): ?string
@@ -50,15 +70,35 @@ public function mapToFieldDefinitionType(FieldDefinition $fieldDefinition): ?str
return 'ContentQueryFieldDefinition';
}
+ public function mapToFieldValueArgsBuilder(FieldDefinition $fieldDefinition): ?string
+ {
+ if (!$this->canMap($fieldDefinition)) {
+ return parent::mapToFieldValueArgsBuilder($fieldDefinition);
+ }
+
+ if ($fieldDefinition->fieldSettings['EnablePagination']) {
+ return 'Relay::Connection';
+ } else {
+ return null;
+ }
+ }
+
protected function getFieldTypeIdentifier(): string
{
return 'ezcontentquery';
}
- private function getDomainTypeName($typeIdentifier)
+ private function nameValueType($typeIdentifier): string
{
return $this->nameHelper->domainContentName(
$this->contentTypeService->loadContentTypeByIdentifier($typeIdentifier)
);
}
+
+ private function nameValueConnectionType($typeIdentifier): string
+ {
+ return $this->nameHelper->domainContentConnection(
+ $this->contentTypeService->loadContentTypeByIdentifier($typeIdentifier)
+ );
+ }
}
diff --git a/src/GraphQL/QueryFieldResolver.php b/src/GraphQL/QueryFieldResolver.php
index b3b82ed..55f699e 100644
--- a/src/GraphQL/QueryFieldResolver.php
+++ b/src/GraphQL/QueryFieldResolver.php
@@ -6,9 +6,12 @@
*/
namespace EzSystems\EzPlatformQueryFieldType\GraphQL;
+use EzSystems\EzPlatformQueryFieldType\API\QueryFieldPaginationService;
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 QueryFieldResolver
{
@@ -25,6 +28,28 @@ public function resolveQueryField(Field $field, Content $content)
return $this->queryFieldService->loadContentItems($content, $field->fieldDefIdentifier);
}
+ public function resolveQueryFieldConnection(Argument $args, Field $field, Content $content)
+ {
+ if (!$this->queryFieldService instanceof QueryFieldPaginationService) {
+ throw new \Exception("The QueryFieldService isn't able to handle pagination, this should not happen");
+ }
+
+ if (!isset($args['first'])) {
+ $args['first'] = $this->queryFieldService->getPaginationConfiguration($content, $field->fieldDefIdentifier);
+ }
+
+ $paginator = new Paginator(function ($offset, $limit) use ($content, $field) {
+ return $this->queryFieldService->loadContentItemsSlice($content, $field->fieldDefIdentifier, $offset, $limit);
+ });
+
+ return $paginator->auto(
+ $args,
+ function () use ($content, $field) {
+ return $this->queryFieldService->countContentItems($content, $field->fieldDefIdentifier);
+ }
+ );
+ }
+
public function resolveQueryFieldDefinitionParameters(array $parameters): array
{
$return = [];
diff --git a/src/Symfony/Resources/config/services/graphql.yml b/src/Symfony/Resources/config/services/graphql.yml
index b5b5cd4..65ecf00 100644
--- a/src/Symfony/Resources/config/services/graphql.yml
+++ b/src/Symfony/Resources/config/services/graphql.yml
@@ -7,9 +7,12 @@ services:
EzSystems\EzPlatformQueryFieldType\GraphQL\QueryFieldResolver:
tags:
- { name: overblog_graphql.resolver, alias: "QueryFieldValue", method: "resolveQueryField" }
+ - { 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:
$innerMapper: '@EzSystems\EzPlatformQueryFieldType\GraphQL\ContentQueryFieldDefinitionMapper.inner'
+ tags:
+ - { name: ezplatform_graphql.field_definition_args_builder_mapper, fieldtype: 'ezcontentquery' }
diff --git a/src/Symfony/Resources/views/content/contentquery.html.twig b/src/Symfony/Resources/views/content/contentquery.html.twig
index f47e5a4..628b8e9 100644
--- a/src/Symfony/Resources/views/content/contentquery.html.twig
+++ b/src/Symfony/Resources/views/content/contentquery.html.twig
@@ -6,3 +6,7 @@
"viewType": itemViewType
})) }}
{% endfor %}
+
+{% if isPaginationEnabled %}
+ {{ pagerfanta( items, 'ez', {'routeName': location, 'pageParameter': pageParameter } ) }}
+{% endif %}
diff --git a/src/Symfony/Resources/views/fieldtype/field_view.html.twig b/src/Symfony/Resources/views/fieldtype/field_view.html.twig
index 9a3219d..c3d46ae 100644
--- a/src/Symfony/Resources/views/fieldtype/field_view.html.twig
+++ b/src/Symfony/Resources/views/fieldtype/field_view.html.twig
@@ -7,7 +7,10 @@
"contentId": contentInfo.id,
"queryFieldDefinitionIdentifier": field.fieldDefIdentifier,
"viewType": ezContentQueryViews['field'],
- "itemViewType": itemViewType|default(ezContentQueryViews['item'])
+ "itemViewType": itemViewType|default(ezContentQueryViews['item']),
+ "enablePagination": parameters.enablePagination|default(false),
+ "disablePagination": parameters.disablePagination|default(false),
+ "itemsPerPage": parameters.itemsPerPage|default(false)
}
)) }}
{% endblock %}
diff --git a/src/Symfony/Resources/views/fieldtype/fielddefinition_edit.html.twig b/src/Symfony/Resources/views/fieldtype/fielddefinition_edit.html.twig
index bcabc5f..4f357a2 100644
--- a/src/Symfony/Resources/views/fieldtype/fielddefinition_edit.html.twig
+++ b/src/Symfony/Resources/views/fieldtype/fielddefinition_edit.html.twig
@@ -13,6 +13,18 @@
{{- form_widget(form.ReturnedType) -}}
+
+
+
+ {{- form_label(form.ItemsPerPage) -}}
+ {{- form_errors(form.ItemsPerPage) -}}
+ {{- form_widget(form.ItemsPerPage) -}}
+
+
{{- form_label(form.Parameters) -}}
{{- form_errors(form.Parameters) -}}
diff --git a/src/eZ/ContentView/QueryResultsInjector.php b/src/eZ/ContentView/QueryResultsInjector.php
index 5837859..9a3a364 100644
--- a/src/eZ/ContentView/QueryResultsInjector.php
+++ b/src/eZ/ContentView/QueryResultsInjector.php
@@ -8,8 +8,11 @@
use eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewParametersEvent;
use eZ\Publish\Core\MVC\Symfony\View\ViewEvents;
+use EzSystems\EzPlatformQueryFieldType\API\QueryFieldPaginationService;
use EzSystems\EzPlatformQueryFieldType\API\QueryFieldService;
+use Pagerfanta\Pagerfanta;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
class QueryResultsInjector implements EventSubscriberInterface
{
@@ -19,13 +22,18 @@ class QueryResultsInjector implements EventSubscriberInterface
/** @var array */
private $views;
- public function __construct(QueryFieldService $queryFieldService, array $views)
+ /** @var \Symfony\Component\HttpFoundation\RequestStack */
+ private $requestStack;
+
+ public function __construct(QueryFieldService $queryFieldService, array $views, RequestStack $requestStack)
{
- $this->queryFieldService = $queryFieldService;
if (!isset($views['item']) || !isset($views['field'])) {
throw new \InvalidArgumentException("Both 'item' and 'field' views must be provided");
}
+
+ $this->queryFieldService = $queryFieldService;
$this->views = $views;
+ $this->requestStack = $requestStack;
}
public static function getSubscribedEvents()
@@ -41,14 +49,91 @@ public function injectQueryResults(FilterViewParametersEvent $event)
$viewType = $event->getView()->getViewType();
if ($viewType === $this->views['field']) {
- $event->getParameterBag()->add([
+ $parameters = [
'itemViewType' => $this->views['item'],
- 'items' => $this->queryFieldService->loadContentItems(
- $event->getView()->getContent(),
- // @todo error handling if parameter not set
- $event->getBuilderParameters()['queryFieldDefinitionIdentifier']
- ),
- ]);
+ 'items' => $this->buildResults($event),
+ ];
+ $parameters['isPaginationEnabled'] = ($parameters['items'] instanceof Pagerfanta);
+ if ($parameters['isPaginationEnabled']) {
+ $fieldDefinitionIdentifier = $event->getBuilderParameters()['queryFieldDefinitionIdentifier'];
+ $parameters['pageParameter'] = sprintf('[%s_page]', $fieldDefinitionIdentifier);
+ }
+ $event->getParameterBag()->add($parameters);
+ }
+ }
+
+ /**
+ * @param \eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewParametersEvent $event
+ *
+ * @return iterable
+ *
+ * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
+ */
+ protected function buildResults(FilterViewParametersEvent $event): iterable
+ {
+ $view = $event->getView();
+ $content = $view->getContent();
+ $viewParameters = $event->getBuilderParameters();
+ $fieldDefinitionIdentifier = $viewParameters['queryFieldDefinitionIdentifier'];
+
+ $paginationLimit = false;
+
+ if ($this->queryFieldService instanceof QueryFieldPaginationService) {
+ $paginationLimit = $this->queryFieldService->getPaginationConfiguration($content, $fieldDefinitionIdentifier);
+ }
+
+ $enablePagination = ($viewParameters['enablePagination'] === true);
+ $disablePagination = ($viewParameters['disablePagination'] === true);
+
+ if ($enablePagination === true && $disablePagination === true) {
+ // @todo custom exception
+ throw new \InvalidArgumentException("the 'enablePagination' and 'disablePagination' parameters can not both be true");
+ }
+
+ if (is_numeric($viewParameters['itemsPerPage'])) {
+ // @todo custom exception
+ if ($viewParameters['itemsPerPage'] <= 0) {
+ throw new \InvalidArgumentException('itemsPerPage must be a positive integer');
+ }
+ $paginationLimit = $viewParameters['itemsPerPage'];
+ }
+
+ if (($enablePagination === true) && (!is_numeric($paginationLimit) || $paginationLimit === 0)) {
+ throw new \InvalidArgumentException("The 'itemsPerPage' parameter must be given with a positive integer value if 'enablePagination' is set");
+ }
+
+ if ($paginationLimit !== 0 && $disablePagination !== true) {
+ if (!$this->queryFieldService instanceof QueryFieldPaginationService) {
+ throw new \Exception(
+ "Pagination was requested, but the QueryFieldService isn't an instance of %s",
+ QueryFieldPaginationService::class
+ );
+ }
+
+ $request = $this->requestStack->getMasterRequest();
+
+ $queryParameters = $view->hasParameter('query') ? $view->getParameter('query') : [];
+
+ $limit = $queryParameters['limit'] ?? $paginationLimit;
+ $pageParam = sprintf('%s_page', $fieldDefinitionIdentifier);
+ $page = isset($request) ? $request->get($pageParam, 1) : 1;
+
+ $pager = new Pagerfanta(
+ new QueryResultsPagerFantaAdapter(
+ $this->queryFieldService, $content, $fieldDefinitionIdentifier
+ )
+ );
+
+ $pager->setMaxPerPage($limit);
+ $pager->setCurrentPage($page);
+
+ return $pager;
+ } else {
+ // @todo error handling if parameter not set
+ return $this->queryFieldService->loadContentItems(
+ $content,
+ $fieldDefinitionIdentifier
+ );
}
}
}
diff --git a/src/eZ/ContentView/QueryResultsPagerFantaAdapter.php b/src/eZ/ContentView/QueryResultsPagerFantaAdapter.php
new file mode 100644
index 0000000..076d68a
--- /dev/null
+++ b/src/eZ/ContentView/QueryResultsPagerFantaAdapter.php
@@ -0,0 +1,51 @@
+queryFieldService = $queryFieldService;
+ $this->content = $content;
+ $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier;
+ }
+
+ public function getNbResults()
+ {
+ return $this->queryFieldService->countContentItems(
+ $this->content,
+ $this->fieldDefinitionIdentifier
+ );
+ }
+
+ public function getSlice($offset, $length)
+ {
+ return $this->queryFieldService->loadContentItemsSlice(
+ $this->content,
+ $this->fieldDefinitionIdentifier,
+ $offset,
+ $length
+ );
+ }
+}
diff --git a/src/eZ/FieldType/Mapper/QueryFormMapper.php b/src/eZ/FieldType/Mapper/QueryFormMapper.php
index fad96d5..04948be 100644
--- a/src/eZ/FieldType/Mapper/QueryFormMapper.php
+++ b/src/eZ/FieldType/Mapper/QueryFormMapper.php
@@ -63,6 +63,19 @@ public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, Field
'required' => true,
]
)
+ ->add('EnablePagination', Type\CheckboxType::class,
+ [
+ 'label' => 'Enable pagination',
+ 'property_path' => 'fieldSettings[EnablePagination]',
+ 'required' => false,
+ ]
+ )
+ ->add('ItemsPerPage', Type\NumberType::class,
+ [
+ 'label' => 'Items per page',
+ 'property_path' => 'fieldSettings[ItemsPerPage]',
+ ]
+ )
->add($parametersForm);
}
diff --git a/src/eZ/FieldType/Query/Type.php b/src/eZ/FieldType/Query/Type.php
index ab955df..1259b71 100644
--- a/src/eZ/FieldType/Query/Type.php
+++ b/src/eZ/FieldType/Query/Type.php
@@ -25,6 +25,8 @@ final class Type extends FieldType
'QueryType' => ['type' => 'string', 'default' => ''],
'Parameters' => ['type' => 'array', 'default' => []],
'ReturnedType' => ['type' => 'string', 'default' => ''],
+ 'EnablePagination' => ['type' => 'boolean', 'default' => true],
+ 'ItemsPerPage' => ['type' => 'integer', 'default' => 10],
];
/** @var \eZ\Publish\Core\QueryType\QueryTypeRegistry */
@@ -215,6 +217,18 @@ public function validateFieldSettings($fieldSettings)
}
}
+ if (isset($fieldSettings['EnablePagination'])) {
+ if (!is_bool($fieldSettings['EnablePagination'])) {
+ $errors[] = new ValidationError('EnablePagination is not a boolean');
+ }
+ }
+
+ if (isset($fieldSettings['ItemsPerPage'])) {
+ if (!is_numeric($fieldSettings['ItemsPerPage'])) {
+ $errors[] = new ValidationError('ItemsPerPage is not an integer');
+ }
+ }
+
if (isset($fieldSettings['Parameters'])) {
if (!is_array($fieldSettings['Parameters'])) {
$errors[] = new ValidationError('Parameters is not a valid YAML string');
diff --git a/src/eZ/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php b/src/eZ/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php
index a083efd..6e31662 100644
--- a/src/eZ/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php
+++ b/src/eZ/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php
@@ -63,6 +63,8 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField
$storageDef->dataText1 = $fieldDef->fieldTypeConstraints->fieldSettings['QueryType'];
$storageDef->dataText2 = $fieldDef->fieldTypeConstraints->fieldSettings['ReturnedType'];
$storageDef->dataText5 = \json_encode($fieldDef->fieldTypeConstraints->fieldSettings['Parameters']);
+ $storageDef->dataInt1 = (int)$fieldDef->fieldTypeConstraints->fieldSettings['EnablePagination'];
+ $storageDef->dataInt2 = $fieldDef->fieldTypeConstraints->fieldSettings['ItemsPerPage'];
}
/**
@@ -77,6 +79,8 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin
'QueryType' => $storageDef->dataText1 ?: null,
'ReturnedType' => $storageDef->dataText2 ?: null,
'Parameters' => \json_decode($storageDef->dataText5, true),
+ 'EnablePagination' => (bool)$storageDef->dataInt1,
+ 'ItemsPerPage' => $storageDef->dataInt2,
];
}