Skip to content

Commit f6bf135

Browse files
committed
Adding columns defined in actAs-templates to the docblock of the generated model class.
1 parent 1d68711 commit f6bf135

File tree

3 files changed

+238
-12
lines changed

3 files changed

+238
-12
lines changed

lib/Doctrine/Import/Builder.php

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@ 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
187+
*/
188+
private $_actAsColumns = array();
189+
182190
/**
183191
* _tpl
184192
*
@@ -396,9 +404,7 @@ public function buildTableDefinition(array $definition)
396404
/**
397405
* buildSetUp
398406
*
399-
* @param array $options
400-
* @param array $columns
401-
* @param array $relations
407+
* @param array $definition
402408
* @return string
403409
*/
404410
public function buildSetUp(array $definition)
@@ -857,21 +863,33 @@ public function buildPhpDocs(array $definition)
857863
return $ret;
858864
}
859865

866+
/**
867+
* find class matching $name
868+
*
869+
* @param $name
870+
* @return class-string<Doctrine_Template>
871+
*/
872+
private function findTemplateClassMatchingName($name)
873+
{
874+
$classname = $name;
875+
if (class_exists("Doctrine_Template_$name", true)) {
876+
$classname = "Doctrine_Template_$name";
877+
}
878+
879+
return $classname;
880+
}
881+
860882
/**
861883
* emit a behavior assign
862884
*
863885
* @param int $level
864886
* @param string $name
865887
* @param string $option
888+
* @param class-string $classname
866889
* @return string assignation code
867890
*/
868-
private function emitAssign($level, $name, $option)
891+
private function emitAssign($level, $name, $option, $classname)
869892
{
870-
// find class matching $name
871-
$classname = $name;
872-
if (class_exists("Doctrine_Template_$name", true)) {
873-
$classname = "Doctrine_Template_$name";
874-
}
875893
return " \$" . strtolower($name) . "$level = new $classname($option);". PHP_EOL;
876894
}
877895

@@ -943,6 +961,7 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
943961
$currentParent = $parent;
944962
if (is_array($actAs)) {
945963
foreach($actAs as $template => $options) {
964+
$className = $this->findTemplateClassMatchingName($template);
946965
if ($template == 'actAs') {
947966
// found another actAs
948967
$build .= $this->innerBuildActAs($options, $level + 1, $parent, $emittedActAs);
@@ -959,7 +978,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
959978
}
960979

961980
$optionPHP = $this->varExport($realOptions);
962-
$build .= $this->emitAssign($level, $template, $optionPHP);
981+
$build .= $this->emitAssign($level, $template, $optionPHP, $className);
982+
$this->determineActAsColumns($className, $realOptions);
963983
if ($level == 0) {
964984
$emittedActAs[] = $this->emitActAs($level, $template);
965985
} else {
@@ -969,7 +989,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
969989
$parent = $template;
970990
$build .= $this->innerBuildActAs($leftActAs, $level, $template, $emittedActAs);
971991
} else {
972-
$build .= $this->emitAssign($level, $template, null);
992+
$build .= $this->emitAssign($level, $template, null, $className);
993+
$this->determineActAsColumns($className, array($options));
973994
if ($level == 0) {
974995
$emittedActAs[] = $this->emitActAs($level, $template);
975996
} else {
@@ -979,7 +1000,9 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
9791000
}
9801001
}
9811002
} else {
982-
$build .= $this->emitAssign($level, $actAs, null);
1003+
$className = $this->findTemplateClassMatchingName($actAs);
1004+
$build .= $this->emitAssign($level, $actAs, null, $className);
1005+
$this->determineActAsColumns($className, array());
9831006
if ($level == 0) {
9841007
$emittedActAs[] = $this->emitActAs($level, $actAs);
9851008
} else {
@@ -990,6 +1013,70 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
9901013
return $build;
9911014
}
9921015

1016+
/**
1017+
* Adds the columns of the used actAs behaviors to the comment block.
1018+
*
1019+
* @param class-string $className
1020+
* @param array $instanceOptions
1021+
*
1022+
* @throws Doctrine_Import_Builder_Exception
1023+
*/
1024+
private function determineActAsColumns($className, $instanceOptions)
1025+
{
1026+
// No class specified or class does not exist.
1027+
if (!$className || !class_exists($className)) {
1028+
return;
1029+
}
1030+
1031+
$actAsInstance = new $className($instanceOptions);
1032+
$options = $actAsInstance->getOptions();
1033+
1034+
// Some behaviors do not contain an array of columns, e.g. SoftDelete.
1035+
if (!is_array(reset($options))) {
1036+
$options = array($options);
1037+
}
1038+
1039+
foreach ($options as $name => $column) {
1040+
if (!is_array($column) || !array_key_exists('name', $column) || !array_key_exists('type', $column)) {
1041+
// 'name' or 'type' not found. Unfortunately there is no logger. What is the best way to abort here?
1042+
continue;
1043+
}
1044+
1045+
if (array_key_exists('disabled', $column) && $column['disabled']) {
1046+
// Column has been disabled.
1047+
continue;
1048+
}
1049+
1050+
// Add field, if it does not exist already.
1051+
if (array_key_exists($name, $this->_actAsColumns)) {
1052+
continue;
1053+
}
1054+
1055+
$this->_actAsColumns[$name] = $column;
1056+
}
1057+
}
1058+
1059+
private function mergeDefinitionAndActAsColumns(array $definitionColumns, array $actAsColumns)
1060+
{
1061+
$result = $definitionColumns;
1062+
1063+
foreach ($actAsColumns as $actAsOptionName => $actAsColumn) {
1064+
$actAsColumnName = isset($actAsColumn['name']) ? $actAsColumn['name'] : $actAsOptionName;
1065+
1066+
foreach ($result as $optionName => $column) {
1067+
$name = isset($column['name']) ? $column['name'] : $optionName;
1068+
if ($name === $actAsColumnName) {
1069+
continue 2;
1070+
}
1071+
}
1072+
1073+
$result[$actAsOptionName] = $actAsColumn;
1074+
}
1075+
1076+
return $result;
1077+
}
1078+
1079+
9931080
/**
9941081
* Build php code for adding record listeners
9951082
*
@@ -1122,6 +1209,8 @@ public function buildDefinition(array $definition)
11221209
$className = $definition['className'];
11231210
$extends = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends']:$this->_baseClassName;
11241211

1212+
// Clear actAsColumns
1213+
$this->_actAsColumns = array();
11251214
if ( ! (isset($definition['no_definition']) && $definition['no_definition'] === true)) {
11261215
$tableDefinitionCode = $this->buildTableDefinition($definition);
11271216
$setUpCode = $this->buildSetUp($definition);
@@ -1136,6 +1225,7 @@ public function buildDefinition(array $definition)
11361225

11371226
$setUpCode.= $this->buildToString($definition);
11381227

1228+
$definition['columns'] = $this->mergeDefinitionAndActAsColumns($definition['columns'], $this->_actAsColumns);
11391229
$docs = PHP_EOL . $this->buildPhpDocs($definition);
11401230

11411231
$content = sprintf(self::$_tpl, $docs, $abstract,
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 {{REPLACED}}
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+
$this->actAs($softdelete0);
56+
$this->actAs($timestampable0);
57+
$this->actAs($unknownactas0);
58+
$this->actAs($gh110_template0);
59+
}
60+
}

tests/Ticket/gh110TestCase.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
)
36+
)
37+
);
38+
39+
// We must replace the version as there is a timestamp in it.
40+
$class = preg_replace('/^.*@version.*$/m', ' * @version {{REPLACED}}', $class);
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+
class Doctrine_Template_gh110_Template extends Doctrine_Template
48+
{
49+
protected $_options = array(
50+
'created' => array(
51+
'name' => 'created_at',
52+
'alias' => null,
53+
'type' => 'timestamp',
54+
'format' => 'Y-m-d H:i:s',
55+
'disabled' => false,
56+
'expression' => false,
57+
'options' => array('notnull' => true)
58+
)
59+
);
60+
61+
/**
62+
* Set table definition for Timestampable behavior
63+
*
64+
* @return void
65+
*/
66+
public function setTableDefinition()
67+
{
68+
if ( ! $this->_options['created']['disabled']) {
69+
$name = $this->_options['created']['name'];
70+
if ($this->_options['created']['alias']) {
71+
$name .= ' as ' . $this->_options['created']['alias'];
72+
}
73+
$this->hasColumn($name, $this->_options['created']['type'], null, $this->_options['created']['options']);
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)