{"record":{"id":"6ef6cbe7ffed572e","repo":"doctrine/orm","slug":"selecting-a-collection-by-index-is-only-supported","errorCode":null,"errorMessage":"Selecting a collection by index is only supported on indexed collections.","messagePattern":"Selecting a collection by index is only supported on indexed collections\\.","errorType":"exception","errorClass":"BadMethodCallException","httpStatus":null,"severity":"error","filePath":"src/Persisters/Collection/ManyToManyPersister.php","lineNumber":89,"sourceCode":"                $deleteTypes,\n            );\n        }\n\n        foreach ($collection->getInsertDiff() as $element) {\n            $this->conn->executeStatement(\n                $insertSql,\n                $this->getInsertRowSQLParameters($collection, $element),\n                $insertTypes,\n            );\n        }\n    }\n\n    public function get(PersistentCollection $collection, mixed $index): object|null\n    {\n        $mapping = $this->getMapping($collection);\n\n        if (! $mapping->isIndexed()) {\n            throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');\n        }\n\n        $persister = $this->uow->getEntityPersister($mapping->targetEntity);\n        $mappedKey = $mapping->isOwningSide()\n            ? $mapping->inversedBy\n            : $mapping->mappedBy;\n\n        assert($mappedKey !== null);\n\n        return $persister->load(\n            [$mappedKey => $collection->getOwner(), $mapping->indexBy() => $index],\n            null,\n            $mapping,\n            [],\n            LockMode::NONE,\n            1,\n        );\n    }","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/Persisters/Collection/ManyToManyPersister.php#L71-L107","documentation":"ManyToManyPersister::get() loads a single element of an EXTRA_LAZY many-to-many collection straight from the database by its key, which requires knowing which target field is the key (indexBy). If the association mapping has no indexBy, the persister cannot translate the key into a WHERE condition and throws BadMethodCallException. PersistentCollection::get()/offsetGet() only reach the persister when the collection is uninitialized and fetch is EXTRA_LAZY.","triggerScenarios":"$collection->get($key) or $collection[$key] on an uninitialized EXTRA_LAZY many-to-many association without `indexBy` in its #[ManyToMany]/YAML/XML mapping.","commonSituations":"Switching an association to fetch: EXTRA_LAZY for performance and keeping array-style access; assuming collections are keyed by target primary key without declaring indexBy.","solutions":["Declare indexBy in the mapping: #[ManyToMany(targetEntity: Tag::class, indexBy: 'id')] (or any unique target field)","Drop EXTRA_LAZY for that association so get() initializes the collection in memory first","Force initialization before access: $collection->initialize(); then $collection->get($key)"],"exampleFix":"// before\n#[ManyToMany(targetEntity: Tag::class), Fetch(FetchMode::EXTRA_LAZY)]\nprivate Collection $tags;\n$tag = $post->getTags()->get(42); // throws\n\n// after\n#[ManyToMany(targetEntity: Tag::class, indexBy: 'id'), Fetch(FetchMode::EXTRA_LAZY)]\nprivate Collection $tags;","handlingStrategy":"validation","validationCode":"$mapping = $em->getClassMetadata($post::class)->getAssociationMapping('tags');\nif (! $mapping->isIndexed()) {\n    $post->getTags()->initialize(); // fall back to in-memory keyed access\n}\n$tag = $post->getTags()->get($key);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair EXTRA_LAZY with indexBy when keyed access is needed","Document the intended key field next to the association","Test keyed reads on every EXTRA_LAZY collection"],"tags":["collection","many-to-many","extra-lazy","indexby"],"backgroundTag":"unindexed-collection-access","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}