Skip to content

Commit

Permalink
Adding columns defined in actAs-templates to the docblock of the gene…
Browse files Browse the repository at this point in the history
…rated model class.
  • Loading branch information
thirsch committed Feb 2, 2024
1 parent 7327db8 commit fca34fd
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 12 deletions.
119 changes: 107 additions & 12 deletions lib/Doctrine/Import/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ class Doctrine_Import_Builder extends Doctrine_Builder
*/
protected $_phpDocEmail = '##EMAIL##';


/**
* Contains the actAs columns after running buildSetUp
*
* @var array
*/
private $_actAsColumns = array();

/**
* _tpl
*
Expand Down Expand Up @@ -396,9 +404,7 @@ public function buildTableDefinition(array $definition)
/**
* buildSetUp
*
* @param array $options
* @param array $columns
* @param array $relations
* @param array $definition
* @return string
*/
public function buildSetUp(array $definition)
Expand Down Expand Up @@ -857,21 +863,33 @@ public function buildPhpDocs(array $definition)
return $ret;
}

/**
* find class matching $name
*
* @param $name
* @return class-string<Doctrine_Template>
*/
private function findTemplateClassMatchingName($name)
{
$classname = $name;
if (class_exists("Doctrine_Template_$name", true)) {
$classname = "Doctrine_Template_$name";
}

return $classname;
}

/**
* emit a behavior assign
*
* @param int $level
* @param string $name
* @param string $option
* @param class-string $classname
* @return string assignation code
*/
private function emitAssign($level, $name, $option)
private function emitAssign($level, $name, $option, $classname)
{
// find class matching $name
$classname = $name;
if (class_exists("Doctrine_Template_$name", true)) {
$classname = "Doctrine_Template_$name";
}
return " \$" . strtolower($name) . "$level = new $classname($option);". PHP_EOL;
}

Expand Down Expand Up @@ -943,6 +961,7 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
$currentParent = $parent;
if (is_array($actAs)) {
foreach($actAs as $template => $options) {
$className = $this->findTemplateClassMatchingName($template);
if ($template == 'actAs') {
// found another actAs
$build .= $this->innerBuildActAs($options, $level + 1, $parent, $emittedActAs);
Expand All @@ -959,7 +978,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
}

$optionPHP = $this->varExport($realOptions);
$build .= $this->emitAssign($level, $template, $optionPHP);
$build .= $this->emitAssign($level, $template, $optionPHP, $className);
$this->determineActAsColumns($className, $realOptions);
if ($level == 0) {
$emittedActAs[] = $this->emitActAs($level, $template);
} else {
Expand All @@ -969,7 +989,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
$parent = $template;
$build .= $this->innerBuildActAs($leftActAs, $level, $template, $emittedActAs);
} else {
$build .= $this->emitAssign($level, $template, null);
$build .= $this->emitAssign($level, $template, null, $className);
$this->determineActAsColumns($className, array($options));
if ($level == 0) {
$emittedActAs[] = $this->emitActAs($level, $template);
} else {
Expand All @@ -979,7 +1000,9 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
}
}
} else {
$build .= $this->emitAssign($level, $actAs, null);
$className = $this->findTemplateClassMatchingName($actAs);
$build .= $this->emitAssign($level, $actAs, null, $className);
$this->determineActAsColumns($className, array());
if ($level == 0) {
$emittedActAs[] = $this->emitActAs($level, $actAs);
} else {
Expand All @@ -990,6 +1013,75 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi
return $build;
}

/**
* Adds the columns of the used actAs behaviors to the comment block.
*
* @param class-string $className
* @param array $instanceOptions
*
* @throws Doctrine_Import_Builder_Exception
*/
private function determineActAsColumns($className, $instanceOptions)
{
// No class specified or class does not exist.
if (!$className || !class_exists($className)) {
return;
}

$actAsInstance = new $className($instanceOptions);

if (!$actAsInstance || !method_exists($actAsInstance, 'getOptions')) {
return;
}

$options = $actAsInstance->getOptions();

// Some behaviors do not contain an array of columns, e.g. SoftDelete.
if (!is_array(reset($options))) {
$options = array($options);
}

foreach ($options as $name => $column) {
if (!is_array($column) || !array_key_exists('name', $column) || !array_key_exists('type', $column)) {
// 'name' or 'type' not found. Unfortunately there is no logger. What is the best way to abort here?
continue;
}

if (array_key_exists('disabled', $column) && $column['disabled']) {
// Column has been disabled.
continue;
}

// Add field, if it does not exist already.
if (array_key_exists($name, $this->_actAsColumns)) {
continue;
}

$this->_actAsColumns[$name] = $column;
}
}

private function mergeDefinitionAndActAsColumns(array $definitionColumns, array $actAsColumns)
{
$result = $definitionColumns;

foreach ($actAsColumns as $actAsOptionName => $actAsColumn) {
$actAsColumnName = isset($actAsColumn['name']) ? $actAsColumn['name'] : $actAsOptionName;

foreach ($result as $optionName => $column) {
$name = isset($column['name']) ? $column['name'] : $optionName;
if ($name === $actAsColumnName) {
continue 2;
}
}

$result[$actAsOptionName] = $actAsColumn;
}

return $result;
}


/**
* Build php code for adding record listeners
*
Expand Down Expand Up @@ -1122,6 +1214,8 @@ public function buildDefinition(array $definition)
$className = $definition['className'];
$extends = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends']:$this->_baseClassName;

// Clear actAsColumns
$this->_actAsColumns = array();
if ( ! (isset($definition['no_definition']) && $definition['no_definition'] === true)) {
$tableDefinitionCode = $this->buildTableDefinition($definition);
$setUpCode = $this->buildSetUp($definition);
Expand All @@ -1136,6 +1230,7 @@ public function buildDefinition(array $definition)

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

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

$content = sprintf(self::$_tpl, $docs, $abstract,
Expand Down
63 changes: 63 additions & 0 deletions tests/Ticket/gh110/Ticket_gh110_TestRecord.snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Ticket_gh110_TestRecord
*
* This class has been auto-generated by the Doctrine ORM Framework
*
* @property int $id Type: integer(4)
* @property my_custom_type $created_at Type: my_custom_type
* @property string $deleted_at Type: timestamp, Timestamp in ISO-8601 format (YYYY-MM-DD HH:MI:SS)
*
* @method int getId() Type: integer(4)
* @method my_custom_type getCreatedAt() Type: my_custom_type
* @method string getDeletedAt() Type: timestamp, Timestamp in ISO-8601 format (YYYY-MM-DD HH:MI:SS)
*
* @method Ticket_gh110_TestRecord setId(int $val) Type: integer(4)
* @method Ticket_gh110_TestRecord setCreatedAt(my_custom_type $val) Type: my_custom_type
* @method Ticket_gh110_TestRecord setDeletedAt(string $val) Type: timestamp, Timestamp in ISO-8601 format (YYYY-MM-DD HH:MI:SS)
*
* @package ##PACKAGE##
* @subpackage ##SUBPACKAGE##
* @author ##NAME## <##EMAIL##>
* @version {{REPLACED}}
*/
class Ticket_gh110_TestRecord extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('id', 'integer', 4, array(
'type' => 'integer',
'length' => 4,
));
$this->hasColumn('created_at', 'my_custom_type', null, array(
'type' => 'my_custom_type',
'length' => '',
));
}

public function setUp()
{
parent::setUp();
$softdelete0 = new Doctrine_Template_SoftDelete(array(
));
$timestampable0 = new Doctrine_Template_Timestampable(array(
'updated' =>
array(
'disabled' => true,
),
'unknown_column' =>
array(
),
));
$unknownactas0 = new UnknownActAs(array(
));
$gh110_template0 = new Doctrine_Template_gh110_Template(array(
));
$gh110_invalid_template0 = new gh110_Invalid_Template(array(
));
$this->actAs($softdelete0);
$this->actAs($timestampable0);
$this->actAs($unknownactas0);
$this->actAs($gh110_template0);
$this->actAs($gh110_invalid_template0);
}
}
80 changes: 80 additions & 0 deletions tests/Ticket/gh110TestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

class Doctrine_Ticket_gh110_TestCase extends Doctrine_UnitTestCase
{
public function testAddActAsColumnsToDocBlock()
{
$builder = new Doctrine_Import_Builder();
$class = $builder->buildDefinition(
array(
'className' => 'Ticket_gh110_TestRecord',
'topLevelClassName' => 'Ticket_gh110_TestRecord',
'is_base_class' => true,
'columns' => array(
'id' => array(
'type' => 'integer',
'length' => 4,
),
'my_custom_created_at' => array(
'name' => 'created_at',
'type' => 'my_custom_type',
'length' => '',
)
),
'actAs' => array(
'SoftDelete' => array(),
'Timestampable' => array(
'updated' => array(
'disabled' => true,
),
'unknown_column' => array()
),
'UnknownActAs' => array(),
// This template brings an already defined column
'gh110_Template' => array(),
'gh110_Invalid_Template' => array(),
)
)
);

// We must replace the version as there is a timestamp in it.
$class = preg_replace('/^.*@version.*$/m', ' * @version {{REPLACED}}', $class);
// Can be used to update the snapshot.
//file_put_contents(dirname(__FILE__) . '/gh110/Ticket_gh110_TestRecord.snapshot', $class);
$this->assertEqual($class, file_get_contents(dirname(__FILE__) . '/gh110/Ticket_gh110_TestRecord.snapshot'));
}
}

/** This is just a simple class without the required getOptions()-Method */
class gh110_Invalid_Template {}

class Doctrine_Template_gh110_Template extends Doctrine_Template
{
protected $_options = array(
'created' => array(
'name' => 'created_at',
'alias' => null,
'type' => 'timestamp',
'format' => 'Y-m-d H:i:s',
'disabled' => false,
'expression' => false,
'options' => array('notnull' => true)
)
);

/**
* Set table definition for Timestampable behavior
*
* @return void
*/
public function setTableDefinition()
{
if ( ! $this->_options['created']['disabled']) {
$name = $this->_options['created']['name'];
if ($this->_options['created']['alias']) {
$name .= ' as ' . $this->_options['created']['alias'];
}
$this->hasColumn($name, $this->_options['created']['type'], null, $this->_options['created']['options']);
}
}
}

0 comments on commit fca34fd

Please sign in to comment.