Skip to content

Commit

Permalink
Fix association mapping with enum fields
Browse files Browse the repository at this point in the history
Enum fields as ID have worked for some time, but referencing these fields from other entities in association mappings such as OneToOne, ManyToOne and ManyToMany resulted in fatal error as there was an attempt to convert enum value to string improperly.
  • Loading branch information
michnovka committed Dec 7, 2022
1 parent aeed977 commit e3076eb
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -2812,6 +2812,10 @@ public function createEntity($className, array $data, &$hints = [])
$joinColumnValue = $data[$srcColumn] ?? null;

if ($joinColumnValue !== null) {
if ($joinColumnValue instanceof BackedEnum) {
$joinColumnValue = $joinColumnValue->value;
}

if ($targetClass->containsForeignIdentifier) {
$associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
} else {
Expand Down
67 changes: 67 additions & 0 deletions tests/Doctrine/Tests/Models/GH10132/Complex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\GH10132;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\Tests\Models\Enums\Suit;

/** @Entity */
class Complex
{
/**
* @Id
* @Column(type = "string", enumType = Suit::class)
*/
protected Suit $type;

/**
* @Id
* @Column(type = "integer")
*/
protected int $number;

/** @OneToMany(targetEntity = ComplexChild::class, mappedBy = "complex", cascade = {"persist"}) */
protected Collection $complexChildren;

public function __construct()
{
$this->complexChildren = new ArrayCollection();
}

public function getType(): Suit
{
return $this->type;
}

public function setType(Suit $type): void
{
$this->type = $type;
}

public function getNumber(): int
{
return $this->number;
}

public function setNumber(int $number): void
{
$this->number = $number;
}

public function getComplexChildren(): Collection
{
return $this->complexChildren;
}

public function addComplexChild(ComplexChild $complexChild): void
{
$this->complexChildren->add($complexChild);
}
}
95 changes: 95 additions & 0 deletions tests/Doctrine/Tests/Models/GH10132/ComplexChild.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\GH10132;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinColumns;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\Tests\Models\Enums\Suit;

/** @Entity */
class ComplexChild
{
/**
* @ManyToOne(targetEntity = Complex::class, inversedBy = "complexChildren")
* @JoinColumns(
* @JoinColumn(name = "complexType", referencedColumnName = "type", nullable = false),
* @JoinColumn(name = "complexNumber", referencedColumnName = "number", nullable = false)
* )
*/
protected Complex $complex;

/**
* @Id
* @Column(type = "string", enumType = Suit::class)
*/
protected Suit $complexType;

/**
* @Id
* @Column(type = "integer")
*/
protected int $complexNumber;

/**
* @Id
* @Column(type = "integer")
*/
protected int $point;

/** @OneToMany(targetEntity = SubComplexChild::class, mappedBy = "childComplex", cascade = {"persist", "remove"}) */
protected Collection $subComplexChildren;

public function setComplex(Complex $complex): void
{
$this->subComplexChildren = new ArrayCollection();

$complex->addComplexChild($this);
$this->complexType = $complex->getType();
$this->complexNumber = $complex->getNumber();
$this->complex = $complex;
}

public function getSuit(): Suit
{
return $this->complexType;
}

public function getComplexNumber(): int
{
return $this->complexNumber;
}

public function getComplex(): Complex
{
return $this->complex;
}

public function setPoint(int $point): void
{
$this->point = $point;
}

public function getPoint(): int
{
return $this->point;
}

public function getSubComplexChildren(): Collection
{
return $this->subComplexChildren;
}

public function addSubComplexChild(SubComplexChild $subComplexChild): void
{
$this->subComplexChildren[] = $subComplexChild;
}
}
75 changes: 75 additions & 0 deletions tests/Doctrine/Tests/Models/GH10132/SubComplexChild.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\GH10132;

use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinColumns;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\Tests\Models\Enums\Suit;

/** @Entity */
class SubComplexChild
{
/**
* @ManyToOne(targetEntity = ComplexChild::class, inversedBy = "complexChildren")
* @JoinColumns(
* @JoinColumn(name = "complexType", referencedColumnName = "complexType", nullable = false),
* @JoinColumn(name = "complexNumber", referencedColumnName = "complexNumber", nullable = false),
* @JoinColumn(name = "complexChildPoint", referencedColumnName = "point", nullable = false)
* )
*/
protected ComplexChild $complexChild;

/**
* @Id
* @Column(type = "string", enumType = Suit::class)
*/
protected Suit $complexType;

/**
* @Id
* @Column(type = "integer")
*/
protected int $complexNumber;

/**
* @Id
* @Column(type = "integer")
*/
protected int $complexChildPoint;

/**
* @Id
* @Column(type = "integer")
*/
protected int $number;

public function getComplexChild(): ComplexChild
{
return $this->complexChild;
}

public function setComplexChild(ComplexChild $complexChild): void
{
$complexChild->addSubComplexChild($this);
$this->complexType = $complexChild->getSuit();
$this->complexNumber = $complexChild->getComplexNumber();
$this->complexChildPoint = $complexChild->getPoint();
$this->complexChild = $complexChild;
}

public function getNumber(): int
{
return $this->number;
}

public function setNumber(int $number): void
{
$this->number = $number;
}
}
74 changes: 74 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/GH10132Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Tests\Models\Enums\Suit;
use Doctrine\Tests\Models\GH10132\Complex;
use Doctrine\Tests\Models\GH10132\ComplexChild;
use Doctrine\Tests\Models\GH10132\SubComplexChild;
use Doctrine\Tests\OrmFunctionalTestCase;

/**
* @requires PHP 8.1
*/
class GH10132Test extends OrmFunctionalTestCase
{
public function setUp(): void
{
parent::setUp();

$this->createSchemaForModels(
Complex::class,
ComplexChild::class,
SubComplexChild::class
);
}

public function testQueryBackedEnumInCompositeKeyJoin(): void
{
$complex = new Complex();
$complex->setType(Suit::Clubs);
$complex->setNumber(1);

$complexChild = new ComplexChild();
$complexChild->setComplex($complex);
$complexChild->setPoint(1);

$subComplexChild1 = new SubComplexChild();
$subComplexChild1->setComplexChild($complexChild);
$subComplexChild1->setNumber(1);

$subComplexChild2 = new SubComplexChild();
$subComplexChild2->setComplexChild($complexChild);
$subComplexChild2->setNumber(2);

$subComplexChild3 = new SubComplexChild();
$subComplexChild3->setComplexChild($complexChild);
$subComplexChild3->setNumber(3);

$this->_em->persist($complex);
$this->_em->persist($complexChild);
$this->_em->persist($subComplexChild1);
$this->_em->persist($subComplexChild2);
$this->_em->persist($subComplexChild3);
$this->_em->flush();
$this->_em->clear();

$qb = $this->_em->createQueryBuilder();
$qb->select('s')
->from(SubComplexChild::class, 's')
->where('s.complexType = :complexType')
->andWhere('s.complexNumber = :complexNumber')
->andWhere('s.complexChildPoint = :complexChildPoint')
->andWhere('s.number = :number');

$qb->setParameter('complexType', Suit::Clubs);
$qb->setParameter('complexNumber', 1);
$qb->setParameter('complexChildPoint', 1);
$qb->setParameter('number', 2);

self::assertNotNull($qb->getQuery()->getOneOrNullResult());
}
}

0 comments on commit e3076eb

Please sign in to comment.