Cannot use criteria matching with entities where the primary key is a binary ULID #6824

Open
opened 2026-01-22 15:39:30 +01:00 by admin · 0 comments
Owner

Originally created by @jdelaune on GitHub (Sep 8, 2021).

I'm trying to do an efficient join using criteria in one of my entities like:

    /**
     * @ORM\Id()
     * @ORM\Column(type="ulid", unique=true)
     */
    private Ulid $id;

    /**
     * @var Collection<User>
     * @ORM\ManyToMany(targetEntity="User", mappedBy="teams", fetch="EXTRA_LAZY")
     */
    private Collection $users;

    public function getMemberSample(): Collection
    {
        $criteria = Criteria::create()
            ->orderBy(['lastName' => 'ASC'])
            ->setMaxResults(5)
        ;

        return $this->users->matching($criteria);
    }

However the query doctrine creates is:

SELECT te.id AS id, te.first_name AS first_name, te.last_name AS last_name FROM users te JOIN user_teams t ON t.user_id = te.id WHERE t.team_id = '01EP7GVKX9K05ZK8G9ZRZTTW3N' ORDER BY last_name ASC LIMIT 5;

As you can see it has just turned the ULID into a string which won't match the binary.

I feel like this maybe come from vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php

    /**
     * {@inheritDoc}
     */
    public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
    {
        $mapping       = $collection->getMapping();
        $owner         = $collection->getOwner();
        $ownerMetadata = $this->em->getClassMetadata(get_class($owner));
        $id            = $this->uow->getEntityIdentifier($owner);
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
        $onConditions  = $this->getOnConditionSQL($mapping);
        $whereClauses  = $params = [];
        if (! $mapping['isOwningSide']) {
            $associationSourceClass = $targetClass;
            $mapping                = $targetClass->associationMappings[$mapping['mappedBy']];
            $sourceRelationMode     = 'relationToTargetKeyColumns';
        } else {
            $associationSourceClass = $ownerMetadata;
            $sourceRelationMode     = 'relationToSourceKeyColumns';
        }
        foreach ($mapping[$sourceRelationMode] as $key => $value) {
            $whereClauses[] = sprintf('t.%s = ?', $key);
            $params[]       = $ownerMetadata->containsForeignIdentifier
                ? $id[$ownerMetadata->getFieldForColumn($value)]
                : $id[$ownerMetadata->fieldNames[$value]];
        }

Where maybe the $params[] being created here isn't using the binary value.

Anyone know where this bug might lie who knows a bit more about how it work, I'm also not sure who is responsible, Doctrine ORM, Doctrine Bundle, Symfony Uid...

Originally created by @jdelaune on GitHub (Sep 8, 2021). I'm trying to do an efficient join using criteria in one of my entities like: ```php /** * @ORM\Id() * @ORM\Column(type="ulid", unique=true) */ private Ulid $id; /** * @var Collection<User> * @ORM\ManyToMany(targetEntity="User", mappedBy="teams", fetch="EXTRA_LAZY") */ private Collection $users; public function getMemberSample(): Collection { $criteria = Criteria::create() ->orderBy(['lastName' => 'ASC']) ->setMaxResults(5) ; return $this->users->matching($criteria); } ``` However the query doctrine creates is: ```sql SELECT te.id AS id, te.first_name AS first_name, te.last_name AS last_name FROM users te JOIN user_teams t ON t.user_id = te.id WHERE t.team_id = '01EP7GVKX9K05ZK8G9ZRZTTW3N' ORDER BY last_name ASC LIMIT 5; ``` As you can see it has just turned the ULID into a string which won't match the binary. I feel like this maybe come from `vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php` ```php /** * {@inheritDoc} */ public function loadCriteria(PersistentCollection $collection, Criteria $criteria) { $mapping = $collection->getMapping(); $owner = $collection->getOwner(); $ownerMetadata = $this->em->getClassMetadata(get_class($owner)); $id = $this->uow->getEntityIdentifier($owner); $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $onConditions = $this->getOnConditionSQL($mapping); $whereClauses = $params = []; if (! $mapping['isOwningSide']) { $associationSourceClass = $targetClass; $mapping = $targetClass->associationMappings[$mapping['mappedBy']]; $sourceRelationMode = 'relationToTargetKeyColumns'; } else { $associationSourceClass = $ownerMetadata; $sourceRelationMode = 'relationToSourceKeyColumns'; } foreach ($mapping[$sourceRelationMode] as $key => $value) { $whereClauses[] = sprintf('t.%s = ?', $key); $params[] = $ownerMetadata->containsForeignIdentifier ? $id[$ownerMetadata->getFieldForColumn($value)] : $id[$ownerMetadata->fieldNames[$value]]; } ``` Where maybe the `$params[]` being created here isn't using the binary value. Anyone know where this bug might lie who knows a bit more about how it work, I'm also not sure who is responsible, Doctrine ORM, Doctrine Bundle, Symfony Uid...
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6824