├── library └── CU │ ├── Form │ ├── Model.php │ └── Model │ │ └── Adapter │ │ ├── DbTable.php │ │ ├── Doctrine.php │ │ ├── Doctrine2.php │ │ └── Interface.php │ └── Validate │ └── DbRowExists.php └── tests ├── TestHelper.php ├── config.php ├── doctrine-cli.php ├── library └── CU │ └── Form │ ├── Model │ └── Adapter │ │ └── DoctrineTest.php │ └── ModelTest.php ├── models ├── Article.php ├── Comment.php ├── User.php └── generated │ ├── BaseArticle.php │ ├── BaseComment.php │ └── BaseUser.php ├── phpunit.xml └── schema └── schema.yml /library/CU/Form/Model.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | class CU_Form_Model extends Zend_Form 7 | { 8 | const RELATION_ONE = 'one'; 9 | const RELATION_MANY = 'many'; 10 | 11 | protected static $_defaultAdapter = null; 12 | 13 | protected $_adapter = null; 14 | 15 | /** 16 | * PluginLoader for loading many relation forms 17 | */ 18 | const FORM = 'form'; 19 | 20 | /** 21 | * Reference to the model's table class 22 | * @var Doctrine_Table 23 | */ 24 | protected $_table; 25 | 26 | /** 27 | * Instance of the Zend_Form based form used 28 | * @var Zend_Form 29 | */ 30 | protected $_form; 31 | 32 | /** 33 | * Which Zend_Form element types are associated with which doctrine type? 34 | * @var array 35 | */ 36 | protected $_columnTypes = array( 37 | 'integer' => 'text', 38 | 'decimal' => 'text', 39 | 'float' => 'text', 40 | 'string' => 'text', 41 | 'varchar' => 'text', 42 | 'boolean' => 'checkbox', 43 | 'timestamp' => 'text', 44 | 'time' => 'text', 45 | 'date' => 'text', 46 | 'enum' => 'select' 47 | ); 48 | 49 | /** 50 | * Array of hooks that are called before saving the column 51 | * @var array 52 | */ 53 | protected $_columnHooks = array(); 54 | 55 | /** 56 | * Default validators for doctrine column types 57 | * @var array 58 | */ 59 | protected $_columnValidators = array( 60 | 'integer' => 'int', 61 | 'float' => 'float', 62 | 'double' => 'float' 63 | ); 64 | 65 | /** 66 | * Prefix fields with this 67 | * @var string 68 | */ 69 | protected $_fieldPrefix = 'f_'; 70 | 71 | /** 72 | * Column names listed in this array will not be shown in the form 73 | * @var array 74 | */ 75 | protected $_ignoreColumns = array(); 76 | 77 | /** 78 | * Whether or not to generate fields for many parts of m2o and m2m relations 79 | * @var bool 80 | */ 81 | protected $_generateManyFields = false; 82 | 83 | /** 84 | * Use this to override field types for columns. key = column, value = field type 85 | * @var array 86 | */ 87 | protected $_fieldTypes = array(); 88 | 89 | /** 90 | * Field labels. key = column name, value = label 91 | * @var array 92 | */ 93 | protected $_fieldLabels = array(); 94 | 95 | /** 96 | * Labels to use with many to many relations. 97 | * key = related class name, value = label 98 | * @var array 99 | */ 100 | protected $_relationLabels = array(); 101 | 102 | /** 103 | * Name of the model class 104 | * @var string 105 | */ 106 | protected $_model = ''; 107 | 108 | /** 109 | * Model instance for editing existing models 110 | * @var Doctrine_Record 111 | */ 112 | protected $_instance = null; 113 | 114 | /** 115 | * Form PluginLoader 116 | * @var Zend_Loader_PluginLoader 117 | */ 118 | protected $_formLoader = null; 119 | 120 | /** 121 | * Stores form class names for many-relations 122 | * @var array 123 | */ 124 | protected $_relationForms = array(); 125 | 126 | /** 127 | * @param array $options Options to pass to the Zend_Form constructor 128 | */ 129 | public function __construct($options = null) 130 | { 131 | parent::__construct($options); 132 | 133 | if($this->_model == '') 134 | throw new Exception('No model defined'); 135 | 136 | if($this->_adapter == null && self::$_defaultAdapter != null) 137 | $this->setAdapter(self::$_defaultAdapter); 138 | elseif($this->_adapter == null) 139 | $this->setAdapter(new CU_Form_Model_Adapter_Doctrine()); 140 | 141 | 142 | $this->_formLoader = new Zend_Loader_PluginLoader(array( 143 | 'App_Form_Model' => 'App/Form/Model/' 144 | )); 145 | 146 | 147 | $this->_preGenerate(); 148 | $this->_generateForm(); 149 | $this->_postGenerate(); 150 | } 151 | 152 | public function getAdapter() 153 | { 154 | return $this->_adapter; 155 | } 156 | 157 | public function setAdapter(CU_Form_Model_Adapter_Interface $adapter) 158 | { 159 | $this->_adapter = $adapter; 160 | $this->_adapter->setTable($this->_model); 161 | } 162 | 163 | public static function setDefaultAdapter(CU_Form_Model_Adapter_Interface $adapter) 164 | { 165 | self::$_defaultAdapter = $adapter; 166 | } 167 | 168 | public function setOptions(array $options) 169 | { 170 | if(isset($options['model'])) 171 | $this->_model = $options['model']; 172 | 173 | //adapter must be set after the model 174 | if(isset($options['adapter'])) 175 | $this->setAdapter($options['adapter']); 176 | 177 | if(isset($options['ignoreColumns'])) 178 | $this->ignoreColumns($options['ignoreColumns']); 179 | 180 | if(isset($options['columnTypes'])) 181 | $this->setColumnTypes($options['columnTypes']); 182 | 183 | if(isset($options['fieldLabels'])) 184 | $this->setFieldLabels($options['fieldLabels']); 185 | 186 | if(isset($options['fieldPrefix'])) 187 | $this->setFieldPrefix($options['fieldPrefix']); 188 | 189 | if(isset($options['generateManyFields'])) 190 | $this->setGenerateManyFields($options['generateManyFields']); 191 | 192 | parent::setOptions($options); 193 | } 194 | 195 | public function setGenerateManyFields($value) 196 | { 197 | $this->_generateManyFields = $value; 198 | } 199 | 200 | public function getGenerateManyFields() 201 | { 202 | return $this->_generateManyFields; 203 | } 204 | 205 | public function setFieldPrefix($prefix) 206 | { 207 | $this->_fieldPrefix = $prefix; 208 | } 209 | 210 | public function getFieldPrefix() 211 | { 212 | return $this->_fieldPrefix; 213 | } 214 | 215 | public function setFieldLabels(array $labels) 216 | { 217 | $this->_fieldLabels = $labels; 218 | } 219 | 220 | public function setColumnTypes(array $types) 221 | { 222 | $this->_columnTypes = $types; 223 | } 224 | 225 | public function ignoreColumns(array $columns) 226 | { 227 | $this->_ignoreColumns = $columns; 228 | } 229 | 230 | public static function create(array $options = array()) 231 | { 232 | $form = new CU_Form_Model($options); 233 | } 234 | 235 | 236 | /** 237 | * Override to provide custom pre-form generation logic 238 | */ 239 | protected function _preGenerate() 240 | { 241 | } 242 | 243 | /** 244 | * Override to provide custom post-form generation logic 245 | */ 246 | protected function _postGenerate() 247 | { 248 | } 249 | 250 | /** 251 | * Override to provide custom post-save logic 252 | */ 253 | protected function _postSave($persist) 254 | { 255 | } 256 | 257 | public function getPluginLoader($type = null) 258 | { 259 | if($type == self::FORM) 260 | return $this->_formLoader; 261 | 262 | return parent::getPluginLoader($type); 263 | } 264 | 265 | public function getTable() 266 | { 267 | return $this->_adapter->getTable(); 268 | } 269 | 270 | /** 271 | * Set the model instance for editing existing rows 272 | * @param Doctrine_Record $instance 273 | */ 274 | public function setRecord($instance) 275 | { 276 | $this->_adapter->setRecord($instance); 277 | foreach($this->_adapter->getColumns() as $name => $definition) 278 | { 279 | if($this->_isIgnoredColumn($name, $definition)) 280 | continue; 281 | 282 | $this->setDefault($this->getColumnElementName($name), $this->_adapter->getRecordValue($name)); 283 | } 284 | 285 | foreach($this->_adapter->getRelations() as $alias => $relation) 286 | { 287 | if($this->_isIgnoredRelation($relation)) 288 | continue; 289 | 290 | switch($relation['type']) 291 | { 292 | case CU_Form_Model::RELATION_ONE: 293 | $related = $this->_adapter->getRelatedRecord($instance, $alias); 294 | $this->setDefault($this->getRelationElementName($alias), $this->_adapter->getRecordIdentifier($related)); 295 | break; 296 | case CU_Form_Model::RELATION_MANY: 297 | $formClass = $this->_relationForms[$relation->getClass()]; 298 | foreach($this->_adapter->getManyRecords($alias) as $num => $rec) 299 | { 300 | $form = new $formClass; 301 | $form->setRecord($rec); 302 | $form->setIsArray(true); 303 | $form->removeDecorator('Form'); 304 | $form->addElement('submit', $this->_getDeleteButtonName($alias, $rec), array( 305 | 'label' => 'Delete' 306 | )); 307 | $label = $relation['model']; 308 | if(isset($this->_relationLabels[$relation['model']])) 309 | $label = $this->_relationLabels[$relation['model']]; 310 | 311 | $form->setLegend($label . ' ' . ($num + 1)) 312 | ->addDecorator('Fieldset'); 313 | $this->addSubForm($form, $this->_getFormName($alias, $rec)); 314 | } 315 | break; 316 | } 317 | } 318 | } 319 | 320 | public function getRecord() 321 | { 322 | $inst = $this->_adapter->getRecord(); 323 | if($inst == null) 324 | { 325 | $inst = $this->_adapter->getNewRecord(); 326 | $this->_adapter->setRecord($inst); 327 | } 328 | 329 | return $inst; 330 | } 331 | 332 | /** 333 | * Generates the form 334 | */ 335 | protected function _generateForm() 336 | { 337 | $this->_columnsToFields(); 338 | $this->_relationsToFields(); 339 | } 340 | 341 | /** 342 | * Parses columns to fields 343 | */ 344 | protected function _columnsToFields() 345 | { 346 | foreach($this->_adapter->getColumns() as $name => $definition) 347 | { 348 | if($this->_isIgnoredColumn($name, $definition)) 349 | continue; 350 | 351 | $type = $this->_columnTypes[$definition['type']]; 352 | if(isset($this->_fieldTypes[$name])) 353 | $type = $this->_fieldTypes[$name]; 354 | 355 | $field = $this->createElement($type, $this->getColumnElementName($name)); 356 | $label = $name; 357 | if(isset($this->_fieldLabels[$name])) 358 | $label = $this->_fieldLabels[$name]; 359 | 360 | if(isset($this->_columnValidators[$definition['type']])) 361 | $field->addValidator($this->_columnValidators[$definition['type']]); 362 | 363 | if(isset($definition['notnull']) && $definition['notnull'] == true) 364 | $field->setRequired(true); 365 | 366 | $field->setLabel($label); 367 | 368 | if($type == 'select' && $definition['type'] == 'enum') 369 | { 370 | foreach($definition['values'] as $text) 371 | { 372 | $field->addMultiOption($text, ucwords($text)); 373 | } 374 | } 375 | 376 | $this->addElement($field); 377 | } 378 | } 379 | 380 | /** 381 | * Parses relations to fields 382 | */ 383 | protected function _relationsToFields() 384 | { 385 | foreach($this->_adapter->getRelations() as $alias => $relation) 386 | { 387 | if($this->_isIgnoredRelation($relation)) 388 | continue; 389 | 390 | $field = null; 391 | 392 | switch($relation['type']) 393 | { 394 | case CU_Form_Model::RELATION_ONE: 395 | $options = array('------'); 396 | foreach($this->_adapter->getOneRecords($relation) as $row) 397 | { 398 | $options[$this->_adapter->getRecordIdentifier($row)] = (string)$row; 399 | } 400 | 401 | $field = $this->createElement('select', $this->getRelationElementName($alias)); 402 | $label = $relation['model']; 403 | if(isset($this->_fieldLabels[$alias])) 404 | $label = $this->_fieldLabels[$alias]; 405 | 406 | $field->setLabel($label); 407 | 408 | if($relation['notnull'] == true) 409 | $field->setRequired(true); 410 | 411 | $field->setMultiOptions($options); 412 | break; 413 | 414 | case CU_Form_Model::RELATION_MANY: 415 | $relCls = $relation['model']; 416 | 417 | //Attempt loading a custom form 418 | try 419 | { 420 | $class = $this->getPluginLoader(self::FORM)->load($relCls); 421 | } 422 | catch(Zend_Loader_PluginLoader_Exception $e) 423 | { 424 | $class = null; 425 | } 426 | $this->_relationForms[$relCls] = $class; 427 | 428 | $label = $relCls; 429 | if(isset($this->_relationLabels[$relCls])) 430 | $label = $this->_relationLabels[$relCls]; 431 | 432 | $field = $this->createElement('submit', $this->_getNewButtonName($alias), array( 433 | 'label' => 'Add new '. $label 434 | )); 435 | break; 436 | } 437 | 438 | if($field != null) 439 | $this->addElement($field); 440 | } 441 | } 442 | 443 | protected function _isIgnoredRelation($definition) 444 | { 445 | if(in_array($definition['local'], $this->_ignoreColumns) || 446 | ($this->_generateManyFields == false && $definition['type'] == CU_Form_Model::RELATION_MANY)) 447 | return true; 448 | 449 | return false; 450 | } 451 | 452 | protected function _isIgnoredColumn($name, $definition) 453 | { 454 | if((isset($definition['primary']) && $definition['primary']) || 455 | !isset($this->_columnTypes[$definition['type']]) || in_array($name, $this->_ignoreColumns)) 456 | return true; 457 | 458 | return false; 459 | } 460 | 461 | /** 462 | * Returns the name of the new button field for relation alias 463 | * @param string $relationAlias alias of the relation 464 | * @return string name of the new button 465 | */ 466 | protected function _getNewButtonName($relationAlias) 467 | { 468 | return $relationAlias . '_new_button'; 469 | } 470 | 471 | /** 472 | * Returns the name of the delete button field for relation alias 473 | * @param string $relationAlias alias of the relation 474 | * @param Doctrine_Record $record if deleting existing records 475 | * @return string name of the new button 476 | */ 477 | protected function _getDeleteButtonName($relationAlias, Doctrine_Record $record = null) 478 | { 479 | $val = 'new'; 480 | if($record != null) 481 | $val = $this->_adapter->getRecordIdentifier($record); 482 | 483 | return $relationAlias . '_' . $val . '_delete'; 484 | } 485 | /** 486 | * Returns the new form name for relation alias 487 | * @param string $relationAlias alias of the relation 488 | * @param Doctrine_Record $record if editing existing records 489 | * @return string name of the new form 490 | */ 491 | protected function _getFormName($relationAlias, Doctrine_Record $record = null) 492 | { 493 | if($record != null) 494 | { 495 | $val = $this->_adapter->getRecordIdentifier($record); 496 | return $relationAlias . '_' . $val; 497 | } 498 | 499 | return $relationAlias . '_new_form'; 500 | } 501 | 502 | public function isValid($data) 503 | { 504 | $ndata = $data; 505 | if ($this->isArray()) 506 | { 507 | $key = $this->_getArrayName($this->getElementsBelongTo()); 508 | if (isset($data[$key])) 509 | { 510 | $ndata = $data[$key]; 511 | } 512 | } 513 | 514 | foreach($this->_adapter->getRelations() as $name => $relation) 515 | { 516 | if($this->_isIgnoredRelation($relation)) 517 | continue; 518 | 519 | if($relation['type'] != CU_Form_Model::RELATION_MANY) 520 | continue; 521 | 522 | if(isset($ndata[$this->_getNewButtonName($name)]) || isset($ndata[$this->_getFormName($name)])) 523 | { 524 | if(isset($ndata[$this->_getFormName($name)]) && 525 | isset($ndata[$this->_getFormName($name)][$this->_getDeleteButtonName($name)])) 526 | { 527 | return false; 528 | } 529 | 530 | $cls = $this->_relationForms[$relation['model']]; 531 | if($cls !== null) 532 | { 533 | $form = new $cls; 534 | } 535 | else 536 | { 537 | $form = new CU_Form_Model(array( 538 | 'model' => $relation['model'] 539 | )); 540 | } 541 | 542 | $form->setIsArray(true); 543 | $form->removeDecorator('Form'); 544 | $form->addElement('submit',$this->_getDeleteButtonName($name), array( 545 | 'label' => 'Delete' 546 | )); 547 | $this->addSubForm($form, $this->_getFormName($name)); 548 | if(isset($ndata[$this->_getNewButtonName($name)])) 549 | return false; 550 | } 551 | 552 | $record = $this->getRecord(); 553 | foreach($this->_adapter->getOneRecords($record, $name) as $rec) 554 | { 555 | $formName = $this->_getFormName($name, $rec); 556 | if(isset($ndata[$formName]) && isset($ndata[$formName][$this->_getDeleteButtonName($name, $rec)])) 557 | { 558 | $this->removeSubForm($formName); 559 | $this->_adapter->deleteRecord($rec); 560 | return false; 561 | } 562 | } 563 | } 564 | 565 | return parent::isValid($data); 566 | } 567 | 568 | /** 569 | * Return name of element for column 570 | * @param string $name Name of column 571 | * @return string 572 | */ 573 | public function getColumnElementName($name) 574 | { 575 | return $this->_fieldPrefix . $name; 576 | } 577 | 578 | /** 579 | * Return name of element for relation 580 | * @param string $name Alias of the relation 581 | * @return string 582 | */ 583 | public function getRelationElementName($name) 584 | { 585 | $relations = $this->_adapter->getRelations(); 586 | $relation = $relations[$name]; 587 | $elName = $this->_fieldPrefix . $relation['local'] . '_' . $relation['id']; 588 | 589 | return $elName; 590 | } 591 | 592 | /** 593 | * Return element for column 594 | * @param string $name Name of column 595 | * @return Zend_Form_Element 596 | */ 597 | public function getElementForColumn($name) 598 | { 599 | return $this->getElement($this->getColumnElementName($name)); 600 | } 601 | 602 | /** 603 | * Return element for relation 604 | * @param string $name Alias of the relation 605 | * @return Zend_Form_Element 606 | */ 607 | public function getElementForRelation($relation) 608 | { 609 | return $this->getElement($this->getRelationElementName($relation)); 610 | } 611 | 612 | /** 613 | * Save the form data 614 | * @param bool $persist Save to DB or not 615 | * @return Doctrine_Record 616 | */ 617 | public function save($persist = true) 618 | { 619 | $inst = $this->getRecord(); 620 | 621 | foreach($this->_adapter->getColumns() as $name => $definition) 622 | { 623 | if($this->_isIgnoredColumn($name, $definition)) 624 | continue; 625 | 626 | $value = $this->getUnfilteredValue($this->getColumnElementName($name)); 627 | $this->_adapter->setRecordValue($name, $value); 628 | } 629 | 630 | foreach($this->_adapter->getRelations() as $name => $relation) 631 | { 632 | if($this->_isIgnoredRelation($relation)) 633 | continue; 634 | 635 | $colName = $relation['local']; 636 | switch($relation['type']) 637 | { 638 | case CU_Form_Model::RELATION_ONE: 639 | //Must use null if value=0 so integrity actions won't fail 640 | $val = $this->getUnfilteredValue($this->getRelationElementName($name)); 641 | if($val == 0) 642 | $val = null; 643 | 644 | if(isset($this->_columnHooks[$colName])) 645 | $val = call_user_func($this->_columnHooks[$colName], $val); 646 | 647 | $this->_adapter->setRecordValue($colName, $val); 648 | break; 649 | 650 | case CU_Form_Model::RELATION_MANY: 651 | $idColumn = $relation['id']; 652 | foreach($this->_adapter->getManyRecords($name) as $rec) 653 | { 654 | $subForm = $this->getSubForm($name . '_' . $this->_adapter->getRecordIdentifier($rec)); 655 | 656 | //Should get saved along with the main instance later 657 | $subForm->save(false); 658 | } 659 | 660 | $subForm = $this->getSubForm($name . '_new_form'); 661 | if($subForm) 662 | { 663 | $newRec = $subForm->save(false); 664 | $this->_adapter->addManyRecord($name, $newRec); 665 | } 666 | 667 | break; 668 | } 669 | } 670 | 671 | if($persist) 672 | $this->_adapter->saveRecord(); 673 | 674 | foreach($this->getSubForms() as $subForm) 675 | $subForm->save($persist); 676 | 677 | $this->_postSave($persist); 678 | return $inst; 679 | } 680 | } 681 | 682 | 683 | -------------------------------------------------------------------------------- /library/CU/Form/Model/Adapter/DbTable.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class CU_Form_Model_Adapter_DbTable implements CU_Form_Model_Adapter_Interface 8 | { 9 | protected $_table = null; 10 | protected $_record = null; 11 | 12 | public function setTable($table) 13 | { 14 | $this->_table = new $table; 15 | } 16 | 17 | public function getTable() 18 | { 19 | return $this->_table; 20 | } 21 | 22 | public function setRecord($record) 23 | { 24 | if(($record instanceof Zend_Db_Table_Row) === false) 25 | throw new RuntimeException('Record not a Zend_Db_Table_Row'); 26 | 27 | $this->_record = $record; 28 | } 29 | 30 | public function getRecord() 31 | { 32 | return $this->_record; 33 | } 34 | 35 | public function getNewRecord() 36 | { 37 | return $this->_table->createRow(); 38 | } 39 | 40 | public function saveRecord() 41 | { 42 | $this->_record->save(); 43 | } 44 | 45 | public function getRecordValue($name) 46 | { 47 | return $this->_record->$name; 48 | } 49 | 50 | public function setRecordValue($name, $value) 51 | { 52 | $this->_record->$name = $value; 53 | } 54 | 55 | public function getColumns() 56 | { 57 | $info = $this->_table->info(); 58 | 59 | $columns = array(); 60 | foreach($info['metadata'] as $colData) 61 | { 62 | $column = array( 63 | 'name' => $colData['COLUMN_NAME'], 64 | 'type' => strtolower($colData['DATA_TYPE']), 65 | 'notnull' => !$colData['NULLABLE'], 66 | 'primary' => $colData['PRIMARY'] 67 | ); 68 | 69 | $columns[$colData['COLUMN_NAME']] = $column; 70 | } 71 | 72 | return $columns; 73 | } 74 | 75 | public function getRelations() 76 | { 77 | return array(); 78 | } 79 | 80 | 81 | public function getRelatedRecord($record, $name) 82 | { 83 | return null; 84 | } 85 | 86 | public function getRecordIdentifier($record) 87 | { 88 | $info = $record->getTable()->info(); 89 | 90 | if(count($info['primary']) < 1) 91 | throw new RuntimeException('Cannot work without a primary key'); 92 | 93 | return $record->{$info['primary'][0]}; 94 | } 95 | 96 | public function getManyRecords($name) 97 | { 98 | return array(); 99 | } 100 | 101 | public function addManyRecord($name, $record) 102 | { 103 | 104 | } 105 | 106 | public function getOneRecords($relation) 107 | { 108 | return array(); 109 | } 110 | 111 | public function deleteRecord($record) 112 | { 113 | 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /library/CU/Form/Model/Adapter/Doctrine.php: -------------------------------------------------------------------------------- 1 | _table = Doctrine::getTable($table); 11 | $this->_model = $table; 12 | } 13 | 14 | public function getTable() 15 | { 16 | return $this->_table; 17 | } 18 | 19 | public function setRecord($record) 20 | { 21 | if(($record instanceof Doctrine_Record) === false) 22 | throw new InvalidArgumentException('Record not a Doctrine_Record'); 23 | 24 | $this->_record = $record; 25 | 26 | } 27 | 28 | public function getRecord() 29 | { 30 | return $this->_record; 31 | } 32 | 33 | public function saveRecord() 34 | { 35 | $this->_record->save(); 36 | } 37 | 38 | public function getRecordValue($name) 39 | { 40 | return $this->_record->$name; 41 | } 42 | 43 | public function setRecordValue($name, $value) 44 | { 45 | $this->_record->$name = $value; 46 | } 47 | 48 | /** 49 | * Return all columns as an array 50 | * 51 | * Array must contain 'type' for column type, 'notnull' true/false 52 | * for the column's nullability, and 'values' for enum values, 'primary' 53 | * true/false for primary key. Key = column's name 54 | * 55 | * @return array 56 | */ 57 | public function getColumns() 58 | { 59 | $data = $this->_table->getColumns(); 60 | $cols = array(); 61 | foreach($data as $name => $d) 62 | { 63 | $cols[$name] = array( 64 | 'type' => $d['type'], 65 | 'notnull' => (isset($d['notnull'])) ? $d['notnull'] : false, 66 | 'values' => (isset($d['values'])) ? $d['values'] : array(), 67 | 'primary' => (isset($d['primary'])) ? $d['primary'] : false 68 | ); 69 | } 70 | 71 | return $cols; 72 | } 73 | 74 | /** 75 | * Return relations as an array 76 | * 77 | * Array must contain 'type' for relation type, 'id' for the name 78 | * of the PK column of the related table, 'model' for the related class 79 | * name, 'notnull' for nullability. 'local' for the name of the local column 80 | * Key must be the alias of the relation column 81 | * 82 | * @return array 83 | */ 84 | public function getRelations() 85 | { 86 | if(defined('Doctrine_Relation::ONE_AGGREGATE')) 87 | $oneType = Doctrine_Relation::ONE_AGGREGATE; 88 | else 89 | $oneType = Doctrine_Relation::ONE; 90 | 91 | $rels = $this->_table->getRelations(); 92 | $relations = array(); 93 | 94 | foreach($rels as $rel) 95 | { 96 | $relation = array(); 97 | 98 | 99 | if($rel->getType() == $oneType) 100 | $relation['type'] = CU_Form_Model::RELATION_ONE; 101 | else 102 | $relation['type'] = CU_Form_Model::RELATION_MANY; 103 | 104 | $relation['id'] = $rel->getTable()->getIdentifier(); 105 | $relation['model'] = $rel->getClass(); 106 | $relation['local'] = $rel->getLocal(); 107 | 108 | $definition = $this->_table->getColumnDefinition($rel->getLocal()); 109 | $relation['notnull'] = (isset($definition['notnull'])) 110 | ? $definition['notnull'] 111 | : false; 112 | 113 | $relations[$rel->getAlias()] = $relation; 114 | } 115 | 116 | return $relations; 117 | } 118 | 119 | public function getRelatedRecord($record, $name) 120 | { 121 | return $record->$name; 122 | } 123 | 124 | /** 125 | * Return the value of a record's primary key 126 | * @param Doctrine_Record $record 127 | * @return mixed 128 | */ 129 | public function getRecordIdentifier($record) 130 | { 131 | $col = $record->getTable()->getIdentifier(); 132 | return $record->$col; 133 | } 134 | 135 | /** 136 | * Get the records for a many-relation 137 | * @param string $name Name of the relation 138 | * @return array 139 | */ 140 | public function getManyRecords($name) 141 | { 142 | return $this->_record->$name; 143 | } 144 | 145 | public function addManyRecord($name, $record) 146 | { 147 | $this->_record->{$name}[] = $record; 148 | } 149 | 150 | public function getOneRecords($relation) 151 | { 152 | return Doctrine::getTable($relation['model'])->findAll(); 153 | } 154 | 155 | public function deleteRecord($record) 156 | { 157 | $record->delete(); 158 | } 159 | 160 | public function getNewRecord() 161 | { 162 | return new $this->_model; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /library/CU/Form/Model/Adapter/Doctrine2.php: -------------------------------------------------------------------------------- 1 | em = $em; 23 | } 24 | 25 | /** 26 | * Add a new record to a many-relation 27 | * @param string $name name of the relation 28 | * @param mixed $record the new record 29 | */ 30 | public function addManyRecord($name, $record) { 31 | // TODO: Implement addManyRecord() method. 32 | } 33 | 34 | /** 35 | * Delete a record 36 | * @param mixed $record 37 | */ 38 | public function deleteRecord($record) { 39 | // TODO: Implement deleteRecord() method. 40 | } 41 | 42 | /** 43 | * Return all columns as an array 44 | * 45 | * Array must contain 'type' for column type, 'notnull' true/false 46 | * for the column's nullability, and 'values' for enum values, 'primary' 47 | * true/false for primary key. Key = column's name 48 | * 49 | * @return array 50 | */ 51 | public function getColumns() { 52 | $columns = array(); 53 | foreach($this->metadata->fieldMappings as $name => $data) { 54 | $columns[$data['columnName']] = array( 55 | 'name' => $data['columnName'], 56 | 'type' => $data['type'], 57 | 'notnull' => !$data['nullable'], 58 | 'primary' => isset($data['id']) && $data['id'] === true 59 | ); 60 | } 61 | 62 | return $columns; 63 | } 64 | 65 | /** 66 | * Get the records for a many-relation 67 | * @param string $name Name of the relation 68 | * @return array 69 | */ 70 | public function getManyRecords($name) { 71 | // TODO: Implement getManyRecords() method. 72 | } 73 | 74 | /** 75 | * Return a new instance of the record for this form 76 | * @return mixed 77 | */ 78 | public function getNewRecord() { 79 | return $this->metadata->newInstance(); 80 | } 81 | 82 | /** 83 | * Get records for a one-relation 84 | * @param array $relation the relation's definition 85 | * @return array array of records 86 | */ 87 | public function getOneRecords($relation) { 88 | return $this->em->getRepository($relation['model'])->findAll(); 89 | } 90 | 91 | /** 92 | * Return the record 93 | * @return mixed|null Null on failure 94 | */ 95 | public function getRecord() { 96 | return $this->record; 97 | } 98 | 99 | /** 100 | * Return the value of a record's unique identifier 101 | * @param mixed $record 102 | * @return mixed 103 | */ 104 | public function getRecordIdentifier($record) { 105 | if(!$record) { 106 | return null; 107 | } 108 | 109 | $pks = $this->em->getClassMetadata(get_class($record))->getIdentifierValues($record); 110 | if(count($pks) > 1) { 111 | throw new \RuntimeException('Currently only support entities with single column PK'); 112 | } 113 | 114 | return array_shift(array_values($pks)); 115 | } 116 | 117 | /** 118 | * Return the value of a column 119 | * @param string $column name of the column 120 | * @return string 121 | */ 122 | public function getRecordValue($column) { 123 | $field = $this->metadata->getFieldForColumn($column); 124 | return $this->metadata->getFieldValue($this->record, $field); 125 | } 126 | 127 | /** 128 | * Return a related object, or null if not found 129 | * @param mixed $record the record where to look at 130 | * @param string $name name of the relation 131 | * @return mixed 132 | */ 133 | public function getRelatedRecord($record, $name) { 134 | return $this->metadata->getFieldValue($record, $name); 135 | } 136 | 137 | /** 138 | * Return relations as an array 139 | * 140 | * Array must contain 'type' for relation type, 'id' for the name 141 | * of the PK column of the related table, 'model' for the related model 142 | * name, 'notnull' for nullability. 'local' for the name of the local column 143 | * Key must be the alias of the relation column 144 | * 145 | * @return array 146 | */ 147 | public function getRelations() { 148 | $relations = array(); 149 | foreach($this->metadata->associationMappings as $name => $data) { 150 | $type = null; 151 | switch($data['type']) { 152 | case ClassMetadata::MANY_TO_ONE: 153 | $type = CU_Form_Model::RELATION_ONE; 154 | break; 155 | 156 | case ClassMetadata::MANY_TO_MANY: 157 | $type = CU_Form_Model::RELATION_MANY; 158 | break; 159 | 160 | } 161 | 162 | //If unsupported relation type, skip to next 163 | if($type === null) { 164 | continue; 165 | } 166 | 167 | $relations[$name] = array( 168 | 'type' => $type, 169 | 'id' => $data['joinColumns'][0]['referencedColumnName'], 170 | 'model' => $data['targetEntity'], 171 | 'notnull' => !$data['joinColumns'][0]['nullable'], 172 | 'local' => $data['joinColumns'][0]['name'] 173 | ); 174 | } 175 | 176 | return $relations; 177 | } 178 | 179 | /** 180 | * Returns the table 181 | * @return mixed 182 | */ 183 | public function getTable() { 184 | return $this->table; 185 | } 186 | 187 | /** 188 | * Save the record 189 | */ 190 | public function saveRecord() { 191 | $this->em->persist($this->record); 192 | } 193 | 194 | /** 195 | * set the record 196 | * @param mixed $instance 197 | */ 198 | public function setRecord($instance) { 199 | $this->record = $instance; 200 | } 201 | 202 | /** 203 | * Set the value of a column 204 | * @param string $column column's name 205 | * @param mixed $value 206 | */ 207 | public function setRecordValue($column, $value) { 208 | $field = $this->metadata->getFieldForColumn($column); 209 | if($value !== null && isset($this->metadata->associationMappings[$field])) { 210 | $refClass = $this->metadata->associationMappings[$field]['targetEntity']; 211 | $this->metadata->setFieldValue($this->record, $field, $this->em->getReference($refClass, $value)); 212 | } 213 | else { 214 | $this->metadata->setFieldValue($this->record, $field, $value); 215 | } 216 | } 217 | 218 | /** 219 | * Set the table 220 | * @param string $table 221 | */ 222 | public function setTable($table) { 223 | $this->table = $table; 224 | $this->metadata = $this->em->getClassMetadata($this->table); 225 | } 226 | } -------------------------------------------------------------------------------- /library/CU/Form/Model/Adapter/Interface.php: -------------------------------------------------------------------------------- 1 | 'Value was not found' 10 | ); 11 | 12 | public function __construct($table) 13 | { 14 | $this->_table = $table; 15 | } 16 | 17 | public function isValid($value) 18 | { 19 | $this->_setValue($value); 20 | $row = $this->_table->find($value); 21 | 22 | if($row == false) 23 | { 24 | $this->_error(); 25 | return false; 26 | } 27 | 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/TestHelper.php: -------------------------------------------------------------------------------- 1 | DATA_FIXTURES_PATH, 7 | 'models_path' => MODELS_PATH, 8 | 'migrations_path' => MIGRATIONS_PATH, 9 | 'sql_path' => SQL_PATH, 10 | 'yaml_schema_path' => YAML_SCHEMA_PATH); 11 | 12 | Doctrine_Manager::connection('sqlite:test.db'); 13 | $cli = new Doctrine_Cli($config); 14 | $cli->run($_SERVER['argv']); 15 | -------------------------------------------------------------------------------- /tests/library/CU/Form/Model/Adapter/DoctrineTest.php: -------------------------------------------------------------------------------- 1 | closeConnection(Doctrine_Manager::connection()); 15 | } 16 | 17 | public function testTableSetup() 18 | { 19 | $adapter = new CU_Form_Model_Adapter_Doctrine(); 20 | $adapter->setTable('User'); 21 | 22 | $this->assertTrue($adapter->getTable() instanceof Doctrine_Table); 23 | } 24 | 25 | public function testGetsColumnAmountRight() 26 | { 27 | $adapter = new CU_Form_Model_Adapter_Doctrine(); 28 | $adapter->setTable('User'); 29 | 30 | $columns = $adapter->getColumns(); 31 | $this->assertEquals(4, count($columns)); 32 | } 33 | 34 | public function testGetsCorrectDataForColumns() 35 | { 36 | $adapter = new CU_Form_Model_Adapter_Doctrine(); 37 | $adapter->setTable('User'); 38 | 39 | $columns = $adapter->getColumns(); 40 | foreach($columns as $name => $c) 41 | { 42 | $this->assertTrue(isset($c['type']), 'Data does not contain type'); 43 | $this->assertTrue(isset($c['notnull']), 'Data does not contain notnull'); 44 | $this->assertTrue(isset($c['values']), 'Data does not contain values'); 45 | $this->assertTrue(isset($c['primary']), 'Data does not contain primary'); 46 | 47 | //Just test that it's not a numeric index 48 | $this->assertTrue(strlen($name) > 1); 49 | } 50 | } 51 | 52 | public function testGetsRelationCountRight() 53 | { 54 | $adapter = new CU_Form_Model_Adapter_Doctrine(); 55 | $adapter->setTable('Comment'); 56 | 57 | $rels = $adapter->getRelations(); 58 | $this->assertEquals(1, count($rels)); 59 | } 60 | 61 | public function testGetsCorrectDataForRelations() 62 | { 63 | $adapter = new CU_Form_Model_Adapter_Doctrine(); 64 | $adapter->setTable('Comment'); 65 | 66 | $rels = $adapter->getRelations(); 67 | 68 | foreach($rels as $alias => $r) 69 | { 70 | $this->assertTrue(isset($r['type']), 'Data does not contain type'); 71 | $this->assertTrue(isset($r['id']), 'Data does not contain id'); 72 | $this->assertTrue(isset($r['model']), 'Data does not contain model'); 73 | $this->assertTrue(isset($r['notnull']), 'Data does not contain notnull'); 74 | $this->assertTrue(isset($r['local']), 'Data does not contain local'); 75 | 76 | //Again just check it's not a numeric index 77 | $this->assertTrue(strlen($alias) > 2); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/library/CU/Form/ModelTest.php: -------------------------------------------------------------------------------- 1 | _columns['User'] = array( 16 | 'id' => array( 17 | 'type' => 'integer', 18 | 'notnull' => true, 19 | 'values' => array(), 20 | 'primary' => true 21 | ), 22 | 'login' => array( 23 | 'type' => 'string', 24 | 'notnull' => true, 25 | 'values' => array(), 26 | 'primary' => false 27 | ), 28 | 'password' => array( 29 | 'type' => 'string', 30 | 'notnull' => true, 31 | 'values' => array(), 32 | 'primary' => false 33 | ) 34 | ); 35 | 36 | $this->_relations['User'] = array(); 37 | 38 | $this->_columns['Comment'] = array( 39 | 'id' => array( 40 | 'type' => 'integer', 41 | 'notnull' => true, 42 | 'values' => array(), 43 | 'primary' => true 44 | ), 45 | 'sender' => array( 46 | 'type' => 'string', 47 | 'notnull' => true, 48 | 'values' => array(), 49 | 'primary' => false 50 | ), 51 | 'article_id' => array( 52 | 'type' => 'integer', 53 | 'notnull' => true, 54 | 'values' => array(), 55 | 'primary' => false 56 | ) 57 | ); 58 | 59 | $this->_relations['Comment'] = array( 60 | 'Article' => array( 61 | 'type' => CU_Form_Model::RELATION_ONE, 62 | 'id' => 'id', 63 | 'model' => 'Article', 64 | 'notnull' => true, 65 | 'local' => 'article_id' 66 | ) 67 | ); 68 | 69 | $this->_columns['Article'] = array( 70 | 'id' => array( 71 | 'type' => 'integer', 72 | 'notnull' => true, 73 | 'values' => array(), 74 | 'primary' => true 75 | ), 76 | 'name' => array( 77 | 'type' => 'string', 78 | 'notnull' => true, 79 | 'values' => array(), 80 | 'primary' => false 81 | ) 82 | ); 83 | 84 | $this->_relations['Article'] = array( 85 | 'Article' => array( 86 | 'type' => CU_Form_Model::RELATION_MANY, 87 | 'id' => 'id', 88 | 'model' => 'Article', 89 | 'notnull' => false, 90 | 'local' => 'article_id' 91 | ) 92 | ); 93 | } 94 | 95 | public function testNoModelFails() 96 | { 97 | try 98 | { 99 | $form = new CU_Form_Model(); 100 | } 101 | catch(Exception $e) 102 | { 103 | return; 104 | } 105 | 106 | $this->fail(); 107 | } 108 | 109 | private function _initAdapter($table) 110 | { 111 | $this->_adapter = $this->getMock('CU_Form_Model_Adapter_Interface'); 112 | 113 | $this->_adapter->expects($this->any()) 114 | ->method('setTable') 115 | ->with($this->equalTo($table)); 116 | 117 | //Should be called on $form->getTable() 118 | $this->_adapter->expects($this->any()) 119 | ->method('getTable') 120 | ->will($this->returnValue($table)); 121 | 122 | $this->_adapter->expects($this->any()) 123 | ->method('getColumns') 124 | ->will($this->returnValue($this->_columns[$table])); 125 | 126 | $this->_adapter->expects($this->any()) 127 | ->method('getRelations') 128 | ->will($this->returnValue($this->_relations[$table])); 129 | } 130 | 131 | public function testTableLoading() 132 | { 133 | $this->_initAdapter('User'); 134 | 135 | $form = new CU_Form_Model(array( 136 | 'model' => 'User', 137 | 'adapter' => $this->_adapter 138 | )); 139 | 140 | $this->assertEquals('User', $form->getTable()); 141 | } 142 | 143 | public function testColumnIgnoring() 144 | { 145 | $this->_initAdapter('User'); 146 | 147 | $form = new CU_Form_Model(array( 148 | 'model' => 'User', 149 | 'adapter' => $this->_adapter, 150 | 'ignoreColumns' => array('login') 151 | )); 152 | 153 | $this->assertNull($form->getElementForColumn('login')); 154 | $this->assertNotNull($form->getElementForColumn('password')); 155 | } 156 | 157 | public function testPrimaryKeyIgnored() 158 | { 159 | $this->_initAdapter('User'); 160 | 161 | $form = new CU_Form_Model(array( 162 | 'model' => 'User', 163 | 'adapter' => $this->_adapter 164 | )); 165 | 166 | $this->assertNull($form->getElementForColumn('id')); 167 | } 168 | 169 | public function testZendFormParametersPass() 170 | { 171 | $this->_initAdapter('User'); 172 | 173 | $form = new CU_Form_Model(array( 174 | 'model' => 'User', 175 | 'action' => 'test', 176 | 'adapter' => $this->_adapter 177 | )); 178 | 179 | $this->assertEquals('test', $form->getAction()); 180 | } 181 | 182 | public function testRecordLoading() 183 | { 184 | $this->_initAdapter('User'); 185 | 186 | $this->_adapter->expects($this->any()) 187 | ->method('getRecord') 188 | ->will($this->onConsecutiveCalls(false, true)); 189 | 190 | $this->_adapter->expects($this->once()) 191 | ->method('getNewRecord') 192 | ->will($this->returnValue(false)); 193 | 194 | $form = new CU_Form_Model(array( 195 | 'model' => 'User', 196 | 'adapter' => $this->_adapter 197 | )); 198 | 199 | //First getRecord is set up to return false 200 | $this->assertFalse($form->getRecord()); 201 | 202 | $user = array( 203 | 'login' => 'Login', 204 | 'password' => 'Password' 205 | ); 206 | 207 | $this->_adapter->expects($this->once()) 208 | ->method('setRecord') 209 | ->with($this->equalTo($user)); 210 | 211 | //NOTE: will cause a wrong value to be inputted into the password field! 212 | $this->_adapter->expects($this->any()) 213 | ->method('getRecordValue') 214 | ->will($this->returnValue('Login')); 215 | 216 | $form->setRecord($user); 217 | 218 | //Second getRecord is set up to return true 219 | $this->assertTrue($form->getRecord()); 220 | 221 | $this->assertEquals('Login', $form->getElementForColumn('login')->getValue()); 222 | } 223 | 224 | public function testRecordSaving() 225 | { 226 | $this->_initAdapter('User'); 227 | 228 | $form = new CU_Form_Model(array( 229 | 'model' => 'User', 230 | 'adapter' => $this->_adapter 231 | )); 232 | 233 | $form->getElementForColumn('login')->setValue('Test'); 234 | $form->getElementForColumn('password')->setValue('Test'); 235 | 236 | //Should not get called if persist param is false 237 | $this->_adapter->expects($this->never()) 238 | ->method('saveRecord'); 239 | 240 | $form->save(false); 241 | 242 | $this->_initAdapter('User'); 243 | $form->setAdapter($this->_adapter); 244 | 245 | $this->_adapter->expects($this->once()) 246 | ->method('saveRecord'); 247 | 248 | //Should get called twice as we set two values 249 | $this->_adapter->expects($this->exactly(2)) 250 | ->method('setRecordValue'); 251 | 252 | $record = $form->save(); 253 | } 254 | 255 | public function testEventHooks() 256 | { 257 | $this->_initAdapter('User'); 258 | 259 | $form = new CU_Form_ModelTest_Form(array( 260 | 'model' => 'User', 261 | 'adapter' => $this->_adapter 262 | )); 263 | 264 | $this->assertTrue($form->preGenerated); 265 | $this->assertTrue($form->postGenerated); 266 | $this->assertFalse($form->postSaved); 267 | 268 | $form->save(false); 269 | 270 | $this->assertTrue($form->postSaved); 271 | } 272 | 273 | public function testCreatingFormWithOneRelation() 274 | { 275 | $this->_initAdapter('Comment'); 276 | 277 | $this->_adapter->expects($this->once()) 278 | ->method('getOneRecords') 279 | ->with($this->_relations['Comment']['Article']) 280 | ->will($this->returnValue(array())); 281 | 282 | 283 | $form = new CU_Form_Model(array( 284 | 'model' => 'Comment', 285 | 'adapter' => $this->_adapter 286 | )); 287 | 288 | $name = $form->getRelationElementName('Article'); 289 | $elem = $form->getElementForRelation('Article'); 290 | $this->assertNotNull($elem); 291 | $this->assertNotEquals('', $name); 292 | $this->assertEquals($elem->getName(), $name); 293 | } 294 | 295 | public function testCreatingFormWithManyRelation() 296 | { 297 | $this->_initAdapter('Article'); 298 | 299 | $form = new CU_Form_Model(array( 300 | 'model' => 'Article', 301 | 'generateManyFields' => true, 302 | 'adapter' => $this->_adapter 303 | )); 304 | 305 | $forms = $form->getSubForms(); 306 | $this->assertTrue(count($forms) == 0); 307 | } 308 | 309 | public function testNotNullColumnsAreRequired() 310 | { 311 | $this->_initAdapter('Comment'); 312 | 313 | $this->_adapter->expects($this->any()) 314 | ->method('getOneRecords') 315 | ->will($this->returnValue(array())); 316 | 317 | $form = new CU_Form_Model(array( 318 | 'model' => 'Comment', 319 | 'adapter' => $this->_adapter 320 | )); 321 | 322 | $this->assertFalse($form->getElementForColumn('sender')->isValid('')); 323 | $this->assertFalse($form->getElementForRelation('Article')->isValid('')); 324 | } 325 | 326 | public function testOneRelationSaving() 327 | { 328 | $this->_initAdapter('Comment'); 329 | 330 | $article = array( 331 | 'id' => 1, 332 | 'name' => 'Test' 333 | ); 334 | 335 | $this->_adapter->expects($this->once()) 336 | ->method('getOneRecords') 337 | ->will($this->returnValue(array($article))); 338 | 339 | $form = new CU_Form_Model(array( 340 | 'model' => 'Comment', 341 | 'adapter' => $this->_adapter 342 | )); 343 | 344 | $form->getElementForRelation('Article')->setValue(1); 345 | $form->getElementForColumn('sender')->setValue('Sender'); 346 | 347 | $this->_adapter->expects($this->any()) 348 | ->method('setRecordValue'); 349 | 350 | $form->save(); 351 | } 352 | } 353 | 354 | class CU_Form_ModelTest_Form extends CU_Form_Model 355 | { 356 | public $preGenerated = false; 357 | public $postGenerated = false; 358 | public $postSaved = false; 359 | 360 | protected function _preGenerate() 361 | { 362 | $this->preGenerated = true; 363 | } 364 | 365 | protected function _postGenerate() 366 | { 367 | $this->postGenerated = true; 368 | } 369 | 370 | protected function _postSave($persist) 371 | { 372 | $this->postSaved = true; 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /tests/models/Article.php: -------------------------------------------------------------------------------- 1 | setTableName('articles'); 15 | $this->hasColumn('id', 'integer', 4, array('type' => 'integer', 'autoincrement' => true, 'primary' => true, 'length' => '4')); 16 | $this->hasColumn('name', 'string', 100, array('notnull' => true, 'type' => 'string', 'length' => '100')); 17 | } 18 | 19 | public function setUp() 20 | { 21 | $this->hasMany('Comment as Comments', array('local' => 'id', 22 | 'foreign' => 'article_id')); 23 | } 24 | } -------------------------------------------------------------------------------- /tests/models/generated/BaseComment.php: -------------------------------------------------------------------------------- 1 | setTableName('comments'); 16 | $this->hasColumn('id', 'integer', 4, array('type' => 'integer', 'autoincrement' => true, 'primary' => true, 'length' => '4')); 17 | $this->hasColumn('sender', 'string', 100, array('notnull' => true, 'type' => 'string', 'length' => '100')); 18 | $this->hasColumn('article_id', 'integer', 4, array('notnull' => true, 'type' => 'integer', 'length' => '4')); 19 | } 20 | 21 | public function setUp() 22 | { 23 | $this->hasOne('Article', array('local' => 'article_id', 24 | 'foreign' => 'id', 25 | 'onDelete' => 'CASCADE')); 26 | } 27 | } -------------------------------------------------------------------------------- /tests/models/generated/BaseUser.php: -------------------------------------------------------------------------------- 1 | setTableName('users'); 16 | $this->hasColumn('id', 'integer', 4, array('type' => 'integer', 'autoincrement' => true, 'primary' => true, 'length' => '4')); 17 | $this->hasColumn('login', 'string', 50, array('notnull' => true, 'type' => 'string', 'length' => '50')); 18 | $this->hasColumn('password', 'string', 32, array('notnull' => true, 'type' => 'string', 'length' => '32')); 19 | $this->hasColumn('email', 'string', 200, array('type' => 'string', 'length' => '200')); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ./ 4 | 5 | 6 | 7 | 8 | ../library/ 9 | ../application/ 10 | 11 | ../application/ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/schema/schema.yml: -------------------------------------------------------------------------------- 1 | User: 2 | tableName: users 3 | columns: 4 | id: 5 | type: integer(4) 6 | notnull: true 7 | autoincrement: true 8 | primary: true 9 | login: 10 | notnull: true 11 | type: string(50) 12 | password: 13 | notnull: true 14 | type: string(32) 15 | email: string(200) 16 | 17 | Article: 18 | tableName: articles 19 | columns: 20 | id: 21 | type: integer(4) 22 | notnull: true 23 | autoincrement: true 24 | primary: true 25 | name: 26 | notnull: true 27 | type: string(100) 28 | 29 | Comment: 30 | tableName: comments 31 | columns: 32 | id: 33 | type: integer(4) 34 | notnull: true 35 | autoincrement: true 36 | primary: true 37 | sender: 38 | notnull: true 39 | type: string(100) 40 | article_id: 41 | notnull: true 42 | type: integer(4) 43 | relations: 44 | Article: 45 | class: Article 46 | local: article_id 47 | type: one 48 | foreign: id 49 | foreignAlias: Comments 50 | foreignType: many 51 | onDelete: CASCADE 52 | --------------------------------------------------------------------------------