1
0
mirror of synced 2025-01-18 22:41:43 +03:00
doctrine2/lib/Doctrine/ORM/Tools/EntityGenerator.php
Markus Lanthaler 1b7ca67fdb Improve DocBlock annotations of generated entities
Currently, the DocBlock annotations for member variables contain the variable name as description which is redundant and should be removed. Furthermore the class is annotated with the FQN instead of just the name. This makes automatically generated documentation quite ugly.
2012-11-02 17:15:44 +01:00

1276 lines
42 KiB
PHP

<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\Common\Util\Inflector;
use Doctrine\DBAL\Types\Type;
/**
* Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances
*
* [php]
* $classes = $em->getClassMetadataFactory()->getAllMetadata();
*
* $generator = new \Doctrine\ORM\Tools\EntityGenerator();
* $generator->setGenerateAnnotations(true);
* $generator->setGenerateStubMethods(true);
* $generator->setRegenerateEntityIfExists(false);
* $generator->setUpdateEntityIfExists(true);
* $generator->generate($classes, '/path/to/generate/entities');
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityGenerator
{
/**
* Specifies class fields should be protected
*/
const FIELD_VISIBLE_PROTECTED = 'protected';
/**
* Specifies class fields should be private
*/
const FIELD_VISIBLE_PRIVATE = 'private';
/**
* @var bool
*/
private $backupExisting = true;
/**
* The extension to use for written php files
*
* @var string
*/
private $extension = '.php';
/**
* Whether or not the current ClassMetadataInfo instance is new or old
*
* @var boolean
*/
private $isNew = true;
/**
* @var array
*/
private $staticReflection = array();
/**
* Number of spaces to use for indention in generated code
*/
private $numSpaces = 4;
/**
* The actual spaces to use for indention
*
* @var string
*/
private $spaces = ' ';
/**
* The class all generated entities should extend
*
* @var string
*/
private $classToExtend;
/**
* Whether or not to generation annotations
*
* @var boolean
*/
private $generateAnnotations = false;
/**
* @var string
*/
private $annotationsPrefix = '';
/**
* Whether or not to generated sub methods
*
* @var boolean
*/
private $generateEntityStubMethods = false;
/**
* Whether or not to update the entity class if it exists already
*
* @var boolean
*/
private $updateEntityIfExists = false;
/**
* Whether or not to re-generate entity class if it exists already
*
* @var boolean
*/
private $regenerateEntityIfExists = false;
/**
* @var boolean
*/
private $fieldVisibility = 'private';
/**
* Hash-map for handle types
*
* @var array
*/
private $typeAlias = array(
Type::DATETIMETZ => '\DateTime',
Type::DATETIME => '\DateTime',
Type::DATE => '\DateTime',
Type::TIME => '\DateTime',
Type::OBJECT => '\stdClass',
Type::BIGINT => 'integer',
Type::SMALLINT => 'integer',
Type::TEXT => 'string',
Type::BLOB => 'string',
Type::DECIMAL => 'float',
Type::JSON_ARRAY => 'array',
Type::SIMPLE_ARRAY => 'array',
);
/**
* @var string
*/
private static $classTemplate =
'<?php
<namespace>
use Doctrine\ORM\Mapping as ORM;
<entityAnnotation>
<entityClassName>
{
<entityBody>
}
';
/**
* @var string
*/
private static $getMethodTemplate =
'/**
* <description>
*
* @return <variableType>
*/
public function <methodName>()
{
<spaces>return $this-><fieldName>;
}';
/**
* @var string
*/
private static $setMethodTemplate =
'/**
* <description>
*
* @param <variableType>$<variableName>
* @return <entity>
*/
public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
{
<spaces>$this-><fieldName> = $<variableName>;
<spaces>return $this;
}';
/**
* @var string
*/
private static $addMethodTemplate =
'/**
* <description>
*
* @param <variableType>$<variableName>
* @return <entity>
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
<spaces>$this-><fieldName>[] = $<variableName>;
<spaces>return $this;
}';
/**
* @var string
*/
private static $removeMethodTemplate =
'/**
* <description>
*
* @param <variableType>$<variableName>
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
<spaces>$this-><fieldName>->removeElement($<variableName>);
}';
/**
* @var string
*/
private static $lifecycleCallbackMethodTemplate =
'/**
* @<name>
*/
public function <methodName>()
{
<spaces>// Add your code here
}';
/**
* @var string
*/
private static $constructorMethodTemplate =
'/**
* Constructor
*/
public function __construct()
{
<spaces><collections>
}
';
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->annotationsPrefix = 'ORM\\';
}
}
/**
* Generate and write entity classes for the given array of ClassMetadataInfo instances
*
* @param array $metadatas
* @param string $outputDirectory
* @return void
*/
public function generate(array $metadatas, $outputDirectory)
{
foreach ($metadatas as $metadata) {
$this->writeEntityClass($metadata, $outputDirectory);
}
}
/**
* Generated and write entity class to disk for the given ClassMetadataInfo instance
*
* @param ClassMetadataInfo $metadata
* @param string $outputDirectory
* @return void
*/
public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
{
$path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension;
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
}
$this->isNew = !file_exists($path) || (file_exists($path) && $this->regenerateEntityIfExists);
if ( ! $this->isNew) {
$this->parseTokensInEntityFile(file_get_contents($path));
} else {
$this->staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array());
}
if ($this->backupExisting && file_exists($path)) {
$backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
if (!copy($path, $backupPath)) {
throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
}
}
// If entity doesn't exist or we're re-generating the entities entirely
if ($this->isNew) {
file_put_contents($path, $this->generateEntityClass($metadata));
// If entity exists and we're allowed to update the entity class
} else if ( ! $this->isNew && $this->updateEntityIfExists) {
file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
}
}
/**
* Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
*
* @param ClassMetadataInfo $metadata
* @return string $code
*/
public function generateEntityClass(ClassMetadataInfo $metadata)
{
$placeHolders = array(
'<namespace>',
'<entityAnnotation>',
'<entityClassName>',
'<entityBody>'
);
$replacements = array(
$this->generateEntityNamespace($metadata),
$this->generateEntityDocBlock($metadata),
$this->generateEntityClassName($metadata),
$this->generateEntityBody($metadata)
);
$code = str_replace($placeHolders, $replacements, self::$classTemplate);
return str_replace('<spaces>', $this->spaces, $code);
}
/**
* Generate the updated code for the given ClassMetadataInfo and entity at path
*
* @param ClassMetadataInfo $metadata
* @param string $path
* @return string $code;
*/
public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
{
$currentCode = file_get_contents($path);
$body = $this->generateEntityBody($metadata);
$body = str_replace('<spaces>', $this->spaces, $body);
$last = strrpos($currentCode, '}');
return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}";
}
/**
* Set the number of spaces the exported class should have
*
* @param integer $numSpaces
* @return void
*/
public function setNumSpaces($numSpaces)
{
$this->spaces = str_repeat(' ', $numSpaces);
$this->numSpaces = $numSpaces;
}
/**
* Set the extension to use when writing php files to disk
*
* @param string $extension
* @return void
*/
public function setExtension($extension)
{
$this->extension = $extension;
}
/**
* Set the name of the class the generated classes should extend from
*
* @return void
*/
public function setClassToExtend($classToExtend)
{
$this->classToExtend = $classToExtend;
}
/**
* Set whether or not to generate annotations for the entity
*
* @param bool $bool
* @return void
*/
public function setGenerateAnnotations($bool)
{
$this->generateAnnotations = $bool;
}
/**
* Set the class fields visibility for the entity (can either be private or protected)
*
* @param bool $bool
* @return void
*/
public function setFieldVisibility($visibility)
{
if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) {
throw new \InvalidArgumentException('Invalid provided visibilty (only private and protected are allowed): ' . $visibility);
}
$this->fieldVisibility = $visibility;
}
/**
* Set an annotation prefix.
*
* @param string $prefix
*/
public function setAnnotationPrefix($prefix)
{
$this->annotationsPrefix = $prefix;
}
/**
* Set whether or not to try and update the entity if it already exists
*
* @param bool $bool
* @return void
*/
public function setUpdateEntityIfExists($bool)
{
$this->updateEntityIfExists = $bool;
}
/**
* Set whether or not to regenerate the entity if it exists
*
* @param bool $bool
* @return void
*/
public function setRegenerateEntityIfExists($bool)
{
$this->regenerateEntityIfExists = $bool;
}
/**
* Set whether or not to generate stub methods for the entity
*
* @param bool $bool
* @return void
*/
public function setGenerateStubMethods($bool)
{
$this->generateEntityStubMethods = $bool;
}
/**
* Should an existing entity be backed up if it already exists?
*/
public function setBackupExisting($bool)
{
$this->backupExisting = $bool;
}
/**
* @param string $type
* @return string
*/
private function getType($type)
{
if (isset($this->typeAlias[$type])) {
return $this->typeAlias[$type];
}
return $type;
}
private function generateEntityNamespace(ClassMetadataInfo $metadata)
{
if ($this->hasNamespace($metadata)) {
return 'namespace ' . $this->getNamespace($metadata) .';';
}
}
private function generateEntityClassName(ClassMetadataInfo $metadata)
{
return 'class ' . $this->getClassName($metadata) .
($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null);
}
private function generateEntityBody(ClassMetadataInfo $metadata)
{
$fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata);
$associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata);
$stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null;
$lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata);
$code = array();
if ($fieldMappingProperties) {
$code[] = $fieldMappingProperties;
}
if ($associationMappingProperties) {
$code[] = $associationMappingProperties;
}
$code[] = $this->generateEntityConstructor($metadata);
if ($stubMethods) {
$code[] = $stubMethods;
}
if ($lifecycleCallbackMethods) {
$code[] = $lifecycleCallbackMethods;
}
return implode("\n", $code);
}
private function generateEntityConstructor(ClassMetadataInfo $metadata)
{
if ($this->hasMethod('__construct', $metadata)) {
return '';
}
$collections = array();
foreach ($metadata->associationMappings as $mapping) {
if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
$collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
}
}
if ($collections) {
return $this->prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->spaces, $collections), self::$constructorMethodTemplate));
}
return '';
}
/**
* @todo this won't work if there is a namespace in brackets and a class outside of it.
* @param string $src
*/
private function parseTokensInEntityFile($src)
{
$tokens = token_get_all($src);
$lastSeenNamespace = "";
$lastSeenClass = false;
$inNamespace = false;
$inClass = false;
for ($i = 0; $i < count($tokens); $i++) {
$token = $tokens[$i];
if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
continue;
}
if ($inNamespace) {
if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
$lastSeenNamespace .= $token[1];
} else if (is_string($token) && in_array($token, array(';', '{'))) {
$inNamespace = false;
}
}
if ($inClass) {
$inClass = false;
$lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
$this->staticReflection[$lastSeenClass]['properties'] = array();
$this->staticReflection[$lastSeenClass]['methods'] = array();
}
if ($token[0] == T_NAMESPACE) {
$lastSeenNamespace = "";
$inNamespace = true;
} else if ($token[0] == T_CLASS) {
$inClass = true;
} else if ($token[0] == T_FUNCTION) {
if ($tokens[$i+2][0] == T_STRING) {
$this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
} else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
$this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
}
} else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
$this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
}
}
}
private function hasProperty($property, ClassMetadataInfo $metadata)
{
if ($this->extendsClass()) {
// don't generate property if its already on the base class.
$reflClass = new \ReflectionClass($this->getClassToExtend());
if ($reflClass->hasProperty($property)) {
return true;
}
}
return (
isset($this->staticReflection[$metadata->name]) &&
in_array($property, $this->staticReflection[$metadata->name]['properties'])
);
}
private function hasMethod($method, ClassMetadataInfo $metadata)
{
if ($this->extendsClass()) {
// don't generate method if its already on the base class.
$reflClass = new \ReflectionClass($this->getClassToExtend());
if ($reflClass->hasMethod($method)) {
return true;
}
}
return (
isset($this->staticReflection[$metadata->name]) &&
in_array($method, $this->staticReflection[$metadata->name]['methods'])
);
}
private function hasNamespace(ClassMetadataInfo $metadata)
{
return strpos($metadata->name, '\\') ? true : false;
}
private function extendsClass()
{
return $this->classToExtend ? true : false;
}
private function getClassToExtend()
{
return $this->classToExtend;
}
private function getClassToExtendName()
{
$refl = new \ReflectionClass($this->getClassToExtend());
return '\\' . $refl->getName();
}
private function getClassName(ClassMetadataInfo $metadata)
{
return ($pos = strrpos($metadata->name, '\\'))
? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name;
}
private function getNamespace(ClassMetadataInfo $metadata)
{
return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
}
private function generateEntityDocBlock(ClassMetadataInfo $metadata)
{
$lines = array();
$lines[] = '/**';
$lines[] = ' * ' . $this->getClassName($metadata);
if ($this->generateAnnotations) {
$lines[] = ' *';
$methods = array(
'generateTableAnnotation',
'generateInheritanceAnnotation',
'generateDiscriminatorColumnAnnotation',
'generateDiscriminatorMapAnnotation'
);
foreach ($methods as $method) {
if ($code = $this->$method($metadata)) {
$lines[] = ' * ' . $code;
}
}
if ($metadata->isMappedSuperclass) {
$lines[] = ' * @' . $this->annotationsPrefix . 'MappedSuperClass';
} else {
$lines[] = ' * @' . $this->annotationsPrefix . 'Entity';
}
if ($metadata->customRepositoryClassName) {
$lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")';
}
if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
$lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks';
}
}
$lines[] = ' */';
return implode("\n", $lines);
}
private function generateTableAnnotation($metadata)
{
$table = array();
if (isset($metadata->table['schema'])) {
$table[] = 'schema="' . $metadata->table['schema'] . '"';
}
if (isset($metadata->table['name'])) {
$table[] = 'name="' . $metadata->table['name'] . '"';
}
if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
$constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
$table[] = 'uniqueConstraints={' . $constraints . '}';
}
if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
$constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']);
$table[] = 'indexes={' . $constraints . '}';
}
return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
}
private function generateTableConstraints($constraintName, $constraints)
{
$annotations = array();
foreach ($constraints as $name => $constraint) {
$columns = array();
foreach ($constraint['columns'] as $column) {
$columns[] = '"' . $column . '"';
}
$annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
}
return implode(', ', $annotations);
}
private function generateInheritanceAnnotation($metadata)
{
if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")';
}
}
private function generateDiscriminatorColumnAnnotation($metadata)
{
if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
$discrColumn = $metadata->discriminatorValue;
$columnDefinition = 'name="' . $discrColumn['name']
. '", type="' . $discrColumn['type']
. '", length=' . $discrColumn['length'];
return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
}
}
private function generateDiscriminatorMapAnnotation($metadata)
{
if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
$inheritanceClassMap = array();
foreach ($metadata->discriminatorMap as $type => $class) {
$inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
}
return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
}
}
private function generateEntityStubMethods(ClassMetadataInfo $metadata)
{
$methods = array();
foreach ($metadata->fieldMappings as $fieldMapping) {
if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) {
if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) {
$methods[] = $code;
}
}
if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) {
$methods[] = $code;
}
}
foreach ($metadata->associationMappings as $associationMapping) {
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
$nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null;
if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
$methods[] = $code;
}
if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
$methods[] = $code;
}
} else if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
if ($code = $this->generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
$methods[] = $code;
}
if ($code = $this->generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
$methods[] = $code;
}
if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) {
$methods[] = $code;
}
}
}
return implode("\n\n", $methods);
}
private function isAssociationIsNullable($associationMapping)
{
if (isset($associationMapping['id']) && $associationMapping['id']) {
return false;
}
if (isset($associationMapping['joinColumns'])) {
$joinColumns = $associationMapping['joinColumns'];
} else {
//@todo thereis no way to retreive targetEntity metadata
$joinColumns = array();
}
foreach ($joinColumns as $joinColumn) {
if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
return false;
}
}
return true;
}
private function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
{
if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
$methods = array();
foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
foreach ($callbacks as $callback) {
if ($code = $this->generateLifecycleCallbackMethod($name, $callback, $metadata)) {
$methods[] = $code;
}
}
}
return implode("\n\n", $methods);
}
return "";
}
private function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
{
$lines = array();
foreach ($metadata->associationMappings as $associationMapping) {
if ($this->hasProperty($associationMapping['fieldName'], $metadata)) {
continue;
}
$lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
$lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName']
. ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
}
return implode("\n", $lines);
}
private function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
{
$lines = array();
foreach ($metadata->fieldMappings as $fieldMapping) {
if ($this->hasProperty($fieldMapping['fieldName'], $metadata) ||
$metadata->isInheritedField($fieldMapping['fieldName'])) {
continue;
}
$lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
$lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName']
. (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n";
}
return implode("\n", $lines);
}
private function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
{
$methodName = $type . Inflector::classify($fieldName);
if (in_array($type, array("add", "remove")) && substr($methodName, -1) == "s") {
$methodName = substr($methodName, 0, -1);
}
if ($this->hasMethod($methodName, $metadata)) {
return;
}
$this->staticReflection[$metadata->name]['methods'][] = $methodName;
$var = sprintf('%sMethodTemplate', $type);
$template = self::$$var;
$types = Type::getTypesMap();
$variableType = $typeHint ? $this->getType($typeHint) . ' ' : null;
$methodTypeHint = $typeHint && ! isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null;
$replacements = array(
'<description>' => ucfirst($type) . ' ' . $fieldName,
'<methodTypeHint>' => $methodTypeHint,
'<variableType>' => $variableType,
'<variableName>' => Inflector::camelize($fieldName),
'<methodName>' => $methodName,
'<fieldName>' => $fieldName,
'<variableDefault>' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '',
'<entity>' => $this->getClassName($metadata)
);
$method = str_replace(
array_keys($replacements),
array_values($replacements),
$template
);
return $this->prefixCodeWithSpaces($method);
}
private function generateLifecycleCallbackMethod($name, $methodName, $metadata)
{
if ($this->hasMethod($methodName, $metadata)) {
return;
}
$this->staticReflection[$metadata->name]['methods'][] = $methodName;
$replacements = array(
'<name>' => $this->annotationsPrefix . ucfirst($name),
'<methodName>' => $methodName,
);
$method = str_replace(
array_keys($replacements),
array_values($replacements),
self::$lifecycleCallbackMethodTemplate
);
return $this->prefixCodeWithSpaces($method);
}
private function generateJoinColumnAnnotation(array $joinColumn)
{
$joinColumnAnnot = array();
if (isset($joinColumn['name'])) {
$joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
}
if (isset($joinColumn['referencedColumnName'])) {
$joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
}
if (isset($joinColumn['unique']) && $joinColumn['unique']) {
$joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
}
if (isset($joinColumn['nullable'])) {
$joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
}
if (isset($joinColumn['onDelete'])) {
$joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
}
if (isset($joinColumn['columnDefinition'])) {
$joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
}
return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
}
private function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
{
$lines = array();
$lines[] = $this->spaces . '/**';
if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
$lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection';
} else {
$lines[] = $this->spaces . ' * @var ' . $associationMapping['targetEntity'];
}
if ($this->generateAnnotations) {
$lines[] = $this->spaces . ' *';
if (isset($associationMapping['id']) && $associationMapping['id']) {
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
}
}
$type = null;
switch ($associationMapping['type']) {
case ClassMetadataInfo::ONE_TO_ONE:
$type = 'OneToOne';
break;
case ClassMetadataInfo::MANY_TO_ONE:
$type = 'ManyToOne';
break;
case ClassMetadataInfo::ONE_TO_MANY:
$type = 'OneToMany';
break;
case ClassMetadataInfo::MANY_TO_MANY:
$type = 'ManyToMany';
break;
}
$typeOptions = array();
if (isset($associationMapping['targetEntity'])) {
$typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
}
if (isset($associationMapping['inversedBy'])) {
$typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
}
if (isset($associationMapping['mappedBy'])) {
$typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
}
if ($associationMapping['cascade']) {
$cascades = array();
if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
if (count($cascades) === 5) {
$cascades = array('"all"');
}
$typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
}
if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
$typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
}
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({';
$joinColumnsLines = array();
foreach ($associationMapping['joinColumns'] as $joinColumn) {
if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) {
$joinColumnsLines[] = $this->spaces . ' * ' . $joinColumnAnnot;
}
}
$lines[] = implode(",\n", $joinColumnsLines);
$lines[] = $this->spaces . ' * })';
}
if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
$joinTable = array();
$joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
if (isset($associationMapping['joinTable']['schema'])) {
$joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
}
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
$lines[] = $this->spaces . ' * joinColumns={';
$joinColumnsLines = array();
foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) {
$joinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn);
}
$lines[] = implode(",". PHP_EOL, $joinColumnsLines);
$lines[] = $this->spaces . ' * },';
$lines[] = $this->spaces . ' * inverseJoinColumns={';
$inverseJoinColumnsLines = array();
foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
$inverseJoinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn);
}
$lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines);
$lines[] = $this->spaces . ' * }';
$lines[] = $this->spaces . ' * )';
}
if (isset($associationMapping['orderBy'])) {
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({';
foreach ($associationMapping['orderBy'] as $name => $direction) {
$lines[] = $this->spaces . ' * "' . $name . '"="' . $direction . '",';
}
$lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
$lines[] = $this->spaces . ' * })';
}
}
$lines[] = $this->spaces . ' */';
return implode("\n", $lines);
}
private function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
{
$lines = array();
$lines[] = $this->spaces . '/**';
$lines[] = $this->spaces . ' * @var ' . $this->getType($fieldMapping['type']);
if ($this->generateAnnotations) {
$lines[] = $this->spaces . ' *';
$column = array();
if (isset($fieldMapping['columnName'])) {
$column[] = 'name="' . $fieldMapping['columnName'] . '"';
}
if (isset($fieldMapping['type'])) {
$column[] = 'type="' . $fieldMapping['type'] . '"';
}
if (isset($fieldMapping['length'])) {
$column[] = 'length=' . $fieldMapping['length'];
}
if (isset($fieldMapping['precision'])) {
$column[] = 'precision=' . $fieldMapping['precision'];
}
if (isset($fieldMapping['scale'])) {
$column[] = 'scale=' . $fieldMapping['scale'];
}
if (isset($fieldMapping['nullable'])) {
$column[] = 'nullable=' . var_export($fieldMapping['nullable'], true);
}
if (isset($fieldMapping['columnDefinition'])) {
$column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
}
if (isset($fieldMapping['unique'])) {
$column[] = 'unique=' . var_export($fieldMapping['unique'], true);
}
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
if (isset($fieldMapping['id']) && $fieldMapping['id']) {
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
$lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
}
if ($metadata->sequenceGeneratorDefinition) {
$sequenceGenerator = array();
if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
$sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
}
if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
$sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
}
if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
$sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
}
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
}
}
if (isset($fieldMapping['version']) && $fieldMapping['version']) {
$lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version';
}
}
$lines[] = $this->spaces . ' */';
return implode("\n", $lines);
}
private function prefixCodeWithSpaces($code, $num = 1)
{
$lines = explode("\n", $code);
foreach ($lines as $key => $value) {
$lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
}
return implode("\n", $lines);
}
private function getInheritanceTypeString($type)
{
switch ($type) {
case ClassMetadataInfo::INHERITANCE_TYPE_NONE:
return 'NONE';
case ClassMetadataInfo::INHERITANCE_TYPE_JOINED:
return 'JOINED';
case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE:
return 'SINGLE_TABLE';
case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS:
return 'PER_CLASS';
default:
throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type);
}
}
private function getChangeTrackingPolicyString($policy)
{
switch ($policy) {
case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT:
return 'DEFERRED_IMPLICIT';
case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT:
return 'DEFERRED_EXPLICIT';
case ClassMetadataInfo::CHANGETRACKING_NOTIFY:
return 'NOTIFY';
default:
throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy);
}
}
private function getIdGeneratorTypeString($type)
{
switch ($type) {
case ClassMetadataInfo::GENERATOR_TYPE_AUTO:
return 'AUTO';
case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE:
return 'SEQUENCE';
case ClassMetadataInfo::GENERATOR_TYPE_TABLE:
return 'TABLE';
case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY:
return 'IDENTITY';
case ClassMetadataInfo::GENERATOR_TYPE_NONE:
return 'NONE';
default:
throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type);
}
}
}