QueryBuilder IndexBy not indexing a result set #7288

Open
opened 2026-01-22 15:49:16 +01:00 by admin · 16 comments
Owner

Originally created by @SergeC on GitHub (Dec 23, 2023).

Bug Report

Q A
BC Break yes
Version 2.17.2

Summary

QueryBuilder ignores indexBy argument and returns the result set indexed by PHP's array-keys.

Current behavior

QueryBuilder returns the result set indexed by PHP's array-keys.

How to reproduce

/* @var EntityRepository $this */
$this
            ->createQueryBuilder(alias: 'j', indexBy: 'j.id')
            ->select('j.value')
            ->andWhere('j.enabled = true')
            ->setMaxResults(10)
            ->getQuery()
            ->getSingleColumnResult();

Expected behavior

QueryBuilder should return the result set indexed by values in the database, specified in the 2nd argument to createQueryBuilder.

Installed dependencies:

doctrine/cache                        2.2.0   PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.
doctrine/collections                  2.1.4   PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.
doctrine/common                       3.4.3   PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.
doctrine/dbal                         3.7.2   Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.
doctrine/deprecations                 1.1.2   A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.
doctrine/doctrine-bundle              2.11.1  Symfony DoctrineBundle
doctrine/doctrine-migrations-bundle   3.3.0   Symfony DoctrineMigrationsBundle
doctrine/event-manager                2.0.0   The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.
doctrine/inflector                    2.0.8   PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.
doctrine/instantiator                 2.0.0   A small, lightweight utility to instantiate objects in PHP without invoking their constructors
doctrine/lexer                        2.1.0   PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.
doctrine/migrations                   3.7.2   PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to us...
doctrine/orm                          2.17.2  Object-Relational-Mapper for PHP
doctrine/persistence                  3.2.0   The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.
doctrine/sql-formatter                1.1.3   a PHP SQL highlighting library

Actual result:
image
Expected result:
image

Originally created by @SergeC on GitHub (Dec 23, 2023). ### Bug Report <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | BC Break | yes | Version | 2.17.2 #### Summary QueryBuilder ignores indexBy argument and returns the result set indexed by PHP's array-keys. #### Current behavior QueryBuilder returns the result set indexed by PHP's array-keys. #### How to reproduce ```PHP /* @var EntityRepository $this */ $this ->createQueryBuilder(alias: 'j', indexBy: 'j.id') ->select('j.value') ->andWhere('j.enabled = true') ->setMaxResults(10) ->getQuery() ->getSingleColumnResult(); ``` #### Expected behavior QueryBuilder should return the result set indexed by values in the database, specified in the 2nd argument to `createQueryBuilder`. Installed dependencies: ``` doctrine/cache 2.2.0 PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others. doctrine/collections 2.1.4 PHP Doctrine Collections library that adds additional functionality on top of PHP arrays. doctrine/common 3.4.3 PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more. doctrine/dbal 3.7.2 Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management. doctrine/deprecations 1.1.2 A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages. doctrine/doctrine-bundle 2.11.1 Symfony DoctrineBundle doctrine/doctrine-migrations-bundle 3.3.0 Symfony DoctrineMigrationsBundle doctrine/event-manager 2.0.0 The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects. doctrine/inflector 2.0.8 PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words. doctrine/instantiator 2.0.0 A small, lightweight utility to instantiate objects in PHP without invoking their constructors doctrine/lexer 2.1.0 PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers. doctrine/migrations 3.7.2 PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to us... doctrine/orm 2.17.2 Object-Relational-Mapper for PHP doctrine/persistence 3.2.0 The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share. doctrine/sql-formatter 1.1.3 a PHP SQL highlighting library ``` Actual result: ![image](https://github.com/doctrine/orm/assets/1062847/d8e165de-5512-4eb3-b725-bbbb0c48cb2d) Expected result: ![image](https://github.com/doctrine/orm/assets/1062847/00bae76e-a7df-47c2-8bb6-adf87759e38d)
Author
Owner

@greg0ire commented on GitHub (Dec 23, 2023):

Assuming $this is an entityrepository instance, createQueryBuilder already calls select internally. With your call, you're replacing that select, making sure j.id is no longer selected.

@greg0ire commented on GitHub (Dec 23, 2023): Assuming `$this` is an entityrepository instance, `createQueryBuilder` already calls `select` internally. With your call, you're replacing that select, making sure `j.id` is no longer selected.
Author
Owner

@SergeC commented on GitHub (Dec 23, 2023):

In this use-case, I fetch only one column. Which means I need to specify it in select. Doctrine should automatically add indexing column to select statement. Even when I add indexing column to select - nothing changes.
image
image

@SergeC commented on GitHub (Dec 23, 2023): In this use-case, I fetch only one column. Which means I need to specify it in select. Doctrine should automatically add indexing column to select statement. Even when I add indexing column to select - nothing changes. ![image](https://github.com/doctrine/orm/assets/1062847/7b2f2962-a3d7-480b-8c71-3daa99e6d128) ![image](https://github.com/doctrine/orm/assets/1062847/611725c5-d6c8-4cde-bc51-193bdb676181)
Author
Owner

@greg0ire commented on GitHub (Dec 23, 2023):

Huh, I did not expect that. Maybe it has to do with getSingleColumnResult() . Can you try other possibilities?

@greg0ire commented on GitHub (Dec 23, 2023): Huh, I did not expect that. Maybe it has to do with `getSingleColumnResult()` . Can you try other possibilities?
Author
Owner

@SergeC commented on GitHub (Dec 23, 2023):

image
image

@SergeC commented on GitHub (Dec 23, 2023): ![image](https://github.com/doctrine/orm/assets/1062847/84c7d06e-37e9-4169-998e-a4c2fde5724d) ![image](https://github.com/doctrine/orm/assets/1062847/1a44b96f-36e4-41b9-91c2-710b94c0cbc9)
Author
Owner

@greg0ire commented on GitHub (Dec 23, 2023):

I meant with j.id selected

@greg0ire commented on GitHub (Dec 23, 2023): I meant with `j.id` selected
Author
Owner

@SergeC commented on GitHub (Dec 24, 2023):

image
This adds id to the result set along with array keys.

@SergeC commented on GitHub (Dec 24, 2023): ![image](https://github.com/doctrine/orm/assets/1062847/69147fb4-f031-41a9-9811-5b4e6cf31305) This adds id to the result set along with array keys.
Author
Owner

@greg0ire commented on GitHub (Dec 24, 2023):

Okay. This means this issue really is about index by + getSingleColumnResult.

@greg0ire commented on GitHub (Dec 24, 2023): Okay. This means this issue really is about index by + `getSingleColumnResult`.
Author
Owner

@beberlei commented on GitHub (Dec 26, 2023):

I dont understand, where is index by configured here? Maybe i missef it, but this is only a non entity query here, getSingleColumnResult has no index by behavior afair

@beberlei commented on GitHub (Dec 26, 2023): I dont understand, where is index by configured here? Maybe i missef it, but this is only a non entity query here, getSingleColumnResult has no index by behavior afair
Author
Owner

@greg0ire commented on GitHub (Dec 26, 2023):

The second argument of EntityRepository::createQueryBuilder() configures it:

f80ef66ffb/lib/Doctrine/ORM/EntityRepository.php (L76-L89)

@greg0ire commented on GitHub (Dec 26, 2023): The second argument of `EntityRepository::createQueryBuilder()` configures it: https://github.com/doctrine/orm/blob/f80ef66ffb68bfa842071e2d818e87a07a2d014e/lib/Doctrine/ORM/EntityRepository.php#L76-L89
Author
Owner

@SergeC commented on GitHub (Feb 5, 2024):

@greg0ire @beberlei Any update on this? Any other info needed from my side?

@SergeC commented on GitHub (Feb 5, 2024): @greg0ire @beberlei Any update on this? Any other info needed from my side?
Author
Owner

@jurchiks commented on GitHub (Jun 17, 2024):

I just stumbled upon this, good thing I decided to google it.
It is stupid that something like this happens; one would think that indexBy is added implicitly regardless of what the user does with select(), because it is REQUIRED by the code. It should NOT be overridable by user.

I was wondering why my "account balance mapped by account ID" query was returning 0-indexed results, made no goddamn sense, and the subsequent code was clearly doing the wrong thing, when it absolutely shouldn't have. Turns out the problem wasn't in my code, but in Doctrine being dumb yet again... Such an overcomplicated mass of code, and yet.

@jurchiks commented on GitHub (Jun 17, 2024): I just stumbled upon this, good thing I decided to google it. It is stupid that something like this happens; one would think that `indexBy` is added implicitly regardless of what the user does with `select()`, because it is REQUIRED by the code. It should NOT be overridable by user. I was wondering why my "account balance mapped by account ID" query was returning 0-indexed results, made no goddamn sense, and the subsequent code was clearly doing the wrong thing, when it absolutely shouldn't have. Turns out the problem wasn't in my code, but in Doctrine being dumb yet again... Such an overcomplicated mass of code, and yet.
Author
Owner

@rasstislav commented on GitHub (Sep 19, 2024):

@greg0ire Hello. Anything new on this issue?

@rasstislav commented on GitHub (Sep 19, 2024): @greg0ire Hello. Anything new on this issue?
Author
Owner

@greg0ire commented on GitHub (Sep 19, 2024):

@rasstislav not as far as I can tell. Do you want to work on a fix?

@greg0ire commented on GitHub (Sep 19, 2024): @rasstislav not as far as I can tell. Do you want to work on a fix?
Author
Owner

@Asenar commented on GitHub (Nov 29, 2024):

Hi @greg0ire , I have the same issue and I would like to contribute, but I'm not sure where to begin.

Is it a good start to write the test in BasicFunctionalTest ?

<?php

class BasicFunctionalTest {

    // …
    
    public function testSingleColumnIndexedByPrimaryKeyQuery(): void
    {

        $asenar           = new CmsUser();
        $asenar->name     = 'Michaël';
        $asenar->username = 'asenar';
        $this->_em->persist($asenar);
        $this->_em->flush();
        $query = $this->_em->createQuery('select u.name from Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.username');
        $users = $query->getSingleColumnResult();
        self::assertCount(1, $users);
        self::assertSame(['asenar' => 'Michaël'], $users);
    }
}
@Asenar commented on GitHub (Nov 29, 2024): Hi @greg0ire , I have the same issue and I would like to contribute, but I'm not sure where to begin. Is it a good start to write the test in BasicFunctionalTest ? ```php <?php class BasicFunctionalTest { // … public function testSingleColumnIndexedByPrimaryKeyQuery(): void { $asenar = new CmsUser(); $asenar->name = 'Michaël'; $asenar->username = 'asenar'; $this->_em->persist($asenar); $this->_em->flush(); $query = $this->_em->createQuery('select u.name from Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.username'); $users = $query->getSingleColumnResult(); self::assertCount(1, $users); self::assertSame(['asenar' => 'Michaël'], $users); } } ```
Author
Owner

@greg0ire commented on GitHub (Nov 29, 2024):

Yes, it's a good start 👍

@greg0ire commented on GitHub (Nov 29, 2024): Yes, it's a good start 👍
Author
Owner

@Arkemlar commented on GitHub (Oct 25, 2025):

Having to add array_column() cycle on the result dataset because of this, not too cool workaround because it is not OK to do so with large datasets.

@Arkemlar commented on GitHub (Oct 25, 2025): Having to add array_column() cycle on the result dataset because of this, not too cool workaround because it is not OK to do so with large datasets.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7288