我在下面尽可能简化了我的3个实体,它显示了Currency <- 1:1 -> Balance <- 1:N -> BalanceLog
的简单关系
实体/Currency.php
/**
* @ORMEntity(repositoryClass=CurrencyRepository::class)
*/
class Currency
{
/**
* @ORMId
* @ORMColumn(type="string", length=3)
*/
private ?string $code;
/**
* @ORMOneToOne(targetEntity="Balance", mappedBy="currency")
**/
private ?Balance $balance;
// ...
}
实体/余额.php
/**
* @ORMEntity(repositoryClass=BalanceRepository::class)
*/
class Balance
{
/**
* @ORMId
* @ORMOneToOne(targetEntity="Currency", inversedBy="balance")
* @ORMJoinColumn(name="currency", referencedColumnName="code", nullable=false)
**/
private ?Currency $currency;
/**
* @ORMOneToMany(targetEntity="AppEntityBalanceLog", mappedBy="balance")
*/
private Collection $balance_logs;
// ...
}
实体/余额日志.php
/**
* @ORMEntity(repositoryClass=BalanceLogRepository::class)
*/
class BalanceLog
{
/**
* @ORMId
* @ORMGeneratedValue
* @ORMColumn(type="integer")
*/
private ?int $id;
/**
* @ORMManyToOne(targetEntity="AppEntityBalance", inversedBy="balance_logs")
* @ORMJoinColumn(name="balance_currency", referencedColumnName="currency")
**/
private ?Balance $balance;
// ...
}
问题发生在我打电话时:
$balanceLog = $this->getDoctrine()
->getRepository('App:BalanceLog')->findAll();
这将BalanceLog::$balance
水合为Balance
类型的适当实例,但不会将BalanceLog::$balance->currency
水合为Currency
实例。相反,它希望只使用string
导致错误:
类型属性App\Entity\Balance::$currency必须是App\Entity\currency的实例或null,字符串使用
脏修复是在没有固定类型?Currency
的情况下使Balance::$currency。然后它将接受字符串;作品";。但这是不对的。Balance::$currency
应该是Currency
类型,有时不是字符串,有时是货币。
我试着在BalanceLogRepository
中制作自己的方法,无论出于什么原因,这都很好:
public function findByBalance(Balance $balance) : iterable
{
$query = $this->createQueryBuilder('bl');
$query->andWhere('bl.balance = :balance')
->setParameter('balance', $balance);
return $query->getQuery()->getResult();
}
因此,我更困惑的是,为什么默认的findAll
或findBy
不进行递归水合
经过进一步调查,我发现了一个非常奇怪的行为:
如果我准备这个代码:
$balance = $this->getDoctrine()->getRepository('App:Balance')->find('USD');
在前面
$balanceLog = $this->getDoctrine()->getRepository('App:BalanceLog')->findAll();
在我的控制器中,错误消失。就好像Balance
的具有依赖关系的App:Balance
ORM模式没有正确加载,直到我尝试直接从先验中获取Balance
对象。
我做了一些调试,看起来BalanceLog
并没有创建一个完整的Balance
实体实例,而是创建了一个代理。解决方案是在BalanceLog
类中添加紧急加载
class BalanceLog
{
/**
* @ORMId
* @ORMGeneratedValue
* @ORMColumn(type="integer")
*/
private ?int $id;
/**
* @ORMManyToOne(targetEntity="AppEntityBalance", inversedBy="balance_logs", fetch="EAGER")
* @ORMJoinColumn(name="balance_currency", referencedColumnName="currency")
**/
private ?Balance $balance;
// ...
}
UnitOfWork.php则不使用Proxy,而是将实体作为一个整体加载。
如果有人想知道为什么事先查询Balance使代码工作,那是因为Doctrine的复杂缓存机制。它为主键USD
保存了Balance
实例,然后在填充BalanceLog时,它使用该实例而不是创建代理。
我仍然认为Proxy不应该从Entity强制执行严格类型的属性,但这是由Doctrine开发者决定的。