php - Doctrine insert/update/modify sub entities on @OneToMany -
i have project based on symfony 2.6. has following structure:
customer->@onetomany->orders->@onetomany->domains->@onetomany->subdomains
same in reverse order:
subdomains->@manytoone->domains->@manytoone->orders->@manytoone->customer
doctrine creates additional pretty cool "magic" columns customer_id
(in domains), order_id
(in domains) , domain_id
(in subdomains
).
it think perfect, if these *_id
cols filled ids of parent table. in case, not match time. tables customer
, order
, *_id
cols filled properly.
my php code creates new domain entities. sets id, if record exists in db, , adds subdomains
arraycollection
.
here storeorder()
method:
protected function storeorder(order $order, array $domains) { if ($order->getdomains()->count() === 0) { // insert foreach ($domains $domain) { $order->getdomains()->add($this->createdomainfromarray($domain)); } } else { // remove/add $order->getdomains()->clear(); foreach ($domains $domain) { $order->getdomains()->add($this->createdomainfromarray($domain)); } } //$updatedorder = $this->entitymanager->merge($order); // starts commit process incl. transactions , prepared queries $this->entitymanager->persist($order); $this->entitymanager->flush(); // clear cached entities in em. speeds processing enormous $this->entitymanager->clear(); $this->amountofdomains += count($domains); } /** * create domain object 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; } /** * prevent duplicate domains in database have set id, * if have found such domain 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 seid * * @param int $seid * @return domain|null */ protected function finddomainbyseid($seid) { // not use empty, because findoneby can return null 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 domain object * * @param domain $domain * @param array $subdomains these subdomains 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 $subdomain) { // create new subdomain $subdomainobject = new subdomain(); $subdomainobject->setordernumber($this->ordernumber); $subdomainobject->fromarray($subdomain); // add id subdomain, if have 1 in db // it's not perfect, long can have equal records in subdomain table // don't see chance change if ($first) { $subdomainfromdatabase = $subdomainsfromdatabase->first(); $first = false; } else { $subdomainfromdatabase = $subdomainsfromdatabase->next(); } if ($subdomainfromdatabase instanceof subdomain) { $subdomainobject->setid($subdomainfromdatabase->getid()); } // add order customer $domain->addsubdomain($subdomainobject); } }
here annotation order entity:
/** * order -> domains * * @var arraycollection * @orm\onetomany(targetentity="fqcn\domain", mappedby="order", cascade={"all"}) */ protected $domains;
here annotations subdomain entity:
/** * relation domain 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;
if use flush()
, *_id
fields not filled. if call detach($order)
, fields filled properly, have second (duplicate) of $order
in database. if use merge()
works on insert, not if want update arraycollections
.
what i'm doing wrong? how update arraycollections
right way without removing , creating them again? how *_id
field filled?
stefan
thank all. here solution:
i have added new function domain model, original subdomain object database:
public function getsubdomain($seid) { foreach ($this->subdomains $subdomain) { if ($subdomain->getseid() === $seid) { return $subdomain; } } }
in domainsynchronization-object decide between updated or new record:
$domainfromdatabase = $order->getdomain((int)$domain['seid']); if ($domainfromdatabase instanceof domain) { // update $this->updatedomainfromarray($domainfromdatabase, $domain); } else { // insert $order->getdomains()->add($this->createdomainfromarray($domain, $order));
}
and, @redbirdo mentioned, add domain object subdomains manually now:
protected function addsubdomainstodomain(domain $domain, array $subdomains) { $this->amountofsubdomains += count($subdomains); foreach ($subdomains $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); } } }
now don't need ->persist() or ->merge() anymore. it's enough call:
$this->entitymanager->flush();
any can see parent uids in subdomain table, too.
thank all
Comments
Post a Comment