Skip to content

Fix relations to object folder - #1054

Merged
robertSt7 merged 6 commits into
pimcore:2026.2from
blankse:fix_folder_relation
Sep 7, 2026
Merged

robertSt7 merged 6 commits into
pimcore:2026.2from
blankse:fix_folder_relation

Conversation

@blankse

@blankse blankse commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Error when you try to load a relation (editable relations, property) with a folder object value:

{
  "errors": [
    {
      "message": "Internal server error",
      "locations": [
        {
          "line": 142,
          "column": 3
        }
      ],
      "path": [
        "getDocument",
        "editables",
        79,
        "relations",
        0
      ],
      "extensions": {
        "debugMessage": "Warning: Undefined array key \"folder\"",
        "file": "/var/www/html/vendor/pimcore/data-hub/src/GraphQL/ClassTypeDefinitions.php",
        "line": 66,
        "trace": [
          {
            "file": "/var/www/html/vendor/pimcore/data-hub/src/GraphQL/General/AnyTargetType.php",
            "line": 90,
            "call": "Pimcore\\Bundle\\DataHubBundle\\GraphQL\\ClassTypeDefinitions::get('folder')"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1086,
            "call": "Pimcore\\Bundle\\DataHubBundle\\GraphQL\\General\\AnyTargetType::resolveType(instance of Pimcore\\Bundle\\DataHubBundle\\GraphQL\\ElementDescriptor(3), array(2), instance of GraphQL\\Type\\Definition\\ResolveInfo)"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 923,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeAbstractValue(GraphQLType: AnyTarget, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(5), array(5), instance of Pimcore\\Bundle\\DataHubBundle\\GraphQL\\ElementDescriptor(3), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 783,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValue(GraphQLType: AnyTarget, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(5), array(5), instance of Pimcore\\Bundle\\DataHubBundle\\GraphQL\\ElementDescriptor(3), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1025,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError(GraphQLType: AnyTarget, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(5), array(5), instance of Pimcore\\Bundle\\DataHubBundle\\GraphQL\\ElementDescriptor(3), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 906,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeListValue(GraphQLType: [AnyTarget], instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(4), array(4), array(1), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 783,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValue(GraphQLType: [AnyTarget], instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(4), array(4), array(1), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 663,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError(GraphQLType: [AnyTarget], instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(4), array(4), array(1), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1372,
            "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: document_editableRelations, instance of Pimcore\\Model\\Document\\Editable\\Relations, instance of ArrayObject(1), 'relations', array(4), array(4), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1304,
            "call": "GraphQL\\Executor\\ReferenceExecutor::executeFields(GraphQLType: document_editableRelations, instance of Pimcore\\Model\\Document\\Editable\\Relations, array(3), array(3), instance of ArrayObject(3), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1256,
            "call": "GraphQL\\Executor\\ReferenceExecutor::collectAndExecuteSubfields(GraphQLType: document_editableRelations, instance of ArrayObject(1), array(3), array(3), instance of Pimcore\\Model\\Document\\Editable\\Relations, array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1115,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeObjectValue(GraphQLType: document_editableRelations, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(3), array(3), instance of Pimcore\\Model\\Document\\Editable\\Relations, array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 923,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeAbstractValue(GraphQLType: DocumentElement, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(3), array(3), instance of Pimcore\\Model\\Document\\Editable\\Relations, array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 783,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValue(GraphQLType: DocumentElement, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(3), array(3), instance of Pimcore\\Model\\Document\\Editable\\Relations, array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1025,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError(GraphQLType: DocumentElement, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(3), array(3), instance of Pimcore\\Model\\Document\\Editable\\Relations, array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 906,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeListValue(GraphQLType: [DocumentElement], instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(2), array(2), array(106), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 783,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValue(GraphQLType: [DocumentElement], instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(2), array(2), array(106), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 663,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError(GraphQLType: [DocumentElement], instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(2), array(2), array(106), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1372,
            "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: document_page, array(8), instance of ArrayObject(1), 'editables', array(2), array(2), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1304,
            "call": "GraphQL\\Executor\\ReferenceExecutor::executeFields(GraphQLType: document_page, array(8), array(1), array(1), instance of ArrayObject(6), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1256,
            "call": "GraphQL\\Executor\\ReferenceExecutor::collectAndExecuteSubfields(GraphQLType: document_page, instance of ArrayObject(1), array(1), array(1), array(8), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1115,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeObjectValue(GraphQLType: document_page, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(1), array(1), array(8), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 923,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeAbstractValue(GraphQLType: Document, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(1), array(1), array(8), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 783,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValue(GraphQLType: Document, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(1), array(1), array(8), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 663,
            "call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError(GraphQLType: Document, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(1), array(1), array(8), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 1372,
            "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Query, array(0), instance of ArrayObject(1), 'getDocument', array(1), array(1), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 310,
            "call": "GraphQL\\Executor\\ReferenceExecutor::executeFields(GraphQLType: Query, array(0), array(0), array(0), instance of ArrayObject(1), array(2))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
            "line": 249,
            "call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, array(0))"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/Executor.php",
            "line": 184,
            "call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/GraphQL.php",
            "line": 164,
            "call": "GraphQL\\Executor\\Executor::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, array(0), array(2), array(1), null, null)"
          },
          {
            "file": "/var/www/html/vendor/webonyx/graphql-php/src/GraphQL.php",
            "line": 98,
            "call": "GraphQL\\GraphQL::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, '...', array(0), array(2), array(1), null, null, null)"
          },
          {
            "file": "/var/www/html/vendor/pimcore/data-hub/src/Controller/WebserviceController.php",
            "line": 200,
            "call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, '...', array(0), array(2), array(1), null, null, null)"
          },
          {
            "file": "/var/www/html/vendor/symfony/http-kernel/HttpKernel.php",
            "line": 163,
            "call": "Pimcore\\Bundle\\DataHubBundle\\Controller\\WebserviceController::webonyxAction(instance of Pimcore\\Bundle\\DataHubBundle\\GraphQL\\Service, instance of Pimcore\\Localization\\LocaleService, instance of Pimcore\\Model\\Factory, instance of Symfony\\Component\\HttpFoundation\\Request, instance of Pimcore\\Helper\\LongRunningHelper)"
          },
          {
            "file": "/var/www/html/vendor/symfony/http-kernel/HttpKernel.php",
            "line": 75,
            "call": "Symfony\\Component\\HttpKernel\\HttpKernel::handleRaw(instance of Symfony\\Component\\HttpFoundation\\Request, 1)"
          },
          {
            "file": "/var/www/html/vendor/symfony/http-kernel/Kernel.php",
            "line": 202,
            "call": "Symfony\\Component\\HttpKernel\\HttpKernel::handle(instance of Symfony\\Component\\HttpFoundation\\Request, 1, true)"
          },
          {
            "file": "/var/www/html/public/index.php",
            "line": 36,
            "call": "Symfony\\Component\\HttpKernel\\Kernel::handle(instance of Symfony\\Component\\HttpFoundation\\Request)"
          }
        ]
      }
    }
  ],
  ...
}

@sonarqubecloud

sonarqubecloud Bot commented Mar 3, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 New Bugs (required ≤ 0)
B Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@blankse
blankse force-pushed the fix_folder_relation branch from 4e30e1d to 5bbf24c Compare June 9, 2026 12:51
@blankse
blankse changed the base branch from 1.9 to 2026.1 June 9, 2026 12:51
@sonarqubecloud

sonarqubecloud Bot commented Jun 9, 2026

Copy link
Copy Markdown

@blankse

blankse commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@kingjia90 Can you review this also please?

@blankse

blankse commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

@robertSt7 Can you review this also please?

@blankse

blankse commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@markus-moser Can you review this also please?

@robertSt7
robertSt7 changed the base branch from 2026.1 to 2026.2 July 9, 2026 13:15
@robertSt7 robertSt7 self-assigned this Jul 9, 2026
@robertSt7 robertSt7 added the Bug label Jul 9, 2026
Copilot AI review requested due to automatic review settings July 22, 2026 08:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates GraphQL relation resolution to recognize data object folders and refreshes Studio build artifacts.

Changes:

  • Resolves folder descriptors using _object_folder.
  • Improves missing class-type handling.
  • Updates generated Studio assets and build paths.

Reviewed changes

Copilot reviewed 12 out of 27 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/GraphQL/PropertyType/ObjectsType.php Adds folder resolution for object properties.
src/GraphQL/General/AnyTargetType.php Adds folder resolution for editable relations.
src/GraphQL/DataObjectType/AbstractRelationsType.php Adds folder union membership and resolution.
src/GraphQL/ClassTypeDefinitions.php Avoids undefined-key warnings.
.../static/js/remoteEntry.js.LICENSE.txt Adds dependency licenses.
.../static/js/main.9c3ad8a2.js.LICENSE.txt Adds bundle license.
.../static/js/main.9c3ad8a2.js Adds generated main bundle.
.../static/js/async/840.4693a4bb.js.LICENSE.txt Adds dependency licenses.
.../static/js/async/696.3b1d6da3.js.LICENSE.txt Adds bundle license.
.../static/js/async/696.3b1d6da3.js Adds generated configuration chunk.
.../async/__federation_expose_plugins.c4cf01bd.js.LICENSE.txt Adds plugin chunk license.
.../async/__federation_expose_plugins.c4cf01bd.js Adds generated plugin chunk.
.../async/__federation_expose_default_export.87553f32.js.LICENSE.txt Adds export chunk license.
.../async/__federation_expose_default_export.87553f32.js Adds generated export chunk.
.../static/js/109.62a4c31b.js.LICENSE.txt Adds runtime dependency licenses.
.../a680030b-9298-45e8-9a3f-ad0bc24086c1/mf-stats.json Updates module federation path.
.../a680030b-9298-45e8-9a3f-ad0bc24086c1/mf-manifest.json Updates federation manifest path.
.../a680030b-9298-45e8-9a3f-ad0bc24086c1/manifest.json Adds new build manifest.
.../a680030b-9298-45e8-9a3f-ad0bc24086c1/main.html References the new build path.
.../a680030b-9298-45e8-9a3f-ad0bc24086c1/exposeRemote.js Updates the remote-entry path.
.../a680030b-9298-45e8-9a3f-ad0bc24086c1/entrypoints.json Adds new build entrypoints.
.../785fd5b2-5311-4070-9fc4-81949c904ac6/manifest.json Removes the old manifest.
.../785fd5b2-5311-4070-9fc4-81949c904ac6/entrypoints.json Removes old entrypoints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/GraphQL/PropertyType/ObjectsType.php
blankse and others added 3 commits July 22, 2026 19:05
Relation/property type resolvers returned a class type definition for
object folders, which fails since folders have no class. Route folders
to the registered '_object_folder' type instead, and guard
ClassTypeDefinitions::get() against undefined indexes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
getTypes() builds the union member types eagerly (via Schema::getTypeMap()).
For a relation field that allows object folders, the 'folder' pseudo-class
reached ClassTypeDefinitions::get('folder') and threw
"type definition folder not found", failing every query against the schema.

Route 'folder' to the _object_folder type, mirroring the resolveType() fix.
resolveType() returns _object_folder for object-folder elements, but the
union never listed it as a possible type, so webonyx rejected folder values
with "resolved to a type that is not a possible type". Register it under the
same querySchemaEnabled('object_folder') guard used by AnyTargetType.
@blankse
blankse force-pushed the fix_folder_relation branch from 5839932 to 231a762 Compare July 22, 2026 17:05
@sonarqubecloud

Copy link
Copy Markdown

@robertSt7

Copy link
Copy Markdown
Contributor

Hey @blankse — the resolveType() fixes look correct, but I think there's one gap left.

AbstractRelationsType::getTypes() only adds _object_folder to the union when 'folder' is explicitly listed in a restricted classes config. When a relation field has no class restriction (!$fd->getClasses()), it still falls back to ClassTypeDefinitions::getAll(), which never includes _object_folder — and AllowObjectRelationTrait shows folders are allowed there too. So an unrestricted relation with a folder value will still fail with "resolved to a type that is not a possible type", same class of bug you already fixed in ObjectsType::getTypes().

Can you mirror that fix into the !$fd->getClasses() branch here? Also, a regression test for this case would be good to add.

Thanks!

AbstractRelationsType::getTypes() only added _object_folder when 'folder'
was listed in the field's class restriction. Without a restriction it fell
back to ClassTypeDefinitions::getAll(), which is built from the classes
table and therefore never contains the folder type - while
allowObjectRelation() accepts folders exactly in that case. A folder value
in such a relation still failed with "resolved to a type that is not a
possible type".

Add the folder type to the unrestricted branch as well and cover all three
cases (unrestricted, restricted to folder, restricted to a class) with a
unit test.
@blankse

blankse commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Confirmed and fixed in 95719b6.

ClassTypeDefinitions::build() only fills $definitions from the classes table, so getAll() can never contain _object_folder, while AllowObjectRelationTrait::allowObjectRelation() rejects a DataObject\Folder only when count($allowedClasses) > 0 — i.e. folders are allowed exactly in the unrestricted case. The union was therefore missing the folder type while resolveType() returned it.

getTypes() now adds _object_folder in the !$fd->getClasses() branch too, plus a unit test (tests/GraphQL/DataObjectType/AbstractRelationsTypeTest.php) covering all three cases: unrestricted, restricted to folder, restricted to a class.

One open point for you: AnyTargetType and PropertyType\ObjectsType gate the folder type behind querySchemaEnabled('object_folder'), whereas both branches in AbstractRelationsType::getTypes() add it unconditionally. I kept it unconditional for consistency inside that class and because resolveType() returns the folder type regardless of that setting. Tell me if you prefer it gated — that would be a change in both relation branches, not only the new one.

Building the service via ReflectionClass::newInstanceWithoutConstructor()
tripped php:S3011 (accessibility bypass). Fetch the real service from the
container like ResolveTest does and register/restore the folder type through
the public registerDataObjectDataTypes()/getDataObjectDataTypes() API.
Also splits the over-long assertion message (php:S103).
@sonarqubecloud

sonarqubecloud Bot commented Sep 1, 2026

Copy link
Copy Markdown

@robertSt7 robertSt7 added this to the 2026.2.7 milestone Sep 4, 2026
@robertSt7
robertSt7 merged commit 368fc3f into pimcore:2026.2 Sep 7, 2026
24 of 25 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 7, 2026
@robertSt7

Copy link
Copy Markdown
Contributor

@blankse Thanks for fixing the relations to object folder

@blankse
blankse deleted the fix_folder_relation branch September 7, 2026 11:00
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants