Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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.doctrine-project.org>.
20
 */
21
 
22
/**
23
 * Doctrine_Record_Generator
24
 *
25
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
26
 * @package     Doctrine
27
 * @subpackage  Plugin
28
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
29
 * @version     $Revision$
30
 * @link        www.doctrine-project.org
31
 * @since       1.0
32
 */
33
abstract class Doctrine_Record_Generator extends Doctrine_Record_Abstract
34
{
35
    /**
36
     * _options
37
     *
38
     * @var array $_options     an array of plugin specific options
39
     */
40
    protected $_options = array(
41
        'generateFiles'  => false,
42
        'generatePath'   => false,
43
        'builderOptions' => array(),
44
        'identifier'     => false,
45
        'table'          => false,
46
        'pluginTable'    => false,
47
        'children'       => array(),
48
        'cascadeDelete'  => true,
49
        'appLevelDelete' => false
50
    );
51
 
52
    /**
53
     * Whether or not the generator has been initialized
54
     *
55
     * @var bool $_initialized
56
     */
57
    protected $_initialized = false;
58
 
59
    /**
60
     * An alias for getOption
61
     *
62
     * @param string $option
63
     */
64
    public function __get($option)
65
    {
66
        if (isset($this->_options[$option])) {
67
            return $this->_options[$option];
68
        }
69
        return null;
70
    }
71
 
72
    /**
73
     * __isset
74
     *
75
     * @param string $option
76
     */
77
    public function __isset($option)
78
    {
79
        return isset($this->_options[$option]);
80
    }
81
 
82
    /**
83
     * Returns the value of an option
84
     *
85
     * @param $option       the name of the option to retrieve
86
     * @return mixed        the value of the option
87
     */
88
    public function getOption($name)
89
    {
90
        if ( ! isset($this->_options[$name])) {
91
            throw new Doctrine_Exception('Unknown option ' . $name);
92
        }
93
 
94
        return $this->_options[$name];
95
    }
96
 
97
    /**
98
     * Sets given value to an option
99
     *
100
     * @param $option       the name of the option to be changed
101
     * @param $value        the value of the option
102
     * @return Doctrine_Plugin  this object
103
     */
104
    public function setOption($name, $value)
105
    {
106
        $this->_options[$name] = $value;
107
 
108
        return $this;
109
    }
110
 
111
    /**
112
     * Add child record generator
113
     *
114
     * @param  Doctrine_Record_Generator $generator
115
     * @return void
116
     */
117
    public function addChild($generator)
118
    {
119
        $this->_options['children'][] = $generator;
120
    }
121
 
122
    /**
123
     * Returns all options and their associated values
124
     *
125
     * @return array    all options as an associative array
126
     */
127
    public function getOptions()
128
    {
129
        return $this->_options;
130
    }
131
 
132
    /**
133
     * Initialize the plugin. Call in Doctrine_Template setTableDefinition() in order to initiate a generator in a template
134
     *
135
     * @see Doctrine_Template_I18n
136
     * @param  Doctrine_Table $table
137
     * @return void
138
     */
139
    public function initialize(Doctrine_Table $table)
140
    {
141
      	if ($this->_initialized) {
142
      	    return false;
143
      	}
144
 
145
        $this->_initialized = true;
146
 
147
        $this->initOptions();
148
 
149
        $table->addGenerator($this, get_class($this));
150
 
151
        $this->_options['table'] = $table;
152
 
153
        $ownerClassName = $this->_options['table']->getComponentName();
154
        $className = $this->_options['className'];
155
        $this->_options['className'] = str_replace('%CLASS%', $ownerClassName, $className);
156
 
157
        if (isset($this->_options['tableName'])) {
158
            $ownerTableName = $this->_options['table']->getTableName();
159
            $tableName = $this->_options['tableName'];
160
            $this->_options['tableName'] = str_replace('%TABLE%', $ownerTableName, $tableName);
161
        }
162
 
163
        // check that class doesn't exist (otherwise we cannot create it)
164
        if ($this->_options['generateFiles'] === false && class_exists($this->_options['className'])) {
165
            $this->_table = Doctrine_Core::getTable($this->_options['className']);
166
            return false;
167
        }
168
 
169
        $this->buildTable();
170
 
171
        $fk = $this->buildForeignKeys($this->_options['table']);
172
 
173
        $this->_table->setColumns($fk);
174
 
175
        $this->buildRelation();
176
 
177
        $this->setTableDefinition();
178
        $this->setUp();
179
 
180
        $this->generateClassFromTable($this->_table);
181
 
182
        $this->buildChildDefinitions();
183
 
184
        $this->_table->initIdentifier();
185
    }
186
 
187
    /**
188
     * Create the new Doctrine_Table instance in $this->_table based on the owning
189
     * table.
190
     *
191
     * @return void
192
     */
193
    public function buildTable()
194
    {
195
        // Bind model
196
        $conn = $this->_options['table']->getConnection();
197
        $bindConnName = $conn->getManager()->getConnectionForComponent($this->_options['table']->getComponentName())->getName();
198
        if ($bindConnName) {
199
            $conn->getManager()->bindComponent($this->_options['className'], $bindConnName);
200
        } else {
201
            $conn->getManager()->bindComponent($this->_options['className'], $conn->getName());
202
        }
203
 
204
        // Create table
205
        $tableClass = $conn->getAttribute(Doctrine_Core::ATTR_TABLE_CLASS);
206
        $this->_table = new $tableClass($this->_options['className'], $conn);
207
        $this->_table->setGenerator($this);
208
 
209
        // If custom table name set then lets use it
210
        if (isset($this->_options['tableName']) && $this->_options['tableName']) {
211
            $this->_table->setTableName($this->_options['tableName']);
212
        }
213
 
214
        // Maintain some options from the parent table
215
        $options = $this->_options['table']->getOptions();
216
 
217
        $newOptions = array();
218
        $maintain = array('type', 'collate', 'charset'); // This list may need updating
219
        foreach ($maintain as $key) {
220
            if (isset($options[$key])) {
221
                $newOptions[$key] = $options[$key];
222
            }
223
        }
224
 
225
        $this->_table->setOptions($newOptions);
226
 
227
        $conn->addTable($this->_table);
228
    }
229
 
230
    /**
231
     * Empty template method for providing the concrete plugins the ability
232
     * to initialize options before the actual definition is being built
233
     *
234
     * @return void
235
     */
236
    public function initOptions()
237
    {
238
 
239
    }
240
 
241
    /**
242
     * Build the child behavior definitions that are attached to this generator
243
     *
244
     * @return void
245
     */
246
    public function buildChildDefinitions()
247
    {
248
        if ( ! isset($this->_options['children'])) {
249
            throw new Doctrine_Record_Exception("Unknown option 'children'.");
250
        }
251
 
252
        foreach ($this->_options['children'] as $child) {
253
            if ($child instanceof Doctrine_Template) {
254
                if ($child->getPlugin() !== null) {
255
                    $this->_table->addGenerator($child->getPlugin(), get_class($child->getPlugin()));
256
                }
257
 
258
                $this->_table->addTemplate(get_class($child), $child);
259
 
260
                $child->setInvoker($this);
261
                $child->setTable($this->_table);
262
                $child->setTableDefinition();
263
                $child->setUp();
264
            } else {
265
                $this->_table->addGenerator($child, get_class($child));
266
                $child->initialize($this->_table);
267
            }
268
        }
269
    }
270
 
271
    /**
272
     * Generates foreign keys for the plugin table based on the owner table.
273
     * These columns are automatically added to the generated model so we can
274
     * create foreign keys back to the table object that owns the plugin.
275
     *
276
     * @param Doctrine_Table $table     the table object that owns the plugin
277
     * @return array                    an array of foreign key definitions
278
     */
279
    public function buildForeignKeys(Doctrine_Table $table)
280
    {
281
        $fk = array();
282
 
283
        foreach ((array) $table->getIdentifier() as $field) {
284
            $def = $table->getDefinitionOf($field);
285
 
286
            unset($def['autoincrement']);
287
            unset($def['sequence']);
288
            unset($def['primary']);
289
 
290
            $col = $table->hasColumn($field) ? $field : $table->getColumnName($field) . ' as ' . $field;
291
 
292
            $def['primary'] = true;
293
            $fk[$col] = $def;
294
        }
295
        return $fk;
296
    }
297
 
298
    /**
299
     * Build the local relationship on the generated model for this generator
300
     * instance which points to the invoking table in $this->_options['table']
301
     *
302
     * @param string $alias Alias of the foreign relation
303
     * @return void
304
     */
305
    public function buildLocalRelation($alias = null)
306
    {
307
        $options = array(
308
            'local'      => $this->getRelationLocalKey(),
309
            'foreign'    => $this->getRelationForeignKey(),
310
            'owningSide' => true
311
        );
312
 
313
        if (isset($this->_options['cascadeDelete']) && $this->_options['cascadeDelete'] && ! $this->_options['appLevelDelete']) {
314
            $options['onDelete'] = 'CASCADE';
315
            $options['onUpdate'] = 'CASCADE';
316
        }
317
 
318
        $aliasStr = '';
319
 
320
        if ($alias !== null) {
321
            $aliasStr = ' as ' . $alias;
322
        }
323
 
324
        $this->hasOne($this->_options['table']->getComponentName() . $aliasStr, $options);
325
    }
326
 
327
    /**
328
     * Add a Doctrine_Relation::MANY relationship to the generator owner table
329
     *
330
     * @param string $name
331
     * @param array $options
332
     * @return void
333
     */
334
    public function ownerHasMany($name, $options)
335
    {
336
        $this->_options['table']->hasMany($name, $options);
337
    }
338
 
339
    /**
340
     * Add a Doctrine_Relation::ONE relationship to the generator owner table
341
     *
342
     * @param string $name
343
     * @param array $options
344
     * @return void
345
     */
346
    public function ownerHasOne($name, $options)
347
    {
348
        $this->_options['table']->hasOne($name, $options);
349
    }
350
 
351
    /**
352
     * Build the foreign relationship on the invoking table in $this->_options['table']
353
     * which points back to the model generated in this generator instance.
354
     *
355
     * @param string $alias Alias of the foreign relation
356
     * @return void
357
     */
358
    public function buildForeignRelation($alias = null)
359
    {
360
        $options = array(
361
            'local'    => $this->getRelationForeignKey(),
362
            'foreign'  => $this->getRelationLocalKey(),
363
            'localKey' => false
364
        );
365
 
366
        if (isset($this->_options['cascadeDelete']) && $this->_options['cascadeDelete'] && $this->_options['appLevelDelete']) {
367
            $options['cascade'] = array('delete');
368
        }
369
 
370
        $aliasStr = '';
371
 
372
        if ($alias !== null) {
373
            $aliasStr = ' as ' . $alias;
374
        }
375
 
376
        $this->ownerHasMany($this->_table->getComponentName() . $aliasStr, $options);
377
    }
378
 
379
    /**
380
     * Get the local key of the generated relationship
381
     *
382
     * @return string $local
383
     */
384
    public function getRelationLocalKey()
385
    {
386
        return $this->getRelationForeignKey();
387
    }
388
 
389
    /**
390
     * Get the foreign key of the generated relationship
391
     *
392
     * @return string $foreign
393
     */
394
    public function getRelationForeignKey()
395
    {
396
        $table = $this->_options['table'];
397
        $identifier = $table->getIdentifier();
398
 
399
        foreach ((array) $identifier as $column) {
400
            $def = $table->getDefinitionOf($column);
401
            if (isset($def['primary']) && $def['primary'] && isset($def['autoincrement']) && $def['autoincrement']) {
402
                return $column;
403
            }
404
        }
405
 
406
        return $identifier;
407
    }
408
 
409
    /**
410
     * This method can be used for generating the relation from the plugin
411
     * table to the owner table. By default buildForeignRelation() and buildLocalRelation() are called
412
     * Those methods can be overridden or this entire method can be overridden
413
     *
414
     * @return void
415
     */
416
    public function buildRelation()
417
    {
418
        $this->buildForeignRelation();
419
        $this->buildLocalRelation();
420
    }
421
 
422
    /**
423
     * Generate a Doctrine_Record from a populated Doctrine_Table instance
424
     *
425
     * @param Doctrine_Table $table
426
     * @return void
427
     */
428
    public function generateClassFromTable(Doctrine_Table $table)
429
    {
430
        $definition = array();
431
        $definition['columns'] = $table->getColumns();
432
        $definition['tableName'] = $table->getTableName();
433
        $definition['actAs'] = $table->getTemplates();
434
 
435
        return $this->generateClass($definition);
436
    }
437
 
438
    /**
439
     * Generates the class definition for plugin class
440
     *
441
     * @param array $definition  Definition array defining columns, relations and options
442
     *                           for the model
443
     * @return void
444
     */
445
    public function generateClass(array $definition = array())
446
    {
447
        $definition['className'] = $this->_options['className'];
448
        $definition['toString'] = isset($this->_options['toString']) ? $this->_options['toString'] : false;
449
        if (isset($this->_options['listeners'])) {
450
            $definition['listeners'] = $this->_options['listeners'];
451
        }
452
 
453
        $builder = new Doctrine_Import_Builder();
454
        $builderOptions = isset($this->_options['builderOptions']) ? (array) $this->_options['builderOptions']:array();
455
        $builder->setOptions($builderOptions);
456
 
457
        if ($this->_options['generateFiles']) {
458
            if (isset($this->_options['generatePath']) && $this->_options['generatePath']) {
459
                $builder->setTargetPath($this->_options['generatePath']);
460
                $builder->buildRecord($definition);
461
            } else {
462
                throw new Doctrine_Record_Exception('If you wish to generate files then you must specify the path to generate the files in.');
463
            }
464
        } else {
465
            $def = $builder->buildDefinition($definition);
466
 
467
            eval($def);
468
        }
469
    }
470
}