source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/ORM/Tools/EntityGenerator.php @ 345

Last change on this file since 345 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 39.4 KB
Line 
1<?php
2/*
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.
14 *
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the LGPL. For more information, see
17 * <http://www.doctrine-project.org>.
18 */
19
20namespace Doctrine\ORM\Tools;
21
22use Doctrine\ORM\Mapping\ClassMetadataInfo,
23    Doctrine\Common\Util\Inflector,
24    Doctrine\DBAL\Types\Type;
25
26/**
27 * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances
28 *
29 *     [php]
30 *     $classes = $em->getClassMetadataFactory()->getAllMetadata();
31 *
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');
38 *
39 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
40 * @link    www.doctrine-project.org
41 * @since   2.0
42 * @version $Revision$
43 * @author  Benjamin Eberlei <kontakt@beberlei.de>
44 * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
45 * @author  Jonathan Wage <jonwage@gmail.com>
46 * @author  Roman Borschel <roman@code-factory.org>
47 */
48class EntityGenerator
49{
50    /**
51     * @var bool
52     */
53    private $_backupExisting = true;
54
55    /** The extension to use for written php files */
56    private $_extension = '.php';
57
58    /** Whether or not the current ClassMetadataInfo instance is new or old */
59    private $_isNew = true;
60
61    private $_staticReflection = array();
62
63    /** Number of spaces to use for indention in generated code */
64    private $_numSpaces = 4;
65
66    /** The actual spaces to use for indention */
67    private $_spaces = '    ';
68
69    /** The class all generated entities should extend */
70    private $_classToExtend;
71
72    /** Whether or not to generation annotations */
73    private $_generateAnnotations = false;
74
75    /**
76     * @var string
77     */
78    private $_annotationsPrefix = '';
79
80    /** Whether or not to generated sub methods */
81    private $_generateEntityStubMethods = false;
82
83    /** Whether or not to update the entity class if it exists already */
84    private $_updateEntityIfExists = false;
85
86    /** Whether or not to re-generate entity class if it exists already */
87    private $_regenerateEntityIfExists = false;
88
89    private static $_classTemplate =
90'<?php
91
92<namespace>
93
94use Doctrine\ORM\Mapping as ORM;
95
96<entityAnnotation>
97<entityClassName>
98{
99<entityBody>
100}';
101
102    private static $_getMethodTemplate =
103'/**
104 * <description>
105 *
106 * @return <variableType>
107 */
108public function <methodName>()
109{
110<spaces>return $this-><fieldName>;
111}';
112
113    private static $_setMethodTemplate =
114'/**
115 * <description>
116 *
117 * @param <variableType>$<variableName>
118 * @return <entity>
119 */
120public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
121{
122<spaces>$this-><fieldName> = $<variableName>;
123<spaces>return $this;
124}';
125
126    private static $_addMethodTemplate =
127'/**
128 * <description>
129 *
130 * @param <variableType>$<variableName>
131 * @return <entity>
132 */
133public function <methodName>(<methodTypeHint>$<variableName>)
134{
135<spaces>$this-><fieldName>[] = $<variableName>;
136<spaces>return $this;
137}';
138
139    /**
140     * @var string
141     */
142    private static $_removeMethodTemplate =
143'/**
144 * <description>
145 *
146 * @param <variableType>$<variableName>
147 */
148public function <methodName>(<methodTypeHint>$<variableName>)
149{
150<spaces>$this-><fieldName>->removeElement($<variableName>);
151}';
152
153    /**
154     * @var string
155     */
156    private static $_lifecycleCallbackMethodTemplate =
157'/**
158 * @<name>
159 */
160public function <methodName>()
161{
162<spaces>// Add your code here
163}';
164
165    private static $_constructorMethodTemplate =
166'public function __construct()
167{
168<spaces><collections>
169}
170';
171
172    public function __construct()
173    {
174        if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
175            $this->_annotationsPrefix = 'ORM\\';
176        }
177    }
178
179    /**
180     * Generate and write entity classes for the given array of ClassMetadataInfo instances
181     *
182     * @param array $metadatas
183     * @param string $outputDirectory
184     * @return void
185     */
186    public function generate(array $metadatas, $outputDirectory)
187    {
188        foreach ($metadatas as $metadata) {
189            $this->writeEntityClass($metadata, $outputDirectory);
190        }
191    }
192
193    /**
194     * Generated and write entity class to disk for the given ClassMetadataInfo instance
195     *
196     * @param ClassMetadataInfo $metadata
197     * @param string $outputDirectory
198     * @return void
199     */
200    public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
201    {
202        $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->_extension;
203        $dir = dirname($path);
204
205        if ( ! is_dir($dir)) {
206            mkdir($dir, 0777, true);
207        }
208
209        $this->_isNew = !file_exists($path) || (file_exists($path) && $this->_regenerateEntityIfExists);
210
211        if ( ! $this->_isNew) {
212            $this->_parseTokensInEntityFile(file_get_contents($path));
213        } else {
214            $this->_staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array());
215        }
216
217        if ($this->_backupExisting && file_exists($path)) {
218            $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
219            if (!copy($path, $backupPath)) {
220                throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
221            }
222        }
223
224        // If entity doesn't exist or we're re-generating the entities entirely
225        if ($this->_isNew) {
226            file_put_contents($path, $this->generateEntityClass($metadata));
227        // If entity exists and we're allowed to update the entity class
228        } else if ( ! $this->_isNew && $this->_updateEntityIfExists) {
229            file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
230        }
231    }
232
233    /**
234     * Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
235     *
236     * @param ClassMetadataInfo $metadata
237     * @return string $code
238     */
239    public function generateEntityClass(ClassMetadataInfo $metadata)
240    {
241        $placeHolders = array(
242            '<namespace>',
243            '<entityAnnotation>',
244            '<entityClassName>',
245            '<entityBody>'
246        );
247
248        $replacements = array(
249            $this->_generateEntityNamespace($metadata),
250            $this->_generateEntityDocBlock($metadata),
251            $this->_generateEntityClassName($metadata),
252            $this->_generateEntityBody($metadata)
253        );
254
255        $code = str_replace($placeHolders, $replacements, self::$_classTemplate);
256        return str_replace('<spaces>', $this->_spaces, $code);
257    }
258
259    /**
260     * Generate the updated code for the given ClassMetadataInfo and entity at path
261     *
262     * @param ClassMetadataInfo $metadata
263     * @param string $path
264     * @return string $code;
265     */
266    public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
267    {
268        $currentCode = file_get_contents($path);
269
270        $body = $this->_generateEntityBody($metadata);
271        $body = str_replace('<spaces>', $this->_spaces, $body);
272        $last = strrpos($currentCode, '}');
273
274        return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}";
275    }
276
277    /**
278     * Set the number of spaces the exported class should have
279     *
280     * @param integer $numSpaces
281     * @return void
282     */
283    public function setNumSpaces($numSpaces)
284    {
285        $this->_spaces = str_repeat(' ', $numSpaces);
286        $this->_numSpaces = $numSpaces;
287    }
288
289    /**
290     * Set the extension to use when writing php files to disk
291     *
292     * @param string $extension
293     * @return void
294     */
295    public function setExtension($extension)
296    {
297        $this->_extension = $extension;
298    }
299
300    /**
301     * Set the name of the class the generated classes should extend from
302     *
303     * @return void
304     */
305    public function setClassToExtend($classToExtend)
306    {
307        $this->_classToExtend = $classToExtend;
308    }
309
310    /**
311     * Set whether or not to generate annotations for the entity
312     *
313     * @param bool $bool
314     * @return void
315     */
316    public function setGenerateAnnotations($bool)
317    {
318        $this->_generateAnnotations = $bool;
319    }
320
321    /**
322     * Set an annotation prefix.
323     *
324     * @param string $prefix
325     */
326    public function setAnnotationPrefix($prefix)
327    {
328        $this->_annotationsPrefix = $prefix;
329    }
330
331    /**
332     * Set whether or not to try and update the entity if it already exists
333     *
334     * @param bool $bool
335     * @return void
336     */
337    public function setUpdateEntityIfExists($bool)
338    {
339        $this->_updateEntityIfExists = $bool;
340    }
341
342    /**
343     * Set whether or not to regenerate the entity if it exists
344     *
345     * @param bool $bool
346     * @return void
347     */
348    public function setRegenerateEntityIfExists($bool)
349    {
350        $this->_regenerateEntityIfExists = $bool;
351    }
352
353    /**
354     * Set whether or not to generate stub methods for the entity
355     *
356     * @param bool $bool
357     * @return void
358     */
359    public function setGenerateStubMethods($bool)
360    {
361        $this->_generateEntityStubMethods = $bool;
362    }
363
364    /**
365     * Should an existing entity be backed up if it already exists?
366     */
367    public function setBackupExisting($bool)
368    {
369        $this->_backupExisting = $bool;
370    }
371
372    private function _generateEntityNamespace(ClassMetadataInfo $metadata)
373    {
374        if ($this->_hasNamespace($metadata)) {
375            return 'namespace ' . $this->_getNamespace($metadata) .';';
376        }
377    }
378
379    private function _generateEntityClassName(ClassMetadataInfo $metadata)
380    {
381        return 'class ' . $this->_getClassName($metadata) .
382            ($this->_extendsClass() ? ' extends ' . $this->_getClassToExtendName() : null);
383    }
384
385    private function _generateEntityBody(ClassMetadataInfo $metadata)
386    {
387        $fieldMappingProperties = $this->_generateEntityFieldMappingProperties($metadata);
388        $associationMappingProperties = $this->_generateEntityAssociationMappingProperties($metadata);
389        $stubMethods = $this->_generateEntityStubMethods ? $this->_generateEntityStubMethods($metadata) : null;
390        $lifecycleCallbackMethods = $this->_generateEntityLifecycleCallbackMethods($metadata);
391
392        $code = array();
393
394        if ($fieldMappingProperties) {
395            $code[] = $fieldMappingProperties;
396        }
397
398        if ($associationMappingProperties) {
399            $code[] = $associationMappingProperties;
400        }
401
402        $code[] = $this->_generateEntityConstructor($metadata);
403
404        if ($stubMethods) {
405            $code[] = $stubMethods;
406        }
407
408        if ($lifecycleCallbackMethods) {
409            $code[] = $lifecycleCallbackMethods;
410        }
411
412        return implode("\n", $code);
413    }
414
415    private function _generateEntityConstructor(ClassMetadataInfo $metadata)
416    {
417        if ($this->_hasMethod('__construct', $metadata)) {
418            return '';
419        }
420
421        $collections = array();
422
423        foreach ($metadata->associationMappings AS $mapping) {
424            if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
425                $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
426            }
427        }
428
429        if ($collections) {
430            return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->_spaces, $collections), self::$_constructorMethodTemplate));
431        }
432
433        return '';
434    }
435
436    /**
437     * @todo this won't work if there is a namespace in brackets and a class outside of it.
438     * @param string $src
439     */
440    private function _parseTokensInEntityFile($src)
441    {
442        $tokens = token_get_all($src);
443        $lastSeenNamespace = "";
444        $lastSeenClass = false;
445
446        $inNamespace = false;
447        $inClass = false;
448        for ($i = 0; $i < count($tokens); $i++) {
449            $token = $tokens[$i];
450            if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
451                continue;
452            }
453
454            if ($inNamespace) {
455                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
456                    $lastSeenNamespace .= $token[1];
457                } else if (is_string($token) && in_array($token, array(';', '{'))) {
458                    $inNamespace = false;
459                }
460            }
461
462            if ($inClass) {
463                $inClass = false;
464                $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
465                $this->_staticReflection[$lastSeenClass]['properties'] = array();
466                $this->_staticReflection[$lastSeenClass]['methods'] = array();
467            }
468
469            if ($token[0] == T_NAMESPACE) {
470                $lastSeenNamespace = "";
471                $inNamespace = true;
472            } else if ($token[0] == T_CLASS) {
473                $inClass = true;
474            } else if ($token[0] == T_FUNCTION) {
475                if ($tokens[$i+2][0] == T_STRING) {
476                    $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
477                } else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
478                    $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
479                }
480            } else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
481                $this->_staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
482            }
483        }
484    }
485
486    private function _hasProperty($property, ClassMetadataInfo $metadata)
487    {
488        if ($this->_extendsClass()) {
489            // don't generate property if its already on the base class.
490            $reflClass = new \ReflectionClass($this->_getClassToExtend());
491            if ($reflClass->hasProperty($property)) {
492                return true;
493            }
494        }
495
496        return (
497            isset($this->_staticReflection[$metadata->name]) &&
498            in_array($property, $this->_staticReflection[$metadata->name]['properties'])
499        );
500    }
501
502    private function _hasMethod($method, ClassMetadataInfo $metadata)
503    {
504        if ($this->_extendsClass()) {
505            // don't generate method if its already on the base class.
506            $reflClass = new \ReflectionClass($this->_getClassToExtend());
507            if ($reflClass->hasMethod($method)) {
508                return true;
509            }
510        }
511
512        return (
513            isset($this->_staticReflection[$metadata->name]) &&
514            in_array($method, $this->_staticReflection[$metadata->name]['methods'])
515        );
516    }
517
518    private function _hasNamespace(ClassMetadataInfo $metadata)
519    {
520        return strpos($metadata->name, '\\') ? true : false;
521    }
522
523    private function _extendsClass()
524    {
525        return $this->_classToExtend ? true : false;
526    }
527
528    private function _getClassToExtend()
529    {
530        return $this->_classToExtend;
531    }
532
533    private function _getClassToExtendName()
534    {
535        $refl = new \ReflectionClass($this->_getClassToExtend());
536
537        return '\\' . $refl->getName();
538    }
539
540    private function _getClassName(ClassMetadataInfo $metadata)
541    {
542        return ($pos = strrpos($metadata->name, '\\'))
543            ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name;
544    }
545
546    private function _getNamespace(ClassMetadataInfo $metadata)
547    {
548        return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
549    }
550
551    private function _generateEntityDocBlock(ClassMetadataInfo $metadata)
552    {
553        $lines = array();
554        $lines[] = '/**';
555        $lines[] = ' * '.$metadata->name;
556
557        if ($this->_generateAnnotations) {
558            $lines[] = ' *';
559
560            $methods = array(
561                '_generateTableAnnotation',
562                '_generateInheritanceAnnotation',
563                '_generateDiscriminatorColumnAnnotation',
564                '_generateDiscriminatorMapAnnotation'
565            );
566
567            foreach ($methods as $method) {
568                if ($code = $this->$method($metadata)) {
569                    $lines[] = ' * ' . $code;
570                }
571            }
572
573            if ($metadata->isMappedSuperclass) {
574                $lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSuperClass';
575            } else {
576                $lines[] = ' * @' . $this->_annotationsPrefix . 'Entity';
577            }
578
579            if ($metadata->customRepositoryClassName) {
580                $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")';
581            }
582
583            if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
584                $lines[] = ' * @' . $this->_annotationsPrefix . 'HasLifecycleCallbacks';
585            }
586        }
587
588        $lines[] = ' */';
589
590        return implode("\n", $lines);
591    }
592
593    private function _generateTableAnnotation($metadata)
594    {
595        $table = array();
596
597        if (isset($metadata->table['schema'])) {
598            $table[] = 'schema="' . $metadata->table['schema'] . '"';
599        }
600
601        if (isset($metadata->table['name'])) {
602            $table[] = 'name="' . $metadata->table['name'] . '"';
603        }
604
605        if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
606            $constraints = $this->_generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
607            $table[] = 'uniqueConstraints={' . $constraints . '}';
608        }
609
610        if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
611            $constraints = $this->_generateTableConstraints('Index', $metadata->table['indexes']);
612            $table[] = 'indexes={' . $constraints . '}';
613        }
614
615        return '@' . $this->_annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
616    }
617
618    private function _generateTableConstraints($constraintName, $constraints)
619    {
620        $annotations = array();
621        foreach ($constraints as $name => $constraint) {
622            $columns = array();
623            foreach ($constraint['columns'] as $column) {
624                $columns[] = '"' . $column . '"';
625            }
626            $annotations[] = '@' . $this->_annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
627        }
628        return implode(', ', $annotations);
629    }
630
631    private function _generateInheritanceAnnotation($metadata)
632    {
633        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
634            return '@' . $this->_annotationsPrefix . 'InheritanceType("'.$this->_getInheritanceTypeString($metadata->inheritanceType).'")';
635        }
636    }
637
638    private function _generateDiscriminatorColumnAnnotation($metadata)
639    {
640        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
641            $discrColumn = $metadata->discriminatorValue;
642            $columnDefinition = 'name="' . $discrColumn['name']
643                . '", type="' . $discrColumn['type']
644                . '", length=' . $discrColumn['length'];
645
646            return '@' . $this->_annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
647        }
648    }
649
650    private function _generateDiscriminatorMapAnnotation($metadata)
651    {
652        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
653            $inheritanceClassMap = array();
654
655            foreach ($metadata->discriminatorMap as $type => $class) {
656                $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
657            }
658
659            return '@' . $this->_annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
660        }
661    }
662
663    private function _generateEntityStubMethods(ClassMetadataInfo $metadata)
664    {
665        $methods = array();
666
667        foreach ($metadata->fieldMappings as $fieldMapping) {
668            if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) {
669                if ($code = $this->_generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) {
670                    $methods[] = $code;
671                }
672            }
673
674            if ($code = $this->_generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) {
675                $methods[] = $code;
676            }
677        }
678
679        foreach ($metadata->associationMappings as $associationMapping) {
680            if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
681                $nullable = $this->_isAssociationIsNullable($associationMapping) ? 'null' : null;
682                if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
683                    $methods[] = $code;
684                }
685                if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
686                    $methods[] = $code;
687                }
688            } else if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
689                if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
690                    $methods[] = $code;
691                }
692                if ($code = $this->_generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
693                    $methods[] = $code;
694                }
695                if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) {
696                    $methods[] = $code;
697                }
698            }
699        }
700
701        return implode("\n\n", $methods);
702    }
703
704    private function _isAssociationIsNullable($associationMapping)
705    {
706        if (isset($associationMapping['id']) && $associationMapping['id']) {
707            return false;
708        }
709        if (isset($associationMapping['joinColumns'])) {
710            $joinColumns = $associationMapping['joinColumns'];
711        } else {
712            //@todo thereis no way to retreive targetEntity metadata
713            $joinColumns = array();
714        }
715        foreach ($joinColumns as $joinColumn) {
716            if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
717                return false;
718            }
719        }
720        return true;
721    }
722
723    private function _generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
724    {
725        if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
726            $methods = array();
727
728            foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
729                foreach ($callbacks as $callback) {
730                    if ($code = $this->_generateLifecycleCallbackMethod($name, $callback, $metadata)) {
731                        $methods[] = $code;
732                    }
733                }
734            }
735
736            return implode("\n\n", $methods);
737        }
738
739        return "";
740    }
741
742    private function _generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
743    {
744        $lines = array();
745
746        foreach ($metadata->associationMappings as $associationMapping) {
747            if ($this->_hasProperty($associationMapping['fieldName'], $metadata)) {
748                continue;
749            }
750
751            $lines[] = $this->_generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
752            $lines[] = $this->_spaces . 'private $' . $associationMapping['fieldName']
753                     . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
754        }
755
756        return implode("\n", $lines);
757    }
758
759    private function _generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
760    {
761        $lines = array();
762
763        foreach ($metadata->fieldMappings as $fieldMapping) {
764            if ($this->_hasProperty($fieldMapping['fieldName'], $metadata) ||
765                $metadata->isInheritedField($fieldMapping['fieldName'])) {
766                continue;
767            }
768
769            $lines[] = $this->_generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
770            $lines[] = $this->_spaces . 'private $' . $fieldMapping['fieldName']
771                     . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n";
772        }
773
774        return implode("\n", $lines);
775    }
776
777    private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null,  $defaultValue = null)
778    {
779        $methodName = $type . Inflector::classify($fieldName);
780        if (in_array($type, array("add", "remove")) && substr($methodName, -1) == "s") {
781            $methodName = substr($methodName, 0, -1);
782        }
783
784        if ($this->_hasMethod($methodName, $metadata)) {
785            return;
786        }
787        $this->_staticReflection[$metadata->name]['methods'][] = $methodName;
788
789        $var = sprintf('_%sMethodTemplate', $type);
790        $template = self::$$var;
791
792        $variableType = $typeHint ? $typeHint . ' ' : null;
793
794        $types = \Doctrine\DBAL\Types\Type::getTypesMap();
795        $methodTypeHint = $typeHint && ! isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null;
796
797        $replacements = array(
798          '<description>'       => ucfirst($type) . ' ' . $fieldName,
799          '<methodTypeHint>'    => $methodTypeHint,
800          '<variableType>'      => $variableType,
801          '<variableName>'      => Inflector::camelize($fieldName),
802          '<methodName>'        => $methodName,
803          '<fieldName>'         => $fieldName,
804          '<variableDefault>'   => ($defaultValue !== null ) ? (' = '.$defaultValue) : '',
805          '<entity>'            => $this->_getClassName($metadata)
806        );
807
808        $method = str_replace(
809            array_keys($replacements),
810            array_values($replacements),
811            $template
812        );
813
814        return $this->_prefixCodeWithSpaces($method);
815    }
816
817    private function _generateLifecycleCallbackMethod($name, $methodName, $metadata)
818    {
819        if ($this->_hasMethod($methodName, $metadata)) {
820            return;
821        }
822        $this->_staticReflection[$metadata->name]['methods'][] = $methodName;
823
824        $replacements = array(
825            '<name>'        => $this->_annotationsPrefix . ucfirst($name),
826            '<methodName>'  => $methodName,
827        );
828
829        $method = str_replace(
830            array_keys($replacements),
831            array_values($replacements),
832            self::$_lifecycleCallbackMethodTemplate
833        );
834
835        return $this->_prefixCodeWithSpaces($method);
836    }
837
838    private function _generateJoinColumnAnnotation(array $joinColumn)
839    {
840        $joinColumnAnnot = array();
841
842        if (isset($joinColumn['name'])) {
843            $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
844        }
845
846        if (isset($joinColumn['referencedColumnName'])) {
847            $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
848        }
849
850        if (isset($joinColumn['unique']) && $joinColumn['unique']) {
851            $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
852        }
853
854        if (isset($joinColumn['nullable'])) {
855            $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
856        }
857
858        if (isset($joinColumn['onDelete'])) {
859            $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
860        }
861
862        if (isset($joinColumn['columnDefinition'])) {
863            $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
864        }
865
866        return '@' . $this->_annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
867    }
868
869    private function _generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
870    {
871        $lines = array();
872        $lines[] = $this->_spaces . '/**';
873
874        if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
875            $lines[] = $this->_spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection';
876        } else {
877            $lines[] = $this->_spaces . ' * @var ' . $associationMapping['targetEntity'];
878        }
879
880        if ($this->_generateAnnotations) {
881            $lines[] = $this->_spaces . ' *';
882           
883            if (isset($associationMapping['id']) && $associationMapping['id']) {
884                $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
885           
886                if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
887                    $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
888                }
889            }
890
891            $type = null;
892            switch ($associationMapping['type']) {
893                case ClassMetadataInfo::ONE_TO_ONE:
894                    $type = 'OneToOne';
895                    break;
896                case ClassMetadataInfo::MANY_TO_ONE:
897                    $type = 'ManyToOne';
898                    break;
899                case ClassMetadataInfo::ONE_TO_MANY:
900                    $type = 'OneToMany';
901                    break;
902                case ClassMetadataInfo::MANY_TO_MANY:
903                    $type = 'ManyToMany';
904                    break;
905            }
906            $typeOptions = array();
907
908            if (isset($associationMapping['targetEntity'])) {
909                $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
910            }
911
912            if (isset($associationMapping['inversedBy'])) {
913                $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
914            }
915
916            if (isset($associationMapping['mappedBy'])) {
917                $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
918            }
919
920            if ($associationMapping['cascade']) {
921                $cascades = array();
922
923                if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
924                if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
925                if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
926                if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
927                if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
928
929                $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
930            }
931
932            if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
933                $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
934            }
935
936            $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
937
938            if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
939                $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinColumns({';
940
941                $joinColumnsLines = array();
942
943                foreach ($associationMapping['joinColumns'] as $joinColumn) {
944                    if ($joinColumnAnnot = $this->_generateJoinColumnAnnotation($joinColumn)) {
945                        $joinColumnsLines[] = $this->_spaces . ' *   ' . $joinColumnAnnot;
946                    }
947                }
948
949                $lines[] = implode(",\n", $joinColumnsLines);
950                $lines[] = $this->_spaces . ' * })';
951            }
952
953            if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
954                $joinTable = array();
955                $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
956
957                if (isset($associationMapping['joinTable']['schema'])) {
958                    $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
959                }
960
961                $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
962                $lines[] = $this->_spaces . ' *   joinColumns={';
963
964                foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) {
965                    $lines[] = $this->_spaces . ' *     ' . $this->_generateJoinColumnAnnotation($joinColumn);
966                }
967
968                $lines[] = $this->_spaces . ' *   },';
969                $lines[] = $this->_spaces . ' *   inverseJoinColumns={';
970
971                foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
972                    $lines[] = $this->_spaces . ' *     ' . $this->_generateJoinColumnAnnotation($joinColumn);
973                }
974
975                $lines[] = $this->_spaces . ' *   }';
976                $lines[] = $this->_spaces . ' * )';
977            }
978
979            if (isset($associationMapping['orderBy'])) {
980                $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'OrderBy({';
981
982                foreach ($associationMapping['orderBy'] as $name => $direction) {
983                    $lines[] = $this->_spaces . ' *     "' . $name . '"="' . $direction . '",';
984                }
985
986                $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
987                $lines[] = $this->_spaces . ' * })';
988            }
989        }
990
991        $lines[] = $this->_spaces . ' */';
992
993        return implode("\n", $lines);
994    }
995
996    private function _generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
997    {
998        $lines = array();
999        $lines[] = $this->_spaces . '/**';
1000        $lines[] = $this->_spaces . ' * @var ' . $fieldMapping['type'] . ' $' . $fieldMapping['fieldName'];
1001
1002        if ($this->_generateAnnotations) {
1003            $lines[] = $this->_spaces . ' *';
1004
1005            $column = array();
1006            if (isset($fieldMapping['columnName'])) {
1007                $column[] = 'name="' . $fieldMapping['columnName'] . '"';
1008            }
1009
1010            if (isset($fieldMapping['type'])) {
1011                $column[] = 'type="' . $fieldMapping['type'] . '"';
1012            }
1013
1014            if (isset($fieldMapping['length'])) {
1015                $column[] = 'length=' . $fieldMapping['length'];
1016            }
1017
1018            if (isset($fieldMapping['precision'])) {
1019                $column[] = 'precision=' .  $fieldMapping['precision'];
1020            }
1021
1022            if (isset($fieldMapping['scale'])) {
1023                $column[] = 'scale=' . $fieldMapping['scale'];
1024            }
1025
1026            if (isset($fieldMapping['nullable'])) {
1027                $column[] = 'nullable=' .  var_export($fieldMapping['nullable'], true);
1028            }
1029
1030            if (isset($fieldMapping['columnDefinition'])) {
1031                $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
1032            }
1033
1034            if (isset($fieldMapping['unique'])) {
1035                $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
1036            }
1037
1038            $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
1039
1040            if (isset($fieldMapping['id']) && $fieldMapping['id']) {
1041                $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
1042
1043                if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
1044                    $lines[] = $this->_spaces.' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
1045                }
1046
1047                if ($metadata->sequenceGeneratorDefinition) {
1048                    $sequenceGenerator = array();
1049
1050                    if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
1051                        $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
1052                    }
1053
1054                    if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
1055                        $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
1056                    }
1057
1058                    if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
1059                        $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
1060                    }
1061
1062                    $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
1063                }
1064            }
1065
1066            if (isset($fieldMapping['version']) && $fieldMapping['version']) {
1067                $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Version';
1068            }
1069        }
1070
1071        $lines[] = $this->_spaces . ' */';
1072
1073        return implode("\n", $lines);
1074    }
1075
1076    private function _prefixCodeWithSpaces($code, $num = 1)
1077    {
1078        $lines = explode("\n", $code);
1079
1080        foreach ($lines as $key => $value) {
1081            $lines[$key] = str_repeat($this->_spaces, $num) . $lines[$key];
1082        }
1083
1084        return implode("\n", $lines);
1085    }
1086
1087    private function _getInheritanceTypeString($type)
1088    {
1089        switch ($type) {
1090            case ClassMetadataInfo::INHERITANCE_TYPE_NONE:
1091                return 'NONE';
1092
1093            case ClassMetadataInfo::INHERITANCE_TYPE_JOINED:
1094                return 'JOINED';
1095
1096            case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE:
1097                return 'SINGLE_TABLE';
1098
1099            case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS:
1100                return 'PER_CLASS';
1101
1102            default:
1103                throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type);
1104        }
1105    }
1106
1107    private function _getChangeTrackingPolicyString($policy)
1108    {
1109        switch ($policy) {
1110            case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT:
1111                return 'DEFERRED_IMPLICIT';
1112
1113            case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT:
1114                return 'DEFERRED_EXPLICIT';
1115
1116            case ClassMetadataInfo::CHANGETRACKING_NOTIFY:
1117                return 'NOTIFY';
1118
1119            default:
1120                throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy);
1121        }
1122    }
1123
1124    private function _getIdGeneratorTypeString($type)
1125    {
1126        switch ($type) {
1127            case ClassMetadataInfo::GENERATOR_TYPE_AUTO:
1128                return 'AUTO';
1129
1130            case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE:
1131                return 'SEQUENCE';
1132
1133            case ClassMetadataInfo::GENERATOR_TYPE_TABLE:
1134                return 'TABLE';
1135
1136            case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY:
1137                return 'IDENTITY';
1138
1139            case ClassMetadataInfo::GENERATOR_TYPE_NONE:
1140                return 'NONE';
1141
1142            default:
1143                throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type);
1144        }
1145    }
1146}
Note: See TracBrowser for help on using the repository browser.