Coverage for Doctrine_Import_Builder

Back to coverage report

1 <?php
2 /*
3  *  $Id: Builder.php 2777 2007-10-08 22:53:28Z Jonathan.Wage $
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.com>.
20  */
21
22 /**
23  * Doctrine_Import_Builder
24  * Import builder is responsible of building Doctrine ActiveRecord classes
25  * based on a database schema.
26  *
27  * @package     Doctrine
28  * @subpackage  Import
29  * @link        www.phpdoctrine.com
30  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
31  * @since       1.0
32  * @version     $Revision: 2777 $
33  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
34  * @author      Jukka Hassinen <Jukka.Hassinen@BrainAlliance.com>
35  * @author      Nicolas BĂ©rard-Nault <nicobn@php.net>
36  */
37 class Doctrine_Import_Builder
38 {
39     /**
40      * @var string $path    the path where imported files are being generated
41      */
42     private $path = '';
43
44     private $suffix = '.class.php';
45     
46     private $generateBaseClasses = false;
47     
48     private $baseClassesDirectory = 'generated';
49     
50     private static $tpl;
51     
52     public function __construct()
53     {
54         $this->loadTemplate();
55     }
56
57     /**
58      * setTargetPath
59      *
60      * @param string path   the path where imported files are being generated
61      * @return
62      */
63     public function setTargetPath($path)
64     {
65         if ( ! file_exists($path)) {
66             mkdir($path, 0777);
67         }
68
69         $this->path = $path;
70     }
71     
72     /**
73      * generateBaseClasses
74      *
75      * Specify whether or not to generate classes which extend from generated base classes
76      *
77      * @param string $bool 
78      * @return void
79      * @author Jonathan H. Wage
80      */
81     public function generateBaseClasses($bool = null)
82     {
83       if ($bool !== null) {
84         $this->generateBaseClasses = $bool;
85       }
86       
87       return $this->generateBaseClasses;
88     }
89     
90     /**
91      * getTargetPath
92      *
93      * @return string       the path where imported files are being generated
94      */
95     public function getTargetPath()
96     {
97         return $this->path;
98     }
99
100     /**
101      * This is a template that was previously in Builder/Record.tpl. Due to the fact
102      * that it was not bundled when compiling, it had to be moved here.
103      *
104      * @return void
105      */
106     public function loadTemplate() 
107     {
108         if (isset(self::$tpl)) {
109             return;
110         }
111
112         self::$tpl =<<<END
113 /**
114  * This class has been auto-generated by the Doctrine ORM Framework
115  */
116 %sclass %s extends %s
117 {
118 %s
119 %s
120 }
121 END;
122
123     }
124
125     /*
126      * Build the table definition of a Doctrine_Record object
127      *
128      * @param  string $table
129      * @param  array  $tableColumns
130      */
131     public function buildTableDefinition(array $options, array $columns, array $relations)
132     {
133         $ret = array();
134         
135         $i = 0;
136         
137         if (isset($options['inheritance']['extends']) && !isset($options['override_parent'])) {
138             $ret[$i] = "\t\t\t\tparent::setTableDefinition();";
139             $i++;
140         }
141         
142         if (isset($options['tableName']) && !empty($options['tableName'])) {
143             $ret[$i] = str_repeat(' ', 8) . '$this->setTableName(\''. $options['tableName'].'\');';
144             
145             $i++;
146         }
147         
148         foreach ($columns as $name => $column) {
149             $ret[$i] = '        $this->hasColumn(\'' . $name . '\', \'' . $column['type'] . '\'';
150             
151             if ($column['length']) {
152                 $ret[$i] .= ', ' . $column['length'];
153             } else {
154                 $ret[$i] .= ', null';
155             }
156
157             $a = array();
158
159             if (isset($column['default']) && $column['default']) {
160                 $a[] = '\'default\' => ' . var_export($column['default'], true);
161             }
162             if (isset($column['notnull']) && $column['notnull']) {
163                 $a[] = '\'notnull\' => true';
164             }
165             if (isset($column['primary']) && $column['primary']) {
166                 $a[] = '\'primary\' => true';
167             }
168             if ((isset($column['autoinc']) && $column['autoinc']) || isset($column['autoincrement']) && $column['autoincrement']) {
169                 $a[] = '\'autoincrement\' => true';
170             }
171             if (isset($column['unique']) && $column['unique']) {
172                 $a[] = '\'unique\' => true';
173             }
174             if (isset($column['unsigned']) && $column['unsigned']) {
175                 $a[] = '\'unsigned\' => true';
176             }
177             if ($column['type'] == 'enum' && isset($column['values']) ) {
178                 $a[] = '\'values\' => array(\'' . implode('\',\'', $column['values']) . '\')';
179             }
180
181             if ( ! empty($a)) {
182                 $ret[$i] .= ', ' . 'array(';
183                 $length = strlen($ret[$i]);
184                 $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')';
185             }
186             
187             $ret[$i] .= ');';
188
189             if ($i < (count($columns) - 1)) {
190                 $ret[$i] .= PHP_EOL;
191             }
192             $i++;
193         }
194         
195         if (!empty($ret)) {
196           return "\n\tpublic function setTableDefinition()"."\n\t{\n".implode("\n", $ret)."\n\t}";
197         }
198     }
199     public function buildSetUp(array $options, array $columns, array $relations)
200     {
201         $ret = array();
202         
203         $i = 0;
204         
205         if (isset($options['inheritance']['extends']) && !isset($options['override_parent'])) {
206             $ret[$i] = "\t\t\t\tparent::setUp();";
207             $i++;
208         }
209         
210         foreach ($relations as $name => $relation) {
211             $class = isset($relation['class']) ? $relation['class']:$name;
212             $alias = (isset($relation['alias']) && $relation['alias'] !== $relation['class']) ? ' as ' . $relation['alias'] : '';
213
214             if ( ! isset($relation['type'])) {
215                 $relation['type'] = Doctrine_Relation::ONE;
216             }
217
218             if ($relation['type'] === Doctrine_Relation::ONE || 
219                 $relation['type'] === Doctrine_Relation::ONE_COMPOSITE) {
220                 $ret[$i] = '        $this->hasOne(\'' . $class . $alias . '\'';
221             } else {
222                 $ret[$i] = '        $this->hasMany(\'' . $class . $alias . '\'';
223             }
224             
225             $a = array();
226
227             if (isset($relation['refClass'])) {
228                 $a[] = '\'refClass\' => ' . var_export($relation['refClass'], true);
229             }
230             
231             if (isset($relation['deferred']) && $relation['deferred']) {
232                 $a[] = '\'default\' => ' . var_export($relation['deferred'], true);
233             }
234             
235             if (isset($relation['local']) && $relation['local']) {
236                 $a[] = '\'local\' => ' . var_export($relation['local'], true);
237             }
238             
239             if (isset($relation['foreign']) && $relation['foreign']) {
240                 $a[] = '\'foreign\' => ' . var_export($relation['foreign'], true);
241             }
242             
243             if (isset($relation['onDelete']) && $relation['onDelete']) {
244                 $a[] = '\'onDelete\' => ' . var_export($relation['onDelete'], true);
245             }
246             
247             if (isset($relation['onUpdate']) && $relation['onUpdate']) {
248                 $a[] = '\'onUpdate\' => ' . var_export($relation['onUpdate'], true);
249             }
250             
251             if ( ! empty($a)) {
252                 $ret[$i] .= ', ' . 'array(';
253                 $length = strlen($ret[$i]);
254                 $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')';
255             }
256             
257             $ret[$i] .= ');';
258             $i++;
259         }
260         
261         if (isset($options['inheritance']['keyField']) && isset($options['inheritance']['keyValue'])) {        
262             $i++;
263             $ret[$i] = "\t\t".'$this->setInheritanceMap(array(\''.$options['inheritance']['keyField'].'\' => '.$options['inheritance']['keyValue'].'));';
264         }
265         
266         if (!empty($ret)) {
267           return "\n\tpublic function setUp()\n\t{\n".implode("\n", $ret)."\n\t}";
268         }
269     }
270     
271     public function buildDefinition(array $options, array $columns, array $relations = array())
272     {
273         if ( ! isset($options['className'])) {
274             throw new Doctrine_Import_Builder_Exception('Missing class name.');
275         }
276         
277         $abstract = isset($options['abstract']) ? 'abstract ':null;        
278         $className = $options['className'];
279         $extends = isset($options['inheritance']['extends']) ? $options['inheritance']['extends']:'Doctrine_Record';
280         $definition = !isset($options['no_definition']) ? $this->buildTableDefinition($options, $columns, $relations):null;
281         $setUp = !isset($options['no_definition']) ? $this->buildSetUp($options, $columns, $relations):null;
282         
283         $content = sprintf(self::$tpl, $abstract,
284                                        $className,
285                                        $extends,
286                                        $definition,
287                                        $setUp);
288         
289         return $content;
290     }
291
292     public function buildRecord(array $options, array $columns, array $relations = array())
293     {
294         if ( !isset($options['className'])) {
295             throw new Doctrine_Import_Builder_Exception('Missing class name.');
296         }
297
298         if ( !isset($options['fileName'])) {
299             if (empty($this->path)) {
300                 throw new Doctrine_Import_Builder_Exception('No build target directory set.');
301             }
302             
303
304             if (is_writable($this->path) === false) {
305                 throw new Doctrine_Import_Builder_Exception('Build target directory ' . $this->path . ' is not writable.');
306             }
307
308             $options['fileName']  = $this->path . DIRECTORY_SEPARATOR . $options['className'] . $this->suffix;
309         }
310         
311         if ($this->generateBaseClasses()) {
312           
313           // We only want to generate this one if it doesn't already exist
314           if (!file_exists($options['fileName'])) {
315             $optionsBak = $options;
316             
317             unset($options['tableName']);
318             $options['inheritance']['extends'] = 'Base' . $options['className'];
319             $options['requires'] = array($this->baseClassesDirectory . DIRECTORY_SEPARATOR  . $options['inheritance']['extends'] . $this->suffix);
320             $options['no_definition'] = true;
321             
322             $this->writeDefinition($options, array(), array());
323             
324             $options = $optionsBak;
325           }
326           
327           $generatedPath = $this->path . DIRECTORY_SEPARATOR . $this->baseClassesDirectory;
328           
329           if (!file_exists($generatedPath)) {
330             mkdir($generatedPath);
331           }
332           
333           $options['className'] = 'Base' . $options['className'];
334           $options['abstract'] = true;
335           $options['fileName']  = $generatedPath . DIRECTORY_SEPARATOR . $options['className'] . $this->suffix;
336           
337           $this->writeDefinition($options, $columns, $relations);
338         } else {
339           $this->writeDefinition($options, $columns, $relations);
340         }
341     }
342     
343     public function writeDefinition(array $options, array $columns, array $relations = array())
344     {
345       $content = $this->buildDefinition($options, $columns, $relations);
346       
347       $code = "<?php\n";
348       
349       if (isset($options['requires'])) {
350           if (!is_array($options['requires'])) {
351               $options['requires'] = array($options['requires']);
352           }
353           
354           foreach ($options['requires'] as $require) {
355               $code .= "require_once('".$require."');";
356           }
357       }
358       
359       $code .= PHP_EOL . $content;
360       
361       $bytes = file_put_contents($options['fileName'], $code);
362
363       if ($bytes === false) {
364           throw new Doctrine_Import_Builder_Exception("Couldn't write file " . $options['fileName']);
365       }
366     }
367 }