Join with composite keys #5409

Closed
opened 2026-01-22 15:07:02 +01:00 by admin · 2 comments
Owner

Originally created by @dautushenka on GitHub (Feb 6, 2017).

Originally assigned to: @Ocramius on GitHub.

I have EAV pattern with 4 entities:
Item.php

class Item
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var ItemAttributeValue[]|Collection
     *
     * @ORM\OneToMany(targetEntity="ItemAttributeValue", mappedBy="item", cascade={"persist", "remove"}, orphanRemoval=true)
     */
    private $attributeValues;

Attribute.php

class Attribute
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var AttributeValue[]|Collection
     *
     * @ORM\OneToMany(targetEntity="AttributeValue", mappedBy="attribute", fetch="EAGER", orphanRemoval=true, cascade={"persist", "remove"})
     */
    private $values;

AttributeValue.php

class AttributeValue
{
    /**
     * @var Attribute
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\ManyToOne(targetEntity="Attribute", inversedBy="values")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="attribute_id", referencedColumnName="id", nullable=false)
     * })
     */
    private $attribute;

    /**
     * @var string
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\Column(name="key", type="string", length=10, nullable=false)
     */
    private $key;

ItemAttributeValue.php

class ItemAttributeValue
{
    /**
     * @var AttributeValue
     *
     * @ORM\ManyToOne(targetEntity="AttributeValue")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="attribute_value", referencedColumnName="key"),
     *   @ORM\JoinColumn(name="attribute_id", referencedColumnName="attribute_id")
     * })
     */
    private $attributeValue;

    /**
     * @var Item
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\ManyToOne(targetEntity="Item", inversedBy="attributeValues")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="item_id", referencedColumnName="id")
     * })
     */
    private $item;

    /**
     * @var Attribute
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\ManyToOne(targetEntity="Attribute")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="attribute_id", referencedColumnName="id")
     * })
     */
    private $attribute;

As you can see entity "ItemAttributeValue" has composite foreign key "attributeValue", now I am trying to find some items with certain attribute values:

$qb = $this->entityManager->createQueryBuilder();
$qb->select('i')
    ->from(Item::class, 'i')
    ->setMaxResults(20)
    ->join(
        ItemAttributeValue::class,
        'ia',
        Join::WITH,
        "ia.item=i"
    )
    ->where("ia.attributeValue = ?1")
    ->setParameter(1, [
        'attribute_id'      => 1,
        'attribute_value'   => 'value'
    ])
    ;

And I am getting following error:
A single-valued association path expression to an entity with a composite primary key is not supported. Explicitly name the components of the composite primary key in the query.
As result I'd like to get query like that:

SELECT i.* FROM item i 
INNER JOIN item_attribute_value ia ON ia.item_id=i.id
WHERE ia.attribute_id=1 AND ia.attribute_value='value'

I don't want to join "AttributeValue" at all.

Used versions:

  • doctrine/orm v2.5.6
  • doctrine/dbal v2.5.10
  • doctrine/collections v1.4.0
  • doctrine/common v2.7.2

Thank you for the answers in advance.

Link: http://stackoverflow.com/questions/42018313/doctrine-join-with-composite-keys

Originally created by @dautushenka on GitHub (Feb 6, 2017). Originally assigned to: @Ocramius on GitHub. I have EAV pattern with 4 entities: **Item.php** class Item { /** * @var integer * * @ORM\Column(name="id", type="integer", nullable=false) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @var ItemAttributeValue[]|Collection * * @ORM\OneToMany(targetEntity="ItemAttributeValue", mappedBy="item", cascade={"persist", "remove"}, orphanRemoval=true) */ private $attributeValues; **Attribute.php** class Attribute { /** * @var integer * * @ORM\Column(name="id", type="integer", nullable=false) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @var AttributeValue[]|Collection * * @ORM\OneToMany(targetEntity="AttributeValue", mappedBy="attribute", fetch="EAGER", orphanRemoval=true, cascade={"persist", "remove"}) */ private $values; **AttributeValue.php** class AttributeValue { /** * @var Attribute * * @ORM\Id * @ORM\GeneratedValue(strategy="NONE") * @ORM\ManyToOne(targetEntity="Attribute", inversedBy="values") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="attribute_id", referencedColumnName="id", nullable=false) * }) */ private $attribute; /** * @var string * * @ORM\Id * @ORM\GeneratedValue(strategy="NONE") * @ORM\Column(name="key", type="string", length=10, nullable=false) */ private $key; **ItemAttributeValue.php** class ItemAttributeValue { /** * @var AttributeValue * * @ORM\ManyToOne(targetEntity="AttributeValue") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="attribute_value", referencedColumnName="key"), * @ORM\JoinColumn(name="attribute_id", referencedColumnName="attribute_id") * }) */ private $attributeValue; /** * @var Item * * @ORM\Id * @ORM\GeneratedValue(strategy="NONE") * @ORM\ManyToOne(targetEntity="Item", inversedBy="attributeValues") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="item_id", referencedColumnName="id") * }) */ private $item; /** * @var Attribute * * @ORM\Id * @ORM\GeneratedValue(strategy="NONE") * @ORM\ManyToOne(targetEntity="Attribute") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="attribute_id", referencedColumnName="id") * }) */ private $attribute; As you can see entity "ItemAttributeValue" has composite foreign key "attributeValue", now I am trying to find some items with certain attribute values: $qb = $this->entityManager->createQueryBuilder(); $qb->select('i') ->from(Item::class, 'i') ->setMaxResults(20) ->join( ItemAttributeValue::class, 'ia', Join::WITH, "ia.item=i" ) ->where("ia.attributeValue = ?1") ->setParameter(1, [ 'attribute_id' => 1, 'attribute_value' => 'value' ]) ; And I am getting following error: **A single-valued association path expression to an entity with a composite primary key is not supported. Explicitly name the components of the composite primary key in the query.** As result I'd like to get query like that: SELECT i.* FROM item i INNER JOIN item_attribute_value ia ON ia.item_id=i.id WHERE ia.attribute_id=1 AND ia.attribute_value='value' I don't want to join "AttributeValue" at all. Used versions: - doctrine/orm v2.5.6 - doctrine/dbal v2.5.10 - doctrine/collections v1.4.0 - doctrine/common v2.7.2 Thank you for the answers in advance. Link: http://stackoverflow.com/questions/42018313/doctrine-join-with-composite-keys
admin added the Question label 2026-01-22 15:07:02 +01:00
admin closed this issue 2026-01-22 15:07:02 +01:00
Author
Owner

@Ocramius commented on GitHub (Feb 7, 2017):

What could probably be attempted is:

    ->where("ia.attributeValue = :attributeValue")
    ->setParameter(
        'attributeValue',
        $entityManager->getReference(
            AttributeValue::class,
            [
                'attribute_id'      => 1,
                'attribute_value'   => 'value'
            ]
        )
    );

Another approach is to combine it with the IDENTITY() function (can't remember if it works with composite PKs).

Another approach is to just join the association (not a big issue, if your indexes are in place).

@Ocramius commented on GitHub (Feb 7, 2017): What could probably be attempted is: ```php ->where("ia.attributeValue = :attributeValue") ->setParameter( 'attributeValue', $entityManager->getReference( AttributeValue::class, [ 'attribute_id' => 1, 'attribute_value' => 'value' ] ) ); ``` Another approach is to combine it with the `IDENTITY()` function (can't remember if it works with composite PKs). Another approach is to just join the association (not a big issue, if your indexes are in place).
Author
Owner

@Ocramius commented on GitHub (Feb 7, 2017):

Closing, since this is a question about a well known limitation (exception is there for a reason), and not an issue.

@Ocramius commented on GitHub (Feb 7, 2017): Closing, since this is a question about a well known limitation (exception is there for a reason), and not an issue.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5409