Skip to content

Commit eeb1777

Browse files
thirschthePanz
authored andcommitted
Adding columns defined in actAs-templates to the docblock of the generated model class.
1 parent 7327db8 commit eeb1777

File tree

3 files changed

+277
-12
lines changed

3 files changed

+277
-12
lines changed

lib/Doctrine/Import/Builder.php

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,25 @@ class Doctrine_Import_Builder extends Doctrine_Builder
179179
*/
180180
protected $_phpDocEmail = '##EMAIL##';
181181

182+
183+
/**
184+
* Contains the actAs columns after running buildSetUp
185+
*
186+
* @var array<string, array{
187+
* name: string,
188+
* type: string,
189+
* disabled?: bool,
190+
* alias?: string,
191+
* length?: int,
192+
* unique?: bool,
193+
* fixed?: bool,
194+
* primary?: bool,
195+
* notblank?: bool,
196+
* default?: mixed,
197+
* }>
198+
*/
199+
private $actAsColumns = array();
200+
182201
/**
183202
* _tpl
184203
*
@@ -396,9 +415,7 @@ public function buildTableDefinition(array $definition)
396415
/**
397416
* buildSetUp
398417
*
399-
* @param array $options
400-
* @param array $columns
401-
* @param array $relations
418+
* @param array $definition
402419
* @return string
403420
*/
404421
public function buildSetUp(array $definition)
@@ -857,21 +874,33 @@ public function buildPhpDocs(array $definition)
857874
return $ret;
858875
}
859876

877+
/**
878+
* find class matching $name
879+
*
880+
* @param $name
881+
* @return class-string<Doctrine_Template>
882+
*/
883+
private function findTemplateClassMatchingName($name)
884+
{
885+
$classname = $name;
886+
if (class_exists("Doctrine_Template_$name", true)) {
887+
$classname = "Doctrine_Template_$name";
888+
}
889+
890+
return $classname;
891+
}
892+
860893
/**
861894
* emit a behavior assign
862895
*
863896
* @param int $level
864897
* @param string $name
865898
* @param string $option
899+
* @param class-string $classname
866900
* @return string assignation code
867901
*/
868-
private function emitAssign($level, $name, $option)
902+
private function emitAssign($level, $name, $option, $classname)
869903
{
870-
// find class matching $name
871-
$classname = $name;
872-
if (class_exists("Doctrine_Template_$name", true)) {
873-
$classname = "Doctrine_Template_$name";
874-
}
875904
return " \$" . strtolower($name) . "$level = new $classname($option);". PHP_EOL;
876905
}
877906

@@ -943,6 +972,7 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
943972
$currentParent = $parent;
944973
if (is_array($actAs)) {
945974
foreach($actAs as $template => $options) {
975+
$className = $this->findTemplateClassMatchingName($template);
946976
if ($template == 'actAs') {
947977
// found another actAs
948978
$build .= $this->innerBuildActAs($options, $level + 1, $parent, $emittedActAs);
@@ -959,7 +989,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
959989
}
960990

961991
$optionPHP = $this->varExport($realOptions);
962-
$build .= $this->emitAssign($level, $template, $optionPHP);
992+
$build .= $this->emitAssign($level, $template, $optionPHP, $className);
993+
$this->determineActAsColumns($className, $realOptions);
963994
if ($level == 0) {
964995
$emittedActAs[] = $this->emitActAs($level, $template);
965996
} else {
@@ -969,7 +1000,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
9691000
$parent = $template;
9701001
$build .= $this->innerBuildActAs($leftActAs, $level, $template, $emittedActAs);
9711002
} else {
972-
$build .= $this->emitAssign($level, $template, null);
1003+
$build .= $this->emitAssign($level, $template, null, $className);
1004+
$this->determineActAsColumns($className, array($options));
9731005
if ($level == 0) {
9741006
$emittedActAs[] = $this->emitActAs($level, $template);
9751007
} else {
@@ -979,7 +1011,9 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
9791011
}
9801012
}
9811013
} else {
982-
$build .= $this->emitAssign($level, $actAs, null);
1014+
$className = $this->findTemplateClassMatchingName($actAs);
1015+
$build .= $this->emitAssign($level, $actAs, null, $className);
1016+
$this->determineActAsColumns($className, array());
9831017
if ($level == 0) {
9841018
$emittedActAs[] = $this->emitActAs($level, $actAs);
9851019
} else {
@@ -990,6 +1024,87 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
9901024
return $build;
9911025
}
9921026

1027+
/**
1028+
* Adds the columns of the used actAs behaviors to the comment block.
1029+
*
1030+
* @param class-string $className
1031+
* @param array $instanceOptions
1032+
*
1033+
* @throws Doctrine_Import_Builder_Exception
1034+
*/
1035+
private function determineActAsColumns($className, $instanceOptions)
1036+
{
1037+
// No class specified or class does not exist.
1038+
if (!$className || !class_exists($className)) {
1039+
return;
1040+
}
1041+
1042+
// PHP >= 7.4 is planned as a minimum version for the upcoming release of doctrine1,
1043+
// therefore we simply skip the generation of actAs columns if run below 7.0, as
1044+
// instantiation exceptions are not supported before PHP 7
1045+
if (PHP_VERSION_ID <= 70000) {
1046+
return;
1047+
}
1048+
1049+
try {
1050+
$actAsInstance = new $className($instanceOptions);
1051+
} catch (Error $e) {
1052+
// The class can't be instantiated, skipping it
1053+
return;
1054+
}
1055+
1056+
if (!$actAsInstance || !method_exists($actAsInstance, 'getOptions')) {
1057+
return;
1058+
}
1059+
1060+
$options = $actAsInstance->getOptions();
1061+
1062+
// Some behaviors do not contain an array of columns, e.g. SoftDelete.
1063+
if (!is_array(reset($options))) {
1064+
$options = array($options);
1065+
}
1066+
1067+
foreach ($options as $name => $column) {
1068+
if (!is_array($column) || !array_key_exists('name', $column) || !array_key_exists('type', $column)) {
1069+
// 'name' or 'type' not found.
1070+
continue;
1071+
}
1072+
1073+
if (array_key_exists('disabled', $column) && $column['disabled']) {
1074+
// Column has been disabled.
1075+
continue;
1076+
}
1077+
1078+
// Add field, if it does not exist already.
1079+
if (array_key_exists($name, $this->actAsColumns)) {
1080+
continue;
1081+
}
1082+
1083+
$this->actAsColumns[$name] = $column;
1084+
}
1085+
}
1086+
1087+
private function mergeDefinitionAndActAsColumns(array $definitionColumns, array $actAsColumns)
1088+
{
1089+
$result = $definitionColumns;
1090+
1091+
foreach ($actAsColumns as $actAsOptionName => $actAsColumn) {
1092+
$actAsColumnName = isset($actAsColumn['name']) ? $actAsColumn['name'] : $actAsOptionName;
1093+
1094+
foreach ($result as $optionName => $column) {
1095+
$name = isset($column['name']) ? $column['name'] : $optionName;
1096+
if ($name === $actAsColumnName) {
1097+
continue 2;
1098+
}
1099+
}
1100+
1101+
$result[$actAsOptionName] = $actAsColumn;
1102+
}
1103+
1104+
return $result;
1105+
}
1106+
1107+
9931108
/**
9941109
* Build php code for adding record listeners
9951110
*
@@ -1122,6 +1237,8 @@ public function buildDefinition(array $definition)
11221237
$className = $definition['className'];
11231238
$extends = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends']:$this->_baseClassName;
11241239

1240+
// Clear actAsColumns
1241+
$this->actAsColumns = array();
11251242
if ( ! (isset($definition['no_definition']) && $definition['no_definition'] === true)) {
11261243
$tableDefinitionCode = $this->buildTableDefinition($definition);
11271244
$setUpCode = $this->buildSetUp($definition);
@@ -1136,6 +1253,7 @@ public function buildDefinition(array $definition)
11361253

11371254
$setUpCode.= $this->buildToString($definition);
11381255

1256+
$definition['columns'] = $this->mergeDefinitionAndActAsColumns($definition['columns'], $this->actAsColumns);
11391257
$docs = PHP_EOL . $this->buildPhpDocs($definition);
11401258

11411259
$content = sprintf(self::$_tpl, $docs, $abstract,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Ticket_gh110_TestRecord
3+
*
4+
* This class has been auto-generated by the Doctrine ORM Framework
5+
*
6+
* @property int $id Type: integer(4)
7+
* @property my_custom_type $created_at Type: my_custom_type
8+
* @property string $deleted_at Type: timestamp, Timestamp in ISO-8601 format (YYYY-MM-DD HH:MI:SS)
9+
*
10+
* @method int getId() Type: integer(4)
11+
* @method my_custom_type getCreatedAt() Type: my_custom_type
12+
* @method string getDeletedAt() Type: timestamp, Timestamp in ISO-8601 format (YYYY-MM-DD HH:MI:SS)
13+
*
14+
* @method Ticket_gh110_TestRecord setId(int $val) Type: integer(4)
15+
* @method Ticket_gh110_TestRecord setCreatedAt(my_custom_type $val) Type: my_custom_type
16+
* @method Ticket_gh110_TestRecord setDeletedAt(string $val) Type: timestamp, Timestamp in ISO-8601 format (YYYY-MM-DD HH:MI:SS)
17+
*
18+
* @package ##PACKAGE##
19+
* @subpackage ##SUBPACKAGE##
20+
* @author ##NAME## <##EMAIL##>
21+
* @version SVN: $Id: Builder.php 7490 2010-03-29 19:53:27Z jwage $
22+
*/
23+
class Ticket_gh110_TestRecord extends Doctrine_Record
24+
{
25+
public function setTableDefinition()
26+
{
27+
$this->hasColumn('id', 'integer', 4, array(
28+
'type' => 'integer',
29+
'length' => 4,
30+
));
31+
$this->hasColumn('created_at', 'my_custom_type', null, array(
32+
'type' => 'my_custom_type',
33+
'length' => '',
34+
));
35+
}
36+
37+
public function setUp()
38+
{
39+
parent::setUp();
40+
$softdelete0 = new Doctrine_Template_SoftDelete(array(
41+
));
42+
$timestampable0 = new Doctrine_Template_Timestampable(array(
43+
'updated' =>
44+
array(
45+
'disabled' => true,
46+
),
47+
'unknown_column' =>
48+
array(
49+
),
50+
));
51+
$unknownactas0 = new UnknownActAs(array(
52+
));
53+
$gh110_template0 = new Doctrine_Template_gh110_Template(array(
54+
));
55+
$gh110_invalid_template0 = new gh110_Invalid_Template(array(
56+
));
57+
$gh110_abstract_template0 = new gh110_Abstract_Template(array(
58+
));
59+
$this->actAs($softdelete0);
60+
$this->actAs($timestampable0);
61+
$this->actAs($unknownactas0);
62+
$this->actAs($gh110_template0);
63+
$this->actAs($gh110_invalid_template0);
64+
$this->actAs($gh110_abstract_template0);
65+
}
66+
}

tests/Ticket/gh110TestCase.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
class Doctrine_Ticket_gh110_TestCase extends Doctrine_UnitTestCase
4+
{
5+
public function testAddActAsColumnsToDocBlock()
6+
{
7+
$builder = new Doctrine_Import_Builder();
8+
$class = $builder->buildDefinition(
9+
array(
10+
'className' => 'Ticket_gh110_TestRecord',
11+
'topLevelClassName' => 'Ticket_gh110_TestRecord',
12+
'is_base_class' => true,
13+
'columns' => array(
14+
'id' => array(
15+
'type' => 'integer',
16+
'length' => 4,
17+
),
18+
'my_custom_created_at' => array(
19+
'name' => 'created_at',
20+
'type' => 'my_custom_type',
21+
'length' => '',
22+
)
23+
),
24+
'actAs' => array(
25+
'SoftDelete' => array(),
26+
'Timestampable' => array(
27+
'updated' => array(
28+
'disabled' => true,
29+
),
30+
'unknown_column' => array()
31+
),
32+
'UnknownActAs' => array(),
33+
// This template brings an already defined column
34+
'gh110_Template' => array(),
35+
'gh110_Invalid_Template' => array(),
36+
'gh110_Abstract_Template' => array(),
37+
)
38+
)
39+
);
40+
41+
// Can be used to update the snapshot.
42+
//file_put_contents(dirname(__FILE__) . '/gh110/Ticket_gh110_TestRecord.snapshot', $class);
43+
$this->assertEqual($class, file_get_contents(dirname(__FILE__) . '/gh110/Ticket_gh110_TestRecord.snapshot'));
44+
}
45+
}
46+
47+
abstract class gh110_Abstract_Template {}
48+
49+
/** This is just a simple class without the required getOptions()-Method */
50+
class gh110_Invalid_Template {}
51+
52+
class Doctrine_Template_gh110_Template extends Doctrine_Template
53+
{
54+
protected $_options = array(
55+
'created' => array(
56+
'name' => 'created_at',
57+
'alias' => null,
58+
'type' => 'timestamp',
59+
'format' => 'Y-m-d H:i:s',
60+
'disabled' => false,
61+
'expression' => false,
62+
'options' => array('notnull' => true)
63+
)
64+
);
65+
66+
/**
67+
* Set table definition for Timestampable behavior
68+
*
69+
* @return void
70+
*/
71+
public function setTableDefinition()
72+
{
73+
if ( ! $this->_options['created']['disabled']) {
74+
$name = $this->_options['created']['name'];
75+
if ($this->_options['created']['alias']) {
76+
$name .= ' as ' . $this->_options['created']['alias'];
77+
}
78+
$this->hasColumn($name, $this->_options['created']['type'], null, $this->_options['created']['options']);
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)