data mapper POC
[m6w6/pq-gateway] / lib / pq / Mapper / Map.php
1 <?php
2
3 namespace pq\Mapper;
4
5 use pq\Gateway\Row;
6 use pq\Gateway\Rowset;
7 use pq\Gateway\Table;
8 use pq\Query\Expr;
9
10 class Map implements MapInterface
11 {
12 private $class;
13 private $gateway;
14 private $objects;
15 private $properties;
16
17 function __construct($class, Table $gateway, PropertyInterface ...$properties) {
18 $this->class = $class;
19 $this->gateway = $gateway;
20 $this->properties = $properties;
21 foreach ($properties as $property) {
22 $property->setContainer($this);
23 }
24 $this->objects = new ObjectCache($this);
25 }
26
27 function getClass() {
28 return $this->class;
29 }
30
31 function getObjects() {
32 return $this->objects;
33 }
34
35 /**
36 * @return Table
37 */
38 function getGateway() {
39 return $this->gateway;
40 }
41
42 function getProperties() {
43 return $this->properties;
44 }
45
46 function addProperty(PropertyInterface $property) {
47 $property->setContainer($this);
48 $this->properties[] = $property;
49 return $this;
50 }
51 /*
52 function idOf(Row $row, $check = false) {
53 $identity = $row->getIdentity();
54 if (is_scalar($identity)) {
55 return $identity;
56 }
57
58 if ($check && !isset($identity)) {
59 return false;
60 }
61
62 if (is_array($identity)) {
63 if ($check && array_search(null, $identity, true)) {
64 return false;
65 }
66 /* one level is better than no level * /
67 asort($identity);
68 }
69 return json_encode($identity);
70 }
71
72 function objectOf(Row $row) {
73 $id = $this->idOf($row);
74
75 if (isset($this->objects["obj"][$id])) {
76 $obj = $this->objects["obj"][$id];
77 } else {
78 $obj = new $this->class;
79 $this->objects["obj"][$id] = $obj;
80 $this->objects["row"][spl_object_hash($obj)] = $row;
81 }
82 return $obj;
83 }
84
85 function rowOf($object) {
86 $id = spl_object_hash($object);
87
88 if (isset($this->objects["row"][$id])) {
89 $row = $this->objects["row"][$id];
90 } else {
91 $row = new Row($this->gateway);
92 $this->objects["row"][$id] = $row;
93 }
94 return $row;
95 }
96 */
97 function allOf(Row $row, $refName, &$objects = null) {
98 /* apply objectOf to populate the object cache */
99 return $this->gateway->of($row, $refName)->apply(function($row) use(&$objects) {
100 $objects[] = $this->objects->asObject($row);
101 });
102 }
103
104 function refOf(Row $row, $refName, &$objects = null) {
105 $rid = [];
106 $rel = $row->getTable()->getRelation($this->gateway->getName(), $refName);
107 // FIXME: check if foreign key points to primary key
108 foreach ($rel as $fgn => $col) {
109 $rid[$col] = $row->$fgn->get();
110 }
111 $rid = $this->objects->serializeRowId($rid);
112 if ($this->objects->hasObject($rid)) {
113 $object = $this->objects->getObjectById($rid);
114 $row = $this->objects->getRow($object);
115 $objects[] = $object;
116 $rowset = new Rowset($this->gateway);
117 return $rowset->append($row);
118 }
119 /* apply objectOf to populate the object cache */
120 return $this->gateway->by($row, $refName)->apply(function($row) use(&$objects) {
121 $objects[] = $this->objects->asObject($row);
122 });
123 }
124
125 function relOf(MapInterface $map, $refName) {
126 return $map->getGateway()->getRelation(
127 $this->gateway->getName(), $refName);
128 }
129
130 private function drain(array $deferred, callable $exec) {
131 while ($deferred) {
132 $cb = array_shift($deferred);
133 if (($cb = $exec($cb))) {
134 $deferred[] = $cb;
135 }
136 }
137 }
138
139 function map(Row $row) {
140 $deferred = [];
141 $object = $this->objects->asObject($row);
142 foreach ($this->properties as $property) {
143 if (($cb = $property->read($row, $object))) {
144 $deferred[] = $cb;
145 }
146 }
147 $this->drain($deferred, function(callable $cb) use($row, $object) {
148 return $cb($row, $object);
149 });
150 return $object;
151 }
152
153 function mapAll(Rowset $rows) {
154 $objects = [];
155 foreach ($rows as $row) {
156 $objects[] = $this->map($row);
157 }
158 return $objects;
159 }
160
161 function unmap($object) {
162 $deferred = [];
163 /* @var $row Row */
164 $row = $this->objects->asRow($object);
165 $upd = $this->objects->rowId($row, true);
166 foreach ($this->properties as $property) {
167 if (($cb = $property->write($object, $row))) {
168 $deferred[] = $cb;
169 }
170 }
171 foreach ($this->gateway->getIdentity() as $col) {
172 if (null === $row->$col->get()
173 || ($row->$col->isExpr() && $row->$col->get()->isNull()))
174 {
175 $row->$col = new Expr("DEFAULT");
176 }
177 }
178 if ($row->isDirty()) {
179 if ($upd) {
180 $row->update();
181 } else {
182 $row->create();
183 }
184 }
185 foreach ($this->properties as $property) {
186 if (($cb = $property->read($row, $object))) {
187 $deferred[] = $cb;
188 }
189 }
190 $this->drain($deferred, function($cb) use($object, $row) {
191 return $cb($object, $row);
192 });
193 if ($row->isDirty()) {
194 $row->update();
195 }
196 }
197
198 }