00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00016 class MetaClassProperty
00017 {
00018 private $class = null;
00019
00020 private $name = null;
00021 private $columnName = null;
00022
00023 private $type = null;
00024 private $size = null;
00025
00026 private $required = false;
00027 private $identifier = false;
00028
00029 private $relation = null;
00030
00031 private $strategy = null;
00032
00033 public function __construct(
00034 $name,
00035 BasePropertyType $type,
00036 MetaClass $class
00037 )
00038 {
00039 $this->name = $name;
00040
00041 $this->type = $type;
00042
00043 $this->class = $class;
00044 }
00045
00046 public function equals(MetaClassProperty $property)
00047 {
00048 return (
00049 ($property->getName() == $this->getName())
00050 && ($property->getColumnName() == $this->getColumnName())
00051 && ($property->getType() == $this->getType())
00052 && ($property->getSize() == $this->getSize())
00053 && ($property->getRelation() == $this->getRelation())
00054 && ($property->isRequired() == $this->isRequired())
00055 && ($property->isIdentifier() == $this->isIdentifier())
00056 );
00057 }
00058
00062 public function getClass()
00063 {
00064 return $this->class;
00065 }
00066
00067 public function getName()
00068 {
00069 return $this->name;
00070 }
00071
00075 public function setName($name)
00076 {
00077 $this->name = $name;
00078
00079 return $this;
00080 }
00081
00082 public function getColumnName()
00083 {
00084 return $this->columnName;
00085 }
00086
00090 public function setColumnName($name)
00091 {
00092 $this->columnName = $name;
00093
00094 return $this;
00095 }
00096
00100 public function getConvertedName()
00101 {
00102 return strtolower(
00103 preg_replace(':([A-Z]):', '_\1', $this->name)
00104 );
00105 }
00106
00110 public function getType()
00111 {
00112 return $this->type;
00113 }
00114
00115 public function getSize()
00116 {
00117 return $this->size;
00118 }
00119
00124 public function setSize($size)
00125 {
00126 if ($this->type instanceof NumericType) {
00127 if (strpos($size, ',') !== false) {
00128 list($size, $precision) = explode(',', $size, 2);
00129
00130 $this->type->setPrecision($precision);
00131 }
00132 }
00133
00134 Assert::isInteger(
00135 $size,
00136 'only integers allowed in size parameter'
00137 );
00138
00139 if ($this->type->isMeasurable()) {
00140 $this->size = $size;
00141 } else
00142 throw new WrongArgumentException(
00143 "size not allowed for '"
00144 .$this->getName().'::'.get_class($this->type)
00145 ."' type"
00146 );
00147
00148 return $this;
00149 }
00150
00151 public function isRequired()
00152 {
00153 return $this->required;
00154 }
00155
00156 public function isOptional()
00157 {
00158 return !$this->required;
00159 }
00160
00164 public function required()
00165 {
00166 $this->required = true;
00167
00168 return $this;
00169 }
00170
00174 public function optional()
00175 {
00176 $this->required = false;
00177
00178 return $this;
00179 }
00180
00181 public function isIdentifier()
00182 {
00183 return $this->identifier;
00184 }
00185
00189 public function setIdentifier($really = false)
00190 {
00191 $this->identifier = ($really === true);
00192
00193 return $this;
00194 }
00195
00199 public function getRelation()
00200 {
00201 return $this->relation;
00202 }
00203
00204 public function getRelationId()
00205 {
00206 if ($this->relation)
00207 return $this->relation->getId();
00208
00209 return null;
00210 }
00211
00215 public function setRelation(MetaRelation $relation)
00216 {
00217 $this->relation = $relation;
00218
00219 return $this;
00220 }
00221
00225 public function setFetchStrategy(FetchStrategy $strategy)
00226 {
00227 $this->strategy = $strategy;
00228
00229 return $this;
00230 }
00231
00235 public function getFetchStrategy()
00236 {
00237 return $this->strategy;
00238 }
00239
00240 public function getFetchStrategyId()
00241 {
00242 if ($this->strategy)
00243 return $this->strategy->getId();
00244
00245 return null;
00246 }
00247
00248 public function toMethods(
00249 MetaClass $class,
00250 MetaClassProperty $holder = null
00251 )
00252 {
00253 if (
00254 !$holder
00255 && $this->type instanceof ObjectType
00256 && !$this->type->isGeneric()
00257 && $this->type->getClass()->getPattern()
00258 instanceof ValueObjectPattern
00259 ) {
00260 $out = null;
00261
00262 $remote = $this->type->getClass();
00263
00264 foreach ($remote->getProperties() as $property) {
00265 $out .= $property->toMethods($remote, $this);
00266 }
00267
00268 return $out.$this->type->toMethods($class, $this);
00269 }
00270
00271 return $this->type->toMethods($class, $this, $holder);
00272 }
00273
00274 public function toPrimitive(MetaClass $class)
00275 {
00276 if (
00277 (
00278 $this->getType() instanceof ObjectType
00279 && !$this->getType()->isGeneric()
00280 )
00281 || $this->isIdentifier()
00282 ) {
00283 if (
00284 !$this->isIdentifier()
00285 && (
00286 $this->getType()->getClass()->getPattern()
00287 instanceof EnumerationClassPattern
00288 )
00289 )
00290 $isEnum = true;
00291 else
00292 $isEnum = false;
00293
00294 if ($isEnum) {
00295 $className = $this->getType()->getClassName();
00296
00297 $primitiveName = $this->getName();
00298 } elseif ($this->isIdentifier()) {
00299 $className = $class->getName();
00300 $primitiveName = 'id';
00301 } else {
00302 $className = $this->getType()->getClassName();
00303 $primitiveName = $this->getName();
00304 }
00305
00306 if ($isEnum) {
00307 $primitive =
00308 "\nPrimitive::enumeration('{$primitiveName}')->\n"
00309 ."of('{$className}')->\n";
00310 } else {
00311 if (
00312 !$this->getRelation()
00313 || ($this->getRelationId() == MetaRelation::ONE_TO_ONE)
00314 ) {
00315 if (
00316 !$this->getType()->isGeneric()
00317 && $this->getType() instanceof ObjectType
00318 && (
00319 $this->getType()->getClass()->getPattern()
00320 instanceof ValueObjectPattern
00321 )
00322 ) {
00323 $primitive = array();
00324 $remote = $this->getType()->getClass();
00325
00326 foreach ($remote->getProperties() as $remoteProperty) {
00327 $primitive[] = $remoteProperty->toPrimitive($remote);
00328 }
00329 } else {
00330 $primitive =
00331 "\nPrimitive::identifier('{$primitiveName}')->\n";
00332
00333
00334 if (
00335 !(
00336 $class->getType()
00337 && (
00338 $class->getTypeId()
00339 == MetaClassType::CLASS_ABSTRACT
00340 )
00341 && $this->isIdentifier()
00342 )
00343 ) {
00344 $primitive .= "of('{$className}')->\n";
00345 }
00346
00347 $id = null;
00348
00349
00350 if ($this->getType() instanceof ObjectType) {
00351 $id =
00352 $this->getType()->
00353 getClass()->
00354 getIdentifier();
00355
00356 } elseif ($this->isIdentifier()) {
00357 $id = $this;
00358 }
00359
00360 if ($id && $id->getType() instanceof IntegerType) {
00361 if ($limits = $id->getType()->toPrimitiveLimits())
00362 $primitive .= $limits."->\n";
00363 }
00364 }
00365 } else {
00366 $primitive = null;
00367 }
00368 }
00369
00370 if ($primitive && !is_array($primitive)) {
00371 if ($this->getType()->hasDefault())
00372 $primitive .=
00373 "setDefault({$this->getType()->getDefault()})->\n";
00374
00375 if ($this->isRequired())
00376 $primitive .= "required()\n";
00377 else
00378 $primitive .= "optional()\n";
00379 }
00380 } else {
00381 $required = ($this->required ? 'required' : 'optional');
00382
00383 $size = $limits = null;
00384
00385 if ($this->size && (!$this->type instanceof NumericType)) {
00386 if ($this->type instanceof FixedLengthStringType)
00387 $limits =
00388 "setMin({$this->size})->\n"
00389 ."setMax({$this->size})";
00390 else
00391 $size = "->\nsetMax({$this->size})";
00392 }
00393
00394 if ($this->type instanceof IntegerType)
00395 $limits = $this->type->toPrimitiveLimits();
00396
00397 if ($limits)
00398 $limits = $limits."->\n";
00399
00400 $primitive = <<<EOT
00401
00402 {$this->type->toPrimitive()}('{$this->name}')->
00403 {$limits}{$required}(){$size}
00404
00405 EOT;
00406 }
00407
00408 return $primitive;
00409 }
00410
00411 public function toDaoSetter($className, $cascade = true)
00412 {
00413 $varName = $this->toVarName($className);
00414 $method = ucfirst($this->name);
00415
00416 $out = null;
00417
00418 if (!$this->type->isGeneric()) {
00419
00420 switch ($this->getRelationId()) {
00421
00422 case MetaRelation::ONE_TO_ONE:
00423
00424 $remote = $this->type->getClass();
00425
00426 if ($remote->getPattern() instanceof ValueObjectPattern) {
00427 if ($cascade) {
00428 $out =
00429 "set{$method}(\n"
00430 ."Singleton::getInstance('{$this->type->getClassName()}DAO')->\nmakeCascade(\n"
00431 ."Singleton::getInstance('{$this->type->getClassName()}DAO')->\nmakeSelf("
00432 ."\$array, \$prefix), "
00433 ."\$array, \$prefix\n)\n"
00434 .')';
00435 } else {
00436 $out =
00437 "set{$method}(\n"
00438 ."Singleton::getInstance('{$this->type->getClassName()}DAO')->\nmakeJoiners(\n"
00439 ."Singleton::getInstance('{$this->type->getClassName()}DAO')->\nmakeSelf("
00440 ."\$array, \$prefix), "
00441 ."\$array, \$prefix\n)\n"
00442 .')';
00443 }
00444 } elseif ($remote->getPattern() instanceof EnumerationClassPattern) {
00445 if ($this->required) {
00446 $out =
00447 "set{$method}("
00448 ."new {$this->type->getClassName()}("
00449 ."\$array[\$prefix.'{$this->getColumnName()}']"
00450 ."))";
00451 } else {
00452 $out = <<<EOT
00453 if (isset(\$array[\$prefix.'{$this->getColumnName()}'])) {
00454 \${$varName}->set{$method}(
00455 new {$this->type->getClassName()}(\$array[\$prefix.'{$this->getColumnName()}'])
00456 );
00457 }
00458
00459 EOT;
00460 }
00461 } else {
00462 $idName =
00463 $this->toVarName(
00464 $remote->getIdentifier()->getName()
00465 );
00466
00467 if ($this->getFetchStrategyId() == FetchStrategy::LAZY) {
00468 if ($this->required) {
00469 $out =
00470 "set{$method}Id("
00471 ."\$array[\$prefix.'{$this->getColumnName()}']"
00472 .')';
00473 } else {
00474 $out = <<<EOT
00475 if (isset(\$array[\$prefix.'{$this->getColumnName()}'])) {
00476 \${$varName}->set{$method}Id(\$array[\$prefix.'{$this->getColumnName()}']);
00477 }
00478
00479 EOT;
00480 }
00481 } else {
00482 if ($cascade) {
00483 if ($this->required) {
00484
00485 $out =
00486 "set{$method}("
00487 ."{$this->type->getClassName()}::dao()->getById("
00488 ."\$array[\$prefix.'{$this->getColumnName()}']"
00489 .'))';
00490
00491 } else {
00492
00493 $out = <<<EOT
00494 if (isset(\$array[\$prefix.'{$this->getColumnName()}'])) {
00495 \${$varName}->set{$method}(
00496 {$this->type->getClassName()}::dao()->getById(\$array[\$prefix.'{$this->getColumnName()}'])
00497 );
00498 }
00499
00500 EOT;
00501 }
00502 } else {
00503 if ($this->required) {
00504
00505 if ($this->type->getClassName() == $className) {
00506 Assert::isUnreachable('notify voxus, please');
00507
00508 $out = <<<EOT
00509 set{$method}(
00510 \$this->makeSelf(\$array, \$this->getJoinPrefix('{$this->getColumnName()}'))
00511 )
00512 EOT;
00513 } else
00514 $out = <<<EOT
00515 set{$method}(
00516 {$this->type->getClassName()}::dao()->makeJoinedObject(\$array, {$this->type->getClassName()}::dao()->getJoinPrefix('{$this->getColumnName()}', \$prefix))
00517 )
00518 EOT;
00519 } else {
00520
00521 if ($this->type->getClassName() == $className) {
00522 Assert::isUnreachable('notify voxus, please');
00523
00524 $out = <<<EOT
00525 if (isset(\$array[{$this->type->getClassName()}::dao()->getJoinPrefix('{$this->getColumnName()}').'{$idName}'])) {
00526 \${$varName}->set{$method}(
00527 \$this->makeSelf(\$array, \$this->getJoinPrefix('{$this->getColumnName()}'))
00528 );
00529 }
00530
00531 EOT;
00532 } else
00533 $out = <<<EOT
00534 if (isset(\$array[{$this->type->getClassName()}::dao()->getJoinPrefix('{$this->getColumnName()}', \$prefix).'{$idName}'])) {
00535 \${$varName}->set{$method}(
00536 {$this->type->getClassName()}::dao()->makeJoinedObject(\$array, {$this->type->getClassName()}::dao()->getJoinPrefix('{$this->getColumnName()}', \$prefix))
00537 );
00538 }
00539
00540 EOT;
00541 }
00542 }
00543 }
00544 }
00545
00546 break;
00547
00548 case MetaRelation::ONE_TO_MANY:
00549 case MetaRelation::MANY_TO_MANY:
00550
00551 return null;
00552
00553 default:
00554
00555 throw new UnsupportedMethodException();
00556 }
00557 } else {
00558
00559 if ($this->type instanceof ObjectType) {
00560 $value = "new {$this->type->getClassName()}(";
00561
00562 if ($this->type instanceof InternalType) {
00563 $value = "\n{$value}\n";
00564
00565 foreach ($this->getType()->getSuffixList() as $suffix)
00566 $value .= "ArrayUtils::getArrayVar(\$array, '{$this->getColumnName()}_{$suffix}'),\n";
00567
00568 $value = rtrim($value, ",\n")."\n)\n";
00569 } else {
00570 $value .= "\$array[\$prefix.'{$this->getColumnName()}'])";
00571 }
00572 } elseif ($this->type instanceof InetType) {
00573 $value = "long2ip(\$array[\$prefix.'{$this->getColumnName()}'])";
00574 } elseif ($this->type instanceof BooleanType) {
00575
00576 $value = "(bool) strtr(\$array[\$prefix.'{$this->getColumnName()}'], array('f' => null))";
00577 } else
00578 $value = "\$array[\$prefix.'{$this->getColumnName()}']";
00579
00580 if ($this->required) {
00581 $out =
00582 "set{$method}("
00583 .$value
00584 .")";
00585 } else {
00586 if ($this->type instanceof ObjectType) {
00587
00588 $out = <<<EOT
00589 if (isset(\$array[\$prefix.'{$this->getColumnName()}'])) {
00590 \${$varName}->set{$method}(
00591 new {$this->type->getClassName()}(\$array[\$prefix.'{$this->getColumnName()}'])
00592 );
00593 }
00594
00595 EOT;
00596 } else {
00597
00598 $out = <<<EOT
00599 set{$method}(
00600 isset(\$array[\$prefix.'{$this->getColumnName()}'])
00601 ? {$value}
00602 : null
00603 )
00604 EOT;
00605 }
00606 }
00607 }
00608
00609 return $out;
00610 }
00611
00612 public function toDaoField($className)
00613 {
00614 $varName = $this->toVarName($className);
00615 $method = ucfirst($this->name);
00616
00617 $out = null;
00618
00619 if (!$this->type->isGeneric()) {
00620
00621 switch ($this->getRelationId()) {
00622
00623 case MetaRelation::ONE_TO_ONE:
00624
00625 $remote = $this->type->getClass();
00626
00627 if ($remote->getPattern() instanceof ValueObjectPattern) {
00628
00629 Assert::isUnreachable();
00630 }
00631
00632 $idName = $remote->getIdentifier()->getName();
00633 $idMethod = ucfirst($idName);
00634
00635 if ($this->getFetchStrategyId() == FetchStrategy::LAZY) {
00636 $out .=
00637 "set('{$this->getColumnName()}', "
00638 ."\${$varName}->get{$method}Id())";
00639 } else {
00640 if ($this->required)
00641 $out = "set('{$this->getColumnName()}', ";
00642 else
00643 $out = "set(\n'{$this->getColumnName()}', ";
00644
00645 if ($this->required)
00646 $out .=
00647 "\${$varName}->get{$method}()->get{$idMethod}())";
00648 else
00649 $out .=
00650 "\n"
00651 ."\${$varName}->get{$method}()\n"
00652 ."? \${$varName}->get{$method}()->get{$idMethod}()\n"
00653 .": null\n)";
00654 }
00655
00656 break;
00657
00658 case MetaRelation::ONE_TO_MANY:
00659 case MetaRelation::MANY_TO_MANY:
00660
00661 return null;
00662
00663 default:
00664
00665 throw new UnsupportedMethodException();
00666 }
00667 } else {
00668
00669 if ($this->type instanceof BooleanType) {
00670 $set = 'setBoolean';
00671 $get = 'is';
00672 } elseif ($this->type instanceof InternalType) {
00673 $set = 'lazySet';
00674 $get = 'get';
00675 } else {
00676 $set = 'set';
00677 $get = 'get';
00678 }
00679
00680 if ($this->type instanceof ObjectType) {
00681
00682 if ($this->type instanceof InternalType)
00683 $toString = null;
00684 else
00685 $toString = '->toString()';
00686
00687 if ($this->required)
00688 $out .=
00689 "{$set}('{$this->getColumnName()}', "
00690 ."\${$varName}->get{$method}(){$toString}";
00691 else
00692 $out .=
00693 "{$set}(\n'{$this->getColumnName()}', "
00694 ."\n"
00695 ."\${$varName}->get{$method}()\n"
00696 ."? \${$varName}->get{$method}(){$toString}\n"
00697 .": null\n";
00698 } else {
00699 if ($this->type instanceof InetType) {
00700 $out .=
00701 "{$set}('{$this->getColumnName()}', "
00702 ."ip2long(\${$varName}->{$get}{$method}())";
00703 } else {
00704 $out .=
00705 "{$set}('{$this->getColumnName()}', "
00706 ."\${$varName}->{$get}{$method}()";
00707 }
00708 }
00709
00710 $out .= ')';
00711 }
00712
00713 return $out;
00714 }
00715
00716 public function getRelationColumnName()
00717 {
00718 if ($this->type instanceof ObjectType && !$this->type->isGeneric()) {
00719 if ($this->relation->getId() == MetaRelation::MANY_TO_MANY)
00720 $columnName = $this->type->getClass()->getTableName().'_id';
00721 else
00722 $columnName = $this->getColumnName();
00723 } elseif ($this->type instanceof InternalType) {
00724 $out = array();
00725 foreach ($this->type->getSuffixList() as $suffix) {
00726 $out[] = $this->getColumnName().'_'.$suffix;
00727 }
00728 return $out;
00729 } else
00730 $columnName = $this->getColumnName();
00731
00732 return $columnName;
00733 }
00734
00735 public function toColumn()
00736 {
00737 if (
00738 $this->getType() instanceof ObjectType
00739 && (
00740 ($this->getType() instanceof InternalType)
00741 || (
00742 !$this->getType()->isGeneric()
00743 && (
00744 $this->getType()->getClass()->getPattern()
00745 instanceof ValueObjectPattern
00746 )
00747 )
00748 )
00749 ) {
00750 $columns = array();
00751
00752 $prefix =
00753 $this->getType() instanceof InternalType
00754 ? $this->getColumnName().'_'
00755 : null;
00756
00757 $remote = $this->getType()->getClass();
00758
00759 foreach ($remote->getProperties() as $property) {
00760 $columns[] = $property->buildColumn(
00761 $prefix.$property->getRelationColumnName()
00762 );
00763 }
00764
00765 return $columns;
00766 }
00767
00768 return $this->buildColumn($this->getRelationColumnName());
00769 }
00770
00771 public function toLightProperty()
00772 {
00773 return
00774 LightMetaProperty::make(
00775 $this->getName(),
00776 $this->getRelationColumnName(),
00777 $this->getType() instanceof ObjectType
00778 ? $this->getType()->getClassName()
00779 : null,
00780 $this->isRequired(),
00781 $this->getType()->isGeneric(),
00782 $this->getRelationId(),
00783 $this->getFetchStrategyId()
00784 );
00785 }
00786
00787 private function buildColumn($columnName)
00788 {
00789 if (is_array($columnName)) {
00790 $out = array();
00791
00792 foreach ($columnName as $name) {
00793 $out[] = $this->buildColumn($name);
00794 }
00795
00796 return $out;
00797 }
00798
00799 $column = <<<EOT
00800 addColumn(
00801 DBColumn::create(
00802 {$this->type->toColumnType($this->size)}
00803 EOT;
00804
00805 if ($this->required) {
00806 $column .= <<<EOT
00807 ->
00808 setNull(false)
00809 EOT;
00810 }
00811
00812 if ($this->size) {
00813 $column .= <<<EOT
00814 ->
00815 setSize({$this->size})
00816 EOT;
00817 }
00818
00819 if ($this->type instanceof NumericType) {
00820 $column .= <<<EOT
00821 ->
00822 setPrecision({$this->type->getPrecision()})
00823 EOT;
00824 }
00825
00826 $column .= <<<EOT
00827 ,
00828 '{$columnName}'
00829 )
00830 EOT;
00831
00832 if ($this->identifier) {
00833 $column .= <<<EOT
00834 ->
00835 setPrimaryKey(true)->
00836 setAutoincrement(true)
00837 EOT;
00838 }
00839
00840 if ($this->type->hasDefault()) {
00841 $default = $this->type->getDefault();
00842
00843 if ($this->type instanceof BooleanType) {
00844 if ($default)
00845 $default = 'true';
00846 else
00847 $default = 'false';
00848 } elseif ($this->type instanceof StringType) {
00849 $default = "'{$default}'";
00850 }
00851
00852 $column .= <<<EOT
00853 ->
00854 setDefault({$default})
00855 EOT;
00856 }
00857
00858 $column .= <<<EOT
00859
00860 )
00861 EOT;
00862
00863 return $column;
00864 }
00865
00866 private function toVarName($name)
00867 {
00868 return strtolower($name[0]).substr($name, 1);
00869 }
00870 }
00871 ?>