doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.86k stars 2.5k forks source link

Make IDENTITY function work with joined columns #8043

Open MatTheCat opened 4 years ago

MatTheCat commented 4 years ago

Feature Request

Q A
New Feature yes
RFC no
BC Break no

Summary

On https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/dql-doctrine-query-language.html#dql-select-examples it is said

the IDENTITY() DQL function also works for composite primary keys

But if these keys are part of another association they won’t be found in fields but in associations mapping, hence throwing.

Example

Given the following entities

class Contract
{
    /**
     * @ORM\ManyToOne(targetEntity="Agent")
     * @ORM\Id
     */
    private $agent;

    /**
     * @ORM\ManyToOne(targetEntity="Agency")
     * @ORM\Id
     */
    private $agency;
}

class Estate
{
    /**
     * @ORM\ManyToOne(targetEntity="Contract")
     * @ORM\JoinColumns({
     *  @ORM\JoinColumn(name="contract_agent_id", referencedColumnName="agent_id"),
     *  @ORM\JoinColumn(name="contract_agency_id", referencedColumnName="agency_id"),
     * })
     */
    protected $contract;
}

I expected to be able to write IDENTITY(estate.contract, 'agent_id').

Is this something that could be implemented?

beberlei commented 4 years ago

@MatTheCat The correct syntax would be IDENTITY(estate.contract, 'agent'), not agent_id is that not working?

MatTheCat commented 4 years ago

@beberlei nope I get

Undefined reference field mapping "agent"

Indeed there is no agent in the ClassMetadata fieldMappings. I guess this is because it is part of an association ; it can be found in associationMappings.

trickeyone commented 4 years ago

I just ran into this same exact issue. I tried to use IDENTITY() with a relation that has a composite ID of two other entities.

namespace MyApp;

use Doctrine\ORM\Mapping as ORM;

class FirstEntity
{
    /** @ORM\Column(type="integer", nullable=false) @ORM\Id() */
    private $id;
}

class SecondEntity
{
    /** @ORM\Column(type="integer", nullable=false) @ORM\Id() */
    private $id;
}

class CompositeEntity
{
    /** @ORM\ManyToOne(targetEntity="MyApp\FirstEntity") @ORM\Id() */
    private $first;

    /** @ORM\ManyToOne(targetEntity="MyApp\SecondEntity") @ORM\Id() */
    private $second;
}

class ChildEntity
{
    /** @ORM\Column(type="integer", nullable=false) @ORM\Id() */
    private $id;

    /** @ORM\ManyToOne(targetEntity="MyApp\CompositeEntity") */
    private $composite;
}

With the above, when I try to do something like:

SELECT childEntity FROM MyApp\ChildEntity childEntity WHERE IDENTITY(childEntity.composite, 'first') IN (:myValue)

I get the above described error saying that there is no field mapping for 'first'.

When I dug into it a little, the fieldMapping value is only checked against the class metadata's fieldMappings and ignores any associations. So, it appears that the IDENTITY() function, when used in conjunction with fieldMapping, is actually retrieving a relation non-association values.

beberlei commented 3 years ago

Check IdentityFunction::getSql here. It should check for fieldMappings (as it does already) and additionally for associatoinMappings.