storage enhancements
[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\Gateway\Table\Reference;
9 use pq\Query\Expr;
10
11 class Map implements MapInterface
12 {
13 /**
14 * @var string
15 */
16 private $class;
17
18 /**
19 * @var Table
20 */
21 private $gateway;
22
23 /**
24 * @var ObjectManager
25 */
26 private $objects;
27
28 /**
29 * @var PropertyInterface[]
30 */
31 private $properties = [];
32
33 /**
34 * Create a new object map definition
35 * @param string $class
36 * @param Table $gateway
37 * @param ...PropertyInterface $properties
38 */
39 function __construct($class, Table $gateway, PropertyInterface ...$properties) {
40 $this->class = $class;
41 $this->gateway = $gateway;
42 foreach ($properties as $property) {
43 $this->addProperty($property);
44 }
45 $this->objects = new ObjectManager($this);
46 }
47
48 /**
49 * Get the name of the mapped class
50 * @return string
51 */
52 function getClass() {
53 return $this->class;
54 }
55
56 /**
57 * Get the object manager
58 * @return ObjectManager
59 */
60 function getObjects() {
61 return $this->objects;
62 }
63
64 /**
65 * Get the underlying table gateway
66 * @return Table
67 */
68 function getGateway() {
69 return $this->gateway;
70 }
71
72 /**
73 * Get the defined properties to map
74 * @return PropertyInterface[]
75 */
76 function getProperties() {
77 return $this->properties;
78 }
79
80 /**
81 * Add a property to map
82 * @param PropertyInterface $property
83 * @return Map
84 */
85 function addProperty(PropertyInterface $property) {
86 $property->setContainer($this);
87 $this->properties[] = $property;
88 return $this;
89 }
90
91 /**
92 * Get all child rows by foreign key
93 * @param Row $row
94 * @param string $refName
95 * @param array $objects
96 * @return Rowset
97 */
98 function allOf(Row $row, $refName, &$objects = null) {
99 /* apply objectOf to populate the object cache */
100 return $this->gateway->of($row, $refName)->apply(function($row) use(&$objects) {
101 $objects[] = $this->objects->asObject($row);
102 });
103 }
104
105 /**
106 * Get the parent row by foreign key
107 * @param Row $row
108 * @param string $refName
109 * @param array $objects
110 * @return Rowset
111 */
112 function refOf(Row $row, $refName, &$objects = null) {
113 $rid = [];
114 $rel = $row->getTable()->getRelation($this->gateway->getName(), $refName);
115 // FIXME: check if foreign key points to primary key
116 foreach ($rel as $fgn => $col) {
117 $rid[$col] = $row->$fgn->get();
118 }
119 $rid = $this->objects->serializeRowId($rid);
120 if ($this->objects->hasObject($rid)) {
121 $object = $this->objects->getObjectById($rid);
122 $row = $this->objects->getRow($object);
123 $objects[] = $object;
124 $rowset = new Rowset($this->gateway);
125 return $rowset->append($row);
126 }
127 /* apply objectOf to populate the object cache */
128 return $this->gateway->by($row, $refName)->apply(function($row) use(&$objects) {
129 $objects[] = $this->objects->asObject($row);
130 });
131 }
132
133 /**
134 * Get the table relation reference
135 * @param MapInterface $map
136 * @param string $refName
137 * @return Reference
138 */
139 function relOf(MapInterface $map, $refName) {
140 return $map->getGateway()->getRelation(
141 $this->gateway->getName(), $refName);
142 }
143
144 /**
145 * Drain the deferred callback queue
146 * @param callable[] $deferred
147 * @param callable $exec
148 */
149 private function drain(array $deferred, callable $exec) {
150 while ($deferred) {
151 $cb = array_shift($deferred);
152 if (($cb = $exec($cb))) {
153 $deferred[] = $cb;
154 }
155 }
156 }
157
158 /**
159 * Map a row to an object
160 * @param Row $row
161 * @return object
162 */
163 function map(Row $row) {
164 $deferred = [];
165 $object = $this->objects->asObject($row);
166 foreach ($this->properties as $property) {
167 if (($cb = $property->read($row, $object))) {
168 $deferred[] = $cb;
169 }
170 }
171 $this->drain($deferred, function(callable $cb) use($row, $object) {
172 return $cb($row, $object);
173 });
174 return $object;
175 }
176
177 /**
178 * Map a rowset to an array of objects
179 * @param Rowset $rows
180 * @return object[]
181 */
182 function mapAll(Rowset $rows) {
183 $objects = [];
184 foreach ($rows as $row) {
185 $objects[] = $this->map($row);
186 }
187 return $objects;
188 }
189
190 /**
191 * Unmap on object
192 * @param object $object
193 */
194 function unmap($object) {
195 $deferred = [];
196 /* @var $row Row */
197 $row = $this->objects->asRow($object);
198 $upd = $this->objects->rowId($row, true);
199 foreach ($this->properties as $property) {
200 if (($cb = $property->write($object, $row))) {
201 $deferred[] = $cb;
202 }
203 }
204 foreach ($this->gateway->getIdentity() as $col) {
205 if (null === $row->$col->get()
206 || ($row->$col->isExpr() && $row->$col->get()->isNull()))
207 {
208 $row->$col = new Expr("DEFAULT");
209 }
210 }
211 if ($row->isDirty()) {
212 if ($upd) {
213 $row->update();
214 } else {
215 $row->create();
216 }
217 }
218 foreach ($this->properties as $property) {
219 if (($cb = $property->read($row, $object))) {
220 $deferred[] = $cb;
221 }
222 }
223 $this->drain($deferred, function($cb) use($object, $row) {
224 return $cb($object, $row);
225 });
226 if ($row->isDirty()) {
227 $row->update();
228 }
229 }
230 }