The DateTime
instances returned by ExampleBundleEntityUser#getServiceExpiresAt()
are the same objects stored in the entity itself, which breaks encapsulation.
The UnitOfWork in Doctrine ORM applies strict comparison for changesets, which basically means that in the case of properties of entities containing objects, if the object instance hasn't changed, the ORM does not detect a change.
In strict comparison, following is true:
$dateTime1 = new DateTime('@0');
$dateTime2 = new DateTime('@0');
$dateTime3 = $dateTime1;
var_dump($dateTime1 !== $dateTime2); // true
var_dump($dateTime1 === $dateTime3); // true
$dateTime1->modify('+1 day');
var_dump($dateTime1 === $dateTime3); // true
This is a very common mistake among newcomers in OOP programming, and it can be solved quickly by fixing your getters and setters so that the original instance is never shared outside of your object, like in following example:
public function getServiceExpiresAt()
{
return clone $this->service_expires_at;
}
public function setServiceExpiresAt(DateTime $service_expires_at)
{
$this->service_expires_at = clone $service_expires_at;
}
This will also fix your problem with Doctrine ORM.
Also, please note that this fixes possible leaks in your logic. For example, following code is buggy and hard to debug (when applying your currently broken getters/setters):
$bankTransaction1 = $someService->getTransaction(1);
$bankTransaction2 = $someService->getTransaction(2);
// leak! Now both objects reference the same DateTime instance!
$bankTransaction2->setDateTime($bankTransaction1->getDateTime());
// bug! now both your objects were modified!
$bankTransaction1->getDateTime()->modify('+1 day');
So, regardless of the ORM part in the question, please don't break encapsulation.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…