Skip to content

Commit 0e057ff

Browse files
mateuszdebinskiMateusz Dębiński
andauthored
IBX-8417: Added the ability to enable pagination for ezobjectrelationlist field (#84)
* IBX-8417: Added the ability to enable pagination for ezobjectrelationlist field * Changed parameter name, made $args nullable * Added missing return type * Corrected parameter name --------- Co-authored-by: Mateusz Dębiński <[email protected]>
1 parent e3e2a90 commit 0e057ff

File tree

7 files changed

+117
-36
lines changed

7 files changed

+117
-36
lines changed

spec/Schema/Domain/Content/Mapper/FieldDefinition/RelationFieldDefinitionMapperSpec.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class RelationFieldDefinitionMapperSpec extends ObjectBehavior
1818

1919
function let(NameHelper $nameHelper, ContentTypeService $contentTypeService, FieldDefinitionMapper $innerMapper)
2020
{
21-
$this->beConstructedWith($innerMapper, $nameHelper, $contentTypeService);
21+
$this->beConstructedWith($innerMapper, $nameHelper, $contentTypeService, true);
2222

2323
$articleContentType = new ContentType(['identifier' => 'article']);
2424
$folderContentType = new ContentType(['identifier' => 'folder']);
@@ -56,19 +56,19 @@ function it_maps_single_selection_with_a_unique_type_limitations_to_a_single_ite
5656
function it_maps_multi_selection_without_type_limitations_to_an_array_of_generic_content()
5757
{
5858
$fieldDefinition = $this->createFieldDefinition(self::DEF_LIMIT_MULTI, []);
59-
$this->mapToFieldValueType($fieldDefinition)->shouldReturn('[Item]');
59+
$this->mapToFieldValueType($fieldDefinition)->shouldReturn('RelationsConnection');
6060
}
6161

6262
function it_maps_multi_selection_with_multiple_type_limitations_to_an_array_of_generic_content()
6363
{
6464
$fieldDefinition = $this->createFieldDefinition(self::DEF_LIMIT_NONE, ['article', 'blog_post']);
65-
$this->mapToFieldValueType($fieldDefinition)->shouldReturn('[Item]');
65+
$this->mapToFieldValueType($fieldDefinition)->shouldReturn('RelationsConnection');
6666
}
6767

6868
function it_maps_multi_selection_with_a_unique_type_limitations_to_an_array_of_that_type()
6969
{
7070
$fieldDefinition = $this->createFieldDefinition(self::DEF_LIMIT_MULTI, ['article']);
71-
$this->mapToFieldValueType($fieldDefinition)->shouldReturn('[ArticleItem]');
71+
$this->mapToFieldValueType($fieldDefinition)->shouldReturn('RelationsConnection');
7272
}
7373

7474
function it_delegates_the_field_definition_type_to_the_inner_mapper(FieldDefinitionMapper $innerMapper)
@@ -81,13 +81,13 @@ function it_delegates_the_field_definition_type_to_the_inner_mapper(FieldDefinit
8181
function it_maps_multi_selection_to_resolve_multiple()
8282
{
8383
$fieldDefinition = $this->createFieldDefinition(self::DEF_LIMIT_MULTI);
84-
$this->mapToFieldValueResolver($fieldDefinition)->shouldReturn('@=resolver("RelationFieldValue", [field, true])');
84+
$this->mapToFieldValueResolver($fieldDefinition)->shouldReturn('@=resolver("RelationFieldValue", [field, true, args])');
8585
}
8686

8787
function it_maps_single_selection_to_resolve_single()
8888
{
8989
$fieldDefinition = $this->createFieldDefinition(self::DEF_LIMIT_SINGLE);
90-
$this->mapToFieldValueResolver($fieldDefinition)->shouldReturn('@=resolver("RelationFieldValue", [field, false])');
90+
$this->mapToFieldValueResolver($fieldDefinition)->shouldReturn('@=resolver("RelationFieldValue", [field, false, args])');
9191
}
9292

9393
private function createFieldDefinition($selectionLimit = 0, $selectionContentTypes = [])

src/bundle/Resources/config/default_settings.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
parameters:
22
ibexa.graphql.schema.should.extend.ezurl: false
3+
ibexa.graphql.schema.ibexa_object_relation_list.enable_pagination: false
34
ibexa.graphql.schema.content.field_name.override:
45
id: id_
56
ibexa.graphql.schema.content.mapping.field_definition_type:

src/bundle/Resources/config/graphql/Field.types.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,13 @@ UrlFieldValue:
279279
type: String
280280
description: "The link's name or description"
281281
resolve: "@=value.text"
282+
283+
RelationsConnection:
284+
type: relay-connection
285+
config:
286+
nodeType: Item
287+
connectionFields:
288+
sliceSize:
289+
type: Int!
290+
orderBy:
291+
type: String

src/bundle/Resources/config/services/resolvers.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ services:
4949
- { name: overblog_graphql.resolver, alias: "ItemsOfTypeAsConnection", method: "resolveItemsOfTypeAsConnection" }
5050

5151
Ibexa\GraphQL\Resolver\RelationFieldResolver:
52+
arguments:
53+
$enablePagination: '%ibexa.graphql.schema.ibexa_object_relation_list.enable_pagination%'
5254
tags:
5355
- { name: overblog_graphql.resolver, alias: "RelationFieldValue", method: "resolveRelationFieldValue" }
5456

src/bundle/Resources/config/services/schema.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ services:
4343
arguments:
4444
$contentTypeService: '@ibexa.siteaccessaware.service.content_type'
4545
$innerMapper: '@Ibexa\GraphQL\Schema\Domain\Content\Mapper\FieldDefinition\RelationFieldDefinitionMapper.inner'
46+
$enablePagination: '%ibexa.graphql.schema.ibexa_object_relation_list.enable_pagination%'
4647

4748
Ibexa\GraphQL\Schema\Domain\Content\Mapper\FieldDefinition\SelectionFieldDefinitionMapper:
4849
decorates: Ibexa\Contracts\GraphQL\Schema\Domain\Content\Mapper\FieldDefinition\FieldDefinitionMapper

src/lib/Resolver/RelationFieldResolver.php

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,92 @@
77
namespace Ibexa\GraphQL\Resolver;
88

99
use GraphQL\Error\UserError;
10+
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
1011
use Ibexa\Contracts\Core\Repository\Values\Content\Query;
1112
use Ibexa\Core\FieldType;
1213
use Ibexa\GraphQL\DataLoader\ContentLoader;
1314
use Ibexa\GraphQL\ItemFactory;
15+
use Ibexa\GraphQL\Relay\PageAwareConnection;
1416
use Ibexa\GraphQL\Value\Field;
17+
use Ibexa\GraphQL\Value\Item;
18+
use Overblog\GraphQLBundle\Definition\Argument;
19+
use Overblog\GraphQLBundle\Relay\Connection\Paginator;
1520

1621
final class RelationFieldResolver
1722
{
18-
/** @var \Ibexa\GraphQL\DataLoader\ContentLoader */
19-
private $contentLoader;
23+
public const DEFAULT_LIMIT = 25;
2024

21-
/** @var \Ibexa\GraphQL\ItemFactory */
22-
private $itemFactory;
25+
private ContentLoader $contentLoader;
2326

24-
public function __construct(ContentLoader $contentLoader, ItemFactory $relatedContentItemFactory)
25-
{
27+
private ItemFactory $itemFactory;
28+
29+
private bool $enablePagination;
30+
31+
public function __construct(
32+
ContentLoader $contentLoader,
33+
ItemFactory $relatedContentItemFactory,
34+
bool $enablePagination
35+
) {
2636
$this->contentLoader = $contentLoader;
2737
$this->itemFactory = $relatedContentItemFactory;
38+
$this->enablePagination = $enablePagination;
2839
}
2940

30-
public function resolveRelationFieldValue(Field $field, $multiple = false)
41+
public function resolveRelationFieldValue(Field $field, $multiple = false, ?Argument $args = null)
3142
{
3243
$destinationContentIds = $this->getContentIds($field);
3344

3445
if (empty($destinationContentIds) || array_key_exists(0, $destinationContentIds) && null === $destinationContentIds[0]) {
3546
return $multiple ? [] : null;
3647
}
3748

38-
$contentItems = $this->contentLoader->find(new Query(
49+
$query = new Query(
3950
['filter' => new Query\Criterion\ContentId($destinationContentIds)]
40-
));
51+
);
4152

4253
if ($multiple) {
43-
return array_map(
44-
function ($contentId) use ($contentItems) {
45-
return $this->itemFactory->fromContent(
46-
$contentItems[array_search($contentId, array_column($contentItems, 'id'))]
47-
);
48-
},
49-
$destinationContentIds
54+
if (!$this->enablePagination || $args === null) {
55+
$contentItems = $this->contentLoader->find($query);
56+
57+
return array_map(
58+
function (int $contentId) use ($contentItems): Item {
59+
return $this->itemFactory->fromContent(
60+
$contentItems[array_search($contentId, array_column($contentItems, 'id'), true)]
61+
);
62+
},
63+
$destinationContentIds
64+
);
65+
}
66+
67+
$paginator = new Paginator(function ($offset, $limit) use ($query): array {
68+
$query->offset = $offset;
69+
$query->limit = $limit ?? self::DEFAULT_LIMIT;
70+
$contentItems = $this->contentLoader->find($query);
71+
72+
return array_map(
73+
function (Content $content): Item {
74+
return $this->itemFactory->fromContent(
75+
$content
76+
);
77+
},
78+
$contentItems
79+
);
80+
});
81+
82+
return PageAwareConnection::fromConnection(
83+
$paginator->auto(
84+
$args,
85+
function () use ($query): int {
86+
return $this->contentLoader->count($query);
87+
}
88+
),
89+
$args
5090
);
5191
}
5292

93+
$query->limit = 1;
94+
$contentItems = $this->contentLoader->find($query);
95+
5396
return $contentItems[0] ? $this->itemFactory->fromContent($contentItems[0]) : null;
5497
}
5598

@@ -62,11 +105,13 @@ private function getContentIds(Field $field): array
62105
{
63106
if ($field->value instanceof FieldType\RelationList\Value) {
64107
return $field->value->destinationContentIds;
65-
} elseif ($field->value instanceof FieldType\Relation\Value) {
108+
}
109+
110+
if ($field->value instanceof FieldType\Relation\Value) {
66111
return [$field->value->destinationContentId];
67-
} else {
68-
throw new UserError('\$field does not contain a RelationList or Relation Field value');
69112
}
113+
114+
throw new UserError('\$field does not contain a RelationList or Relation Field value');
70115
}
71116
}
72117

src/lib/Schema/Domain/Content/Mapper/FieldDefinition/RelationFieldDefinitionMapper.php

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,22 @@
1414

1515
class RelationFieldDefinitionMapper extends DecoratingFieldDefinitionMapper implements FieldDefinitionMapper
1616
{
17-
/**
18-
* @var \Ibexa\GraphQL\Schema\Domain\Content\NameHelper
19-
*/
20-
private $nameHelper;
17+
private NameHelper $nameHelper;
2118

22-
/**
23-
* @var \Ibexa\Contracts\Core\Repository\ContentTypeService
24-
*/
25-
private $contentTypeService;
19+
private ContentTypeService $contentTypeService;
20+
21+
private bool $enablePagination;
2622

2723
public function __construct(
2824
FieldDefinitionMapper $innerMapper,
2925
NameHelper $nameHelper,
30-
ContentTypeService $contentTypeService
26+
ContentTypeService $contentTypeService,
27+
bool $enablePagination
3128
) {
3229
parent::__construct($innerMapper);
3330
$this->nameHelper = $nameHelper;
3431
$this->contentTypeService = $contentTypeService;
32+
$this->enablePagination = $enablePagination;
3533
}
3634

3735
public function mapToFieldValueType(FieldDefinition $fieldDefinition): ?string
@@ -54,7 +52,18 @@ public function mapToFieldValueType(FieldDefinition $fieldDefinition): ?string
5452
}
5553

5654
if ($this->isMultiple($fieldDefinition)) {
57-
$type = "[$type]";
55+
if ($this->enablePagination) {
56+
$type = 'RelationsConnection';
57+
} else {
58+
@trigger_error(
59+
'Disable pagination for ezobjectrelationlist has been deprecated since version 4.6 ' .
60+
'and will be removed in version 5.0. To start receiving `RelationsConnection` instead of the deprecated ' .
61+
'`[' . $type . ']`, set the parameter `ibexa.graphql.schema.ibexa_object_relation_list.enable_pagination` to `true`.',
62+
E_USER_DEPRECATED
63+
);
64+
65+
$type = "[$type]";
66+
}
5867
}
5968

6069
return $type;
@@ -68,14 +77,27 @@ public function mapToFieldValueResolver(FieldDefinition $fieldDefinition): ?stri
6877

6978
$isMultiple = $this->isMultiple($fieldDefinition) ? 'true' : 'false';
7079

71-
return sprintf('@=resolver("RelationFieldValue", [field, %s])', $isMultiple);
80+
return sprintf('@=resolver("RelationFieldValue", [field, %s, args])', $isMultiple);
7281
}
7382

7483
protected function canMap(FieldDefinition $fieldDefinition)
7584
{
7685
return in_array($fieldDefinition->fieldTypeIdentifier, ['ezobjectrelation', 'ezobjectrelationlist']);
7786
}
7887

88+
public function mapToFieldValueArgsBuilder(FieldDefinition $fieldDefinition): ?string
89+
{
90+
if (!$this->canMap($fieldDefinition)) {
91+
return parent::mapToFieldValueArgsBuilder($fieldDefinition);
92+
}
93+
94+
if ($this->isMultiple($fieldDefinition) && $this->enablePagination) {
95+
return 'Relay::Connection';
96+
}
97+
98+
return parent::mapToFieldValueArgsBuilder($fieldDefinition);
99+
}
100+
79101
/**
80102
* Not implemented since we don't use it (canMap is overridden).
81103
*/

0 commit comments

Comments
 (0)