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

Prune unneeded entry fields from the GraphQL schema #16326

Merged
merged 3 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
- Added `craft\elements\User::getAffiliatedSite()`.
- Added `craft\elements\conditions\entries\FieldConditionRule`.
- Added `craft\events\DefineAltActionsEvent`.
- Added `craft\fields\BaseRelationField::gqlFieldArguments()`.
- Added `craft\fields\Color::$allowCustomColors`. ([#16249](https://github.com/craftcms/cms/pull/16249))
- Added `craft\fields\Color::$palette`. ([#16249](https://github.com/craftcms/cms/pull/16249))
- Added `craft\fields\Color::getDefaultColor()`. ([#16249](https://github.com/craftcms/cms/pull/16249))
Expand All @@ -78,6 +79,7 @@
- Added `craft\mail\Mailer::$siteOverrides`.
- Added `craft\models\MailSettings::$siteOverrides`.
- Added `craft\services\Elements::canSaveCanonical()`.
- Added `craft\services\Gql::getFieldLayoutArguments()`.
- Added `craft\web\View::setTwig()`.
- `craft\elements\NestedElementManager::getIndexHtml()` now supports passing `defaultSort` in the `$config` array. ([#16236](https://github.com/craftcms/cms/discussions/16236))
- `craft\elements\conditions\entries\MatrixFieldConditionRule` is now an alias of `FieldConditionRule`.
Expand All @@ -95,5 +97,6 @@
- Craft now keeps track of which site users registered from. When sending an email from the control panel, the current site is now set to the user’s affiliated site, if known. ([#16174](https://github.com/craftcms/cms/pull/16174))
- Database rows with foreign keys referencing nonexistent rows are now deleted via garbage collection.
- Pages which contain image transform generation URLs now set no-cache headers. ([#16195](https://github.com/craftcms/cms/discussions/16195))
- Reduced the size of GraphQL introspection schemas. ([#16326](https://github.com/craftcms/cms/pull/16326))
- Updated Twig to 3.15. ([#16207](https://github.com/craftcms/cms/discussions/16207))
- Fixed a bug where embedded element index filter HUDs were including condition rules for fields that weren’t applicable to the nested elements. ([#16289](https://github.com/craftcms/cms/discussions/16289))
27 changes: 27 additions & 0 deletions src/fields/BaseRelationField.php
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,33 @@ public function getContentGqlMutationArgumentType(): Type|array
];
}

/**
* Returns the custom field arguments for the selected source(s).
*
* @return array
* @since 5.6.0
*/
protected function gqlFieldArguments(): array
{
$elementSourcesService = Craft::$app->getElementSources();
$gqlService = Craft::$app->getGql();
$fieldLayouts = [];
$arguments = [];

foreach ((array)$this->getInputSources() as $source) {
$sourceFieldLayouts = $elementSourcesService->getFieldLayoutsForSource(static::elementType(), $source);
foreach ($sourceFieldLayouts as $fieldLayout) {
$fieldLayouts[$fieldLayout->uid] = $fieldLayout;
}
}

foreach ($fieldLayouts as $fieldLayout) {
$arguments += $gqlService->getFieldLayoutArguments($fieldLayout);
}

return $arguments;
}

// Events
// -------------------------------------------------------------------------

Expand Down
5 changes: 4 additions & 1 deletion src/fields/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ public function getContentGqlType(): Type|array
return [
'name' => $this->handle,
'type' => Type::nonNull(Type::listOf(EntryInterface::getType())),
'args' => EntryArguments::getArguments(),
'args' => [
...EntryArguments::getArguments(),
...$this->gqlFieldArguments(),
],
'resolve' => EntryResolver::class . '::resolve',
'complexity' => GqlHelper::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
];
Expand Down
9 changes: 8 additions & 1 deletion src/fields/Matrix.php
Original file line number Diff line number Diff line change
Expand Up @@ -1202,10 +1202,17 @@ public function getContentGqlType(): Type|array
$typeArray = EntryTypeGenerator::generateTypes($this);
$typeName = $this->handle . '_MatrixField';

$arguments = EntryArguments::getArguments();
$gqlService = Craft::$app->getGql();

foreach ($this->getEntryTypes() as $entryType) {
$arguments += $gqlService->getFieldLayoutArguments($entryType->getFieldLayout());
}

return [
'name' => $this->handle,
'type' => Type::nonNull(Type::listOf(Gql::getUnionType($typeName, $typeArray))),
'args' => EntryArguments::getArguments(),
'args' => $arguments,
'resolve' => EntryResolver::class . '::resolve',
'complexity' => Gql::eagerLoadComplexity(),
];
Expand Down
2 changes: 1 addition & 1 deletion src/gql/arguments/elements/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Entry extends StructureElementArguments
*/
public static function getArguments(): array
{
return array_merge(parent::getArguments(), self::getContentArguments(), [
return array_merge(parent::getArguments(), [
'editable' => [
'name' => 'editable',
'type' => Type::boolean(),
Expand Down
48 changes: 37 additions & 11 deletions src/gql/interfaces/elements/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use craft\gql\types\DateTime;
use craft\gql\types\generators\EntryType;
use craft\helpers\Gql;
use craft\models\Section;
use craft\services\Gql as GqlService;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\Type;
Expand Down Expand Up @@ -69,7 +70,23 @@ public static function getName(): string
*/
public static function getFieldDefinitions(): array
{
return Craft::$app->getGql()->prepareFieldDefinitions(array_merge(parent::getFieldDefinitions(), static::getDraftFieldDefinitions(), self::getConditionalFields(), [
$gqlService = Craft::$app->getGql();
$entryArguments = EntryArguments::getArguments();
$allFieldArguments = EntryArguments::getContentArguments();
$sectionFieldArguments = [...$entryArguments];
$structureSectionFieldArguments = [...$entryArguments];

foreach (Gql::getSchemaContainedSections() as $section) {
foreach ($section->getEntryTypes() as $entryType) {
$entryTypeArguments = $gqlService->getFieldLayoutArguments($entryType->getFieldLayout());
$sectionFieldArguments += $entryTypeArguments;
if ($section->type === Section::TYPE_STRUCTURE) {
$structureSectionFieldArguments += $entryTypeArguments;
}
}
}

return Craft::$app->getGql()->prepareFieldDefinitions(array_merge(parent::getFieldDefinitions(), static::getDraftFieldDefinitions(), self::getConditionalFields($sectionFieldArguments), [
'canonicalId' => [
'name' => 'canonicalId',
'type' => Type::int(),
Expand Down Expand Up @@ -147,28 +164,28 @@ public static function getFieldDefinitions(): array
],
'children' => [
'name' => 'children',
'args' => EntryArguments::getArguments(),
'args' => $structureSectionFieldArguments,
'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
'description' => 'The entry’s children, if the section is a structure. Accepts the same arguments as the `entries` query.',
'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
],
'descendants' => [
'name' => 'descendants',
'args' => EntryArguments::getArguments(),
'args' => $structureSectionFieldArguments,
'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
'description' => 'The entry’s descendants, if the section is a structure. Accepts the same arguments as the `entries` query.',
'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
],
'parent' => [
'name' => 'parent',
'args' => EntryArguments::getArguments(),
'args' => $structureSectionFieldArguments,
'type' => EntryInterface::getType(),
'description' => 'The entry’s parent, if the section is a structure.',
'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
],
'ancestors' => [
'name' => 'ancestors',
'args' => EntryArguments::getArguments(),
'args' => $structureSectionFieldArguments,
'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
'description' => 'The entry’s ancestors, if the section is a structure. Accepts the same arguments as the `entries` query.',
'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
Expand All @@ -180,15 +197,21 @@ public static function getFieldDefinitions(): array
],
'localized' => [
'name' => 'localized',
'args' => EntryArguments::getArguments(),
'args' => [
...$entryArguments,
...$allFieldArguments,
],
'type' => Type::nonNull(Type::listOf(Type::nonNull(static::getType()))),
'description' => 'The same element in other locales.',
'complexity' => Gql::eagerLoadComplexity(),
],
'prev' => [
'name' => 'prev',
'type' => self::getType(),
'args' => EntryArguments::getArguments(),
'args' => [
...$entryArguments,
...$allFieldArguments,
],
'description' => 'Returns the previous element relative to this one, from a given set of criteria.',
'complexity' => function($childrenComplexity, $args) {
return $childrenComplexity + GqlService::GRAPHQL_COMPLEXITY_NPLUS1 * (int)!empty($args);
Expand All @@ -197,7 +220,10 @@ public static function getFieldDefinitions(): array
'next' => [
'name' => 'next',
'type' => self::getType(),
'args' => EntryArguments::getArguments(),
'args' => [
...$entryArguments,
...$allFieldArguments,
],
'description' => 'Returns the next element relative to this one, from a given set of criteria.',
'complexity' => function($childrenComplexity, $args) {
return $childrenComplexity + GqlService::GRAPHQL_COMPLEXITY_NPLUS1 * (int)!empty($args);
Expand All @@ -214,7 +240,7 @@ public static function getFieldDefinitions(): array
/**
* @inheritdoc
*/
protected static function getConditionalFields(): array
private static function getConditionalFields(array $sectionFieldArguments): array
{
$fields = [];
if (Gql::canQueryUsers()) {
Expand Down Expand Up @@ -254,7 +280,7 @@ protected static function getConditionalFields(): array
],
'drafts' => [
'name' => 'drafts',
'args' => EntryArguments::getArguments(),
'args' => $sectionFieldArguments,
'type' => Type::listOf(EntryInterface::getType()),
'description' => 'The drafts for the entry.',
'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
Expand All @@ -278,7 +304,7 @@ protected static function getConditionalFields(): array
],
'revisions' => [
'name' => 'revisions',
'args' => EntryArguments::getArguments(),
'args' => $sectionFieldArguments,
'type' => Type::listOf(EntryInterface::getType()),
'description' => 'The revisions for the entry.',
'complexity' => Gql::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD),
Expand Down
40 changes: 32 additions & 8 deletions src/gql/queries/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace craft\gql\queries;

use Craft;
use craft\gql\arguments\elements\Entry as EntryArguments;
use craft\gql\base\Query;
use craft\gql\GqlEntityRegistry;
Expand Down Expand Up @@ -50,21 +51,30 @@ public static function getQueries(bool $checkToken = true): array
return [
'entries' => [
'type' => Type::listOf(EntryInterface::getType()),
'args' => EntryArguments::getArguments(),
'args' => [
...EntryArguments::getArguments(),
...EntryArguments::getContentArguments(),
],
'resolve' => EntryResolver::class . '::resolve',
'description' => 'This query is used to query for entries.',
'complexity' => GqlHelper::relatedArgumentComplexity(),
],
'entryCount' => [
'type' => Type::nonNull(Type::int()),
'args' => EntryArguments::getArguments(),
'args' => [
...EntryArguments::getArguments(),
...EntryArguments::getContentArguments(),
],
'resolve' => EntryResolver::class . '::resolveCount',
'description' => 'This query is used to return the number of entries.',
'complexity' => GqlHelper::singleQueryComplexity(),
],
'entry' => [
'type' => EntryInterface::getType(),
'args' => EntryArguments::getArguments(),
'args' => [
...EntryArguments::getArguments(),
...EntryArguments::getContentArguments(),
],
'resolve' => EntryResolver::class . '::resolveOne',
'description' => 'This query is used to query for a single entry.',
'complexity' => GqlHelper::singleQueryComplexity(),
Expand All @@ -84,6 +94,7 @@ public static function getQueries(bool $checkToken = true): array
private static function sectionLevelFields(array $entryTypeGqlTypes): array
{
$gqlTypes = [];
$gqlService = Craft::$app->getGql();

foreach (GqlHelper::getSchemaContainedSections() as $section) {
$typeName = "{$section->handle}SectionEntriesQuery";
Expand All @@ -103,8 +114,9 @@ private static function sectionLevelFields(array $entryTypeGqlTypes): array
continue;
}

// Unset unusable arguments
$arguments = EntryArguments::getArguments();

// Unset unusable arguments
unset(
$arguments['section'],
$arguments['sectionId'],
Expand All @@ -113,6 +125,10 @@ private static function sectionLevelFields(array $entryTypeGqlTypes): array
$arguments['ownerId'],
);

foreach ($section->getEntryTypes() as $entryType) {
$arguments += $gqlService->getFieldLayoutArguments($entryType->getFieldLayout());
}

// Create the section query field
$sectionQueryType = [
'name' => "{$section->handle}Entries",
Expand Down Expand Up @@ -141,40 +157,48 @@ private static function sectionLevelFields(array $entryTypeGqlTypes): array
private static function nestedEntryFieldLevelFields(array $entryTypeGqlTypes): array
{
$gqlTypes = [];
$gqlService = Craft::$app->getGql();

foreach (GqlHelper::getSchemaContainedNestedEntryFields() as $field) {
$typeName = "{$field->handle}NestedEntriesQuery";
$fieldQueryType = GqlEntityRegistry::getEntity($typeName);

if (!$fieldQueryType) {
$entryTypesInField = [];
$entryTypeGqlTypesInField = [];

// Loop through the entry types and create further queries
foreach ($field->getFieldLayoutProviders() as $provider) {
if ($provider instanceof EntryType && isset($entryTypeGqlTypes[$provider->id])) {
$entryTypesInField[] = $entryTypeGqlTypes[$provider->id];
$entryTypesInField[] = $provider;
$entryTypeGqlTypesInField[] = $entryTypeGqlTypes[$provider->id];
}
}

if (empty($entryTypesInField)) {
if (empty($entryTypeGqlTypesInField)) {
continue;
}

// Unset unusable arguments
$arguments = EntryArguments::getArguments();

// Unset unusable arguments
unset(
$arguments['section'],
$arguments['sectionId'],
$arguments['field'],
$arguments['fieldId'],
);

foreach ($entryTypesInField as $entryType) {
$arguments += $gqlService->getFieldLayoutArguments($entryType->getFieldLayout());
}

// Create the query field
$fieldQueryType = [
'name' => "{$field->handle}FieldEntries",
'args' => $arguments,
'description' => sprintf('Entries within the “%s” %s field.', $field->name, $field::displayName()),
'type' => Type::listOf(GqlHelper::getUnionType("{$field->handle}FieldEntryUnion", $entryTypesInField)),
'type' => Type::listOf(GqlHelper::getUnionType("{$field->handle}FieldEntryUnion", $entryTypeGqlTypesInField)),
// Enforce the section argument and set the source to `null`, to enforce a new element query.
'resolve' => fn($source, array $arguments, $context, ResolveInfo $resolveInfo) =>
EntryResolver::resolve(null, $arguments + ['field' => $field->handle], $context, $resolveInfo),
Expand Down
5 changes: 4 additions & 1 deletion src/gql/types/input/criteria/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ public static function getType(): mixed

return GqlEntityRegistry::getOrCreate($typeName, fn() => new InputObjectType([
'name' => $typeName,
'fields' => fn() => EntryArguments::getArguments(),
'fields' => fn() => [
...EntryArguments::getArguments(),
...EntryArguments::getContentArguments(),
],
]));
}
}
6 changes: 5 additions & 1 deletion src/gql/types/input/criteria/EntryRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public static function getType(): mixed

return GqlEntityRegistry::getOrCreate($typeName, fn() => new InputObjectType([
'name' => $typeName,
'fields' => fn() => EntryArguments::getArguments() + RelationCriteria::getArguments(),
'fields' => fn() => [
...EntryArguments::getArguments(),
...EntryArguments::getContentArguments(),
...RelationCriteria::getArguments(),
],
]));
}
}
Loading