data mapper POC
[m6w6/pq-gateway] / lib / pq / Mapper / Map.php
diff --git a/lib/pq/Mapper/Map.php b/lib/pq/Mapper/Map.php
new file mode 100644 (file)
index 0000000..834ffb1
--- /dev/null
@@ -0,0 +1,198 @@
+<?php
+
+namespace pq\Mapper;
+
+use pq\Gateway\Row;
+use pq\Gateway\Rowset;
+use pq\Gateway\Table;
+use pq\Query\Expr;
+
+class Map implements MapInterface
+{
+       private $class;
+       private $gateway;
+       private $objects;
+       private $properties;
+
+       function __construct($class, Table $gateway, PropertyInterface ...$properties) {
+               $this->class = $class;
+               $this->gateway = $gateway;
+               $this->properties = $properties;
+               foreach ($properties as $property) {
+                       $property->setContainer($this);
+               }
+               $this->objects = new ObjectCache($this);
+       }
+
+       function getClass() {
+               return $this->class;
+       }
+
+       function getObjects() {
+               return $this->objects;
+       }
+
+       /**
+        * @return Table
+        */
+       function getGateway() {
+               return $this->gateway;
+       }
+
+       function getProperties() {
+               return $this->properties;
+       }
+
+       function addProperty(PropertyInterface $property) {
+               $property->setContainer($this);
+               $this->properties[] = $property;
+               return $this;
+       }
+/*
+       function idOf(Row $row, $check = false) {
+               $identity = $row->getIdentity();
+               if (is_scalar($identity)) {
+                       return $identity;
+               }
+
+               if ($check && !isset($identity)) {
+                       return false;
+               }
+
+               if (is_array($identity)) {
+                       if ($check && array_search(null, $identity, true)) {
+                               return false;
+                       }
+                       /* one level is better than no level * /
+                       asort($identity);
+               }
+               return json_encode($identity);
+       }
+       
+       function objectOf(Row $row) {
+               $id = $this->idOf($row);
+
+               if (isset($this->objects["obj"][$id])) {
+                       $obj = $this->objects["obj"][$id];
+               } else {
+                       $obj = new $this->class;
+                       $this->objects["obj"][$id] = $obj;
+                       $this->objects["row"][spl_object_hash($obj)] = $row;
+               }
+               return $obj;
+       }
+
+       function rowOf($object) {
+               $id = spl_object_hash($object);
+
+               if (isset($this->objects["row"][$id])) {
+                       $row = $this->objects["row"][$id];
+               } else {
+                       $row = new Row($this->gateway);
+                       $this->objects["row"][$id] = $row;
+               }
+               return $row;
+       }
+*/
+       function allOf(Row $row, $refName, &$objects = null) {
+               /* apply objectOf to populate the object cache */
+               return $this->gateway->of($row, $refName)->apply(function($row) use(&$objects) {
+                       $objects[] = $this->objects->asObject($row);
+               });
+       }
+
+       function refOf(Row $row, $refName, &$objects = null) {
+               $rid = [];
+               $rel = $row->getTable()->getRelation($this->gateway->getName(), $refName);
+               // FIXME: check if foreign key points to primary key
+               foreach ($rel as $fgn => $col) {
+                       $rid[$col] = $row->$fgn->get();
+               }
+               $rid = $this->objects->serializeRowId($rid);
+               if ($this->objects->hasObject($rid)) {
+                       $object = $this->objects->getObjectById($rid);
+                       $row = $this->objects->getRow($object);
+                       $objects[] = $object;
+                       $rowset = new Rowset($this->gateway);
+                       return $rowset->append($row);
+               }
+               /* apply objectOf to populate the object cache */
+               return $this->gateway->by($row, $refName)->apply(function($row) use(&$objects) {
+                       $objects[] = $this->objects->asObject($row);
+               });
+       }
+
+       function relOf(MapInterface $map, $refName) {
+               return $map->getGateway()->getRelation(
+                       $this->gateway->getName(), $refName);
+       }
+
+       private function drain(array $deferred, callable $exec) {
+               while ($deferred) {
+                       $cb = array_shift($deferred);
+                       if (($cb = $exec($cb))) {
+                               $deferred[] = $cb;
+                       }
+               }
+       }
+
+       function map(Row $row) {
+               $deferred = [];
+               $object = $this->objects->asObject($row);
+               foreach ($this->properties as $property) {
+                       if (($cb = $property->read($row, $object))) {
+                               $deferred[] = $cb;
+                       }
+               }
+               $this->drain($deferred, function(callable $cb) use($row, $object) {
+                       return $cb($row, $object);
+               });
+               return $object;
+       }
+
+       function mapAll(Rowset $rows) {
+               $objects = [];
+               foreach ($rows as $row) {
+                       $objects[] = $this->map($row);
+               }
+               return $objects;
+       }
+
+       function unmap($object) {
+               $deferred = [];
+               /* @var $row Row */
+               $row = $this->objects->asRow($object);
+               $upd = $this->objects->rowId($row, true);
+               foreach ($this->properties as $property) {
+                       if (($cb = $property->write($object, $row))) {
+                               $deferred[] = $cb;
+                       }
+               }
+               foreach ($this->gateway->getIdentity() as $col) {
+                       if (null === $row->$col->get()
+                       || ($row->$col->isExpr() && $row->$col->get()->isNull()))
+                       {
+                               $row->$col = new Expr("DEFAULT");
+                       }
+               }
+               if ($row->isDirty()) {
+                       if ($upd) {
+                               $row->update();
+                       } else {
+                               $row->create();
+                       }
+               }
+               foreach ($this->properties as $property) {
+                       if (($cb = $property->read($row, $object))) {
+                               $deferred[] = $cb;
+                       }
+               }
+               $this->drain($deferred, function($cb) use($object, $row) {
+                       return $cb($object, $row);
+               });
+               if ($row->isDirty()) {
+                       $row->update();
+               }
+       }
+
+}
\ No newline at end of file