Skip to content

Commit 3dc340e

Browse files
authored
Merge pull request #13354 from craftcms/feature/gql-improvements
GraphQL improvements
2 parents 97f3664 + 43ac37d commit 3dc340e

36 files changed

+153
-245
lines changed

CHANGELOG-WIP.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
- Added `craft\base\FsTrait::$showHasUrlSetting`. ([#13224](https://github.com/craftcms/cms/pull/13224))
4646
- Added `craft\base\FsTrait::$showUrlSetting`. ([#13224](https://github.com/craftcms/cms/pull/13224))
4747
- Added `craft\controllers\AssetsControllerTrait`.
48+
- Added `craft\gql\GqlEntityRegistry::getOrCreate()`. ([#13354](https://github.com/craftcms/cms/pull/13354))
4849
- Added `craft\helpers\Assets::iconSvg()`.
4950
- Added `craft\helpers\StringHelper::escapeShortcodes()`. ([#12935](https://github.com/craftcms/cms/issues/12935))
5051
- Added `craft\helpers\StringHelper::unescapeShortcodes()`. ([#12935](https://github.com/craftcms/cms/issues/12935))
@@ -72,6 +73,7 @@
7273
- When `content` table columns are resized, if any existing values are too long, all column data is now backed up into a new table, and the overflowing values are set to `null`. ([#13025](https://github.com/craftcms/cms/pull/13025))
7374
- When `content` table columns are renamed, if an existing column with the same name already exists, the original column data is now backed up into a new table and then deleted from the `content` table. ([#13025](https://github.com/craftcms/cms/pull/13025))
7475
- Plain Text and Table fields no longer convert emoji to shortcodes on PostgreSQL.
76+
- Improved GraphQL performance. ([#13354](https://github.com/craftcms/cms/pull/13354))
7577
- Fixed a bug where Plain Text and Table fields were converting posted shortcode-looking strings to emoji. ([#12935](https://github.com/craftcms/cms/issues/12935))
7678
- Fixed a bug where `craft\elements\Asset::getUrl()` was returning invalid URLs for GIF and SVG assets within filesystems without base URLs, if the `transformGifs` or `transformSvgs` config settings were disabled. ([#13306](https://github.com/craftcms/cms/issues/13306))
7779
- Updated Selectize to 0.15.2. ([#13273](https://github.com/craftcms/cms/discussions/13273))

src/fields/Matrix.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ public function serializeValue(mixed $value, ?ElementInterface $element = null):
575575
'type' => $block->getType()->handle,
576576
'enabled' => $block->enabled,
577577
'collapsed' => $block->collapsed,
578-
'fields' => $block->getSerializedFieldValues(),
578+
'fields' => fn() => $block->getSerializedFieldValues(),
579579
];
580580
}
581581

src/fields/Table.php

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -571,20 +571,10 @@ public function getContentGqlMutationArgumentType(): Type|array
571571
{
572572
$typeName = $this->handle . '_TableRowInput';
573573

574-
if ($argumentType = GqlEntityRegistry::getEntity($typeName)) {
575-
return Type::listOf($argumentType);
576-
}
577-
578-
$contentFields = TableRow::prepareRowFieldDefinition($this->columns, false);
579-
580-
$argumentType = GqlEntityRegistry::createEntity($typeName, new InputObjectType([
574+
return Type::listOf(GqlEntityRegistry::getOrCreate($typeName, fn() => new InputObjectType([
581575
'name' => $typeName,
582-
'fields' => function() use ($contentFields) {
583-
return $contentFields;
584-
},
585-
]));
586-
587-
return Type::listOf($argumentType);
576+
'fields' => fn() => TableRow::prepareRowFieldDefinition($this->columns, false),
577+
])));
588578
}
589579

590580
/**

src/gql/GqlEntityRegistry.php

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@ class GqlEntityRegistry
3535
*/
3636
public static function prefixTypeName(string $typeName): string
3737
{
38+
$prefix = self::getPrefix();
39+
40+
if (!$prefix || str_starts_with($typeName, $prefix)) {
41+
return $typeName;
42+
}
43+
3844
$rootTypes = ['Query', 'Mutation', 'Subscription'];
3945

4046
if (Craft::$app->getConfig()->getGeneral()->prefixGqlRootTypes || !in_array($typeName, $rootTypes)) {
41-
return self::getPrefix() . $typeName;
47+
return $prefix . $typeName;
4248
}
4349

4450
return $typeName;
@@ -71,19 +77,14 @@ public static function setPrefix(string $prefix): void
7177
}
7278

7379
/**
74-
* Get a registered entity.
80+
* Return a registered entity.
7581
*
7682
* @param string $entityName
7783
* @return mixed
7884
*/
7985
public static function getEntity(string $entityName): mixed
8086
{
81-
// Check if we need to apply the prefix.
82-
$prefix = self::getPrefix();
83-
if ($prefix && !str_starts_with($entityName, $prefix)) {
84-
$entityName = self::prefixTypeName($entityName);
85-
}
86-
87+
$entityName = self::prefixTypeName($entityName);
8788
return self::$_entities[$entityName] ?? false;
8889
}
8990

@@ -100,14 +101,25 @@ public static function createEntity(string $entityName, mixed $entity): mixed
100101
$entity->name = self::prefixTypeName($entity->name);
101102

102103
self::$_entities[$entityName] = $entity;
103-
104-
TypeLoader::registerType($entityName, function() use ($entity) {
105-
return $entity;
106-
});
104+
TypeLoader::registerType($entityName, fn() => $entity);
107105

108106
return $entity;
109107
}
110108

109+
/**
110+
* Returns a registered entity, creating it in the process if it doesn’t exist yet.
111+
*
112+
* @param string $name
113+
* @param callable $factory
114+
* @return mixed
115+
* @since 4.5.0
116+
*/
117+
public static function getOrCreate(string $name, callable $factory): mixed
118+
{
119+
$name = self::prefixTypeName($name);
120+
return self::$_entities[$name] ??= self::createEntity($name, $factory());
121+
}
122+
111123
/**
112124
* Flush all registered entities.
113125
*/

src/gql/base/GqlTypeTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ trait GqlTypeTrait
2727
*/
2828
public static function getType(?array $fields = null): Type
2929
{
30-
return GqlEntityRegistry::getEntity(static::class) ?: GqlEntityRegistry::createEntity(static::class, new GqlObjectType([
30+
return GqlEntityRegistry::getOrCreate(static::class, fn() => new GqlObjectType([
3131
/** @phpstan-ignore-next-line */
3232
'name' => static::getName(),
3333
'fields' => $fields ?: (static::class . '::getFieldDefinitions'),

src/gql/directives/FormatDateTime.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,10 @@ class FormatDateTime extends Directive
3535
*/
3636
public static function create(): GqlDirective
3737
{
38-
if ($type = GqlEntityRegistry::getEntity(self::name())) {
39-
return $type;
40-
}
38+
$typeName = static::name();
4139

42-
return GqlEntityRegistry::createEntity(static::name(), new self([
43-
'name' => static::name(),
40+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new self([
41+
'name' => $typeName,
4442
'locations' => [
4543
DirectiveLocation::FIELD,
4644
],

src/gql/directives/Markdown.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ class Markdown extends Directive
3232
*/
3333
public static function create(): GqlDirective
3434
{
35-
if ($type = GqlEntityRegistry::getEntity(self::name())) {
36-
return $type;
37-
}
35+
$typeName = static::name();
3836

39-
return GqlEntityRegistry::createEntity(static::name(), new self([
40-
'name' => static::name(),
37+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new self([
38+
'name' => $typeName,
4139
'locations' => [
4240
DirectiveLocation::FIELD,
4341
],

src/gql/directives/Money.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,10 @@ class Money extends Directive
4242
*/
4343
public static function create(): GqlDirective
4444
{
45-
if ($type = GqlEntityRegistry::getEntity(self::name())) {
46-
return $type;
47-
}
45+
$typeName = static::name();
4846

49-
return GqlEntityRegistry::createEntity(static::name(), new self([
50-
'name' => static::name(),
47+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new self([
48+
'name' => $typeName,
5149
'locations' => [
5250
DirectiveLocation::FIELD,
5351
],

src/gql/directives/ParseRefs.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@ class ParseRefs extends Directive
2727
*/
2828
public static function create(): GqlDirective
2929
{
30-
if ($type = GqlEntityRegistry::getEntity(self::name())) {
31-
return $type;
32-
}
30+
$typeName = static::name();
3331

34-
return GqlEntityRegistry::createEntity(static::name(), new self([
35-
'name' => static::name(),
32+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new self([
33+
'name' => $typeName,
3634
'locations' => [
3735
DirectiveLocation::FIELD,
3836
],

src/gql/directives/Transform.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,10 @@ public function __construct(array $config)
4343
*/
4444
public static function create(): GqlDirective
4545
{
46-
if ($type = GqlEntityRegistry::getEntity(self::name())) {
47-
return $type;
48-
}
46+
$typeName = static::name();
4947

50-
return GqlEntityRegistry::createEntity(static::name(), new self([
51-
'name' => static::name(),
48+
return GqlEntityRegistry::getOrCreate(static::name(), fn() => new self([
49+
'name' => $typeName,
5250
'locations' => [
5351
DirectiveLocation::FIELD,
5452
],

src/gql/types/DateTime.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class DateTime extends ScalarType implements SingularTypeInterface
3939
*/
4040
public static function getType(): DateTime
4141
{
42-
return GqlEntityRegistry::getEntity(self::getName()) ?: GqlEntityRegistry::createEntity(self::getName(), new self());
42+
return GqlEntityRegistry::getOrCreate(self::getName(), fn() => new self());
4343
}
4444

4545
/**

src/gql/types/Money.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Money extends ScalarType implements SingularTypeInterface
4242
*/
4343
public static function getType(): Money
4444
{
45-
return GqlEntityRegistry::getEntity(static::getName()) ?: GqlEntityRegistry::createEntity(self::getName(), new self());
45+
return GqlEntityRegistry::getOrCreate(static::getName(), fn() => new self());
4646
}
4747

4848
/**

src/gql/types/Number.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Number extends ScalarType implements SingularTypeInterface
4141
*/
4242
public static function getType(): Number
4343
{
44-
return GqlEntityRegistry::getEntity(static::getName()) ?: GqlEntityRegistry::createEntity(self::getName(), new self());
44+
return GqlEntityRegistry::getOrCreate(static::getName(), fn() => new self());
4545
}
4646

4747
/**

src/gql/types/QueryArgument.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function __construct(array $config = [])
4545
*/
4646
public static function getType(): QueryArgument
4747
{
48-
return GqlEntityRegistry::getEntity(self::getName()) ?: GqlEntityRegistry::createEntity(self::getName(), new self());
48+
return GqlEntityRegistry::getOrCreate(self::getName(), fn() => new self());
4949
}
5050

5151
/**

src/gql/types/generators/AddressType.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,15 @@ public static function generateTypes(mixed $context = null): array
4141
*/
4242
public static function generateType(mixed $context): ObjectType
4343
{
44-
// Users don't have different types, so the context for a user will be the same every time.
45-
$context = $context ?: Craft::$app->getFields()->getLayoutByType(AddressElement::class);
46-
4744
$typeName = AddressElement::gqlTypeNameByContext(null);
48-
$contentFieldGqlTypes = self::getContentFields($context);
49-
$addressFields = array_merge(AddressInterface::getFieldDefinitions(), $contentFieldGqlTypes);
5045

51-
return GqlEntityRegistry::getEntity($typeName) ?: GqlEntityRegistry::createEntity($typeName, new Address([
46+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new Address([
5247
'name' => $typeName,
53-
'fields' => function() use ($addressFields, $typeName) {
48+
'fields' => function() use ($context, $typeName) {
49+
// Users don't have different types, so the context for a user will be the same every time.
50+
$context ??= Craft::$app->getFields()->getLayoutByType(AddressElement::class);
51+
$contentFieldGqlTypes = self::getContentFields($context);
52+
$addressFields = array_merge(AddressInterface::getFieldDefinitions(), $contentFieldGqlTypes);
5453
return Craft::$app->getGql()->prepareFieldDefinitions($addressFields, $typeName);
5554
},
5655
]));

src/gql/types/generators/AssetType.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,12 @@ public static function generateTypes(mixed $context = null): array
5656
public static function generateType(mixed $context): ObjectType
5757
{
5858
$typeName = AssetElement::gqlTypeNameByContext($context);
59-
$contentFieldGqlTypes = self::getContentFields($context);
6059

61-
$assetFields = array_merge(AssetInterface::getFieldDefinitions(), $contentFieldGqlTypes);
62-
63-
return GqlEntityRegistry::getEntity($typeName) ?: GqlEntityRegistry::createEntity($typeName, new Asset([
60+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new Asset([
6461
'name' => $typeName,
65-
'fields' => function() use ($assetFields, $typeName) {
62+
'fields' => function() use ($context, $typeName) {
63+
$contentFieldGqlTypes = self::getContentFields($context);
64+
$assetFields = array_merge(AssetInterface::getFieldDefinitions(), $contentFieldGqlTypes);
6665
return Craft::$app->getGql()->prepareFieldDefinitions($assetFields, $typeName);
6766
},
6867
]));

src/gql/types/generators/CategoryType.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,12 @@ public static function generateTypes(mixed $context = null): array
5555
public static function generateType(mixed $context): ObjectType
5656
{
5757
$typeName = CategoryElement::gqlTypeNameByContext($context);
58-
$contentFieldGqlTypes = self::getContentFields($context);
5958

60-
$categoryGroupFields = array_merge(CategoryInterface::getFieldDefinitions(), $contentFieldGqlTypes);
61-
62-
return GqlEntityRegistry::getEntity($typeName) ?: GqlEntityRegistry::createEntity($typeName, new Category([
59+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new Category([
6360
'name' => $typeName,
64-
'fields' => function() use ($categoryGroupFields, $typeName) {
61+
'fields' => function() use ($context, $typeName) {
62+
$contentFieldGqlTypes = self::getContentFields($context);
63+
$categoryGroupFields = array_merge(CategoryInterface::getFieldDefinitions(), $contentFieldGqlTypes);
6564
return Craft::$app->getGql()->prepareFieldDefinitions($categoryGroupFields, $typeName);
6665
},
6766
]));

src/gql/types/generators/ElementType.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ public static function generateTypes(mixed $context = null): array
4040
public static function generateType(mixed $context): ObjectType
4141
{
4242
$typeName = BaseElement::gqlTypeNameByContext(null);
43-
$elementFields = ElementInterface::getFieldDefinitions();
4443

45-
return GqlEntityRegistry::getEntity($typeName) ?: GqlEntityRegistry::createEntity($typeName, new Element([
44+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new Element([
4645
'name' => $typeName,
47-
'fields' => function() use ($elementFields, $typeName) {
46+
'fields' => function() use ($typeName) {
47+
$elementFields = ElementInterface::getFieldDefinitions();
4848
return Craft::$app->getGql()->prepareFieldDefinitions($elementFields, $typeName);
4949
},
5050
]));

src/gql/types/generators/EntryType.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,11 @@ public static function generateType(mixed $context): ObjectType
5151
{
5252
$typeName = EntryElement::gqlTypeNameByContext($context);
5353

54-
if ($createdType = GqlEntityRegistry::getEntity($typeName)) {
55-
return $createdType;
56-
}
57-
58-
$contentFieldGqlTypes = self::getContentFields($context);
59-
$entryTypeFields = array_merge(EntryInterface::getFieldDefinitions(), $contentFieldGqlTypes);
60-
61-
return GqlEntityRegistry::createEntity($typeName, new Entry([
54+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new Entry([
6255
'name' => $typeName,
63-
'fields' => function() use ($entryTypeFields, $typeName) {
56+
'fields' => function() use ($context, $typeName) {
57+
$contentFieldGqlTypes = self::getContentFields($context);
58+
$entryTypeFields = array_merge(EntryInterface::getFieldDefinitions(), $contentFieldGqlTypes);
6459
return Craft::$app->getGql()->prepareFieldDefinitions($entryTypeFields, $typeName);
6560
},
6661
]));

src/gql/types/generators/GlobalSetType.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,11 @@ public static function generateType(mixed $context): ObjectType
6565
{
6666
$typeName = self::getName($context);
6767

68-
$contentFieldGqlTypes = self::getContentFields($context);
69-
70-
$globalSetFields = array_merge(GlobalSetInterface::getFieldDefinitions(), $contentFieldGqlTypes);
71-
72-
return GqlEntityRegistry::getEntity($typeName) ?: GqlEntityRegistry::createEntity($typeName, new GlobalSet([
68+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new GlobalSet([
7369
'name' => $typeName,
74-
'fields' => function() use ($globalSetFields, $typeName) {
70+
'fields' => function() use ($context, $typeName) {
71+
$contentFieldGqlTypes = self::getContentFields($context);
72+
$globalSetFields = array_merge(GlobalSetInterface::getFieldDefinitions(), $contentFieldGqlTypes);
7573
return Craft::$app->getGql()->prepareFieldDefinitions($globalSetFields, $typeName);
7674
},
7775
]));

src/gql/types/generators/MatrixBlockType.php

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,13 @@ public static function generateType(mixed $context): ObjectType
5656
{
5757
$typeName = MatrixBlockElement::gqlTypeNameByContext($context);
5858

59-
if (!($entity = GqlEntityRegistry::getEntity($typeName))) {
60-
$contentFieldGqlTypes = self::getContentFields($context);
61-
$blockTypeFields = array_merge(MatrixBlockInterface::getFieldDefinitions(), $contentFieldGqlTypes);
62-
63-
// Generate a type for each block type
64-
$entity = GqlEntityRegistry::getEntity($typeName);
65-
66-
if (!$entity) {
67-
$entity = new MatrixBlock([
68-
'name' => $typeName,
69-
'fields' => function() use ($blockTypeFields, $typeName) {
70-
return Craft::$app->getGql()->prepareFieldDefinitions($blockTypeFields, $typeName);
71-
},
72-
]);
73-
74-
// It's possible that creating the matrix block triggered creating all matrix block types, so check again.
75-
$entity = GqlEntityRegistry::getEntity($typeName) ?: GqlEntityRegistry::createEntity($typeName, $entity);
76-
}
77-
}
78-
79-
return $entity;
59+
return GqlEntityRegistry::getOrCreate($typeName, fn() => new MatrixBlock([
60+
'name' => $typeName,
61+
'fields' => function() use ($context, $typeName) {
62+
$contentFieldGqlTypes = self::getContentFields($context);
63+
$blockTypeFields = array_merge(MatrixBlockInterface::getFieldDefinitions(), $contentFieldGqlTypes);
64+
return Craft::$app->getGql()->prepareFieldDefinitions($blockTypeFields, $typeName);
65+
},
66+
]));
8067
}
8168
}

0 commit comments

Comments
 (0)