3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
20 namespace Doctrine\ORM\Tools;
22 use Doctrine\ORM\Mapping\ClassMetadataInfo,
23 Doctrine\Common\Util\Inflector,
24 Doctrine\DBAL\Types\Type;
27 * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances
30 * $classes = $em->getClassMetadataFactory()->getAllMetadata();
32 * $generator = new \Doctrine\ORM\Tools\EntityGenerator();
33 * $generator->setGenerateAnnotations(true);
34 * $generator->setGenerateStubMethods(true);
35 * $generator->setRegenerateEntityIfExists(false);
36 * $generator->setUpdateEntityIfExists(true);
37 * $generator->generate($classes, '/path/to/generate/entities');
40 * @link www.doctrine-project.org
42 * @author Benjamin Eberlei <kontakt@beberlei.de>
43 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
44 * @author Jonathan Wage <jonwage@gmail.com>
45 * @author Roman Borschel <roman@code-factory.org>
50 * Specifies class fields should be protected
52 const FIELD_VISIBLE_PROTECTED = 'protected';
55 * Specifies class fields should be private
57 const FIELD_VISIBLE_PRIVATE = 'private';
62 private $backupExisting = true;
65 * The extension to use for written php files
69 private $extension = '.php';
72 * Whether or not the current ClassMetadataInfo instance is new or old
76 private $isNew = true;
81 private $staticReflection = array();
84 * Number of spaces to use for indention in generated code
86 private $numSpaces = 4;
89 * The actual spaces to use for indention
93 private $spaces = ' ';
96 * The class all generated entities should extend
100 private $classToExtend;
103 * Whether or not to generation annotations
107 private $generateAnnotations = false;
112 private $annotationsPrefix = '';
115 * Whether or not to generated sub methods
119 private $generateEntityStubMethods = false;
122 * Whether or not to update the entity class if it exists already
126 private $updateEntityIfExists = false;
129 * Whether or not to re-generate entity class if it exists already
133 private $regenerateEntityIfExists = false;
138 private $fieldVisibility = 'private';
141 * Hash-map for handle types
145 private $typeAlias = array(
146 Type::DATETIMETZ => '\DateTime',
147 Type::DATETIME => '\DateTime',
148 Type::DATE => '\DateTime',
149 Type::TIME => '\DateTime',
150 Type::OBJECT => '\stdClass',
151 Type::BIGINT => 'integer',
152 Type::SMALLINT => 'integer',
153 Type::TEXT => 'string',
154 Type::BLOB => 'string',
155 Type::DECIMAL => 'float',
156 Type::JSON_ARRAY => 'array',
157 Type::SIMPLE_ARRAY => 'array',
163 private static $classTemplate =
168 use Doctrine\ORM\Mapping as ORM;
180 private static $getMethodTemplate =
184 * @return <variableType>
186 public function <methodName>()
188 <spaces>return $this-><fieldName>;
194 private static $setMethodTemplate =
198 * @param <variableType>$<variableName>
201 public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
203 <spaces>$this-><fieldName> = $<variableName>;
205 <spaces>return $this;
211 private static $addMethodTemplate =
215 * @param <variableType>$<variableName>
218 public function <methodName>(<methodTypeHint>$<variableName>)
220 <spaces>$this-><fieldName>[] = $<variableName>;
222 <spaces>return $this;
228 private static $removeMethodTemplate =
232 * @param <variableType>$<variableName>
234 public function <methodName>(<methodTypeHint>$<variableName>)
236 <spaces>$this-><fieldName>->removeElement($<variableName>);
242 private static $lifecycleCallbackMethodTemplate =
246 public function <methodName>()
248 <spaces>// Add your code here
254 private static $constructorMethodTemplate =
258 public function __construct()
260 <spaces><collections>
264 public function __construct()
266 if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
267 $this->annotationsPrefix = 'ORM\\';
272 * Generate and write entity classes for the given array of ClassMetadataInfo instances
274 * @param array $metadatas
275 * @param string $outputDirectory
278 public function generate(array $metadatas, $outputDirectory)
280 foreach ($metadatas as $metadata) {
281 $this->writeEntityClass($metadata, $outputDirectory);
286 * Generated and write entity class to disk for the given ClassMetadataInfo instance
288 * @param ClassMetadataInfo $metadata
289 * @param string $outputDirectory
292 public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
294 $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension;
295 $dir = dirname($path);
297 if ( ! is_dir($dir)) {
298 mkdir($dir, 0777, true);
301 $this->isNew = !file_exists($path) || (file_exists($path) && $this->regenerateEntityIfExists);
303 if ( ! $this->isNew) {
304 $this->parseTokensInEntityFile(file_get_contents($path));
306 $this->staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array());
309 if ($this->backupExisting && file_exists($path)) {
310 $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
311 if (!copy($path, $backupPath)) {
312 throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
316 // If entity doesn't exist or we're re-generating the entities entirely
318 file_put_contents($path, $this->generateEntityClass($metadata));
319 // If entity exists and we're allowed to update the entity class
320 } else if ( ! $this->isNew && $this->updateEntityIfExists) {
321 file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
326 * Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
328 * @param ClassMetadataInfo $metadata
329 * @return string $code
331 public function generateEntityClass(ClassMetadataInfo $metadata)
333 $placeHolders = array(
335 '<entityAnnotation>',
340 $replacements = array(
341 $this->generateEntityNamespace($metadata),
342 $this->generateEntityDocBlock($metadata),
343 $this->generateEntityClassName($metadata),
344 $this->generateEntityBody($metadata)
347 $code = str_replace($placeHolders, $replacements, self::$classTemplate);
348 return str_replace('<spaces>', $this->spaces, $code);
352 * Generate the updated code for the given ClassMetadataInfo and entity at path
354 * @param ClassMetadataInfo $metadata
355 * @param string $path
356 * @return string $code;
358 public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
360 $currentCode = file_get_contents($path);
362 $body = $this->generateEntityBody($metadata);
363 $body = str_replace('<spaces>', $this->spaces, $body);
364 $last = strrpos($currentCode, '}');
366 return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}";
370 * Set the number of spaces the exported class should have
372 * @param integer $numSpaces
375 public function setNumSpaces($numSpaces)
377 $this->spaces = str_repeat(' ', $numSpaces);
378 $this->numSpaces = $numSpaces;
382 * Set the extension to use when writing php files to disk
384 * @param string $extension
387 public function setExtension($extension)
389 $this->extension = $extension;
393 * Set the name of the class the generated classes should extend from
397 public function setClassToExtend($classToExtend)
399 $this->classToExtend = $classToExtend;
403 * Set whether or not to generate annotations for the entity
408 public function setGenerateAnnotations($bool)
410 $this->generateAnnotations = $bool;
414 * Set the class fields visibility for the entity (can either be private or protected)
419 public function setFieldVisibility($visibility)
421 if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) {
422 throw new \InvalidArgumentException('Invalid provided visibilty (only private and protected are allowed): ' . $visibility);
425 $this->fieldVisibility = $visibility;
429 * Set an annotation prefix.
431 * @param string $prefix
433 public function setAnnotationPrefix($prefix)
435 $this->annotationsPrefix = $prefix;
439 * Set whether or not to try and update the entity if it already exists
444 public function setUpdateEntityIfExists($bool)
446 $this->updateEntityIfExists = $bool;
450 * Set whether or not to regenerate the entity if it exists
455 public function setRegenerateEntityIfExists($bool)
457 $this->regenerateEntityIfExists = $bool;
461 * Set whether or not to generate stub methods for the entity
466 public function setGenerateStubMethods($bool)
468 $this->generateEntityStubMethods = $bool;
472 * Should an existing entity be backed up if it already exists?
474 public function setBackupExisting($bool)
476 $this->backupExisting = $bool;
480 * @param string $type
483 private function getType($type)
485 if (isset($this->typeAlias[$type])) {
486 return $this->typeAlias[$type];
492 private function generateEntityNamespace(ClassMetadataInfo $metadata)
494 if ($this->hasNamespace($metadata)) {
495 return 'namespace ' . $this->getNamespace($metadata) .';';
499 private function generateEntityClassName(ClassMetadataInfo $metadata)
501 return 'class ' . $this->getClassName($metadata) .
502 ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null);
505 private function generateEntityBody(ClassMetadataInfo $metadata)
507 $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata);
508 $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata);
509 $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null;
510 $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata);
514 if ($fieldMappingProperties) {
515 $code[] = $fieldMappingProperties;
518 if ($associationMappingProperties) {
519 $code[] = $associationMappingProperties;
522 $code[] = $this->generateEntityConstructor($metadata);
525 $code[] = $stubMethods;
528 if ($lifecycleCallbackMethods) {
529 $code[] = $lifecycleCallbackMethods;
532 return implode("\n", $code);
535 private function generateEntityConstructor(ClassMetadataInfo $metadata)
537 if ($this->hasMethod('__construct', $metadata)) {
541 $collections = array();
543 foreach ($metadata->associationMappings as $mapping) {
544 if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
545 $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
550 return $this->prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->spaces, $collections), self::$constructorMethodTemplate));
557 * @todo this won't work if there is a namespace in brackets and a class outside of it.
560 private function parseTokensInEntityFile($src)
562 $tokens = token_get_all($src);
563 $lastSeenNamespace = "";
564 $lastSeenClass = false;
566 $inNamespace = false;
568 for ($i = 0; $i < count($tokens); $i++) {
569 $token = $tokens[$i];
570 if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
575 if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
576 $lastSeenNamespace .= $token[1];
577 } else if (is_string($token) && in_array($token, array(';', '{'))) {
578 $inNamespace = false;
584 $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
585 $this->staticReflection[$lastSeenClass]['properties'] = array();
586 $this->staticReflection[$lastSeenClass]['methods'] = array();
589 if ($token[0] == T_NAMESPACE) {
590 $lastSeenNamespace = "";
592 } else if ($token[0] == T_CLASS) {
594 } else if ($token[0] == T_FUNCTION) {
595 if ($tokens[$i+2][0] == T_STRING) {
596 $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
597 } else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
598 $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
600 } else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
601 $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
606 private function hasProperty($property, ClassMetadataInfo $metadata)
608 if ($this->extendsClass()) {
609 // don't generate property if its already on the base class.
610 $reflClass = new \ReflectionClass($this->getClassToExtend());
611 if ($reflClass->hasProperty($property)) {
617 isset($this->staticReflection[$metadata->name]) &&
618 in_array($property, $this->staticReflection[$metadata->name]['properties'])
622 private function hasMethod($method, ClassMetadataInfo $metadata)
624 if ($this->extendsClass()) {
625 // don't generate method if its already on the base class.
626 $reflClass = new \ReflectionClass($this->getClassToExtend());
627 if ($reflClass->hasMethod($method)) {
633 isset($this->staticReflection[$metadata->name]) &&
634 in_array($method, $this->staticReflection[$metadata->name]['methods'])
638 private function hasNamespace(ClassMetadataInfo $metadata)
640 return strpos($metadata->name, '\\') ? true : false;
643 private function extendsClass()
645 return $this->classToExtend ? true : false;
648 private function getClassToExtend()
650 return $this->classToExtend;
653 private function getClassToExtendName()
655 $refl = new \ReflectionClass($this->getClassToExtend());
657 return '\\' . $refl->getName();
660 private function getClassName(ClassMetadataInfo $metadata)
662 return ($pos = strrpos($metadata->name, '\\'))
663 ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name;
666 private function getNamespace(ClassMetadataInfo $metadata)
668 return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
671 private function generateEntityDocBlock(ClassMetadataInfo $metadata)
675 $lines[] = ' * '.$metadata->name;
677 if ($this->generateAnnotations) {
681 'generateTableAnnotation',
682 'generateInheritanceAnnotation',
683 'generateDiscriminatorColumnAnnotation',
684 'generateDiscriminatorMapAnnotation'
687 foreach ($methods as $method) {
688 if ($code = $this->$method($metadata)) {
689 $lines[] = ' * ' . $code;
693 if ($metadata->isMappedSuperclass) {
694 $lines[] = ' * @' . $this->annotationsPrefix . 'MappedSuperClass';
696 $lines[] = ' * @' . $this->annotationsPrefix . 'Entity';
699 if ($metadata->customRepositoryClassName) {
700 $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")';
703 if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
704 $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks';
710 return implode("\n", $lines);
713 private function generateTableAnnotation($metadata)
717 if (isset($metadata->table['schema'])) {
718 $table[] = 'schema="' . $metadata->table['schema'] . '"';
721 if (isset($metadata->table['name'])) {
722 $table[] = 'name="' . $metadata->table['name'] . '"';
725 if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
726 $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
727 $table[] = 'uniqueConstraints={' . $constraints . '}';
730 if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
731 $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']);
732 $table[] = 'indexes={' . $constraints . '}';
735 return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
738 private function generateTableConstraints($constraintName, $constraints)
740 $annotations = array();
741 foreach ($constraints as $name => $constraint) {
743 foreach ($constraint['columns'] as $column) {
744 $columns[] = '"' . $column . '"';
746 $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
748 return implode(', ', $annotations);
751 private function generateInheritanceAnnotation($metadata)
753 if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
754 return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")';
758 private function generateDiscriminatorColumnAnnotation($metadata)
760 if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
761 $discrColumn = $metadata->discriminatorValue;
762 $columnDefinition = 'name="' . $discrColumn['name']
763 . '", type="' . $discrColumn['type']
764 . '", length=' . $discrColumn['length'];
766 return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
770 private function generateDiscriminatorMapAnnotation($metadata)
772 if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
773 $inheritanceClassMap = array();
775 foreach ($metadata->discriminatorMap as $type => $class) {
776 $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
779 return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
783 private function generateEntityStubMethods(ClassMetadataInfo $metadata)
787 foreach ($metadata->fieldMappings as $fieldMapping) {
788 if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) {
789 if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) {
794 if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) {
799 foreach ($metadata->associationMappings as $associationMapping) {
800 if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
801 $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null;
802 if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
805 if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
808 } else if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
809 if ($code = $this->generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
812 if ($code = $this->generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
815 if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) {
821 return implode("\n\n", $methods);
824 private function isAssociationIsNullable($associationMapping)
826 if (isset($associationMapping['id']) && $associationMapping['id']) {
829 if (isset($associationMapping['joinColumns'])) {
830 $joinColumns = $associationMapping['joinColumns'];
832 //@todo thereis no way to retreive targetEntity metadata
833 $joinColumns = array();
835 foreach ($joinColumns as $joinColumn) {
836 if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
843 private function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
845 if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
848 foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
849 foreach ($callbacks as $callback) {
850 if ($code = $this->generateLifecycleCallbackMethod($name, $callback, $metadata)) {
856 return implode("\n\n", $methods);
862 private function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
866 foreach ($metadata->associationMappings as $associationMapping) {
867 if ($this->hasProperty($associationMapping['fieldName'], $metadata)) {
871 $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
872 $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName']
873 . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
876 return implode("\n", $lines);
879 private function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
883 foreach ($metadata->fieldMappings as $fieldMapping) {
884 if ($this->hasProperty($fieldMapping['fieldName'], $metadata) ||
885 $metadata->isInheritedField($fieldMapping['fieldName'])) {
889 $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
890 $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName']
891 . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n";
894 return implode("\n", $lines);
897 private function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
899 $methodName = $type . Inflector::classify($fieldName);
900 if (in_array($type, array("add", "remove")) && substr($methodName, -1) == "s") {
901 $methodName = substr($methodName, 0, -1);
904 if ($this->hasMethod($methodName, $metadata)) {
907 $this->staticReflection[$metadata->name]['methods'][] = $methodName;
909 $var = sprintf('%sMethodTemplate', $type);
910 $template = self::$$var;
912 $types = Type::getTypesMap();
913 $variableType = $typeHint ? $this->getType($typeHint) . ' ' : null;
914 $methodTypeHint = $typeHint && ! isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null;
916 $replacements = array(
917 '<description>' => ucfirst($type) . ' ' . $fieldName,
918 '<methodTypeHint>' => $methodTypeHint,
919 '<variableType>' => $variableType,
920 '<variableName>' => Inflector::camelize($fieldName),
921 '<methodName>' => $methodName,
922 '<fieldName>' => $fieldName,
923 '<variableDefault>' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '',
924 '<entity>' => $this->getClassName($metadata)
927 $method = str_replace(
928 array_keys($replacements),
929 array_values($replacements),
933 return $this->prefixCodeWithSpaces($method);
936 private function generateLifecycleCallbackMethod($name, $methodName, $metadata)
938 if ($this->hasMethod($methodName, $metadata)) {
941 $this->staticReflection[$metadata->name]['methods'][] = $methodName;
943 $replacements = array(
944 '<name>' => $this->annotationsPrefix . ucfirst($name),
945 '<methodName>' => $methodName,
948 $method = str_replace(
949 array_keys($replacements),
950 array_values($replacements),
951 self::$lifecycleCallbackMethodTemplate
954 return $this->prefixCodeWithSpaces($method);
957 private function generateJoinColumnAnnotation(array $joinColumn)
959 $joinColumnAnnot = array();
961 if (isset($joinColumn['name'])) {
962 $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
965 if (isset($joinColumn['referencedColumnName'])) {
966 $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
969 if (isset($joinColumn['unique']) && $joinColumn['unique']) {
970 $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
973 if (isset($joinColumn['nullable'])) {
974 $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
977 if (isset($joinColumn['onDelete'])) {
978 $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
981 if (isset($joinColumn['columnDefinition'])) {
982 $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
985 return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
988 private function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
991 $lines[] = $this->spaces . '/**';
993 if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
994 $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection';
996 $lines[] = $this->spaces . ' * @var ' . $associationMapping['targetEntity'];
999 if ($this->generateAnnotations) {
1000 $lines[] = $this->spaces . ' *';
1002 if (isset($associationMapping['id']) && $associationMapping['id']) {
1003 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1005 if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
1006 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1011 switch ($associationMapping['type']) {
1012 case ClassMetadataInfo::ONE_TO_ONE:
1015 case ClassMetadataInfo::MANY_TO_ONE:
1016 $type = 'ManyToOne';
1018 case ClassMetadataInfo::ONE_TO_MANY:
1019 $type = 'OneToMany';
1021 case ClassMetadataInfo::MANY_TO_MANY:
1022 $type = 'ManyToMany';
1025 $typeOptions = array();
1027 if (isset($associationMapping['targetEntity'])) {
1028 $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
1031 if (isset($associationMapping['inversedBy'])) {
1032 $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
1035 if (isset($associationMapping['mappedBy'])) {
1036 $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
1039 if ($associationMapping['cascade']) {
1040 $cascades = array();
1042 if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
1043 if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
1044 if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
1045 if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
1046 if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
1048 $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
1051 if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
1052 $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
1055 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
1057 if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
1058 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({';
1060 $joinColumnsLines = array();
1062 foreach ($associationMapping['joinColumns'] as $joinColumn) {
1063 if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) {
1064 $joinColumnsLines[] = $this->spaces . ' * ' . $joinColumnAnnot;
1068 $lines[] = implode(",\n", $joinColumnsLines);
1069 $lines[] = $this->spaces . ' * })';
1072 if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
1073 $joinTable = array();
1074 $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
1076 if (isset($associationMapping['joinTable']['schema'])) {
1077 $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
1080 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
1081 $lines[] = $this->spaces . ' * joinColumns={';
1083 foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) {
1084 $lines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn);
1087 $lines[] = $this->spaces . ' * },';
1088 $lines[] = $this->spaces . ' * inverseJoinColumns={';
1090 foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
1091 $lines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn);
1094 $lines[] = $this->spaces . ' * }';
1095 $lines[] = $this->spaces . ' * )';
1098 if (isset($associationMapping['orderBy'])) {
1099 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({';
1101 foreach ($associationMapping['orderBy'] as $name => $direction) {
1102 $lines[] = $this->spaces . ' * "' . $name . '"="' . $direction . '",';
1105 $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
1106 $lines[] = $this->spaces . ' * })';
1110 $lines[] = $this->spaces . ' */';
1112 return implode("\n", $lines);
1115 private function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
1118 $lines[] = $this->spaces . '/**';
1119 $lines[] = $this->spaces . ' * @var ' . $this->getType($fieldMapping['type']) . ' $' . $fieldMapping['fieldName'];
1121 if ($this->generateAnnotations) {
1122 $lines[] = $this->spaces . ' *';
1125 if (isset($fieldMapping['columnName'])) {
1126 $column[] = 'name="' . $fieldMapping['columnName'] . '"';
1129 if (isset($fieldMapping['type'])) {
1130 $column[] = 'type="' . $fieldMapping['type'] . '"';
1133 if (isset($fieldMapping['length'])) {
1134 $column[] = 'length=' . $fieldMapping['length'];
1137 if (isset($fieldMapping['precision'])) {
1138 $column[] = 'precision=' . $fieldMapping['precision'];
1141 if (isset($fieldMapping['scale'])) {
1142 $column[] = 'scale=' . $fieldMapping['scale'];
1145 if (isset($fieldMapping['nullable'])) {
1146 $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true);
1149 if (isset($fieldMapping['columnDefinition'])) {
1150 $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
1153 if (isset($fieldMapping['unique'])) {
1154 $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
1157 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
1159 if (isset($fieldMapping['id']) && $fieldMapping['id']) {
1160 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
1162 if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
1163 $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1166 if ($metadata->sequenceGeneratorDefinition) {
1167 $sequenceGenerator = array();
1169 if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
1170 $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
1173 if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
1174 $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
1177 if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
1178 $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
1181 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
1185 if (isset($fieldMapping['version']) && $fieldMapping['version']) {
1186 $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version';
1190 $lines[] = $this->spaces . ' */';
1192 return implode("\n", $lines);
1195 private function prefixCodeWithSpaces($code, $num = 1)
1197 $lines = explode("\n", $code);
1199 foreach ($lines as $key => $value) {
1200 $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
1203 return implode("\n", $lines);
1206 private function getInheritanceTypeString($type)
1209 case ClassMetadataInfo::INHERITANCE_TYPE_NONE:
1212 case ClassMetadataInfo::INHERITANCE_TYPE_JOINED:
1215 case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE:
1216 return 'SINGLE_TABLE';
1218 case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS:
1222 throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type);
1226 private function getChangeTrackingPolicyString($policy)
1229 case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT:
1230 return 'DEFERRED_IMPLICIT';
1232 case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT:
1233 return 'DEFERRED_EXPLICIT';
1235 case ClassMetadataInfo::CHANGETRACKING_NOTIFY:
1239 throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy);
1243 private function getIdGeneratorTypeString($type)
1246 case ClassMetadataInfo::GENERATOR_TYPE_AUTO:
1249 case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE:
1252 case ClassMetadataInfo::GENERATOR_TYPE_TABLE:
1255 case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY:
1258 case ClassMetadataInfo::GENERATOR_TYPE_NONE:
1262 throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type);