1: <?php
2:
3: namespace Alchemy\orm;
4: use Alchemy\core\schema\Table;
5: use Alchemy\core\query;
6: use Alchemy\util\promise\Promise;
7: use Alchemy\util\DataTypeLexer;
8: use Exception;
9: use ReflectionClass;
10:
11:
12: 13: 14: 15: 16: 17:
18: abstract class DataMapper {
19:
20: 21: 22: 23:
24: protected static $table_name = null;
25: protected static $schema_args = array();
26:
27: private static $schema_cache = array();
28:
29: private $deltas = array();
30: private $dependancies = array();
31: private $relatedSets = array();
32: private $session;
33: private $sessionID;
34: private $persisting;
35:
36:
37: 38: 39: 40: 41: 42:
43: public static function from_session(Session $session, $sessionID) {
44: $cls = get_called_class();
45: $obj = new $cls();
46: $obj->setSession($session, $sessionID, true);
47: return $obj;
48: }
49:
50:
51: 52: 53: 54: 55: 56:
57: public static function list_mappers() {
58: $result = array();
59: foreach (get_declared_classes() as $cls) {
60: if (is_subclass_of($cls, __CLASS__)) {
61: $reflection = new ReflectionClass($cls);
62: if (!$reflection->isAbstract()) {
63: $result[] = $cls;
64: }
65: }
66: }
67:
68: return $result;
69: }
70:
71:
72: 73: 74:
75: public static function register() {
76: static::schema();
77: }
78:
79:
80: 81: 82: 83: 84: 85:
86: public static function schema() {
87: $cls = get_called_class();
88:
89: if (!array_key_exists($cls, self::$schema_cache)) {
90: $name = static::table_name();
91: $args = static::$schema_args + array('class' => $cls);
92:
93: $tablefn = function() use ($name, $args) {
94: return Table::ORM($name, $args);
95: };
96:
97: self::$schema_cache[$cls] = new Promise($tablefn, "Alchemy\orm\ORMTable");
98: Table::register_name($name, self::$schema_cache[$cls], true);
99: self::$schema_cache[$cls]->register(true);
100: }
101:
102: return self::$schema_cache[$cls];
103: }
104:
105:
106: 107: 108: 109: 110:
111: public static function table() {
112: return new ORMTableRef(static::schema());
113: }
114:
115:
116: 117: 118: 119: 120:
121: public static function table_name() {
122: $cls = static::$table_name ?: get_called_class();
123: $cls = preg_replace("/[^A-Za-z0-9]/", "_", $cls);
124: return $cls;
125: }
126:
127:
128:
129: 130: 131:
132: public function __construct() {
133: $this->persisting = new Promise();
134: foreach (static::schema()->listRelationships() as $name => $r) {
135: $this->relatedSets[$name] = new RelatedSet($this, $r);
136: }
137: }
138:
139:
140: 141: 142: 143: 144: 145:
146: public function __get($prop) {
147: if (array_key_exists($prop, $this->relatedSets)) {
148: $set = $this->getRelatedSet($prop);
149: return $set->isSingleObject() ? $set->first() : $set;
150: }
151:
152: if (!static::schema()->hasColumn($prop)) {
153: throw new Exception("Property [{$prop}] is not a configured column");
154: }
155:
156: $cls = get_called_class();
157: if (array_key_exists($prop, $this->deltas)) {
158: return $this->deltas[$prop];
159: }
160:
161: if (is_object($this->session)) {
162: return $this->session->getProperty($cls, $this->sessionID, $prop);
163: }
164:
165: return null;
166: }
167:
168:
169: 170: 171: 172: 173: 174: 175:
176: public function __set($prop, $value) {
177: if (array_key_exists($prop, $this->relatedSets)) {
178: $this->relatedSets[$prop]->set($value);
179: return;
180: }
181:
182: if (!static::schema()->hasColumn($prop)) {
183: throw new Exception("Property [{$prop}] is not a configured column");
184: }
185:
186: $this->deltas[$prop] = $value;
187: }
188:
189:
190: 191: 192: 193: 194:
195: public function __toString() {
196: $s = '<' . get_class($this) . ' ';
197: $s .= implode(', ', $this->getPrimaryKey());
198: $s = trim($s);
199: $s .= '>';
200: return $s;
201: }
202:
203:
204: 205: 206: 207: 208: 209:
210: protected function addPersistanceDependancy(DataMapper $obj) {
211: if ($this->isTransient()) {
212: $this->dependancies[] = $obj;
213: }
214: }
215:
216:
217: 218: 219: 220: 221: 222: 223: 224:
225: public function cascadeForeignKey($child, $rel) {
226: $child->addPersistanceDependancy($this);
227:
228: return $this->persisting->then(function($self) use ($child, $rel) {
229: $child->set($rel->getRemoteColumnMap($self));
230:
231: if ($child->getSession()) {
232: $child->save(!$child->isTransient());
233: }
234:
235: return $self;
236: });
237: }
238:
239:
240: 241: 242: 243: 244: 245:
246: public function getPrimaryKey() {
247: $pk = array();
248: foreach (static::schema()->getPrimaryKey()->listColumns() as $column) {
249: $pk[] = $this->{$column->getName()};
250: }
251:
252: return $pk;
253: }
254:
255:
256: 257: 258:
259: public function getRelatedSet($name) {
260: return $this->relatedSets[$name];
261: }
262:
263:
264: 265: 266: 267: 268:
269: public function getSession() {
270: return $this->session;
271: }
272:
273:
274: 275: 276: 277: 278:
279: public function getSessionID() {
280: return $this->sessionID;
281: }
282:
283:
284: 285: 286: 287: 288:
289: public function isTransient() {
290: foreach ($this->getPrimaryKey() as $value) {
291: if (is_null($value)) {
292: return true;
293: }
294: }
295:
296: return false;
297: }
298:
299:
300: 301: 302: 303: 304: 305:
306: public function onDependanciesPersisted() {
307: $promises = array();
308: foreach ($this->dependancies as $d) {
309: $promises[] = $d->onPersisted();
310: }
311:
312: return Promise::when($promises);
313: }
314:
315:
316: 317: 318: 319: 320: 321:
322: public function onPersisted() {
323: return $this->persisting;
324: }
325:
326:
327: 328: 329:
330: public function rollback() {
331: $this->deltas = array();
332: }
333:
334:
335: 336: 337: 338: 339: 340: 341:
342: public function save($queueUpdate = true) {
343: if (!$this->session) {
344: throw new Exception("Can not save this DataMapper because it is not associated with a session");
345: }
346:
347: $cls = get_class($this);
348: foreach ($this->deltas as $prop => $value) {
349: $this->session->setProperty($cls, $this->sessionID, $prop, $value);
350: }
351:
352: if ($queueUpdate) {
353: $this->session->save($cls, $this->sessionID);
354: }
355:
356: $this->deltas = array();
357: }
358:
359:
360: 361: 362: 363: 364:
365: public function set(array $map) {
366: foreach ($map as $key => $value) {
367: $this->{$key} = $value;
368: }
369:
370: return $this;
371: }
372:
373:
374: 375: 376: 377: 378: 379:
380: public function setSession(Session $session, $sessionID, $persisted = false) {
381: $this->session = $session;
382: $this->sessionID = $sessionID;
383:
384: if ($persisted) {
385: $this->persisting->resolve($this);
386: $this->dependancies = array();
387: }
388: }
389: }
390: