doctrine2 - Initializing empty relationships in entities -
i have entities 1:1 or 1:m relations other entities. relations nullable.
i want proxy operations related entity. i'm giving example below. problem if relation still not exist, have null, i'm ending checking nulls, wrong. hydrate entities empty objects. reasons:
- doctrine knows instance should created field anyway. should provide empty instance instead of null
- i don't want fill code initializations,
$object->setsettings(new settingsentity) - if requests should proxied somehow disputable, want hide db representation client code. if direction totally wrong, please point me right direction. may accept responsibility of model, not of entity, doctrine returns entities me
sure, can add initialization either in constructor of entity, or provide getter creates new instance of object, if such not exists. there couple of reasons don't want this:
- i don't know how objects hydrated. assume such initialization should happen in event , not in constructor
- i don't want write code each entity (at point, forget add initialization in getter) , want make automatically each relation instead.
some example code:
/** * someobject * @orm\entity() * @orm\table( name="some_object" * ) */ class someobject implements datatransfer { /** * @orm\onetoone(targetentity="settings", mappedby="someobject") */ protected $settings; public function getsettings() { return $this->settings; } public function get() { $record = new \stdclass(); $record->id = $this->getid(); ... $settingsobject = $this->getsettings(); $record->somekey = $settingsobject ? $settingsobject->getsomekey() : null; $record->someotherkey = $settingsobject ? $settingsobject->getsomeotherkey() : null; return $record; } any suggestions, including hacking doctrine, welcome.
p.s. doctrine-orm version 2.3. can upgrade if solving problem.
i won't discuss proxy-thingie-theory: code, design, don't have enough knowlegde of these have opinion.
about knowing how doctrine hydrates entities, can see how it's done in \doctrine\orm\unitofwork::createentity. doesn't seem invoke constructor (uses \reflectionclass::newinstancewithoutconstructor, shouldn't use constructor), may interested in listening doctrine's post-load event (part of lifecycle events logic).
about initializing null properties, i.e. code post-load event should trigger, should begin having superclass on of entities: instead of class someobject implements datatransfer {...}, you'd have class someobject extends myentity {...} (and have myentity implement datatransfer keep interface). myentity class "mapped superclass", annotated @haslifecyclecallbacks, , declare method annotated @postload. there have hook run null-to-something code.
for code generic (as it'd coded superclass), can rely on doctrine's entity metadata, retains association mappings , data unit of work needs figure out low-level db-accessing business. should following:
/** @haslifecyclecallbacks @mappedsuperclass ... */ public class myentity implements datatransfer { ... /** @postload */ public function dopostload(\doctrine\common\persistence\event\lifecycleeventargs $event) { //the argument needed here, , passed since 2.4! if don't want upgrade, can work around using event listeners, it's more complicated implement ;) $em = $event->getentitymanager(); $this->enablefakemappings($em); } private function enablefakemappings(\doctrine\orm\entitymanager $em) { $mappings = $em->getclassmetadata(get_class($this))->getassociationmappings(); //try , dump $mappings array, it's full o'good things! foreach ($mappings $mapping) { if (null === $this->{$mapping['fieldname']}) { $class = $mapping['targetentity']; $this->{$mapping['fieldname']} = new $class(); //this cached in static , cloned when needed } } } } now, consider case have new entity, , want access properties without null values checks: have forge decent constructor job. still need entity manager, straightforward way pass em constructor. in zf2 (and symfony believe) can have service locator injected , retrieve em there. several ways, it's story. so, basic, in myentity:
public function __construct(\doctrine\orm\entitymanager $em) { $this->enablefakemappings($em); } doing this, however, confuse doctrine when entity persisted: should these instantiated empty objects? it'll cascade-persist them, not want (if is, well, can stop reading ;)). sacrificing cascade-persisting, easy solution this, still in superclass:
/** @prepersist */ public function doprepersist(\doctrine\common\persistence\event\lifecycleeventargs $event) { $em = $event->getentitymanager(); $this->disablefakemappings($em); } /** @preupdate */ public function dopreupdate(\doctrine\common\persistence\event\lifecycleeventargs $event) { $em = $event->getentitymanager(); $this->disablefakemappings($em); } private function disablefakemappings(\doctrine\orm\entitymanager $em) { $uow = $em->getunitofwork(); $mappings = $em->getclassmetadata()->getassociationmappings(); foreach ($mappings $mapping) { if (!$this->{$mapping['fieldname']} instanceof myentity) { continue; } //"reset" faked associations: assume they're fake if object not yet handled doctrine, breaks cascading auto-persist... risk nothing, gain nothing, heh? ;) if (null === $uow->getentitystate($this->{$mapping['fieldname']}, null)) { $this->{$mapping['fieldname']} = null; } } } hope helps! :)
Comments
Post a Comment