Hoa central
Model.php
Go to the documentation of this file.
1 <?php
2 
37 namespace {
38 
39 from('Hoa')
40 
41 
44 -> import('Model.Exception')
45 
49 -> import('Praspel.~');
50 
51 }
52 
53 namespace Hoa\Model {
54 
65 abstract class Model
66  implements \Hoa\Core\Data\Datable,
67  \ArrayAccess,
68  \IteratorAggregate,
69  \Countable {
70 
76  private $__validation = true;
77 
83  private $__attributes = array();
84 
90  private $__currentAccess = null;
91 
97  protected static $__mappingLayer = array('_default' => null);
98 
99 
100 
107  final public function __construct ( ) {
108 
109  $class = new \ReflectionClass($this);
110  $ucfirst = function ( Array $matches ) {
111 
112  return ucfirst($matches[1]);
113  };
114  $default = $class->getDefaultProperties();
115 
116  foreach($class->getProperties() as $property) {
117 
118  $_name = $property->getName();
119 
120  if( '_' !== $_name[0]
121  || '_' === $_name[1])
122  continue;
123 
124  $name = substr($_name, 1);
125  $comment = $property->getDocComment();
126  $comment = preg_replace('#^(\s*/\*\*\s*)#', '', $comment);
127  $comment = preg_replace('#(\s*\*/)#', '', $comment);
128  $comment = preg_replace('#^(\s*\*\s*)#m', '', $comment);
129  $relation = false !== strpos($comment, 'relation(');
130  $validator = 'validate' . ucfirst(preg_replace_callback(
131  '#_(.)#',
132  $ucfirst,
133  strtolower($name)
134  ));
135 
136  $this->__attributes[$name] = array(
137  'comment' => $comment ?: false,
138  'name' => $name,
139  '_name' => $_name,
140  'validator' => method_exists($this, $validator)
141  ? $validator
142  : null,
143  'contract' => null, // be lazy
144  'default' => $default[$_name],
145  'value' => &$this->$_name,
146  'relation' => $relation
147  );
148 
149  if(true === $relation && empty($this->$_name))
150  $this->$_name = array();
151  }
152 
153  $this->construct();
154 
155  return;
156  }
157 
164  protected function construct ( ) {
165 
166  return;
167  }
168 
176  public function openMany ( Array $constraints = array() ) {
177 
178  return;
179  }
180 
188  public function open ( Array $constraints = array() ) {
189 
190  return;
191  }
192 
199  public function save ( ) {
200 
201  return;
202  }
203 
212  protected function map ( Array $data, Array $map = null ) {
213 
214  if(null !== $this->__currentAccess)
215  return $this->mapRelation(
216  substr($this->__currentAccess, 1),
217  $data,
218  $map
219  );
220 
221  if(empty($map))
222  $map = array_combine($handle = array_keys($data), $handle);
223 
224  foreach($data as $name => $value) {
225 
226  if(array_key_exists($name, $map))
227  $name = $map[$name];
228 
229  if($value == $this->$name)
230  continue;
231 
232  $this->$name = $value;
233  }
234 
235  return $this;
236  }
237 
247  protected function mapRelation ( $name, Array $data, Array $map = null ) {
248 
249  if(!isset($this->$name))
250  throw new Exception(
251  'Cannot map relation %s because it does not exist.', 42, $name);
252 
253  $attribute = &$this->getAttribute($name);
254 
255  if(true !== $attribute['relation'])
256  throw new Exception(
257  'Cannot map relation %s because it is not a relation.',
258  43, $name);
259 
260  if(null === $attribute['contract'])
261  $attribute['contract'] = \Hoa\Praspel::interpret($attribute['comment']);
262 
263  $realdom = $attribute['contract']
264  ->getClause('invariant')
265  ->getVariable($name)
266  ->getDomains()
267  ->offsetGet(0);
268  $classname = $realdom['classname']->getConstantValue();
269  $_name = '_' . $name;
270 
271  foreach($data as $i => $d) {
272 
273  $this->__currentAccess = $_name;
274  $class = new $classname();
275  $this->offsetSet($i, $class->map($d, $map));
276  }
277 
278  $this->__currentAccess = null;
279 
280  return $this;
281  }
282 
292  protected function getConstraints ( $defaultValues = false ) {
293 
294  $out = array();
295 
296  foreach($this->__attributes as $name => $attribute)
297  if( false === $attribute['relation']
298  && (true === $defaultValues
299  || $attribute['default'] !== $attribute['value']))
300  $out[$name] = $attribute['value'];
301 
302  return $out;
303  }
304 
312  public function __isset ( $name ) {
313 
314  return array_key_exists($name, $this->__attributes);
315  }
316 
326  public function __set ( $name, $value) {
327 
328  if(!isset($this->$name))
329  return null;
330 
331  $_name = '_' . $name;
332  $attribute = &$this->getAttribute($name);
333 
334  if(true === $attribute['relation']) {
335 
336  if(!is_array($value))
337  $value = array($value);
338 
339  $this->__currentAccess = $_name;
340 
341  foreach($value as $k => $v)
342  $this->offsetSet($k, $v);
343 
344  $this->__currentAccess = null;
345 
346  return;
347  }
348 
349  if(is_numeric($value)) {
350 
351  if($value == $_value = (int) $value)
352  $value = $_value;
353  else
354  $value = (float) $value;
355  }
356 
357  if(false === $this->isValidationEnabled()) {
358 
359  $old = $this->$_name;
360  $this->$_name = $value;
361 
362  return $old;
363  }
364 
365  if(false !== $attribute['comment']) {
366 
367  if(null === $attribute['contract'])
368  $attribute['contract'] = \Hoa\Praspel::interpret($attribute['comment']);
369 
370  $verdict = $attribute['contract']
371  ->getClause('invariant')
372  ->getVariable($name)
373  ->predicate($value);
374 
375  if(false === $verdict)
376  throw new Exception(
377  'Try to set the %s attribute with an invalid data.',
378  0, $name);
379  }
380 
381  if( (null !== $validator = $attribute['validator'])
382  && false === $this->{$validator}($value))
383  throw new Exception(
384  'Try to set the %s attribute with an invalid data.',
385  1, $name);
386 
387  $old = $this->$_name;
388  $this->$_name = $value;
389 
390  return;
391  }
392 
400  public function __get ( $name ) {
401 
402  if(!isset($this->$name))
403  return null;
404 
405  $attribute = &$this->getAttribute($name);
406 
407  if(true === $attribute['relation']) {
408 
409  $this->__currentAccess = '_' . $name;
410 
411  return $this;
412  }
413  elseif(null !== $this->__currentAccess)
414  $this->__currentAccess = null;
415 
416  return $this->{'_' . $name};
417  }
418 
426  private function _offsetExists ( $offset ) {
427 
428  if(null === $this->__currentAccess)
429  return false;
430 
431  return array_key_exists($offset, $this->{$this->__currentAccess});
432  }
433 
441  public function offsetExists ( $offset ) {
442 
443  $out = $this->_offsetExists($offset);
444  $this->__currentAccess = null;
445 
446  return $out;
447  }
448 
458  public function offsetSet ( $offset, $value ) {
459 
460  if(false === $this->isValidationEnabled()) {
461 
462  $this->{$this->__currentAccess}[$offset] = $value;
463 
464  return null;
465  }
466 
467  $oldOffset = false !== $this->_offsetExists($offset)
468  ? $this->{$this->__currentAccess}[$offset]
469  : null;
470 
471  $this->{$this->__currentAccess}[$offset] = $value;
472 
473  $name = substr($this->__currentAccess, 1);
474  $attribute = &$this->getAttribute($name);
475 
476  if(false !== $attribute['comment']) {
477 
478  if(null === $attribute['contract'])
479  $attribute['contract'] = \Hoa\Praspel::interpret($attribute['comment']);
480 
481  $verdict = $attribute['contract']
482  ->getClause('invariant')
483  ->getVariable($name)
484  ->predicate($this->{$this->__currentAccess});
485  }
486  else
487  $verdict = true;
488 
489  if(false === $verdict) {
490 
491  if(null !== $oldOffset)
492  $this->{$this->__currentAccess}[$offset] = $oldOffset;
493  else
494  unset($this->{$this->__currentAccess}[$offset]);
495 
496  throw new Exception(
497  'Try to set the %s attribute with an invalid data.', 2, $name);
498  }
499 
500  if( (null !== $validator = $attribute['validator'])
501  && false === $this->{$validator}($value)) {
502 
503  if(null !== $oldOffset)
504  $this->{$this->__currentAccess}[$offset] = $oldOffset;
505 
506  throw new Exception(
507  'Try to set the %s attribute with an invalid data.',
508  3, $name);
509  }
510 
511  return $this->__currentAccess = null;
512  }
513 
521  public function offsetGet ( $offset ) {
522 
523  if(false === $this->_offsetExists($offset))
524  return $this->__currentAccess = null;
525 
526  $out = &$this->{$this->__currentAccess}[$offset];
527  $this->__currentAccess = null;
528 
529  return $out;
530  }
531 
539  public function offsetUnset ( $offset ) {
540 
541  if(false !== $this->_offsetExists($offset))
542  unset($this->__currentAccess[$offset]);
543 
544  $this->__currentAccess = null;
545 
546  return;
547  }
548 
555  public function getIterator ( ) {
556 
557  if(null === $this->__currentAccess)
558  return null;
559 
560  return new \ArrayIterator($this->{$this->__currentAccess});
561  }
562 
569  public function count ( ) {
570 
571  if(null === $this->__currentAccess)
572  return count($this->__attributes);
573 
574  $out = count($this->{$this->__currentAccess});
575  $this->__currentAccess = null;
576 
577  return $out;
578  }
579 
587  private function &getAttribute ( $name ) {
588 
589  if(!isset($this->$name)) {
590 
591  $out = null;
592 
593  return $out;
594  }
595 
596  return $this->__attributes[$name];
597  }
598 
606  public function setEnableValidation ( $enable ) {
607 
608  $old = $this->__validation;
609  $this->__validation = $enable;
610 
611  return $old;
612  }
613 
620  public function isValidationEnabled ( ) {
621 
622  return $this->__validation;
623  }
624 
633  protected static function setMappingLayer ( $layer, $name = '_default' ) {
634 
635  $old = null;
636 
637  if(array_key_exists($name, static::$__mappingLayer))
638  $old = static::$__mappingLayer[$name];
639 
640  static::$__mappingLayer[$name] = $layer;
641 
642  return $old;
643  }
644 
652  public static function getMappingLayer ( $name = '_default' ) {
653 
654  if(!array_key_exists($name, static::$__mappingLayer))
655  return null;
656 
657  return static::$__mappingLayer[$name];
658  }
659 
666  public function toArray ( ) {
667 
668  if(null === $this->__currentAccess) {
669 
670  $out = array();
671 
672  foreach($this->__attributes as $attribute)
673  if(false === $attribute['relation'])
674  $out[$attribute['name']] = $attribute['value'];
675 
676  return $out;
677  }
678 
679  $out = array();
680 
681  foreach($this->{$this->__currentAccess} as $i => $value)
682  $out[$i] = $value->toArray();
683 
684  $this->__currentAccess = null;
685 
686  return $out;
687  }
688 }
689 
690 }
691 
692 namespace {
693 
697 Hoa\Core\Consistency::flexEntity('Hoa\Model\Model');
698 
699 }
offsetGet($offset)
Definition: Model.php:521
open(Array $constraints=array())
Definition: Model.php:188
__set($name, $value)
Definition: Model.php:326
getConstraints($defaultValues=false)
Definition: Model.php:292
offsetSet($offset, $value)
Definition: Model.php:458
map(Array $data, Array $map=null)
Definition: Model.php:212
setEnableValidation($enable)
Definition: Model.php:606
static getMappingLayer($name= '_default')
Definition: Model.php:652
_offsetExists($offset)
Definition: Model.php:426
openMany(Array $constraints=array())
Definition: Model.php:176
isValidationEnabled()
Definition: Model.php:620
offsetUnset($offset)
Definition: Model.php:539
mapRelation($name, Array $data, Array $map=null)
Definition: Model.php:247
static setMappingLayer($layer, $name= '_default')
Definition: Model.php:633
__get($name)
Definition: Model.php:400
static $__mappingLayer
Definition: Model.php:97
& getAttribute($name)
Definition: Model.php:587
offsetExists($offset)
Definition: Model.php:441
__isset($name)
Definition: Model.php:312