root/branches/1.1/lib/Doctrine/Import/Builder.php

Revision 6401, 38.7 KB (checked in by guilhermeblanco, 5 months ago)

[1.0, 1.1] Fixed missing setUp build when using inheritance. Fixes #2453

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id Revision
Line 
1<?php
2/*
3 *  $Id$
4 *
5 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 *
17 * This software consists of voluntary contributions made by many individuals
18 * and is licensed under the LGPL. For more information, see
19 * <http://www.phpdoctrine.org>.
20 */
21
22/**
23 * Doctrine_Import_Builder
24 *
25 * Import builder is responsible of building Doctrine_Record classes
26 * based on a database schema.
27 *
28 * @package     Doctrine
29 * @subpackage  Import
30 * @link        www.phpdoctrine.org
31 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
32 * @since       1.0
33 * @version     $Revision$
34 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
35 * @author      Jukka Hassinen <Jukka.Hassinen@BrainAlliance.com>
36 * @author      Nicolas Bérard-Nault <nicobn@php.net>
37 * @author      Jonathan H. Wage <jwage@mac.com>
38 */
39class Doctrine_Import_Builder extends Doctrine_Builder
40{
41    /**
42     * _path
43     *
44     * the path where imported files are being generated
45     *
46     * @var string $_path
47     */
48    protected $_path = '';
49
50    /**
51     * _packagesPrefix
52     *
53     * @var string
54     */
55    protected $_packagesPrefix = 'Package';
56
57    /**
58     * _packagesPath
59     *
60     * @var string
61     */
62    protected $_packagesPath = '';
63
64    /**
65     * _packagesFolderName
66     *
67     * @var string
68     */
69    protected $_packagesFolderName = 'packages';
70
71    /**
72     * _suffix
73     *
74     * File suffix to use when writing class definitions
75     *
76     * @var string $suffix
77     */
78    protected $_suffix = '.php';
79
80    /**
81     * _generateBaseClasses
82     *
83     * Bool true/false for whether or not to generate base classes
84     *
85     * @var boolean $generateBaseClasses
86     */
87    protected $_generateBaseClasses = true;
88
89    /**
90     * _generateTableClasses
91     *
92     * Bool true/false for whether or not to generate child table classes
93     *
94     * @var boolean $generateTableClasses
95     */
96    protected $_generateTableClasses = false;
97
98    /**
99     * _base
100     *
101     * Prefix to use for generated base classes
102     *
103     * @var string
104     */
105    protected $_baseClassPrefix = 'Base';
106
107    /**
108     * _baseClassesDirectory
109     *
110     * Directory to put the generate base classes in
111     *
112     * @var string $suffix
113     */
114    protected $_baseClassesDirectory = 'generated';
115
116    /**
117     * _baseClassName
118     *
119     * @var string
120     */
121    protected $_baseClassName = 'Doctrine_Record';
122
123    /**
124     * Prefix to all generated classes
125     *
126     * @var string
127     */
128    protected $_classPrefix = null;
129
130    /**
131     * The package name to use for the generated php docs
132     *
133     * @var string
134     */
135    protected $_phpDocPackage = '##PACKAGE##';
136
137    /**
138     * The subpackage name to use for the generated php docs
139     *
140     * @var string
141     */
142    protected $_phpDocSubpackage = '##SUBPACKAGE##';
143
144    /**
145     * Full name of the author to use for the generated php docs
146     *
147     * @var string
148     */
149    protected $_phpDocName = '##NAME##';
150
151    /**
152     * Email of the author to use for the generated php docs
153     *
154     * @var string
155     */
156    protected $_phpDocEmail = '##EMAIL##';
157
158    /**
159     * _tpl
160     *
161     * Class template used for writing classes
162     *
163     * @var $_tpl
164     */
165    protected static $_tpl;
166
167    /**
168     * __construct
169     *
170     * @return void
171     */
172    public function __construct()
173    {
174        $this->loadTemplate();
175    }
176
177    /**
178     * setTargetPath
179     *
180     * @param string path   the path where imported files are being generated
181     * @return
182     */
183    public function setTargetPath($path)
184    {
185        if ($path) {
186            if ( ! $this->_packagesPath) {
187                $this->setPackagesPath($path . DIRECTORY_SEPARATOR . $this->_packagesFolderName);
188            }
189
190            $this->_path = $path;
191        }
192    }
193
194    /**
195     * setPackagePath
196     *
197     * @param string $packagesPrefix
198     * @return void
199     */
200    public function setPackagesPrefix($packagesPrefix)
201    {
202        $this->_packagesPrefix = $packagesPrefix;
203    }
204
205    /**
206     * setPackagesPath
207     *
208     * @param string $packagesPath
209     * @return void
210     */
211    public function setPackagesPath($packagesPath)
212    {
213        if ($packagesPath) {
214            $this->_packagesPath = $packagesPath;
215        }
216    }
217
218    /**
219     * generateBaseClasses
220     *
221     * Specify whether or not to generate classes which extend from generated base classes
222     *
223     * @param  boolean $bool
224     * @return boolean $bool
225     */
226    public function generateBaseClasses($bool = null)
227    {
228        if ($bool !== null) {
229            $this->_generateBaseClasses = $bool;
230        }
231
232        return $this->_generateBaseClasses;
233    }
234
235    /**
236     * generateTableClasses
237     *
238     * Specify whether or not to generate children table classes
239     *
240     * @param  boolean $bool
241     * @return boolean $bool
242     */
243    public function generateTableClasses($bool = null)
244    {
245        if ($bool !== null) {
246            $this->_generateTableClasses = $bool;
247        }
248
249        return $this->_generateTableClasses;
250    }
251
252    /**
253     * setBaseClassPrefix
254     *
255     * @param string $prefix
256     * @return void
257     */
258    public function setBaseClassPrefix($prefix)
259    {
260        $this->_baseClassPrefix = $prefix;
261    }
262
263    /**
264     * getBaseClassPrefix
265     *
266     * @return void
267     */
268    public function getBaseClassPrefix()
269    {
270        return $this->_baseClassPrefix;
271    }
272
273    /**
274     * setBaseClassesDirectory
275     *
276     * @return void
277     */
278    public function setBaseClassesDirectory($baseClassesDirectory)
279    {
280        $this->_baseClassesDirectory = $baseClassesDirectory;
281    }
282
283    /**
284     * setBaseClassName
285     *
286     * @package default
287     */
288    public function setBaseClassName($className)
289    {
290        $this->_baseClassName = $className;
291    }
292
293    /**
294     * setSuffix
295     *
296     * @param string $suffix
297     * @return void
298     */
299    public function setSuffix($suffix)
300    {
301        $this->_suffix = $suffix;
302    }
303
304    /**
305     * getTargetPath
306     *
307     * @return string       the path where imported files are being generated
308     */
309    public function getTargetPath()
310    {
311        return $this->_path;
312    }
313
314    /**
315     * setOptions
316     *
317     * @param string $options
318     * @return void
319     */
320    public function setOptions($options)
321    {
322        if ( ! empty($options)) {
323            foreach ($options as $key => $value) {
324                $this->setOption($key, $value);
325            }
326        }
327    }
328
329    /**
330     * setOption
331     *
332     * @param string $key
333     * @param string $value
334     * @return void
335     */
336    public function setOption($key, $value)
337    {
338        $name = 'set' . Doctrine_Inflector::classify($key);
339
340        if (method_exists($this, $name)) {
341            $this->$name($value);
342        } else {
343            $key = '_' . $key;
344            $this->$key = $value;
345        }
346    }
347
348    /**
349     * loadTemplate
350     *
351     * Loads the class template used for generating classes
352     *
353     * @return void
354     */
355    public function loadTemplate()
356    {
357        if (isset(self::$_tpl)) {
358            return;
359        }
360
361        self::$_tpl = '/**'
362                    . '%s' . PHP_EOL
363                    . ' */' . PHP_EOL
364                    . '%sclass %s extends %s' . PHP_EOL
365                    . '{'
366                    . '%s' . PHP_EOL
367                    . '%s' . PHP_EOL
368                    . '}';
369    }
370
371    /*
372     * Build the table definition of a Doctrine_Record object
373     *
374     * @param  string $table
375     * @param  array  $tableColumns
376     */
377    public function buildTableDefinition(array $definition)
378    {
379        if (isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'column_aggregation')) {
380            return;
381        }
382
383        $ret = array();
384
385        $i = 0;
386
387        if (isset($definition['inheritance']['type']) && $definition['inheritance']['type'] == 'concrete') {
388            $ret[$i] = "        parent::setTableDefinition();";
389            $i++;
390        }
391
392        if (isset($definition['tableName']) && !empty($definition['tableName'])) {
393            $ret[$i] = "        ".'$this->setTableName(\''. $definition['tableName'].'\');';
394            $i++;
395        }
396
397        if (isset($definition['columns']) && is_array($definition['columns']) && !empty($definition['columns'])) {
398            $ret[$i] = $this->buildColumns($definition['columns']);
399            $i++;
400        }
401
402        if (isset($definition['indexes']) && is_array($definition['indexes']) && !empty($definition['indexes'])) {
403            $ret[$i] = $this->buildIndexes($definition['indexes']);
404            $i++;
405        }
406
407        if (isset($definition['attributes']) && is_array($definition['attributes']) && !empty($definition['attributes'])) {
408            $ret[$i] = $this->buildAttributes($definition['attributes']);
409            $i++;
410        }
411
412        if (isset($definition['options']) && is_array($definition['options']) && !empty($definition['options'])) {
413            $ret[$i] = $this->buildOptions($definition['options']);
414            $i++;
415        }
416
417        if (isset($definition['checks']) && is_array($definition['checks']) && !empty($definition['checks'])) {
418            $ret[$i] = $this->buildChecks($definition['checks']);
419            $i++;
420        }
421
422        if (isset($definition['inheritance']['subclasses']) && ! empty($definition['inheritance']['subclasses'])) {
423            $ret[$i] = "        ".'$this->setSubClasses('. $this->varExport($definition['inheritance']['subclasses']).');';
424            $i++;
425        }
426
427        $code = implode(PHP_EOL, $ret);
428        $code = trim($code);
429
430        return PHP_EOL . "    public function setTableDefinition()" . PHP_EOL . '    {' . PHP_EOL . '        ' . $code . PHP_EOL . '    }';
431    }
432
433    /**
434     * buildSetUp
435     *
436     * @param  array $options
437     * @param  array $columns
438     * @param  array $relations
439     * @return string
440     */
441    public function buildSetUp(array $definition)
442    {
443        $ret = array();
444        $i = 0;
445
446        if (isset($definition['relations']) && is_array($definition['relations']) && ! empty($definition['relations'])) {
447            foreach ($definition['relations'] as $name => $relation) {
448                $class = isset($relation['class']) ? $relation['class']:$name;
449                $alias = (isset($relation['alias']) && $relation['alias'] !== $relation['class']) ? ' as ' . $relation['alias'] : '';
450
451                if ( ! isset($relation['type'])) {
452                    $relation['type'] = Doctrine_Relation::ONE;
453                }
454
455                if ($relation['type'] === Doctrine_Relation::ONE) {
456                    $ret[$i] = "        ".'$this->hasOne(\'' . $class . $alias . '\'';
457                } else {
458                    $ret[$i] = "        ".'$this->hasMany(\'' . $class . $alias . '\'';
459                }
460
461                $a = array();
462
463                if (isset($relation['refClass'])) {
464                    $a[] = '\'refClass\' => ' . $this->varExport($relation['refClass']);
465                }
466               
467                if (isset($relation['refClassRelationAlias'])) {
468                    $a[] = '\'refClassRelationAlias\' => ' . $this->varExport($relation['refClassRelationAlias']);
469                }
470               
471                if (isset($relation['deferred']) && $relation['deferred']) {
472                    $a[] = '\'default\' => ' . $this->varExport($relation['deferred']);
473                }
474
475                if (isset($relation['local']) && $relation['local']) {
476                    $a[] = '\'local\' => ' . $this->varExport($relation['local']);
477                }
478
479                if (isset($relation['foreign']) && $relation['foreign']) {
480                    $a[] = '\'foreign\' => ' . $this->varExport($relation['foreign']);
481                }
482
483                if (isset($relation['onDelete']) && $relation['onDelete']) {
484                    $a[] = '\'onDelete\' => ' . $this->varExport($relation['onDelete']);
485                }
486
487                if (isset($relation['onUpdate']) && $relation['onUpdate']) {
488                    $a[] = '\'onUpdate\' => ' . $this->varExport($relation['onUpdate']);
489                }
490
491                if (isset($relation['cascade']) && $relation['cascade']) {
492                    $a[] = '\'cascade\' => ' . $this->varExport($relation['cascade']);
493                }
494
495                if (isset($relation['equal']) && $relation['equal']) {
496                    $a[] = '\'equal\' => ' . $this->varExport($relation['equal']);
497                }
498
499                if (isset($relation['owningSide']) && $relation['owningSide']) {
500                    $a[] = '\'owningSide\' => ' . $this->varExport($relation['owningSide']);
501                }
502
503                if ( ! empty($a)) {
504                    $ret[$i] .= ', ' . 'array(' . PHP_EOL . str_repeat(' ', 13);
505                    $length = strlen($ret[$i]);
506                    $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', 13), $a) . ')';
507                }
508
509                $ret[$i] .= ');'.PHP_EOL;
510                $i++;
511            }
512        }
513
514        if (isset($definition['actAs']) && is_array($definition['actAs']) && !empty($definition['actAs'])) {
515            $ret[$i] = $this->buildActAs($definition['actAs']);
516            $i++;
517        }
518
519        if (isset($definition['listeners']) && is_array($definition['listeners']) && !empty($definition['listeners'])) {
520            $ret[$i] = $this->buildListeners($definition['listeners']);
521            $i++;
522        }
523
524        $code = implode(PHP_EOL, $ret);
525        $code = trim($code);
526
527        // If the body of the function has contents then we need to
528        if ($code) {
529            // If the body of the function has contents and we are using inheritance
530            // then we need call the parent::setUp() before the body of the function
531            // Class table inheritance is the only one we shouldn't call parent::setUp() for
532            if ( ! isset($definition['inheritance']['type']) || $definition['inheritance']['type'] != 'class_table') {
533                $code = "parent::setUp();" . PHP_EOL . '    ' . $code;
534            }
535        }
536
537        // If we have some code for the function then lets define it and return it
538        if ($code) {
539            return '    public function setUp()' . PHP_EOL . '    {' . PHP_EOL . '        ' . $code . PHP_EOL . '    }';
540        }
541    }
542
543    /**
544     * Build php code for record checks
545     *
546     * @param array $checks
547     * @return string $build
548     */
549    public function buildChecks($checks)
550    {
551        $build = '';
552        foreach ($checks as $check) {
553            $build .= "        \$this->check('" . $check . "');" . PHP_EOL;
554        }
555        return $build;
556    }
557
558    /**
559     * buildColumns
560     *
561     * @param string $array
562     * @return void
563     */
564    public function buildColumns(array $columns)
565    {
566        $build = null;
567        foreach ($columns as $name => $column) {
568            $columnName = isset($column['name']) ? $column['name']:$name;
569            $build .= "        ".'$this->hasColumn(\'' . $columnName . '\', \'' . $column['type'] . '\'';
570
571            if ($column['length']) {
572                $build .= ', ' . $column['length'];
573            } else {
574                $build .= ', null';
575            }
576
577            $options = $column;
578
579            // Remove name, alltypes, ntype. They are not needed in options array
580            unset($options['name']);
581            unset($options['alltypes']);
582            unset($options['ntype']);
583
584            // Remove notnull => true if the column is primary
585            // Primary columns are implied to be notnull in Doctrine
586            if (isset($options['primary']) && $options['primary'] == true && (isset($options['notnull']) && $options['notnull'] == true)) {
587                unset($options['notnull']);
588            }
589
590            // Remove default if the value is 0 and the column is a primary key
591            // Doctrine defaults to 0 if it is a primary key
592            if (isset($options['primary']) && $options['primary'] == true && (isset($options['default']) && $options['default'] == 0)) {
593                unset($options['default']);
594            }
595
596            // Remove null and empty array values
597            foreach ($options as $key => $value) {
598                if (is_null($value) || (is_array($value) && empty($value))) {
599                    unset($options[$key]);
600                }
601            }
602
603            if (is_array($options) && !empty($options)) {
604                $build .= ', ' . $this->varExport($options);
605            }
606
607            $build .= ');' . PHP_EOL;
608        }
609
610        return $build;
611    }
612
613    /*
614     * Build the accessors
615     *
616     * @param  string $table
617     * @param  array  $columns
618     */
619    public function buildAccessors(array $definition)
620    {
621        $accessors = array();
622        foreach (array_keys($definition['columns']) as $name) {
623            $accessors[] = $name;
624        }
625
626        foreach ($definition['relations'] as $relation) {
627            $accessors[] = $relation['alias'];
628        }
629
630        $ret = '';
631        foreach ($accessors as $name) {
632            // getters
633            $ret .= PHP_EOL . '  public function get' . Doctrine_Inflector::classify(Doctrine_Inflector::tableize($name)) . "(\$load = true)" . PHP_EOL;
634            $ret .= "  {" . PHP_EOL;
635            $ret .= "    return \$this->get('{$name}', \$load);" . PHP_EOL;
636            $ret .= "  }" . PHP_EOL;
637
638            // setters
639            $ret .= PHP_EOL . '  public function set' . Doctrine_Inflector::classify(Doctrine_Inflector::tableize($name)) . "(\${$name}, \$load = true)" . PHP_EOL;
640            $ret .= "  {" . PHP_EOL;
641            $ret .= "    return \$this->set('{$name}', \${$name}, \$load);" . PHP_EOL;
642            $ret .= "  }" . PHP_EOL;
643        }
644
645        return $ret;
646    }
647
648    /*
649     * Build the phpDoc for a class definition
650     *
651     * @param  array  $definition
652     */
653    public function buildPhpDocs(array $definition)
654    {
655        $ret = array();
656
657        $ret[] = $definition['className'];
658        $ret[] = '';
659        $ret[] = 'This class has been auto-generated by the Doctrine ORM Framework';
660        $ret[] = '';
661
662        if ((isset($definition['is_base_class']) && $definition['is_base_class']) || ! $this->generateBaseClasses()) {
663            foreach ($definition['columns'] as $name => $column) {
664                $name = isset($column['name']) ? $column['name']:$name;
665                // extract column name & field name
666                if (stripos($name, ' as '))
667                {
668                    if (strpos($name, ' as')) {
669                        $parts = explode(' as ', $name);
670                    } else {
671                        $parts = explode(' AS ', $name);
672                    }
673
674                    if (count($parts) > 1) {
675                        $fieldName = $parts[1];
676                    } else {
677                        $fieldName = $parts[0];
678                    }
679
680                    $name = $parts[0];
681                } else {
682                    $fieldName = $name;
683                    $name = $name;
684                }
685
686                $name = trim($name);
687                $fieldName = trim($fieldName);
688
689                $ret[] = '@property ' . $column['type'] . ' $' . $fieldName;
690            }
691
692            if (isset($definition['relations']) && ! empty($definition['relations'])) {
693                foreach ($definition['relations'] as $relation) {
694                    $type = (isset($relation['type']) && $relation['type'] == Doctrine_Relation::MANY) ? 'Doctrine_Collection':$relation['class'];
695                    $ret[] = '@property ' . $type . ' $' . $relation['alias'];
696                }
697            }
698            $ret[] = '';
699        }
700
701        $ret[] = '@package    ' . $this->_phpDocPackage;
702        $ret[] = '@subpackage ' . $this->_phpDocSubpackage;
703        $ret[] = '@author     ' . $this->_phpDocName . ' <' . $this->_phpDocEmail . '>';
704
705        $fileName = $definition['className']  . $this->_suffix;
706        $ret[] = '@version    SVN: $Id$';
707
708        $ret = ' * ' . implode(PHP_EOL . ' * ', $ret);
709        $ret = ' ' . trim($ret);
710
711        return $ret;
712    }
713
714    /**
715     * emit a behavior assign
716     *
717     * @param int $level
718     * @param string $name
719     * @param string $option
720     * @return string assignation code
721     */
722    private function emitAssign($level, $name, $option)
723    {
724        // find class matching $name
725        $classname = $name;
726        if (class_exists("Doctrine_Template_$name", true)) {
727            $classname = "Doctrine_Template_$name";
728        }
729        return "        \$" . strtolower($name) . "$level = new $classname($option);". PHP_EOL;
730    }
731
732    /**
733     * emit an addChild
734     *
735     * @param int $level
736     * @param string $name
737     * @param string $option
738     * @return string addChild code
739     */
740    private function emitAddChild($level, $parent, $name)
741    {
742        return "        \$" . strtolower($parent) . ($level - 1) . "->addChild(\$" . strtolower($name) . "$level);" . PHP_EOL;
743    }
744
745    /**
746     * emit an indented actAs
747     *
748     * @param int $level
749     * @param string $name
750     * @param string $option
751     * @return string actAs code
752     */
753    private function emitActAs($level, $name)
754    {
755        return "        \$this->actAs(\$" . strtolower($name) . "$level);" . PHP_EOL;
756    }
757
758
759    /**
760     * buildActAs: builds a complete actAs code. It supports hierarchy of plugins
761     * @param array $actAs array of plugin definitions and options
762     */
763    public function buildActAs($actAs)
764    {
765        $emittedActAs = array();
766        $build = $this->innerBuildActAs($actAs, 0, null, $emittedActAs);
767        foreach($emittedActAs as $str) {
768            $build .= $str;
769        }
770        return $build;
771    }
772
773    /**
774     * innerBuildActAs: build a complete actAs code that handles hierarchy of plugins
775     *
776     * @param array  $actAs array of plugin definitions and options
777     * @param int    $level current indentation level
778     * @param string $parent name of the parent template/plugin
779     * @param array  $emittedActAs contains on output an array of actAs command to be appended to output
780     * @return string actAs full definition
781     */
782    private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emittedActAs)
783    {
784        // rewrite special case of actAs: [Behavior] which gave [0] => Behavior
785        if(is_array($actAs) && isset($actAs[0]) && !is_array($actAs[0])) {
786            $actAs = array_flip($actAs);
787        }
788
789        $build = '';
790        $currentParent = $parent;
791        if(is_array($actAs)) {
792            foreach($actAs as $template => $options) {
793                if ($template == 'actAs') {
794                    // found another actAs
795                    $build .= $this->innerBuildActAs($options, $level + 1, $parent, $emittedActAs);
796                } else if (is_array($options)) {
797                    // remove actAs from options
798                    $realOptions = array();
799                    $leftActAs = array();
800                    foreach($options as $name => $value) {
801                        if ($name != 'actAs') {
802                            $realOptions[$name] = $options[$name];
803                        } else {
804                            $leftActAs[$name] = $options[$name];
805                        }
806                    } 
807
808                    $optionPHP = $this->varExport($realOptions);
809                    $build .= $this->emitAssign($level, $template, $optionPHP); 
810                    if ($level == 0) {
811                        $emittedActAs[] = $this->emitActAs($level, $template);
812                    } else {
813                        $build .= $this->emitAddChild($level, $currentParent, $template);
814                    }
815                    // descend for the remainings actAs
816                    $parent = $template;           
817                    $build .= $this->innerBuildActAs($leftActAs, $level, $template, $emittedActAs);
818                } else {
819                    $build .= $this->emitAssign($level, $template, null);
820                    if ($level == 0) {
821                        $emittedActAs[] = $this->emitActAs($level, $template);
822                    } else {
823                        $build .= $this->emitAddChild($level, $currentParent, $template);
824                    }
825                    $parent = $template;           
826                }
827            }
828        } else {
829            $build .= $this->emitAssign($level, $actAs, null);
830            if ($level == 0) {
831                $emittedActAs[] = $this->emitActAs($level, $actAs);
832            } else {
833                $build .= $this->emitAddChild($level, $currentParent, $actAs);
834            }
835        }
836
837        return $build;
838    }
839
840    /**
841     * Build php code for adding record listeners
842     *
843     * @param string $listeners
844     * @return string $build
845     */
846    public function buildListeners($listeners)
847    {
848        $build = '';
849       
850        foreach($listeners as $name => $options) {
851            if ( ! is_array($options) && $options !== null) {
852                $name = $options;
853                $options = null;
854            }
855
856            $useOptions = ( ! empty($options) && isset($options['useOptions']) && $options['useOptions'] == true) 
857                ? '$this->_options' : '';
858            $class = ( ! empty($options) && isset($options['class'])) ? $options['class'] : $name;
859
860            $build .= "    \$this->addListener(new " . $class . "(" . $useOptions . "), '" . $name . "');" . PHP_EOL;
861        }
862
863        return $build;
864    }
865
866    /**
867     * buildAttributes
868     *
869     * @param string $array
870     * @return void
871     */
872    public function buildAttributes(array $attributes)
873    {
874        $build = PHP_EOL;
875        foreach ($attributes as $key => $value) {
876   
877            $values = array();
878            if (is_bool($value))
879            {
880              $values[] = $value ? 'true':'false';
881            } else {
882                if ( ! is_array($value)) {
883                    $value = array($value);
884                }
885   
886                foreach ($value as $attr) {
887                    $const = "Doctrine::" . strtoupper($key) . "_" . strtoupper($attr);
888                    if (defined($const)) {
889                        $values[] = $const;
890                    } else {
891                        $values[] = "'" . $attr . "'";
892                    }
893                }
894            }
895   
896            $string = implode(' ^ ', $values);
897            $build .= "        \$this->setAttribute(Doctrine::ATTR_" . strtoupper($key) . ", " . $string . ");" . PHP_EOL;
898        }
899   
900        return $build;
901    }
902
903    /**
904     * buildTableOptions
905     *
906     * @param string $array
907     * @return void
908     */
909    public function buildOptions(array $options)
910    {
911        $build = '';
912        foreach ($options as $name => $value) {
913            $build .= "        \$this->option('$name', " . $this->varExport($value) . ");" . PHP_EOL;
914        }
915
916        return $build;
917    }
918
919    /**
920     * buildIndexes
921     *
922     * @param string $array
923     * @return void
924     */
925    public function buildIndexes(array $indexes)
926    {
927      $build = '';
928
929      foreach ($indexes as $indexName => $definitions) {
930          $build .= PHP_EOL . "        \$this->index('" . $indexName . "'";
931          $build .= ', ' . $this->varExport($definitions);
932          $build .= ');';
933      }
934
935      return $build;
936    }
937
938    /**
939     * buildDefinition
940     *
941     * @param array $definition
942     * @return string
943     */
944    public function buildDefinition(array $definition)
945    {
946        if ( ! isset($definition['className'])) {
947            throw new Doctrine_Import_Builder_Exception('Missing class name.');
948        }
949        $abstract = isset($definition['abstract']) && $definition['abstract'] === true ? 'abstract ':null;
950        $className = $definition['className'];
951        $extends = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends']:$this->_baseClassName;
952
953        if ( ! (isset($definition['no_definition']) && $definition['no_definition'] === true)) {
954            $tableDefinitionCode = $this->buildTableDefinition($definition);
955            $setUpCode = $this->buildSetUp($definition);
956        } else {
957            $tableDefinitionCode = null;
958            $setUpCode = null;
959        }
960
961        if ($tableDefinitionCode && $setUpCode) {
962            $setUpCode = PHP_EOL . $setUpCode;
963        }
964
965       
966        $docs = PHP_EOL . $this->buildPhpDocs($definition);
967
968        $content = sprintf(self::$_tpl, $docs, $abstract,
969                                       $className,
970                                       $extends,
971                                       $tableDefinitionCode,
972                                       $setUpCode);
973
974        return $content;
975    }
976
977    /**
978     * buildRecord
979     *
980     * @param array $options
981     * @param array $columns
982     * @param array $relations
983     * @param array $indexes
984     * @param array $attributes
985     * @param array $templates
986     * @param array $actAs
987     * @return void=
988     */
989    public function buildRecord(array $definition)
990    {
991        if ( ! isset($definition['className'])) {
992            throw new Doctrine_Import_Builder_Exception('Missing class name.');
993        }
994
995        $definition['topLevelClassName'] = $definition['className'];
996
997        if ($this->generateBaseClasses()) {
998            $definition['is_package'] = (isset($definition['package']) && $definition['package']) ? true:false;
999
1000            if ($definition['is_package']) {
1001                $e = explode('.', trim($definition['package']));
1002                $definition['package_name'] = $e[0];
1003
1004                $definition['package_path'] = ! empty($e) ? implode(DIRECTORY_SEPARATOR, $e):$definition['package_name'];
1005            }
1006            // Top level definition that extends from all the others
1007            $topLevel = $definition;
1008            unset($topLevel['tableName']);
1009
1010            // If we have a package then we need to make this extend the package definition and not the base definition
1011            // The package definition will then extends the base definition
1012            $topLevel['inheritance']['extends'] = (isset($topLevel['package']) && $topLevel['package']) ? $this->_packagesPrefix . $topLevel['className']:$this->_baseClassPrefix . $topLevel['className'];
1013            $topLevel['no_definition'] = true;
1014            $topLevel['generate_once'] = true;
1015            $topLevel['is_main_class'] = true;
1016            unset($topLevel['connection']);
1017
1018            // Package level definition that extends from the base definition
1019            if (isset($definition['package'])) {
1020
1021                $packageLevel = $definition;
1022                $packageLevel['className'] = $topLevel['inheritance']['extends'];
1023                $packageLevel['inheritance']['extends'] = $this->_baseClassPrefix . $topLevel['className'];
1024                $packageLevel['no_definition'] = true;
1025                $packageLevel['abstract'] = true;
1026                $packageLevel['override_parent'] = true;
1027                $packageLevel['generate_once'] = true;
1028                $packageLevel['is_package_class'] = true;
1029                unset($packageLevel['connection']);
1030
1031                $packageLevel['tableClassName'] = $packageLevel['className'] . 'Table';
1032                $packageLevel['inheritance']['tableExtends'] = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends'] . 'Table':'Doctrine_Table';
1033
1034                $topLevel['tableClassName'] = $topLevel['topLevelClassName'] . 'Table';
1035                $topLevel['inheritance']['tableExtends'] = $packageLevel['className'] . 'Table';
1036            } else {
1037                $topLevel['tableClassName'] = $topLevel['className'] . 'Table';
1038                $topLevel['inheritance']['tableExtends'] = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends'] . 'Table':'Doctrine_Table';
1039            }
1040
1041            $baseClass = $definition;
1042            $baseClass['className'] = $this->_baseClassPrefix . $baseClass['className'];
1043            $baseClass['abstract'] = true;
1044            $baseClass['override_parent'] = false;
1045            $baseClass['is_base_class'] = true;
1046
1047            $this->writeDefinition($baseClass);
1048
1049            if ( ! empty($packageLevel)) {
1050                $this->writeDefinition($packageLevel);
1051            }
1052
1053            $this->writeDefinition($topLevel);
1054        } else {
1055            $this->writeDefinition($definition);
1056        }
1057    }
1058
1059    /**
1060     * writeTableDefinition
1061     *
1062     * @return void
1063     */
1064    public function writeTableDefinition($className, $path, $options = array())
1065    {
1066        $content  = '<?php' . PHP_EOL;
1067        $content .= sprintf(self::$_tpl, null, false,
1068                                       $className,
1069                                       isset($options['extends']) ? $options['extends']:'Doctrine_Table',
1070                                       null,
1071                                       null,
1072                                       null
1073                                       );
1074
1075        Doctrine_Lib::makeDirectories($path);
1076
1077        $writePath = $path . DIRECTORY_SEPARATOR . $className . $this->_suffix;
1078
1079        Doctrine::loadModel($className, $writePath);
1080
1081        if ( ! file_exists($writePath)) {
1082            file_put_contents($writePath, $content);
1083        }
1084    }
1085
1086    /**
1087     * writeDefinition
1088     *
1089     * @param array $options
1090     * @param array $columns
1091     * @param array $relations
1092     * @param array $indexes
1093     * @param array $attributes
1094     * @param array $templates
1095     * @param array $actAs
1096     * @return void
1097     */
1098    public function writeDefinition(array $definition)
1099    {
1100        $originalClassName = $definition['className'];
1101        if ($prefix = $this->_classPrefix) {
1102            if (isset($definition['tableClassName'])) {
1103                $definition['tableClassName'] = $prefix . $definition['tableClassName'];
1104            }
1105            $definition['className'] = $prefix . $definition['className'];
1106            if (isset($definition['connectionClassName'])) {
1107                $definition['connectionClassName'] = $prefix . $definition['connectionClassName'];
1108            }
1109            $definition['topLevelClassName'] = $prefix . $definition['topLevelClassName'];
1110            if (isset($definition['inheritance']['extends'])) {
1111                $definition['inheritance']['extends'] = $prefix . $definition['inheritance']['extends'];
1112            }
1113        }
1114
1115        $definitionCode = $this->buildDefinition($definition);
1116
1117        if ($prefix) {
1118            $definitionCode = str_replace("this->hasOne('", "this->hasOne('$prefix", $definitionCode);
1119            $definitionCode = str_replace("this->hasMany('", "this->hasMany('$prefix", $definitionCode);
1120            $definitionCode = str_replace("'refClass' => '", "'refClass' => '$prefix", $definitionCode);
1121        }
1122
1123        $fileName = $definition['className'] . $this->_suffix;
1124
1125        $packagesPath = $this->_packagesPath ? $this->_packagesPath:$this->_path;
1126
1127        // If this is a main class that either extends from Base or Package class
1128        if (isset($definition['is_main_class']) && $definition['is_main_class']) {
1129            // If is package then we need to put it in a package subfolder
1130            if (isset($definition['is_package']) && $definition['is_package']) {
1131                $writePath = $this->_path . DIRECTORY_SEPARATOR . $definition['package_name'];
1132            // Otherwise lets just put it in the root of the path
1133            } else {
1134                $writePath = $this->_path;
1135            }
1136
1137            if ($this->generateTableClasses()) {
1138                $this->writeTableDefinition($definition['tableClassName'], $writePath, array('extends' => $definition['inheritance']['tableExtends']));
1139            }
1140        }
1141        // If is the package class then we need to make the path to the complete package
1142        else if (isset($definition['is_package_class']) && $definition['is_package_class']) {
1143            if (isset($definition['package_custom_path'])) {
1144              $writePath = $definition['package_custom_path'];
1145            } else {
1146              $writePath = $packagesPath . DIRECTORY_SEPARATOR . $definition['package_path'];
1147            }
1148
1149            if ($this->generateTableClasses()) {
1150                $this->writeTableDefinition($definition['tableClassName'], $writePath, array('extends' => $definition['inheritance']['tableExtends']));
1151            }
1152        }
1153        // If it is the base class of the doctrine record definition
1154        else if (isset($definition['is_base_class']) && $definition['is_base_class']) {
1155            // If it is a part of a package then we need to put it in a package subfolder
1156            if (isset($definition['is_package']) && $definition['is_package']) {
1157                $basePath = $this->_path . DIRECTORY_SEPARATOR . $definition['package_name'];
1158                $writePath = $basePath . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory;
1159            // Otherwise lets just put it in the root generated folder
1160            } else {
1161                $writePath = $this->_path . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory;
1162            }
1163        }
1164
1165        // If we have a writePath from the if else conditionals above then use it
1166        if (isset($writePath)) {
1167            Doctrine_Lib::makeDirectories($writePath);
1168
1169            $writePath .= DIRECTORY_SEPARATOR . $fileName;
1170        // Otherwise none of the conditions were met and we aren't generating base classes
1171        } else {
1172            Doctrine_Lib::makeDirectories($this->_path);
1173
1174            $writePath = $this->_path . DIRECTORY_SEPARATOR . $fileName;
1175        }
1176
1177        $code = "<?php" . PHP_EOL;
1178
1179        if (isset($definition['connection']) && $definition['connection']) {
1180            $code .= "// Connection Component Binding" . PHP_EOL;
1181            $code .= "Doctrine_Manager::getInstance()->bindComponent('" . $definition['connectionClassName'] . "', '" . $definition['connection'] . "');" . PHP_EOL;
1182        }
1183
1184        $code .= PHP_EOL . $definitionCode;
1185
1186        if (isset($definition['generate_once']) && $definition['generate_once'] === true) {
1187            if ( ! file_exists($writePath)) {
1188                $bytes = file_put_contents($writePath, $code);
1189            }
1190        } else {
1191            $bytes = file_put_contents($writePath, $code);
1192        }
1193
1194        if (isset($bytes) && $bytes === false) {
1195            throw new Doctrine_Import_Builder_Exception("Couldn't write file " . $writePath);
1196        }
1197
1198        Doctrine::loadModel($definition['className'], $writePath);
1199    }
1200}
Note: See TracBrowser for help on using the browser.