我有一个基于Symfony 2.6的项目。它具有以下结构:
Customer->@OneToMany->Orders->@OneToMany->Domains->@OneToMany->SubDomains
以相反的顺序相同:
SubDomains->@ManyToOne->Domains->@ManyToOne->Orders->@ManyToOne->Customer
Doctrine创造了一些非常酷的魔法"类似于customer_id
(在域中),order_id
(在域中)和domain_id
(在subdomains
中)的列。
如果所有这些*_id
列都填充了父表的ID,它认为这将是完美的。但在我的情况下,这不会一直匹配。
仅对表customer
和order
,*_id
列正确填充。
我的PHP代码创建了新的域实体。如果记录存在于DB中,它还会设置ID,并将所有subdomains
添加为ArrayCollection
。
这是我的storeOrder()
方法:
protected function storeOrder(Order $order, array $domains) {
if ($order->getDomains()->count() === 0) {
// insert
foreach ($domains as $domain) {
$order->getDomains()->add($this->createDomainFromArray($domain));
}
} else {
// remove/add
$order->getDomains()->clear();
foreach ($domains as $domain) {
$order->getDomains()->add($this->createDomainFromArray($domain));
}
}
//$updatedOrder = $this->entityManager->merge($order);
// starts the commit process incl. transactions and prepared queries
$this->entityManager->persist($order);
$this->entityManager->flush();
// clear all cached entities in em. That speeds up processing enormous
$this->entityManager->clear();
$this->amountOfDomains += count($domains);
}
/**
* create a domain object from delivered data array
*
* @param array $data
* @return Domain
*/
protected function createDomainFromArray($data) {
$domain = new Domain();
$domain->setOrderNumber($this->orderNumber);
$domain->fromArray(ArrayUtility::removeSubEntriesFromArray($data));
$this->addIdToDomainIfFoundInDatabase($domain);
$this->addSubDomainsToDomain($domain, $data['subdomain']);
return $domain;
}
/**
* To prevent duplicate domains in database we have to set the id,
* if we have found such domain already in DB
* @param Domain $domain
*/
protected function addIdToDomainIfFoundInDatabase(Domain $domain) {
/** @var Domain|NULL $domainFromDatabase */
$domainFromDatabase = $this->findDomainBySeid($domain->getSeid());
if ($domainFromDatabase instanceof Domain) {
$domain->setId($domainFromDatabase->getId());
}
}
/**
* find domain by seid
*
* @param int $seid
* @return Domain|NULL
*/
protected function findDomainBySeid($seid) {
// do not use empty, because findOneBy can also return NULL as result
if (!isset($this->cache['domainFromDatabase']) || (is_object($this->cache['domainFromDatabase']) && $this->cache['domainFromDatabase']->getSeid() !== $seid)) {
$this->cache['domainFromDatabase'] = $this->domainRepository->findOneBy(array(
'seid' => $seid,
'orderNumber' => $this->orderNumber
));
}
return $this->cache['domainFromDatabase'];
}
/**
* add/override subdomains to domain object
*
* @param Domain $domain
* @param array $subDomains These are the subDomains from request
*/
protected function addSubDomainsToDomain(Domain $domain, array $subDomains) {
$first = TRUE;
$this->amountOfSubDomains += count($subDomains);
$domainFromDatabase = $this->findDomainBySeid($domain->getSeid());
if ($domainFromDatabase instanceof Domain) {
$subDomainsFromDatabase = $domainFromDatabase->getSubDomains();
} else {
$subDomainsFromDatabase = new ArrayCollection();
}
foreach ($subDomains as $subDomain) {
// create new SubDomain
$subDomainObject = new SubDomain();
$subDomainObject->setOrderNumber($this->orderNumber);
$subDomainObject->fromArray($subDomain);
// add ID to subDomain, if we have one already in DB
// It's not perfect, but as long as we can have fully equal records in subdomain table
// I don't see any chance to change that
if ($first) {
$subDomainFromDatabase = $subDomainsFromDatabase->first();
$first = FALSE;
} else {
$subDomainFromDatabase = $subDomainsFromDatabase->next();
}
if ($subDomainFromDatabase instanceof SubDomain) {
$subDomainObject->setId($subDomainFromDatabase->getId());
}
// add Order to Customer
$domain->addSubDomain($subDomainObject);
}
}
以下是订单实体的注释:
/**
* Order -> domains
*
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="FQCN\Domain", mappedBy="order", cascade={"all"})
*/
protected $domains;
以下是子域实体的注释:
/**
* relation from domain to order
*
* @ORM\ManyToOne(targetEntity="FQCN\Order", inversedBy="domains")
*/
protected $order = NULL;
/**
* domain -> subDomains
*
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="FQCN\SubDomain", mappedBy="domain", cascade={"all"})
*/
protected $subDomains;
如果我仅使用flush()
,则不会填写*_id
字段。如果我调用detach($order)
,字段将被正确填充,但现在我的数据库中有第二个(重复)$order
。如果我使用merge()
,则所有内容都适用于 INSERT ,但如果我想更新所有ArrayCollections
,则无效。
我做错了什么?如何更新 ArrayCollections
正确的方式,而不用删除并再次创建它们?如何填写*_id
字段?
的Stefan
答案 0 :(得分:1)
谢谢大家。在这里我的解决方案:
我已经为我的域模型添加了一个新函数,以从数据库中获取原始的子域对象:
public function getSubDomain($seid) {
foreach ($this->subDomains as $subDomain) {
if ($subDomain->getSeid() === $seid) {
return $subDomain;
}
}
}
在我的DomainSynchronization-Object中,我决定更新或新记录:
$domainFromDatabase = $order->getDomain((int)$domain['seid']);
if ($domainFromDatabase instanceof Domain) {
// update
$this->updateDomainFromArray($domainFromDatabase, $domain);
} else {
// insert
$order->getDomains()->add($this->createDomainFromArray($domain, $order));
}
而且,正如@redbirdo已经提到的,我现在手动将域对象添加到子域:
protected function addSubDomainsToDomain(Domain $domain, array $subDomains) {
$this->amountOfSubDomains += count($subDomains);
foreach ($subDomains as $subDomain) {
$subDomainObject = $domain->getSubDomain((int)$subDomain['seid']);
if ($subDomainObject instanceof SubDomain) {
// update
$subDomainObject->fromArray($subDomain);
} else {
// insert
$subDomainObject = new SubDomain();
$subDomainObject->setDomain($domain);
$subDomainObject->setOrderNumber($this->orderNumber);
$subDomainObject->fromArray($subDomain);
$domain->addSubDomain($subDomainObject);
}
}
}
现在我不再需要 - > persist()或 - > merge()了。这足以称呼:
$this->entityManager->flush();
我现在也可以在子域表中看到父UID。
谢谢大家
答案 1 :(得分:0)
首先,我注意到当您向正在调用的订单添加域名时:
$order->getDomains()->add($this->createDomainFromArray($domain));
您的订单类应该有这些方法,然后您可以使用addDomain():
public function addDomain(Domain $domain)
{
$this->domains[] = $domain;
return $this;
}
public function removeDomain(Domain $domain)
{
$this->domains->removeElement($domain);
}
添加这些方法可能足以解决您的问题。但是,我通常发现有必要明确设置关系的两端,在这种情况下,在Order和Domain之间。我认为你不是这样做的,因为当你创建一个域时,你没有传递订单:
$order->getDomains()->add($this->createDomainFromArray($domain));
请改为尝试:
$domain = $this->createDomainFromArray($domain);
$domain->setOrder($order);
$order->addDomain($domain);
您需要对子域进行类似的操作。
我无法理解为什么你需要使用$ em-> detach()或$ em-> merge()虽然从问题中的代码我不知道订单的位置传入storeOrder()来自。