-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Description
Issue was found after updating to v.2.6 and associated with improvements in CommitOrderCalculator class.
Here is the example to reproduce.
Objects:
- Book
- PCT with mandatory relation to Book (ManyToOne).
Book has optional relation to the PCT object. Some Bookes has PCT and some not. But all PCT has relation to Book. - PCTFee as a child object of PCT (ManyToOne).
/**
* @ORM\Entity
* @ORM\Table(name="book")
*/
class Book
{
/**
* @var int
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* @var string
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $exchangeCode;
/**
* @var PCT
* @ORM\OneToOne(targetEntity="PCT", cascade={"persist", "remove"})
* @ORM\JoinColumn(name="paymentCardTransactionId", referencedColumnName="id")
*/
private $paymentCardTransaction;
public function __construct(int $id)
{
$this->id = $id;
}
public function getId(): int
{
return $this->id;
}
public function getExchangeCode(): ?string
{
return $this->exchangeCode;
}
public function setExchangeCode(string $exchangeCode = null): void
{
$this->exchangeCode = $exchangeCode;
}
public function getPaymentCardTransaction(): ?PCT
{
return $this->paymentCardTransaction;
}
public function setPaymentCardTransaction(PCT $paymentCardTransaction = null): void
{
$this->paymentCardTransaction = $paymentCardTransaction;
}
}
/**
* @ORM\Entity
* @ORM\Table(name="pct")
*/
class PCT
{
/**
* @var int
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* @var Book
* @ORM\ManyToOne(targetEntity="Book")
* @ORM\JoinColumn(name="bookingId", referencedColumnName="id", nullable=false)
*/
private $book;
/**
* @var PCTFee[]
* @ORM\OneToMany(targetEntity="PCTFee", mappedBy="pct", cascade={"persist", "remove"})
* @ORM\OrderBy({"id" = "ASC"})
*/
private $fees;
public function __construct(int $id, Book $book)
{
$this->id = $id;
$this->book = $book;
$this->fees = new ArrayCollection();
}
public function getId(): int
{
return $this->id;
}
/**
* @return PCTFee[]|Collection
*/
public function getFees(): Collection
{
return $this->fees;
}
public function addFee(PCTFee $fee)
{
$this->fees->add($fee);
}
public function removeFee(PCTFee $fee)
{
$this->fees->removeElement($fee);
}
public function getBook(): Book
{
return $this->book;
}
}
/**
* @ORM\Entity
* @ORM\Table(name="pct_fee")
*/
class PCTFee
{
/**
* @var int
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var PCT
* @ORM\ManyToOne(targetEntity="PCT", inversedBy="fees")
* @ORM\JoinColumn(name="paymentCardTransactionId", referencedColumnName="id", nullable=false)
*/
private $pct;
public function __construct(PCT $pct)
{
$this->pct = $pct;
$pct->addFee($this);
}
public function getId(): ?int
{
return $this->id;
}
public function getPCT(): PCT
{
return $this->pct;
}
}
How to reproduce:
Find one Book (or create new).
Create new PCT object with even one PCTFee object for this Book object.
Try to flush.
$booking = $this->em()->getRepository(Book::class)->find(1);
if (!$booking) {
$booking = new Book(1);
$booking->setExchangeCode('1');
$this->em()->persist($booking);
}
$id = (int) $booking->getExchangeCode();
$id++;
$booking->setExchangeCode((string) $id); // Change smth.
$paymentCardTransaction = new PCT($id, $booking);
$paymentCardTransactionFee = new PCTFee($paymentCardTransaction);
$this->em()->persist($paymentCardTransaction);
$this->save();
Error:
An exception occurred while executing 'INSERT INTO pct_fee (paymentCardTransactionId) VALUES (?)' with params [null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'paymentCardTransactionId' cannot be null
I've try to figure out why and found that CommitOrderCalculator produce wrong commit order: PCTFee, Book, PCT.
It's wrong because PCTFee depends on PCT - PCT must be saved earlier.
New code in CommitOrderCalculator uses weights of relations. But weights is wrong.
We have 2 relations with different weights (nullable and not nullable):
- PCT to Book with weight 0 (not nullable)
- Book to PCT with weight 1 (nullable).
Before version 2.6 CommitOrderCalculator has checked both relations. But now it checks only relation with maximum weight!
I've removed code using weights from CommitOrderCalculator:
case self::IN_PROGRESS:
if (isset($adjacentVertex->dependencyList[$vertex->hash]) &&
$adjacentVertex->dependencyList[$vertex->hash]->weight < $edge->weight) {
$adjacentVertex->state = self::VISITED;
$this->sortedNodeList[] = $adjacentVertex->value;
}
break;
and bug has disappeared.