Lau*_*kas 21 oop encapsulation doctrine-orm
我有一个用户实体:
use Doctrine\ORM\Mapping as ORM;
/**
* ExampleBundle\Entity\User
*
* @ORM\Entity()
*/
class User
{
// ...
/**
* @ORM\Column(type="service_expires_at", type="date", nullable=true)
*/
private $service_expires_at;
public function getServiceExpiresAt()
{
return $this->service_expires_at;
}
public function setServiceExpiresAt(\DateTime $service_expires_at)
{
$this->service_expires_at = $service_expires_at;
}
}
Run Code Online (Sandbox Code Playgroud)
当我将用户更新service_expires_at为以下内容时,更新后的service_expires_at值不会保存回数据库:
$date = $user->getServiceExpiresAt();
var_dump($date->format('Y-m-d')); // 2013-03-08
$date->modify('+10 days');
var_dump($date->format('Y-m-d')); // 2013-03-18
$user->setServiceExpiresAt($date);
$em->persist($user);
$em->flush();
Run Code Online (Sandbox Code Playgroud)
但是,如果我传递一个新DateTime对象service_expires_at,则更新的值将正确保存:
$date = $user->getServiceExpiresAt();
$date->modify('+10 days');
$user->setServiceExpiresAt(new \DateTime($date->format('Y-m-d'));
$em->persist($user);
$em->flush();
Run Code Online (Sandbox Code Playgroud)
为什么会这样?
Ocr*_*ius 80
DateTime返回的实例ExampleBundle\Entity\User#getServiceExpiresAt() 是存储在实体本身中的相同对象,这会破坏封装.
Doctrine ORM中的UnitOfWork对变更集应用严格比较,这基本上意味着在包含对象的实体的属性的情况下,如果对象实例未更改,则ORM不会检测到更改.
严格比较,以下是真实的:
$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
Run Code Online (Sandbox Code Playgroud)
这是OOP编程中新手的一个常见错误,可以通过修复getter和setter来快速解决,以便原始实例永远不会在对象之外共享,如下例所示:
public function getServiceExpiresAt()
{
return clone $this->service_expires_at;
}
public function setServiceExpiresAt(\DateTime $service_expires_at)
{
$this->service_expires_at = clone $service_expires_at;
}
Run Code Online (Sandbox Code Playgroud)
这也将解决Doctrine ORM的问题.
此外,请注意,这可以修复逻辑中可能存在的泄漏.例如,以下代码是错误的并且难以调试(当应用您当前损坏的getter/setter时):
$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');
Run Code Online (Sandbox Code Playgroud)
因此,无论问题中的ORM部分如何,请不要破坏封装.