Details
Description
Hi guys,
It seems that I found a bug in the Doctrine\ORM\Persisters\BasicEntityPersister. Let me explain.
Put it simply, I have the following entity:
/**
* @Entity
*/
class Foo
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ManyToOne(targetEntity="Foo", inversedBy="children")
* @JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
/**
* @OneToMany(targetEntity="Foo", mappedBy="parent")
*/
private $children;
/**
* @Column(type="integer", nullable=false)
*/
private $verticalOrder;
// a bunch of getters/setters
As you can see, there's a mandatory field called "verticalOrder", it is not intended to be managed manually but rather with a listener attached to onFlush event. ( In that listener, after a value for the verticalOrder is set, the uow::recomputeSingleEntityChangeSet() method is invoked, as it was suggested in documentation ).
The thing is that when I persist an instance of this class and then invoke flush ( without providing a value for it, the verticalOrder ), Doctrine dies silently, with no exception being thrown. The easy fix can be done with adding this piece of source code to the BasicEntityPersister on line 218:
if (!$stmt->execute()) { throw new \Exception('Unable to execute query: '.var_export($stmt->errorInfo(), true)); }
That was the first problem I have faced with while trying to find out why my entity is not persisted. After I have delved a bit deeper, I found out
that the problem lies in BasicEntityPersister ::_getInsertColumnList() or in BasicEntityPersister ::_prepareUpdateData(). The thing is that order of columns in the SQL query generated by the BasicEntityPersister::_getInsertColumnList() and values to be bound to the statement and after executed is different. Line 214 in BasicEntityPersister is meant here.
In my case it was reversed, instead of parent_id, a value for verticalOrder was inserted and vice versa.
Array of parameters to be bound to the Doctrine\DBAL\Statement:
Array
(
[verticalOrder] => 1278334736 // at the moment timestamp is used rather than MAX from the table
[parent_id] =>
)
And the query that will be executed:
INSERT INTO Foo(parent_id, verticalOrder) VALUES (?, ?)
These snippets clearly illustrate the point.
I hope I was clear in my explanations.
I will try to devote some spare time and write tests to lock this issue, but i'm not able to say at the moment when it happens.
All the best,
Sergei Lissovski
This issue is a duplicate of
DDC-656. which was fixed this weekend. The current master has a fix for it.