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