Skip to content

Fix aggregate entity query return type. - #1063

Open
travis-bradbury wants to merge 1 commit into
mglaman:mainfrom
travis-bradbury:getAggregateQuery-return-type
Open

travis-bradbury wants to merge 1 commit into
mglaman:mainfrom
travis-bradbury:getAggregateQuery-return-type

Conversation

@travis-bradbury

@travis-bradbury travis-bradbury commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

This changes the entity query extension so aggregate queries get the return type array<int, array<string, string|null>>, instead of list<string>. Aggregate queries give arrays whose keys are the aggregated columns.

I added a fixture that includes the pattern I happened to see this with: an aggregate query wrapped in array_column(), then using that in a condition. Phpstan incorrectly reported that's it's always false, because array_column('foo', $array) when $array is list<string> would always be an empty array.

@travis-bradbury

Copy link
Copy Markdown
Contributor Author

The lint job failed due to apparently new things in phpstan, not things introduced in this PR.

@mglaman mglaman left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for this. The array_column() false positive is real and tracking aggregate queries on EntityQueryType is the right approach. A few changes below before merging.

#1064 and #1065 are on main now, so merging main into this branch should clear the lint failure.

Not blocking: \Drupal::entityQueryAggregate() still isn't covered, because DrupalStaticEntityQueryDynamicReturnTypeExtension doesn't handle it. That can be a follow-up. #669 is related.

if ($varType->isAggregate()) {
// An aggregate query returns a list of result rows, each keyed
// by the aggregate results and the groupBy columns.
$rowType = new ArrayType(new StringType(), new StringType());

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Row values can be NULL: SUM() over only NULL values, or groupBy() on a field some entities don't have, since the field is added with a LEFT join. Other values are strings because the core MySQL, PostgreSQL, and SQLite drivers all set PDO::ATTR_STRINGIFY_FETCHES.

$rowType = new ArrayType(new StringType(), TypeCombinator::addNull(new StringType()));

The fixture's expected types need to change to match.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The values are now expected to be string|null instead of string.

// by the aggregate results and the groupBy columns.
$rowType = new ArrayType(new StringType(), new StringType());
return $varType->hasAccessCheck()
? new ArrayType(new IntegerType(), $rowType)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

nit: QueryAggregate::result() builds the rows with $return[] =, so this is really a list. The existing branches use a plain ArrayType too, so it's fine to leave this as is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I left this as is.

@travis-bradbury
travis-bradbury force-pushed the getAggregateQuery-return-type branch from d876d0f to 3e7d57a Compare November 5, 2026 20:22
@travis-bradbury

travis-bradbury commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor Author

"Tests / Drupal core HEAD baseline check (pull_request)" failed. https://github.com/mglaman/phpstan-drupal/actions/runs/42102801854/job/127511254161?pr=1063. There's more fails in there than a previous merged PR (https://github.com/mglaman/phpstan-drupal/actions/runs/42082179892/job/127439501184) but they ran on the same drupal/core commit (7fa85d2).

The "Ignored error pattern loadIncludes.fileNotLoadable" error is an existing issue — it happened in the other branch too.

The other three, node/src/Plugin/views/argument/Vid.php, UniqueFieldValueValidator.php, and NoFieldItemsExistWithHigherCardinalityValidator.php are because there's errors that this PR fixes that are ignored in core's baseline. So after this lands and core updates, they can take those ignored errors out of the baseline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants