~patrix-sbs/oraculum/git

« back to all changes in this revision

Viewing changes to library/components/doctrine/lib/Doctrine/Table.php

  • Committer: Patrick Kaminski
  • Date: 2009-09-02 02:33:07 UTC
  • Revision ID: git-v1:943803254fca67bfb4c0374422b1b836b14dc518
Tags: v0.1a
Sending Oraculum Framework v0.1 alpha

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/*
 
3
 *  $Id: Table.php 5608 2009-03-17 18:00:18Z jwage $
 
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_Table   represents a database table
 
24
 *                  each Doctrine_Table holds the information of foreignKeys and associations
 
25
 *
 
26
 *
 
27
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 
28
 * @package     Doctrine
 
29
 * @subpackage  Table
 
30
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 
31
 * @version     $Revision: 5608 $
 
32
 * @link        www.phpdoctrine.org
 
33
 * @since       1.0
 
34
 */
 
35
class Doctrine_Table extends Doctrine_Configurable implements Countable
 
36
{
 
37
    /**
 
38
     * @var array $data                                 temporary data which is then loaded into Doctrine_Record::$_data
 
39
     */
 
40
    protected $_data             = array();
 
41
 
 
42
    /**
 
43
     * @var mixed $identifier   The field names of all fields that are part of the identifier/primary key
 
44
     */
 
45
    protected $_identifier = array();
 
46
 
 
47
    /**
 
48
     * @see Doctrine_Identifier constants
 
49
     * @var integer $identifierType                     the type of identifier this table uses
 
50
     */
 
51
    protected $_identifierType;
 
52
 
 
53
    /**
 
54
     * @var Doctrine_Connection $conn                   Doctrine_Connection object that created this table
 
55
     */
 
56
    protected $_conn;
 
57
 
 
58
    /**
 
59
     * @var array $identityMap                          first level cache
 
60
     */
 
61
    protected $_identityMap        = array();
 
62
 
 
63
    /**
 
64
     * @var Doctrine_Table_Repository $repository       record repository
 
65
     */
 
66
    protected $_repository;
 
67
 
 
68
    /**
 
69
     * @var array $columns                  an array of column definitions,
 
70
     *                                      keys are column names and values are column definitions
 
71
     *
 
72
     *                                      the definition array has atleast the following values:
 
73
     *
 
74
     *                                      -- type         the column type, eg. 'integer'
 
75
     *                                      -- length       the column length, eg. 11
 
76
     *
 
77
     *                                      additional keys:
 
78
     *                                      -- notnull      whether or not the column is marked as notnull
 
79
     *                                      -- values       enum values
 
80
     *                                      -- notblank     notblank validator + notnull constraint
 
81
     *                                      ... many more
 
82
     */
 
83
    protected $_columns          = array();
 
84
 
 
85
    /**
 
86
     * @var array $_fieldNames            an array of field names. used to look up field names
 
87
     *                                    from column names.
 
88
     *                                    keys are column names and values are field names
 
89
     */
 
90
    protected $_fieldNames    = array();
 
91
 
 
92
    /**
 
93
     *
 
94
     * @var array $_columnNames             an array of column names
 
95
     *                                      keys are field names and values column names.
 
96
     *                                      used to look up column names from field names.
 
97
     *                                      this is the reverse lookup map of $_fieldNames.
 
98
     */
 
99
    protected $_columnNames = array();
 
100
 
 
101
    /**
 
102
     * @var integer $columnCount            cached column count, Doctrine_Record uses this column count in when
 
103
     *                                      determining its state
 
104
     */
 
105
    protected $columnCount;
 
106
 
 
107
    /**
 
108
     * @var boolean $hasDefaultValues       whether or not this table has default values
 
109
     */
 
110
    protected $hasDefaultValues;
 
111
 
 
112
    /**
 
113
     * @var array $options                  an array containing all options
 
114
     *
 
115
     *      -- name                         name of the component, for example component name of the GroupTable is 'Group'
 
116
     *
 
117
     *      -- parents                      the parent classes of this component
 
118
     *
 
119
     *      -- declaringClass               name of the table definition declaring class (when using inheritance the class
 
120
     *                                      that defines the table structure can be any class in the inheritance hierarchy,
 
121
     *                                      hence we need reflection to check out which class actually calls setTableDefinition)
 
122
     *
 
123
     *      -- tableName                    database table name, in most cases this is the same as component name but in some cases
 
124
     *                                      where one-table-multi-class inheritance is used this will be the name of the inherited table
 
125
     *
 
126
     *      -- sequenceName                 Some databases need sequences instead of auto incrementation primary keys,
 
127
     *                                      you can set specific sequence for your table by calling setOption('sequenceName', $seqName)
 
128
     *                                      where $seqName is the name of the desired sequence
 
129
     *
 
130
     *      -- enumMap                      enum value arrays
 
131
     *
 
132
     *      -- inheritanceMap               inheritanceMap is used for inheritance mapping, keys representing columns and values
 
133
     *                                      the column values that should correspond to child classes
 
134
     *
 
135
     *      -- type                         table type (mysql example: INNODB)
 
136
     *
 
137
     *      -- charset                      character set
 
138
     *
 
139
     *      -- foreignKeys                  the foreign keys of this table
 
140
     *
 
141
     *      -- checks                       the check constraints of this table, eg. 'price > dicounted_price'
 
142
     *
 
143
     *      -- collate                      collate attribute
 
144
     *
 
145
     *      -- indexes                      the index definitions of this table
 
146
     *
 
147
     *      -- treeImpl                     the tree implementation of this table (if any)
 
148
     *
 
149
     *      -- treeOptions                  the tree options
 
150
     *
 
151
     *      -- queryParts                   the bound query parts
 
152
     *
 
153
     *      -- versioning
 
154
     */
 
155
    protected $_options      = array('name'           => null,
 
156
                                     'tableName'      => null,
 
157
                                     'sequenceName'   => null,
 
158
                                     'inheritanceMap' => array(),
 
159
                                     'enumMap'        => array(),
 
160
                                     'type'           => null,
 
161
                                     'charset'        => null,
 
162
                                     'collate'        => null,
 
163
                                     'treeImpl'       => null,
 
164
                                     'treeOptions'    => array(),
 
165
                                     'indexes'        => array(),
 
166
                                     'parents'        => array(),
 
167
                                     'joinedParents'  => array(),
 
168
                                     'queryParts'     => array(),
 
169
                                     'versioning'     => null,
 
170
                                     'subclasses'     => array(),
 
171
                                     );
 
172
 
 
173
    /**
 
174
     * @var Doctrine_Tree $tree                 tree object associated with this table
 
175
     */
 
176
    protected $_tree;
 
177
 
 
178
    /**
 
179
     * @var Doctrine_Relation_Parser $_parser   relation parser object
 
180
     */
 
181
    protected $_parser;
 
182
 
 
183
    /**
 
184
     * @see Doctrine_Template
 
185
     * @var array $_templates                   an array containing all templates attached to this table
 
186
     */
 
187
    protected $_templates   = array();
 
188
 
 
189
    /**
 
190
     * @see Doctrine_Record_Filter
 
191
     * @var array $_filters                     an array containing all record filters attached to this table
 
192
     */
 
193
    protected $_filters     = array();
 
194
 
 
195
    /**
 
196
     * @see Doctrine_Record_Generator
 
197
     * @var array $_generators                  an array containing all generators attached to this table
 
198
     */
 
199
    protected $_generators     = array();
 
200
 
 
201
    /**
 
202
     * @var array $_invokedMethods              method invoker cache
 
203
     */
 
204
    protected $_invokedMethods = array();
 
205
 
 
206
    /**
 
207
     * @var Doctrine_Record $record             empty instance of the given model
 
208
     */
 
209
    protected $record;
 
210
 
 
211
    /**
 
212
     * the constructor
 
213
     *
 
214
     * @throws Doctrine_Connection_Exception    if there are no opened connections
 
215
     * @param string $name                      the name of the component
 
216
     * @param Doctrine_Connection $conn         the connection associated with this table
 
217
     */
 
218
    public function __construct($name, Doctrine_Connection $conn, $initDefinition = false)
 
219
    {
 
220
        $this->_conn = $conn;
 
221
 
 
222
        $this->setParent($this->_conn);
 
223
 
 
224
        $this->_options['name'] = $name;
 
225
        $this->_parser = new Doctrine_Relation_Parser($this);
 
226
 
 
227
        if ($initDefinition) {
 
228
            $this->record = $this->initDefinition();
 
229
 
 
230
            $this->initIdentifier();
 
231
 
 
232
            $this->record->setUp();
 
233
 
 
234
            // if tree, set up tree
 
235
            if ($this->isTree()) {
 
236
                $this->getTree()->setUp();
 
237
            }
 
238
        } else {
 
239
            if ( ! isset($this->_options['tableName'])) {
 
240
                $this->setTableName(Doctrine_Inflector::tableize($this->_options['name']));
 
241
            }
 
242
        }
 
243
        $this->_filters[]  = new Doctrine_Record_Filter_Standard();
 
244
        $this->_repository = new Doctrine_Table_Repository($this);
 
245
        
 
246
        $this->construct();
 
247
    }
 
248
    
 
249
    /**
 
250
     * construct
 
251
     * Empty template method to provide concrete Table classes with the possibility
 
252
     * to hook into the constructor procedure
 
253
     *
 
254
     * @return void
 
255
     */
 
256
    public function construct()
 
257
    { }
 
258
 
 
259
    /**
 
260
     * Initializes the in-memory table definition.
 
261
     *
 
262
     * @param string $name
 
263
     */
 
264
    public function initDefinition()
 
265
    {
 
266
        $name = $this->_options['name'];
 
267
        if ( ! class_exists($name) || empty($name)) {
 
268
            throw new Doctrine_Exception("Couldn't find class " . $name);
 
269
        }
 
270
        $record = new $name($this);
 
271
 
 
272
        $names = array();
 
273
 
 
274
        $class = $name;
 
275
 
 
276
        // get parent classes
 
277
 
 
278
        do {
 
279
            if ($class === 'Doctrine_Record') {
 
280
                break;
 
281
            }
 
282
 
 
283
            $name = $class;
 
284
            $names[] = $name;
 
285
        } while ($class = get_parent_class($class));
 
286
 
 
287
        if ($class === false) {
 
288
            throw new Doctrine_Table_Exception('Class "' . $name . '" must be a child class of Doctrine_Record');
 
289
        }
 
290
 
 
291
        // reverse names
 
292
        $names = array_reverse($names);
 
293
        // save parents
 
294
        array_pop($names);
 
295
        $this->_options['parents'] = $names;
 
296
 
 
297
        // create database table
 
298
        if (method_exists($record, 'setTableDefinition')) {
 
299
            $record->setTableDefinition();
 
300
            // get the declaring class of setTableDefinition method
 
301
            $method = new ReflectionMethod($this->_options['name'], 'setTableDefinition');
 
302
            $class = $method->getDeclaringClass();
 
303
 
 
304
        } else {
 
305
            $class = new ReflectionClass($class);
 
306
        }
 
307
 
 
308
        $this->_options['joinedParents'] = array();
 
309
 
 
310
        foreach (array_reverse($this->_options['parents']) as $parent) {
 
311
 
 
312
            if ($parent === $class->getName()) {
 
313
                continue;
 
314
            }
 
315
            $ref = new ReflectionClass($parent);
 
316
 
 
317
            if ($ref->isAbstract() || ! $class->isSubClassOf($parent)) {
 
318
                continue;
 
319
            }
 
320
            $parentTable = $this->_conn->getTable($parent);
 
321
 
 
322
            $found = false;
 
323
            $parentColumns = $parentTable->getColumns();
 
324
 
 
325
            foreach ($parentColumns as $columnName => $definition) {
 
326
                if ( ! isset($definition['primary']) || $definition['primary'] === false) {
 
327
                    if (isset($this->_columns[$columnName])) {
 
328
                        $found = true;
 
329
                        break;
 
330
                    } else {
 
331
                        if ( ! isset($parentColumns[$columnName]['owner'])) {
 
332
                            $parentColumns[$columnName]['owner'] = $parentTable->getComponentName();
 
333
                        }
 
334
 
 
335
                        $this->_options['joinedParents'][] = $parentColumns[$columnName]['owner'];
 
336
                    }
 
337
                } else {
 
338
                    unset($parentColumns[$columnName]);
 
339
                }
 
340
            }
 
341
 
 
342
            if ($found) {
 
343
                continue;
 
344
            }
 
345
 
 
346
            foreach ($parentColumns as $columnName => $definition) {
 
347
                $fullName = $columnName . ' as ' . $parentTable->getFieldName($columnName);
 
348
                $this->setColumn($fullName, $definition['type'], $definition['length'], $definition, true);
 
349
            }
 
350
 
 
351
            break;
 
352
        }
 
353
 
 
354
        $this->_options['joinedParents'] = array_values(array_unique($this->_options['joinedParents']));
 
355
 
 
356
        $this->_options['declaringClass'] = $class;
 
357
 
 
358
        // set the table definition for the given tree implementation
 
359
        if ($this->isTree()) {
 
360
            $this->getTree()->setTableDefinition();
 
361
        }
 
362
 
 
363
        $this->columnCount = count($this->_columns);
 
364
 
 
365
        if ( ! isset($this->_options['tableName'])) {
 
366
            $this->setTableName(Doctrine_Inflector::tableize($class->getName()));
 
367
        }
 
368
 
 
369
        return $record;
 
370
    }
 
371
 
 
372
    /**
 
373
     * Initializes the table identifier(s)/primary key(s)
 
374
     *
 
375
     * @return void
 
376
     */
 
377
    public function initIdentifier()
 
378
    {
 
379
        switch (count($this->_identifier)) {
 
380
            case 0:
 
381
                if ( ! empty($this->_options['joinedParents'])) {
 
382
                    $root = current($this->_options['joinedParents']);
 
383
 
 
384
                    $table = $this->_conn->getTable($root);
 
385
 
 
386
                    $this->_identifier = $table->getIdentifier();
 
387
 
 
388
                    $this->_identifierType = ($table->getIdentifierType() !== Doctrine::IDENTIFIER_AUTOINC)
 
389
                                            ? $table->getIdentifierType() : Doctrine::IDENTIFIER_NATURAL;
 
390
 
 
391
                    // add all inherited primary keys
 
392
                    foreach ((array) $this->_identifier as $id) {
 
393
                        $definition = $table->getDefinitionOf($id);
 
394
 
 
395
                        // inherited primary keys shouldn't contain autoinc
 
396
                        // and sequence definitions
 
397
                        unset($definition['autoincrement']);
 
398
                        unset($definition['sequence']);
 
399
 
 
400
                        // add the inherited primary key column
 
401
                        $fullName = $id . ' as ' . $table->getFieldName($id);
 
402
                        $this->setColumn($fullName, $definition['type'], $definition['length'],
 
403
                                $definition, true);
 
404
                    }
 
405
                } else {
 
406
                    $definition = array('type' => 'integer',
 
407
                                        'length' => 20,
 
408
                                        'autoincrement' => true,
 
409
                                        'primary' => true);
 
410
                    $this->setColumn('id', $definition['type'], $definition['length'], $definition, true);
 
411
                    $this->_identifier = 'id';
 
412
                    $this->_identifierType = Doctrine::IDENTIFIER_AUTOINC;
 
413
                }
 
414
                $this->columnCount++;
 
415
                break;
 
416
            case 1:
 
417
                foreach ($this->_identifier as $pk) {
 
418
                    $e = $this->getDefinitionOf($pk);
 
419
 
 
420
                    $found = false;
 
421
 
 
422
                    foreach ($e as $option => $value) {
 
423
                        if ($found) {
 
424
                            break;
 
425
                        }
 
426
 
 
427
                        $e2 = explode(':', $option);
 
428
 
 
429
                        switch (strtolower($e2[0])) {
 
430
                            case 'autoincrement':
 
431
                            case 'autoinc':
 
432
                                if ($value !== false) {
 
433
                                    $this->_identifierType = Doctrine::IDENTIFIER_AUTOINC;
 
434
                                    $found = true;
 
435
                                }
 
436
                                break;
 
437
                            case 'seq':
 
438
                            case 'sequence':
 
439
                                $this->_identifierType = Doctrine::IDENTIFIER_SEQUENCE;
 
440
                                $found = true;
 
441
 
 
442
                                if (is_string($value)) {
 
443
                                    $this->_options['sequenceName'] = $value;
 
444
                                } else {
 
445
                                    if (($sequence = $this->getAttribute(Doctrine::ATTR_DEFAULT_SEQUENCE)) !== null) {
 
446
                                        $this->_options['sequenceName'] = $sequence;
 
447
                                    } else {
 
448
                                        $this->_options['sequenceName'] = $this->_conn->formatter->getSequenceName($this->_options['tableName']);
 
449
                                    }
 
450
                                }
 
451
                                break;
 
452
                        }
 
453
                    }
 
454
                    if ( ! isset($this->_identifierType)) {
 
455
                        $this->_identifierType = Doctrine::IDENTIFIER_NATURAL;
 
456
                    }
 
457
                }
 
458
 
 
459
                $this->_identifier = $pk;
 
460
 
 
461
                break;
 
462
            default:
 
463
                $this->_identifierType = Doctrine::IDENTIFIER_COMPOSITE;
 
464
        }
 
465
    }
 
466
 
 
467
    /**
 
468
     * Gets the owner of a column.
 
469
     * The owner of a column is the name of the component in a hierarchy that
 
470
     * defines the column.
 
471
     *
 
472
     * @param string $columnName   The column name
 
473
     * @return string  The name of the owning/defining component
 
474
     */
 
475
    public function getColumnOwner($columnName)
 
476
    {
 
477
        if (isset($this->_columns[$columnName]['owner'])) {
 
478
            return $this->_columns[$columnName]['owner'];
 
479
        } else {
 
480
            return $this->getComponentName();
 
481
        }
 
482
    }
 
483
 
 
484
    /**
 
485
     * Gets the record instance for this table. The Doctrine_Table instance always holds at least one
 
486
     * instance of a model so that it can be reused for several things, but primarily it is first
 
487
     * used to instantiate all the internal in memory meta data
 
488
     *
 
489
     * @return object  Empty instance of the record
 
490
     */
 
491
    public function getRecordInstance()
 
492
    {
 
493
        if ( ! $this->record) {
 
494
            $this->record = new $this->_options['name'];
 
495
        }
 
496
        return $this->record;
 
497
    }
 
498
 
 
499
    /**
 
500
     * Checks whether a column is inherited from a component further up in the hierarchy.
 
501
     *
 
502
     * @param $columnName  The column name
 
503
     * @return boolean  TRUE if column is inherited, FALSE otherwise.
 
504
     */
 
505
    public function isInheritedColumn($columnName)
 
506
    {
 
507
        return (isset($this->_columns[$columnName]['owner']));
 
508
    }
 
509
 
 
510
    /**
 
511
     * Checks whether a field is part of the table identifier/primary key field(s).
 
512
     *
 
513
     * @param string $fieldName  The field name
 
514
     * @return boolean  TRUE if the field is part of the table identifier/primary key field(s),
 
515
     *                  FALSE otherwise.
 
516
     */
 
517
    public function isIdentifier($fieldName)
 
518
    {
 
519
        return ($fieldName === $this->getIdentifier() ||
 
520
                in_array($fieldName, (array) $this->getIdentifier()));
 
521
    }
 
522
 
 
523
    /**
 
524
     * Checks whether a field identifier is of type autoincrement
 
525
     *
 
526
     * @return boolean TRUE  if the identifier is autoincrement
 
527
     *                 FALSE otherwise
 
528
     */
 
529
    public function isIdentifierAutoincrement()
 
530
    {
 
531
        return $this->getIdentifierType() === Doctrine::IDENTIFIER_AUTOINC;
 
532
    }
 
533
 
 
534
    /**
 
535
     * Checks whether a field identifier is a composite key.
 
536
     *
 
537
     * @return boolean TRUE  if the identifier is a composite key,
 
538
     *                 FALSE otherwise
 
539
     */
 
540
    public function isIdentifierComposite()
 
541
    {
 
542
        return $this->getIdentifierType() === Doctrine::IDENTIFIER_COMPOSITE;
 
543
    }
 
544
 
 
545
    /**
 
546
     * getMethodOwner
 
547
     *
 
548
     * @param string $method
 
549
     * @return void
 
550
     */
 
551
    public function getMethodOwner($method)
 
552
    {
 
553
        return (isset($this->_invokedMethods[$method])) ?
 
554
                      $this->_invokedMethods[$method] : false;
 
555
    }
 
556
 
 
557
    /**
 
558
     * setMethodOwner
 
559
     *
 
560
     * @param string $method
 
561
     * @param string $class
 
562
     */
 
563
    public function setMethodOwner($method, $class)
 
564
    {
 
565
        $this->_invokedMethods[$method] = $class;
 
566
    }
 
567
 
 
568
    /**
 
569
     * export
 
570
     * exports this table to database based on column and option definitions
 
571
     *
 
572
     * @throws Doctrine_Connection_Exception    if some error other than Doctrine::ERR_ALREADY_EXISTS
 
573
     *                                          occurred during the create table operation
 
574
     * @return boolean                          whether or not the export operation was successful
 
575
     *                                          false if table already existed in the database
 
576
     */
 
577
    public function export()
 
578
    {
 
579
        $this->_conn->export->exportTable($this);
 
580
    }
 
581
 
 
582
    /**
 
583
     * getExportableFormat
 
584
     * returns exportable presentation of this object
 
585
     *
 
586
     * @return array
 
587
     */
 
588
    public function getExportableFormat($parseForeignKeys = true)
 
589
    {
 
590
        $columns = array();
 
591
        $primary = array();
 
592
 
 
593
        foreach ($this->getColumns() as $name => $definition) {
 
594
 
 
595
            if (isset($definition['owner'])) {
 
596
                continue;
 
597
            }
 
598
 
 
599
            switch ($definition['type']) {
 
600
                case 'boolean':
 
601
                    if (isset($definition['default'])) {
 
602
                        $definition['default'] = $this->getConnection()->convertBooleans($definition['default']);
 
603
                    }
 
604
                    break;
 
605
            }
 
606
            $columns[$name] = $definition;
 
607
 
 
608
            if (isset($definition['primary']) && $definition['primary']) {
 
609
                $primary[] = $name;
 
610
            }
 
611
        }
 
612
 
 
613
        $options['foreignKeys'] = isset($this->_options['foreignKeys']) ?
 
614
                $this->_options['foreignKeys'] : array();
 
615
 
 
616
        if ($parseForeignKeys && $this->getAttribute(Doctrine::ATTR_EXPORT)
 
617
                & Doctrine::EXPORT_CONSTRAINTS) {
 
618
 
 
619
            $constraints = array();
 
620
 
 
621
            $emptyIntegrity = array('onUpdate' => null,
 
622
                                    'onDelete' => null);
 
623
 
 
624
            foreach ($this->getRelations() as $name => $relation) {
 
625
                $fk = $relation->toArray();
 
626
                $fk['foreignTable'] = $relation->getTable()->getTableName();
 
627
 
 
628
                if ($relation->getTable() === $this && in_array($relation->getLocal(), $primary)) {
 
629
                    if ($relation->hasConstraint()) {
 
630
                        throw new Doctrine_Table_Exception("Badly constructed integrity constraints. Cannot define constraint of different fields in the same table.");
 
631
                    }
 
632
                    continue;
 
633
                }
 
634
 
 
635
                $integrity = array('onUpdate' => $fk['onUpdate'],
 
636
                                   'onDelete' => $fk['onDelete']);
 
637
 
 
638
                if ($relation instanceof Doctrine_Relation_LocalKey) {
 
639
                    $def = array('local'        => $relation->getLocalColumnName(),
 
640
                                 'foreign'      => $relation->getForeignColumnName(),
 
641
                                 'foreignTable' => $relation->getTable()->getTableName());
 
642
 
 
643
                    if (($key = array_search($def, $options['foreignKeys'])) === false) {
 
644
                        $options['foreignKeys'][] = $def;
 
645
                        if ($integrity !== $emptyIntegrity) {
 
646
                            $constraints[] = $integrity;
 
647
                        }
 
648
                    } else {
 
649
                        if ($integrity !== $emptyIntegrity) {
 
650
                            $constraints[$key] = $integrity;
 
651
                        }
 
652
                    }
 
653
                }
 
654
            }
 
655
 
 
656
            foreach ($constraints as $k => $def) {
 
657
                $options['foreignKeys'][$k] = array_merge($options['foreignKeys'][$k], $def);
 
658
            }
 
659
        }
 
660
 
 
661
        $options['primary'] = $primary;
 
662
 
 
663
        return array('tableName' => $this->getOption('tableName'),
 
664
                     'columns'   => $columns,
 
665
                     'options'   => array_merge($this->getOptions(), $options));
 
666
    }
 
667
 
 
668
    /**
 
669
     * getRelationParser
 
670
     * return the relation parser associated with this table
 
671
     *
 
672
     * @return Doctrine_Relation_Parser     relation parser object
 
673
     */
 
674
    public function getRelationParser()
 
675
    {
 
676
        return $this->_parser;
 
677
    }
 
678
 
 
679
    /**
 
680
     * __get
 
681
     * an alias for getOption
 
682
     *
 
683
     * @param string $option
 
684
     */
 
685
    public function __get($option)
 
686
    {
 
687
        if (isset($this->_options[$option])) {
 
688
            return $this->_options[$option];
 
689
        }
 
690
        return null;
 
691
    }
 
692
 
 
693
    /**
 
694
     * __isset
 
695
     *
 
696
     * @param string $option
 
697
     */
 
698
    public function __isset($option)
 
699
    {
 
700
        return isset($this->_options[$option]);
 
701
    }
 
702
 
 
703
    /**
 
704
     * getOptions
 
705
     * returns all options of this table and the associated values
 
706
     *
 
707
     * @return array    all options and their values
 
708
     */
 
709
    public function getOptions()
 
710
    {
 
711
        return $this->_options;
 
712
    }
 
713
 
 
714
    /**
 
715
     * setOptions
 
716
     *
 
717
     * @param string $options
 
718
     * @return void
 
719
     */
 
720
    public function setOptions($options)
 
721
    {
 
722
        foreach ($options as $key => $value) {
 
723
            $this->setOption($key, $value);
 
724
        }
 
725
    }
 
726
 
 
727
    /**
 
728
     * addForeignKey
 
729
     *
 
730
     * adds a foreignKey to this table
 
731
     *
 
732
     * @return void
 
733
     */
 
734
    public function addForeignKey(array $definition)
 
735
    {
 
736
        $this->_options['foreignKeys'][] = $definition;
 
737
    }
 
738
 
 
739
    /**
 
740
     * addCheckConstraint
 
741
     *
 
742
     * adds a check constraint to this table
 
743
     *
 
744
     * @return void
 
745
     */
 
746
    public function addCheckConstraint($definition, $name)
 
747
    {
 
748
        if (is_string($name)) {
 
749
            $this->_options['checks'][$name] = $definition;
 
750
        } else {
 
751
            $this->_options['checks'][] = $definition;
 
752
        }
 
753
 
 
754
        return $this;
 
755
    }
 
756
 
 
757
    /**
 
758
     * addIndex
 
759
     *
 
760
     * adds an index to this table
 
761
     *
 
762
     * @return void
 
763
     */
 
764
    public function addIndex($index, array $definition)
 
765
    {
 
766
        if (isset($definition['fields'])) {
 
767
                foreach ((array) $definition['fields'] as $key => $field) {
 
768
                        if (is_numeric($key)) {
 
769
                    $definition['fields'][$key] = $this->getColumnName($field);
 
770
                } else {
 
771
                    $columnName = $this->getColumnName($key);
 
772
 
 
773
                    unset($definition['fields'][$key]);
 
774
 
 
775
                    $definition['fields'][$columnName] = $field;
 
776
                }
 
777
            }
 
778
        }
 
779
 
 
780
        $this->_options['indexes'][$index] = $definition;
 
781
    }
 
782
 
 
783
    /**
 
784
     * getIndex
 
785
     *
 
786
     * @return array|boolean        array on success, FALSE on failure
 
787
     */
 
788
    public function getIndex($index)
 
789
    {
 
790
        if (isset($this->_options['indexes'][$index])) {
 
791
            return $this->_options['indexes'][$index];
 
792
        }
 
793
 
 
794
        return false;
 
795
    }
 
796
 
 
797
    /**
 
798
     * DESCRIBE WHAT THIS METHOD DOES, PLEASE!
 
799
     *
 
800
     * @todo Name proposal: addRelation
 
801
     */
 
802
    public function bind($args, $type)
 
803
    {
 
804
        $options = ( ! isset($args[1])) ? array() : $args[1];
 
805
        $options['type'] = $type;
 
806
 
 
807
        $this->_parser->bind($args[0], $options);
 
808
    }
 
809
 
 
810
    /**
 
811
     * hasRelation
 
812
     *
 
813
     * @param string $alias      the relation to check if exists
 
814
     * @return boolean           true if the relation exists otherwise false
 
815
     */
 
816
    public function hasRelation($alias)
 
817
    {
 
818
        return $this->_parser->hasRelation($alias);
 
819
    }
 
820
 
 
821
    /**
 
822
     * getRelation
 
823
     *
 
824
     * @param string $alias      relation alias
 
825
     */
 
826
    public function getRelation($alias, $recursive = true)
 
827
    {
 
828
        return $this->_parser->getRelation($alias, $recursive);
 
829
    }
 
830
 
 
831
    /**
 
832
     * getRelations
 
833
     * returns an array containing all relation objects
 
834
     *
 
835
     * @return array        an array of Doctrine_Relation objects
 
836
     */
 
837
    public function getRelations()
 
838
    {
 
839
        return $this->_parser->getRelations();
 
840
    }
 
841
 
 
842
    /**
 
843
     * createQuery
 
844
     * creates a new Doctrine_Query object and adds the component name
 
845
     * of this table as the query 'from' part
 
846
     *
 
847
     * @param string Optional alias name for component aliasing.
 
848
     *
 
849
     * @return Doctrine_Query
 
850
     */
 
851
    public function createQuery($alias = '')
 
852
    {
 
853
        if ( ! empty($alias)) {
 
854
            $alias = ' ' . trim($alias);
 
855
        }
 
856
        return Doctrine_Query::create($this->_conn)->from($this->getComponentName() . $alias);
 
857
    }
 
858
 
 
859
    /**
 
860
     * getRepository
 
861
     *
 
862
     * @return Doctrine_Table_Repository
 
863
     */
 
864
    public function getRepository()
 
865
    {
 
866
        return $this->_repository;
 
867
    }
 
868
 
 
869
    /**
 
870
     * setOption
 
871
     * sets an option and returns this object in order to
 
872
     * allow flexible method chaining
 
873
     *
 
874
     * @see Doctrine_Table::$_options   for available options
 
875
     * @param string $name              the name of the option to set
 
876
     * @param mixed $value              the value of the option
 
877
     * @return Doctrine_Table           this object
 
878
     */
 
879
    public function setOption($name, $value)
 
880
    {
 
881
        switch ($name) {
 
882
            case 'name':
 
883
            case 'tableName':
 
884
                break;
 
885
            case 'enumMap':
 
886
            case 'inheritanceMap':
 
887
            case 'index':
 
888
            case 'treeOptions':
 
889
                if ( ! is_array($value)) {
 
890
                throw new Doctrine_Table_Exception($name . ' should be an array.');
 
891
                }
 
892
                break;
 
893
        }
 
894
        $this->_options[$name] = $value;
 
895
    }
 
896
 
 
897
    /**
 
898
     * getOption
 
899
     * returns the value of given option
 
900
     *
 
901
     * @param string $name  the name of the option
 
902
     * @return mixed        the value of given option
 
903
     */
 
904
    public function getOption($name)
 
905
    {
 
906
        if (isset($this->_options[$name])) {
 
907
            return $this->_options[$name];
 
908
        }
 
909
        return null;
 
910
    }
 
911
 
 
912
    /**
 
913
     * getColumnName
 
914
     *
 
915
     * returns a column name for column alias
 
916
     * if the actual name for the alias cannot be found
 
917
     * this method returns the given alias
 
918
     *
 
919
     * @param string $alias         column alias
 
920
     * @return string               column name
 
921
     */
 
922
    public function getColumnName($fieldName)
 
923
    {
 
924
        // FIX ME: This is being used in places where an array is passed, but it should not be an array
 
925
        // For example in places where Doctrine should support composite foreign/primary keys
 
926
        $fieldName = is_array($fieldName) ? $fieldName[0]:$fieldName;
 
927
 
 
928
        if (isset($this->_columnNames[$fieldName])) {
 
929
            return $this->_columnNames[$fieldName];
 
930
        }
 
931
 
 
932
        return strtolower($fieldName);
 
933
    }
 
934
 
 
935
    /**
 
936
     * getColumnDefinition
 
937
     *
 
938
     * @param string $columnName
 
939
     * @return void
 
940
     */
 
941
    public function getColumnDefinition($columnName)
 
942
    {
 
943
        if ( ! isset($this->_columns[$columnName])) {
 
944
            return false;
 
945
        }
 
946
        return $this->_columns[$columnName];
 
947
    }
 
948
 
 
949
    /**
 
950
     * getColumnAlias
 
951
     *
 
952
     * returns a column alias for a column name
 
953
     * if no alias can be found the column name is returned.
 
954
     *
 
955
     * @param string $columnName    column name
 
956
     * @return string               column alias
 
957
     */
 
958
    public function getFieldName($columnName)
 
959
    {
 
960
        if (isset($this->_fieldNames[$columnName])) {
 
961
            return $this->_fieldNames[$columnName];
 
962
        }
 
963
        return $columnName;
 
964
    }
 
965
    public function setColumns(array $definitions)
 
966
    {
 
967
        foreach ($definitions as $name => $options) {
 
968
            $this->setColumn($name, $options['type'], $options['length'], $options);
 
969
        }
 
970
    }
 
971
    /**
 
972
     * setColumn
 
973
     *
 
974
     * @param string $name
 
975
     * @param string $type
 
976
     * @param integer $length
 
977
     * @param mixed $options
 
978
     * @param boolean $prepend   Whether to prepend or append the new column to the column list.
 
979
     *                           By default the column gets appended.
 
980
     * @throws Doctrine_Table_Exception     if trying use wrongly typed parameter
 
981
     * @return void
 
982
     */
 
983
    public function setColumn($name, $type, $length = null, $options = array(), $prepend = false)
 
984
    {
 
985
        if (is_string($options)) {
 
986
            $options = explode('|', $options);
 
987
        }
 
988
 
 
989
        foreach ($options as $k => $option) {
 
990
            if (is_numeric($k)) {
 
991
                if ( ! empty($option)) {
 
992
                    $options[$option] = true;
 
993
                }
 
994
                unset($options[$k]);
 
995
            }
 
996
        }
 
997
 
 
998
        // extract column name & field name
 
999
        if (stripos($name, ' as '))
 
1000
        {
 
1001
            if (strpos($name, ' as')) {
 
1002
                $parts = explode(' as ', $name);
 
1003
            } else {
 
1004
                $parts = explode(' AS ', $name);
 
1005
            }
 
1006
 
 
1007
            if (count($parts) > 1) {
 
1008
                $fieldName = $parts[1];
 
1009
            } else {
 
1010
                $fieldName = $parts[0];
 
1011
            }
 
1012
 
 
1013
            $name = strtolower($parts[0]);
 
1014
        } else {
 
1015
            $fieldName = $name;
 
1016
            $name = strtolower($name);
 
1017
        }
 
1018
 
 
1019
        $name = trim($name);
 
1020
        $fieldName = trim($fieldName);
 
1021
 
 
1022
        if ($prepend) {
 
1023
            $this->_columnNames = array_merge(array($fieldName => $name), $this->_columnNames);
 
1024
            $this->_fieldNames = array_merge(array($name => $fieldName), $this->_fieldNames);
 
1025
        } else {
 
1026
            $this->_columnNames[$fieldName] = $name;
 
1027
            $this->_fieldNames[$name] = $fieldName;
 
1028
        }
 
1029
 
 
1030
        if ($length == null) {
 
1031
            switch ($type) {
 
1032
                case 'decimal':
 
1033
                    $length = 18;
 
1034
                break;
 
1035
                case 'string':
 
1036
                case 'clob':
 
1037
                case 'float':
 
1038
                case 'integer':
 
1039
                case 'array':
 
1040
                case 'object':
 
1041
                case 'blob':
 
1042
                case 'gzip':
 
1043
                    // use php int max
 
1044
                    $length = 2147483647;
 
1045
                break;
 
1046
                case 'boolean':
 
1047
                    $length = 1;
 
1048
                case 'date':
 
1049
                    // YYYY-MM-DD ISO 8601
 
1050
                    $length = 10;
 
1051
                case 'time':
 
1052
                    // HH:NN:SS+00:00 ISO 8601
 
1053
                    $length = 14;
 
1054
                case 'timestamp':
 
1055
                    // YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601
 
1056
                    $length = 25;
 
1057
                break;
 
1058
            }
 
1059
        }
 
1060
 
 
1061
        $options['type'] = $type;
 
1062
        $options['length'] = $length;
 
1063
 
 
1064
        if ($prepend) {
 
1065
            $this->_columns = array_merge(array($name => $options), $this->_columns);
 
1066
        } else {
 
1067
            $this->_columns[$name] = $options;
 
1068
        }
 
1069
 
 
1070
        if (isset($options['primary']) && $options['primary']) {
 
1071
            if (isset($this->_identifier)) {
 
1072
                $this->_identifier = (array) $this->_identifier;
 
1073
            }
 
1074
            if ( ! in_array($fieldName, $this->_identifier)) {
 
1075
                $this->_identifier[] = $fieldName;
 
1076
            }
 
1077
        }
 
1078
        if (isset($options['default'])) {
 
1079
            $this->hasDefaultValues = true;
 
1080
        }
 
1081
    }
 
1082
 
 
1083
    /**
 
1084
     * hasDefaultValues
 
1085
     * returns true if this table has default values, otherwise false
 
1086
     *
 
1087
     * @return boolean
 
1088
     */
 
1089
    public function hasDefaultValues()
 
1090
    {
 
1091
        return $this->hasDefaultValues;
 
1092
    }
 
1093
 
 
1094
    /**
 
1095
     * getDefaultValueOf
 
1096
     * returns the default value(if any) for given column
 
1097
     *
 
1098
     * @param string $fieldName
 
1099
     * @return mixed
 
1100
     */
 
1101
    public function getDefaultValueOf($fieldName)
 
1102
    {
 
1103
        $columnName = $this->getColumnName($fieldName);
 
1104
        if ( ! isset($this->_columns[$columnName])) {
 
1105
            throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$columnName." doesn't exist.");
 
1106
        }
 
1107
        if (isset($this->_columns[$columnName]['default'])) {
 
1108
            return $this->_columns[$columnName]['default'];
 
1109
        } else {
 
1110
            return null;
 
1111
        }
 
1112
    }
 
1113
 
 
1114
    /**
 
1115
     * @return mixed
 
1116
     */
 
1117
    public function getIdentifier()
 
1118
    {
 
1119
        return $this->_identifier;
 
1120
    }
 
1121
 
 
1122
    /**
 
1123
     * @return integer
 
1124
     */
 
1125
    public function getIdentifierType()
 
1126
    {
 
1127
        return $this->_identifierType;
 
1128
    }
 
1129
 
 
1130
    /**
 
1131
     * hasColumn
 
1132
     * @return boolean
 
1133
     */
 
1134
    public function hasColumn($columnName)
 
1135
    {
 
1136
        return isset($this->_columns[strtolower($columnName)]);
 
1137
    }
 
1138
 
 
1139
    /**
 
1140
     * hasField
 
1141
     * @return boolean
 
1142
     */
 
1143
    public function hasField($fieldName)
 
1144
    {
 
1145
        return isset($this->_columnNames[$fieldName]);
 
1146
    }
 
1147
 
 
1148
    /**
 
1149
     * sets the connection for this class
 
1150
     *
 
1151
     * @params Doctrine_Connection      a connection object
 
1152
     * @return Doctrine_Table           this object
 
1153
     */
 
1154
    public function setConnection(Doctrine_Connection $conn)
 
1155
    {
 
1156
        $this->_conn = $conn;
 
1157
 
 
1158
        $this->setParent($this->_conn);
 
1159
 
 
1160
        return $this;
 
1161
    }
 
1162
 
 
1163
    /**
 
1164
     * returns the connection associated with this table (if any)
 
1165
     *
 
1166
     * @return Doctrine_Connection|null     the connection object
 
1167
     */
 
1168
    public function getConnection()
 
1169
    {
 
1170
        return $this->_conn;
 
1171
    }
 
1172
 
 
1173
    /**
 
1174
     * creates a new record
 
1175
     *
 
1176
     * @param $array             an array where keys are field names and
 
1177
     *                           values representing field values
 
1178
     * @return Doctrine_Record   the created record object
 
1179
     */
 
1180
    public function create(array $array = array())
 
1181
    {
 
1182
        $record = new $this->_options['name']($this, true);
 
1183
        $record->fromArray($array);
 
1184
 
 
1185
        return $record;
 
1186
    }
 
1187
    
 
1188
    /**
 
1189
     * adds a named query in the query registry
 
1190
     *
 
1191
     * @param $queryKey  Query key name
 
1192
     * @param $query      DQL string or Doctrine_Query object
 
1193
     * @return void
 
1194
         */
 
1195
        public function addNamedQuery($queryKey, $query)
 
1196
    {
 
1197
        $registry = Doctrine_Manager::getInstance()->getQueryRegistry();
 
1198
        $registry->add($this->getComponentName() . '/' . $queryKey, $query);
 
1199
    }
 
1200
    
 
1201
    /**
 
1202
     * creates a named query in the query registry
 
1203
     *
 
1204
     * @param $queryKey  Query key name
 
1205
     * @return Doctrine_Query
 
1206
         */
 
1207
        public function createNamedQuery($queryKey)
 
1208
    {
 
1209
        $queryRegistry = Doctrine_Manager::getInstance()->getQueryRegistry();
 
1210
 
 
1211
        if (strpos($queryKey, '/') !== false) {
 
1212
            $e = explode('/', $queryKey);
 
1213
            
 
1214
            return $queryRegistry->get($e[1], $e[0]);
 
1215
        }
 
1216
 
 
1217
        return $queryRegistry->get($queryKey, $this->getComponentName());
 
1218
    }
 
1219
 
 
1220
 
 
1221
    /**
 
1222
     * finds a record by its identifier
 
1223
     *
 
1224
     * @param mixed $name         Database Row ID or Query Name defined previously as a NamedQuery
 
1225
     * @param mixed $params       This argument is the hydration mode (Doctrine::HYDRATE_ARRAY or 
 
1226
     *                            Doctrine::HYDRATE_RECORD) if first param is a Database Row ID. 
 
1227
     *                            Otherwise this argument expect an array of query params.
 
1228
     * @param int $hydrationMode  Optional Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD if 
 
1229
     *                            first argument is a NamedQuery
 
1230
     * @return mixed              Doctrine_Collection, array, Doctrine_Record or false if no result
 
1231
     */
 
1232
    public function find()
 
1233
    {
 
1234
        $num_args = func_num_args();
 
1235
 
 
1236
        // Named Query or IDs
 
1237
        $name = func_get_arg(0);
 
1238
        
 
1239
        if (is_null($name)) { 
 
1240
            return false;
 
1241
        }
 
1242
 
 
1243
        $ns = $this->getComponentName();
 
1244
        $m = $name;
 
1245
        
 
1246
        // Check for possible cross-access
 
1247
        if ( ! is_array($name) && strpos($name, '/') !== false) {
 
1248
            list($ns, $m) = explode('/', $name);
 
1249
        }
 
1250
 
 
1251
        // Define query to be used
 
1252
        if (
 
1253
            ! is_array($name) && 
 
1254
            Doctrine_Manager::getInstance()->getQueryRegistry()->has($m, $ns)
 
1255
        ) {
 
1256
            // We're dealing with a named query
 
1257
            $q = $this->createNamedQuery($name);
 
1258
 
 
1259
            // Parameters construction
 
1260
            $params = ($num_args >= 2) ? func_get_arg(1) : array();
 
1261
 
 
1262
            // Hydration mode
 
1263
            $hydrationMode = ($num_args == 3) ? func_get_arg(2) : null;
 
1264
 
 
1265
            // Executing query
 
1266
            $res = $q->execute($params, $hydrationMode);
 
1267
        } else {
 
1268
            // We're passing a single ID or an array of IDs
 
1269
            $q = $this->createQuery('dctrn_find')
 
1270
                ->where('dctrn_find.' . implode(' = ? AND dctrn_find.', (array) $this->getIdentifier()) . ' = ?')
 
1271
                ->limit(1);
 
1272
                
 
1273
            // Parameters construction
 
1274
            $params = is_array($name) ? array_values($name) : array($name);
 
1275
 
 
1276
            // Hydration mode
 
1277
            $hydrationMode = ($num_args == 2) ? func_get_arg(1) : null;
 
1278
            
 
1279
            // Executing query
 
1280
            $res = $q->fetchOne($params, $hydrationMode);
 
1281
        }
 
1282
 
 
1283
        $q->free();
 
1284
        
 
1285
        return $res;
 
1286
    }
 
1287
 
 
1288
    /**
 
1289
     * findAll
 
1290
     * returns a collection of records
 
1291
     *
 
1292
     * @param int $hydrationMode        Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD
 
1293
     * @return Doctrine_Collection
 
1294
     */
 
1295
    public function findAll($hydrationMode = null)
 
1296
    {
 
1297
        return $this->createQuery('dctrn_find')
 
1298
            ->execute(array(), $hydrationMode);
 
1299
    }
 
1300
 
 
1301
    /**
 
1302
     * findBySql
 
1303
     * finds records with given SQL where clause
 
1304
     * returns a collection of records
 
1305
     *
 
1306
     * @param string $dql               DQL after WHERE clause
 
1307
     * @param array $params             query parameters
 
1308
     * @param int $hydrationMode        Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD
 
1309
     * @return Doctrine_Collection
 
1310
     *
 
1311
     * @todo This actually takes DQL, not SQL, but it requires column names
 
1312
     *       instead of field names. This should be fixed to use raw SQL instead.
 
1313
     */
 
1314
    public function findBySql($dql, $params = array(), $hydrationMode = null)
 
1315
    {
 
1316
        return $this->createQuery('dctrn_find')
 
1317
            ->where($dql)->execute($params, $hydrationMode);
 
1318
    }
 
1319
 
 
1320
    /**
 
1321
     * findByDql
 
1322
     * finds records with given DQL where clause
 
1323
     * returns a collection of records
 
1324
     *
 
1325
     * @param string $dql               DQL after WHERE clause
 
1326
     * @param array $params             query parameters
 
1327
     * @param int $hydrationMode        Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD
 
1328
     * @return Doctrine_Collection
 
1329
     */
 
1330
    public function findByDql($dql, $params = array(), $hydrationMode = null)
 
1331
    {
 
1332
        $parser = new Doctrine_Query($this->_conn);
 
1333
        $component = $this->getComponentName();
 
1334
        $query = 'FROM ' . $component . ' dctrn_find WHERE ' . $dql;
 
1335
 
 
1336
        return $parser->query($query, $params, $hydrationMode);
 
1337
    }
 
1338
    
 
1339
    /**
 
1340
     * findBy
 
1341
     *
 
1342
     * @param string $column
 
1343
     * @param string $value
 
1344
     * @param string $hydrationMode
 
1345
     * @return void
 
1346
     */
 
1347
    protected function findBy($fieldName, $value, $hydrationMode = null)
 
1348
    {
 
1349
        return $this->createQuery('dctrn_find')
 
1350
            ->where('dctrn_find.' . $fieldName . ' = ?', array($value))
 
1351
            ->execute(array(), $hydrationMode);
 
1352
    }
 
1353
 
 
1354
    /**
 
1355
     * findOneBy
 
1356
     *
 
1357
     * @param string $column
 
1358
     * @param string $value
 
1359
     * @param string $hydrationMode
 
1360
     * @return void
 
1361
     */
 
1362
    protected function findOneBy($fieldName, $value, $hydrationMode = null)
 
1363
    {
 
1364
        return $this->createQuery('dctrn_find')
 
1365
                    ->where('dctrn_find.' . $fieldName . ' = ?', array($value))
 
1366
                    ->limit(1)
 
1367
                    ->fetchOne(array(), $hydrationMode);
 
1368
    }
 
1369
 
 
1370
    /**
 
1371
     * execute
 
1372
     * fetches data using the provided queryKey and
 
1373
     * the associated query in the query registry
 
1374
     *
 
1375
     * if no query for given queryKey is being found a
 
1376
     * Doctrine_Query_Registry exception is being thrown
 
1377
     *
 
1378
     * @param string $queryKey      the query key
 
1379
     * @param array $params         prepared statement params (if any)
 
1380
     * @return mixed                the fetched data
 
1381
     */
 
1382
    public function execute($queryKey, $params = array(), $hydrationMode = Doctrine::HYDRATE_RECORD)
 
1383
    {
 
1384
        return $this->createNamedQuery($queryKey)->execute($params, $hydrationMode);
 
1385
    }
 
1386
 
 
1387
    /**
 
1388
     * executeOne
 
1389
     * fetches data using the provided queryKey and
 
1390
     * the associated query in the query registry
 
1391
     *
 
1392
     * if no query for given queryKey is being found a
 
1393
     * Doctrine_Query_Registry exception is being thrown
 
1394
     *
 
1395
     * @param string $queryKey      the query key
 
1396
     * @param array $params         prepared statement params (if any)
 
1397
     * @return mixed                the fetched data
 
1398
     */
 
1399
    public function executeOne($queryKey, $params = array(), $hydrationMode = Doctrine::HYDRATE_RECORD)
 
1400
    {
 
1401
        return $this->createNamedQuery($queryKey)->fetchOne($params, $hydrationMode);
 
1402
    }
 
1403
 
 
1404
    /**
 
1405
     * clear
 
1406
     * clears the first level cache (identityMap)
 
1407
     *
 
1408
     * @return void
 
1409
     * @todo what about a more descriptive name? clearIdentityMap?
 
1410
     */
 
1411
    public function clear()
 
1412
    {
 
1413
        $this->_identityMap = array();
 
1414
    }
 
1415
 
 
1416
    /**
 
1417
     * addRecord
 
1418
     * adds a record to identity map
 
1419
     *
 
1420
     * @param Doctrine_Record $record       record to be added
 
1421
     * @return boolean
 
1422
     * @todo Better name? registerRecord?
 
1423
     */
 
1424
    public function addRecord(Doctrine_Record $record)
 
1425
    {
 
1426
        $id = implode(' ', $record->identifier());
 
1427
 
 
1428
        if (isset($this->_identityMap[$id])) {
 
1429
            return false;
 
1430
        }
 
1431
 
 
1432
        $this->_identityMap[$id] = $record;
 
1433
 
 
1434
        return true;
 
1435
    }
 
1436
 
 
1437
    /**
 
1438
     * removeRecord
 
1439
     * removes a record from the identity map, returning true if the record
 
1440
     * was found and removed and false if the record wasn't found.
 
1441
     *
 
1442
     * @param Doctrine_Record $record       record to be removed
 
1443
     * @return boolean
 
1444
     */
 
1445
    public function removeRecord(Doctrine_Record $record)
 
1446
    {
 
1447
        $id = implode(' ', $record->identifier());
 
1448
 
 
1449
        if (isset($this->_identityMap[$id])) {
 
1450
            unset($this->_identityMap[$id]);
 
1451
            return true;
 
1452
        }
 
1453
 
 
1454
        return false;
 
1455
    }
 
1456
 
 
1457
    /**
 
1458
     * getRecord
 
1459
     * first checks if record exists in identityMap, if not
 
1460
     * returns a new record
 
1461
     *
 
1462
     * @return Doctrine_Record
 
1463
     */
 
1464
    public function getRecord()
 
1465
    {
 
1466
        if ( ! empty($this->_data)) {
 
1467
            $identifierFieldNames = $this->getIdentifier();
 
1468
 
 
1469
            if ( ! is_array($identifierFieldNames)) {
 
1470
                $identifierFieldNames = array($identifierFieldNames);
 
1471
            }
 
1472
 
 
1473
            $found = false;
 
1474
            foreach ($identifierFieldNames as $fieldName) {
 
1475
                if ( ! isset($this->_data[$fieldName])) {
 
1476
                    // primary key column not found return new record
 
1477
                    $found = true;
 
1478
                    break;
 
1479
                }
 
1480
                $id[] = $this->_data[$fieldName];
 
1481
            }
 
1482
 
 
1483
            if ($found) {
 
1484
                $recordName = $this->getComponentName();
 
1485
                $record = new $recordName($this, true);
 
1486
                $this->_data = array();
 
1487
                return $record;
 
1488
            }
 
1489
            
 
1490
            $id = implode(' ', $id);
 
1491
 
 
1492
            if (isset($this->_identityMap[$id])) {
 
1493
                $record = $this->_identityMap[$id];
 
1494
                if ($record->getTable()->getAttribute(Doctrine::ATTR_HYDRATE_OVERWRITE)) {
 
1495
                    $record->hydrate($this->_data);
 
1496
                    if ($record->state() == Doctrine_Record::STATE_PROXY) {
 
1497
                        if (count($this->_data) >= $this->getColumnCount()) {
 
1498
                            $record->state(Doctrine_Record::STATE_CLEAN);
 
1499
                        }
 
1500
                    }
 
1501
                } else {
 
1502
                    $record->hydrate($this->_data, false);
 
1503
                }
 
1504
            } else {
 
1505
                $recordName = $this->getComponentName();
 
1506
                $record = new $recordName($this);
 
1507
                $this->_identityMap[$id] = $record;
 
1508
            }
 
1509
            $this->_data = array();
 
1510
        } else {
 
1511
            $recordName = $this->getComponentName();
 
1512
            $record = new $recordName($this, true);
 
1513
        }
 
1514
 
 
1515
        return $record;
 
1516
    }
 
1517
 
 
1518
    /**
 
1519
     * Get the classname to return. Most often this is just the options['name']
 
1520
     *
 
1521
     * Check the subclasses option and the inheritanceMap for each subclass to see
 
1522
     * if all the maps in a subclass is met. If this is the case return that
 
1523
     * subclass name. If no subclasses match or if there are no subclasses defined
 
1524
     * return the name of the class for this tables record.
 
1525
     *
 
1526
     * @todo this function could use reflection to check the first time it runs
 
1527
     * if the subclassing option is not set.
 
1528
     *
 
1529
     * @return string The name of the class to create
 
1530
     * @deprecated
 
1531
     */
 
1532
    public function getClassnameToReturn()
 
1533
    {
 
1534
        if ( ! isset($this->_options['subclasses'])) {
 
1535
            return $this->_options['name'];
 
1536
        }
 
1537
        foreach ($this->_options['subclasses'] as $subclass) {
 
1538
            $table = $this->_conn->getTable($subclass);
 
1539
            $inheritanceMap = $table->getOption('inheritanceMap');
 
1540
            $nomatch = false;
 
1541
            foreach ($inheritanceMap as $key => $value) {
 
1542
                if ( ! isset($this->_data[$key]) || $this->_data[$key] != $value) {
 
1543
                    $nomatch = true;
 
1544
                    break;
 
1545
                }
 
1546
            }
 
1547
            if ( ! $nomatch) {
 
1548
                return $table->getComponentName();
 
1549
            }
 
1550
        }
 
1551
        return $this->_options['name'];
 
1552
    }
 
1553
 
 
1554
    /**
 
1555
     * @param $id                       database row id
 
1556
     * @throws Doctrine_Find_Exception
 
1557
     */
 
1558
    final public function getProxy($id = null)
 
1559
    {
 
1560
        if ($id !== null) {
 
1561
            $identifierColumnNames = $this->getIdentifierColumnNames();
 
1562
            $query = 'SELECT ' . implode(', ', (array) $identifierColumnNames)
 
1563
                . ' FROM ' . $this->getTableName()
 
1564
                . ' WHERE ' . implode(' = ? && ', (array) $identifierColumnNames) . ' = ?';
 
1565
            $query = $this->applyInheritance($query);
 
1566
 
 
1567
            $params = array_merge(array($id), array_values($this->_options['inheritanceMap']));
 
1568
 
 
1569
            $this->_data = $this->_conn->execute($query, $params)->fetch(PDO::FETCH_ASSOC);
 
1570
 
 
1571
            if ($this->_data === false)
 
1572
                return false;
 
1573
        }
 
1574
        return $this->getRecord();
 
1575
    }
 
1576
 
 
1577
    /**
 
1578
     * applyInheritance
 
1579
     * @param $where                    query where part to be modified
 
1580
     * @return string                   query where part with column aggregation inheritance added
 
1581
     */
 
1582
    final public function applyInheritance($where)
 
1583
    {
 
1584
        if ( ! empty($this->_options['inheritanceMap'])) {
 
1585
            $a = array();
 
1586
            foreach ($this->_options['inheritanceMap'] as $field => $value) {
 
1587
                $a[] = $this->getColumnName($field) . ' = ?';
 
1588
            }
 
1589
            $i = implode(' AND ', $a);
 
1590
            $where .= ' AND ' . $i;
 
1591
        }
 
1592
        return $where;
 
1593
    }
 
1594
 
 
1595
    /**
 
1596
     * count
 
1597
     *
 
1598
     * @return integer
 
1599
     */
 
1600
    public function count()
 
1601
    {
 
1602
        return $this->createQuery()->count();
 
1603
    }
 
1604
 
 
1605
    /**
 
1606
     * @return Doctrine_Query  a Doctrine_Query object
 
1607
     */
 
1608
    public function getQueryObject()
 
1609
    {
 
1610
        $graph = new Doctrine_Query($this->getConnection());
 
1611
        $graph->load($this->getComponentName());
 
1612
        return $graph;
 
1613
    }
 
1614
 
 
1615
    /**
 
1616
     * @param string $fieldName
 
1617
     * @return array
 
1618
     */
 
1619
    public function getEnumValues($fieldName)
 
1620
    {
 
1621
        $columnName = $this->getColumnName($fieldName);
 
1622
        if (isset($this->_columns[$columnName]['values'])) {
 
1623
            return $this->_columns[$columnName]['values'];
 
1624
        } else {
 
1625
            return array();
 
1626
        }
 
1627
    }
 
1628
 
 
1629
    /**
 
1630
     * enumValue
 
1631
     *
 
1632
     * @param string $field
 
1633
     * @param integer $index
 
1634
     * @return mixed
 
1635
     */
 
1636
    public function enumValue($fieldName, $index)
 
1637
    {
 
1638
        if ($index instanceof Doctrine_Null) {
 
1639
            return $index;
 
1640
        }
 
1641
 
 
1642
        $columnName = $this->getColumnName($fieldName);
 
1643
        if ( ! $this->_conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)
 
1644
            && isset($this->_columns[$columnName]['values'][$index])
 
1645
        ) {
 
1646
            return $this->_columns[$columnName]['values'][$index];
 
1647
        }
 
1648
 
 
1649
        return $index;
 
1650
    }
 
1651
 
 
1652
    /**
 
1653
     * enumIndex
 
1654
     *
 
1655
     * @param string $field
 
1656
     * @param mixed $value
 
1657
     * @return mixed
 
1658
     */
 
1659
    public function enumIndex($fieldName, $value)
 
1660
    {
 
1661
        $values = $this->getEnumValues($fieldName);
 
1662
 
 
1663
        $index = array_search($value, $values);
 
1664
        if ($index === false || !$this->_conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) {
 
1665
            return $index;
 
1666
        }
 
1667
        return $value;
 
1668
    }
 
1669
 
 
1670
    /**
 
1671
     * validateField
 
1672
     *
 
1673
     * @param string $name
 
1674
     * @param string $value
 
1675
     * @param Doctrine_Record $record
 
1676
     * @return Doctrine_Validator_ErrorStack $errorStack
 
1677
     */
 
1678
    public function validateField($fieldName, $value, Doctrine_Record $record = null)
 
1679
    {
 
1680
        if ($record instanceof Doctrine_Record) {
 
1681
            $errorStack = $record->getErrorStack();
 
1682
        } else {
 
1683
            $record  = $this->create();
 
1684
            $errorStack = new Doctrine_Validator_ErrorStack($this->getOption('name'));
 
1685
        }
 
1686
 
 
1687
        if ($value === self::$_null) {
 
1688
            $value = null;
 
1689
        } else if ($value instanceof Doctrine_Record && $value->exists()) {
 
1690
            $value = $value->getIncremented();
 
1691
        } else if ($value instanceof Doctrine_Record && ! $value->exists()) {
 
1692
            foreach($this->getRelations() as $relation) {
 
1693
                if ($fieldName == $relation->getLocalFieldName() && (get_class($value) == $relation->getClass() || is_subclass_of($value, $relation->getClass()))) {
 
1694
                    return $errorStack;
 
1695
                }
 
1696
            }
 
1697
        }
 
1698
 
 
1699
        $dataType = $this->getTypeOf($fieldName);
 
1700
 
 
1701
        // Validate field type, if type validation is enabled
 
1702
        if ($this->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_TYPES) {
 
1703
            if ( ! Doctrine_Validator::isValidType($value, $dataType)) {
 
1704
                $errorStack->add($fieldName, 'type');
 
1705
            }
 
1706
            if ($dataType == 'enum') {
 
1707
                $enumIndex = $this->enumIndex($fieldName, $value);
 
1708
                if ($enumIndex === false && $value !== null) {
 
1709
                    $errorStack->add($fieldName, 'enum');
 
1710
                }
 
1711
            }
 
1712
        }
 
1713
 
 
1714
        // Validate field length, if length validation is enabled
 
1715
        if ($this->getAttribute(Doctrine::ATTR_VALIDATE) & Doctrine::VALIDATE_LENGTHS) {
 
1716
            if ( ! Doctrine_Validator::validateLength($value, $dataType, $this->getFieldLength($fieldName))) {
 
1717
                $errorStack->add($fieldName, 'length');
 
1718
            }
 
1719
        }
 
1720
 
 
1721
        // Run all custom validators
 
1722
        foreach ($this->getFieldValidators($fieldName) as $validatorName => $args) {
 
1723
            if ( ! is_string($validatorName)) {
 
1724
                $validatorName = $args;
 
1725
                $args = array();
 
1726
            }
 
1727
 
 
1728
            $validator = Doctrine_Validator::getValidator($validatorName);
 
1729
            $validator->invoker = $record;
 
1730
            $validator->field = $fieldName;
 
1731
            $validator->args = $args;
 
1732
            if ( ! $validator->validate($value)) {
 
1733
                $errorStack->add($fieldName, $validator);
 
1734
            }
 
1735
        }
 
1736
 
 
1737
        return $errorStack;
 
1738
    }
 
1739
 
 
1740
    /**
 
1741
     * getColumnCount
 
1742
     *
 
1743
     * @return integer      the number of columns in this table
 
1744
     */
 
1745
    public function getColumnCount()
 
1746
    {
 
1747
        return $this->columnCount;
 
1748
    }
 
1749
 
 
1750
    /**
 
1751
     * returns all columns and their definitions
 
1752
     *
 
1753
     * @return array
 
1754
     */
 
1755
    public function getColumns()
 
1756
    {
 
1757
        return $this->_columns;
 
1758
    }
 
1759
 
 
1760
    /**
 
1761
     * Removes the passed field name from the table schema information
 
1762
     *
 
1763
     * @return boolean
 
1764
     */
 
1765
    public function removeColumn($fieldName)
 
1766
    {
 
1767
        if ( ! $this->hasField($fieldName)) {
 
1768
          return false;
 
1769
        }
 
1770
 
 
1771
        $columnName = $this->getColumnName($fieldName);
 
1772
        unset($this->_columnNames[$fieldName], $this->_fieldNames[$columnName], $this->_columns[$columnName]);
 
1773
        $this->columnCount = count($this->_columns);
 
1774
        return true;
 
1775
    }
 
1776
 
 
1777
    /**
 
1778
     * returns an array containing all the column names.
 
1779
     *
 
1780
     * @return array
 
1781
     */
 
1782
    public function getColumnNames(array $fieldNames = null)
 
1783
    {
 
1784
        if ($fieldNames === null) {
 
1785
            return array_keys($this->_columns);
 
1786
        } else {
 
1787
           $columnNames = array();
 
1788
           foreach ($fieldNames as $fieldName) {
 
1789
               $columnNames[] = $this->getColumnName($fieldName);
 
1790
           }
 
1791
           return $columnNames;
 
1792
        }
 
1793
    }
 
1794
 
 
1795
    /**
 
1796
     * returns an array with all the identifier column names.
 
1797
     *
 
1798
     * @return array
 
1799
     */
 
1800
    public function getIdentifierColumnNames()
 
1801
    {
 
1802
        return $this->getColumnNames((array) $this->getIdentifier());
 
1803
    }
 
1804
 
 
1805
    /**
 
1806
     * returns an array containing all the field names.
 
1807
     *
 
1808
     * @return array
 
1809
     */
 
1810
    public function getFieldNames()
 
1811
    {
 
1812
        return array_values($this->_fieldNames);
 
1813
    }
 
1814
 
 
1815
    /**
 
1816
     * getDefinitionOf
 
1817
     *
 
1818
     * @return mixed        array on success, false on failure
 
1819
     */
 
1820
    public function getDefinitionOf($fieldName)
 
1821
    {
 
1822
        $columnName = $this->getColumnName($fieldName);
 
1823
        return $this->getColumnDefinition($columnName);
 
1824
    }
 
1825
 
 
1826
    /**
 
1827
     * getTypeOf
 
1828
     *
 
1829
     * @return mixed        string on success, false on failure
 
1830
     */
 
1831
    public function getTypeOf($fieldName)
 
1832
    {
 
1833
        return $this->getTypeOfColumn($this->getColumnName($fieldName));
 
1834
    }
 
1835
 
 
1836
    /**
 
1837
     * getTypeOfColumn
 
1838
     *
 
1839
     * @return mixed  The column type or FALSE if the type cant be determined.
 
1840
     */
 
1841
    public function getTypeOfColumn($columnName)
 
1842
    {
 
1843
        return isset($this->_columns[$columnName]) ? $this->_columns[$columnName]['type'] : false;
 
1844
    }
 
1845
 
 
1846
    /**
 
1847
     * setData
 
1848
     * doctrine uses this function internally
 
1849
     * users are strongly discouraged to use this function
 
1850
     *
 
1851
     * @param array $data               internal data
 
1852
     * @return void
 
1853
     */
 
1854
    public function setData(array $data)
 
1855
    {
 
1856
        $this->_data = $data;
 
1857
    }
 
1858
 
 
1859
    /**
 
1860
     * returns internal data, used by Doctrine_Record instances
 
1861
     * when retrieving data from database
 
1862
     *
 
1863
     * @return array
 
1864
     */
 
1865
    public function getData()
 
1866
    {
 
1867
        return $this->_data;
 
1868
    }
 
1869
 
 
1870
    /**
 
1871
     * prepareValue
 
1872
     * this method performs special data preparation depending on
 
1873
     * the type of the given column
 
1874
     *
 
1875
     * 1. It unserializes array and object typed columns
 
1876
     * 2. Uncompresses gzip typed columns
 
1877
     * 3. Gets the appropriate enum values for enum typed columns
 
1878
     * 4. Initializes special null object pointer for null values (for fast column existence checking purposes)
 
1879
     *
 
1880
     * example:
 
1881
     * <code type='php'>
 
1882
     * $field = 'name';
 
1883
     * $value = null;
 
1884
     * $table->prepareValue($field, $value); // Doctrine_Null
 
1885
     * </code>
 
1886
     *
 
1887
     * @throws Doctrine_Table_Exception     if unserialization of array/object typed column fails or
 
1888
     * @throws Doctrine_Table_Exception     if uncompression of gzip typed column fails         *
 
1889
     * @param string $field     the name of the field
 
1890
     * @param string $value     field value
 
1891
     * @param string $typeHint  Type hint used to pass in the type of the value to prepare
 
1892
     *                          if it is already known. This enables the method to skip
 
1893
     *                          the type determination. Used i.e. during hydration.
 
1894
     * @return mixed            prepared value
 
1895
     */
 
1896
    public function prepareValue($fieldName, $value, $typeHint = null)
 
1897
    {
 
1898
        if ($value === self::$_null) {
 
1899
            return self::$_null;
 
1900
        } else if ($value === null) {
 
1901
            return null;
 
1902
        } else {
 
1903
            $type = is_null($typeHint) ? $this->getTypeOf($fieldName) : $typeHint;
 
1904
 
 
1905
            switch ($type) {
 
1906
                case 'integer':
 
1907
                case 'string';
 
1908
                    // don't do any casting here PHP INT_MAX is smaller than what the databases support
 
1909
                break;
 
1910
                case 'enum':
 
1911
                    return $this->enumValue($fieldName, $value);
 
1912
                break;
 
1913
                case 'boolean':
 
1914
                    return (boolean) $value;
 
1915
                break;
 
1916
                case 'array':
 
1917
                case 'object':
 
1918
                    if (is_string($value)) {
 
1919
                        $value = empty($value) ? null:unserialize($value);
 
1920
 
 
1921
                        if ($value === false) {
 
1922
                            throw new Doctrine_Table_Exception('Unserialization of ' . $fieldName . ' failed.');
 
1923
                        }
 
1924
                        return $value;
 
1925
                    }
 
1926
                break;
 
1927
                case 'gzip':
 
1928
                    $value = gzuncompress($value);
 
1929
 
 
1930
                    if ($value === false) {
 
1931
                        throw new Doctrine_Table_Exception('Uncompressing of ' . $fieldName . ' failed.');
 
1932
                    }
 
1933
                    return $value;
 
1934
                break;
 
1935
            }
 
1936
        }
 
1937
        return $value;
 
1938
    }
 
1939
 
 
1940
    /**
 
1941
     * getTree
 
1942
     *
 
1943
     * getter for associated tree
 
1944
     *
 
1945
     * @return mixed  if tree return instance of Doctrine_Tree, otherwise returns false
 
1946
     */
 
1947
    public function getTree()
 
1948
    {
 
1949
        if (isset($this->_options['treeImpl'])) {
 
1950
            if ( ! $this->_tree) {
 
1951
                $options = isset($this->_options['treeOptions']) ? $this->_options['treeOptions'] : array();
 
1952
                $this->_tree = Doctrine_Tree::factory($this,
 
1953
                    $this->_options['treeImpl'],
 
1954
                    $options
 
1955
                );
 
1956
            }
 
1957
            return $this->_tree;
 
1958
        }
 
1959
        return false;
 
1960
    }
 
1961
 
 
1962
    /**
 
1963
     * getComponentName
 
1964
     *
 
1965
     * @return void
 
1966
     */
 
1967
    public function getComponentName()
 
1968
    {
 
1969
        return $this->_options['name'];
 
1970
    }
 
1971
 
 
1972
    /**
 
1973
     * getTableName
 
1974
     *
 
1975
     * @return void
 
1976
     */
 
1977
    public function getTableName()
 
1978
    {
 
1979
        return $this->_options['tableName'];
 
1980
    }
 
1981
 
 
1982
    /**
 
1983
     * setTableName
 
1984
     *
 
1985
     * @param string $tableName
 
1986
     * @return void
 
1987
     */
 
1988
    public function setTableName($tableName)
 
1989
    {
 
1990
        $this->setOption('tableName', $this->_conn->formatter->getTableName($tableName));
 
1991
    }
 
1992
 
 
1993
    /**
 
1994
     * isTree
 
1995
     *
 
1996
     * determine if table acts as tree
 
1997
     *
 
1998
     * @return mixed  if tree return true, otherwise returns false
 
1999
     */
 
2000
    public function isTree()
 
2001
    {
 
2002
        return ( ! is_null($this->_options['treeImpl'])) ? true : false;
 
2003
    }
 
2004
    
 
2005
    /**
 
2006
     * getTemplates
 
2007
     * returns all templates attached to this table
 
2008
     *
 
2009
     * @return array     an array containing all templates
 
2010
     */
 
2011
    public function getTemplates()
 
2012
    {
 
2013
        return $this->_templates;
 
2014
    }
 
2015
 
 
2016
    /**
 
2017
     * getTemplate
 
2018
     *
 
2019
     * @param string $template
 
2020
     * @return void
 
2021
     */
 
2022
    public function getTemplate($template)
 
2023
    {
 
2024
        if ( ! isset($this->_templates[$template])) {
 
2025
            throw new Doctrine_Table_Exception('Template ' . $template . ' not loaded');
 
2026
        }
 
2027
 
 
2028
        return $this->_templates[$template];
 
2029
    }
 
2030
 
 
2031
    /**
 
2032
     * Check if the table has a template name
 
2033
     *
 
2034
     * @param string $template
 
2035
     * @return boolean $bool
 
2036
     */
 
2037
    public function hasTemplate($template)
 
2038
    {
 
2039
        return isset($this->_templates[$template]);
 
2040
    }
 
2041
 
 
2042
    /**
 
2043
     * Add template to the table
 
2044
     *
 
2045
     * @param string $template
 
2046
     * @param Doctrine_Template $impl
 
2047
     * @return Doctrine_Table
 
2048
     */
 
2049
    public function addTemplate($template, Doctrine_Template $impl)
 
2050
    {
 
2051
        $this->_templates[$template] = $impl;
 
2052
 
 
2053
        return $this;
 
2054
    }
 
2055
 
 
2056
    /**
 
2057
     * Get all the generators for the table
 
2058
     *
 
2059
     * @return array $generators
 
2060
     */
 
2061
 
 
2062
    public function getGenerators()
 
2063
    {
 
2064
        return $this->_generators;
 
2065
    }
 
2066
 
 
2067
    /**
 
2068
     * Get generator instance for a passed name
 
2069
     *
 
2070
     * @param string $generator
 
2071
     * @return Doctrine_Record_Generator $generator
 
2072
     */
 
2073
    public function getGenerator($generator)
 
2074
    {
 
2075
        if ( ! isset($this->_generators[$generator])) {
 
2076
            throw new Doctrine_Table_Exception('Generator ' . $generator . ' not loaded');
 
2077
        }
 
2078
 
 
2079
        return $this->_generators[$generator];
 
2080
    }
 
2081
 
 
2082
    /**
 
2083
     * Check if a generator name exists
 
2084
     *
 
2085
     * @param string $generator
 
2086
     * @return void
 
2087
     */
 
2088
    public function hasGenerator($generator)
 
2089
    {
 
2090
        return isset($this->_generators[$generator]);
 
2091
    }
 
2092
 
 
2093
    /**
 
2094
     * Add a generate to the table instance
 
2095
     *
 
2096
     * @param Doctrine_Record_Generator $generator
 
2097
     * @param string $name
 
2098
     * @return Doctrine_Table
 
2099
     */
 
2100
    public function addGenerator(Doctrine_Record_Generator $generator, $name = null)
 
2101
    {
 
2102
        if ($name === null) {
 
2103
            $this->_generators[] = $generator;
 
2104
        } else {
 
2105
            $this->_generators[$name] = $generator;
 
2106
        }
 
2107
        return $this;
 
2108
    }
 
2109
 
 
2110
    /**
 
2111
     * bindQueryParts
 
2112
     * binds query parts to given component
 
2113
     *
 
2114
     * @param array $queryParts         an array of pre-bound query parts
 
2115
     * @return Doctrine_Record          this object
 
2116
     */
 
2117
    public function bindQueryParts(array $queryParts)
 
2118
    {
 
2119
        $this->_options['queryParts'] = $queryParts;
 
2120
 
 
2121
        return $this;
 
2122
    }
 
2123
 
 
2124
    /**
 
2125
     * bindQueryPart
 
2126
     * binds given value to given query part
 
2127
     *
 
2128
     * @param string $queryPart
 
2129
     * @param mixed $value
 
2130
     * @return Doctrine_Record          this object
 
2131
     */
 
2132
    public function bindQueryPart($queryPart, $value)
 
2133
    {
 
2134
        $this->_options['queryParts'][$queryPart] = $value;
 
2135
 
 
2136
        return $this;
 
2137
    }
 
2138
 
 
2139
    /**
 
2140
     * Gets the names of all validators that are applied on a field.
 
2141
     *
 
2142
     * @param string  The field name.
 
2143
     * @return array  The names of all validators that are applied on the specified field.
 
2144
     */
 
2145
    public function getFieldValidators($fieldName)
 
2146
    {
 
2147
        $validators = array();
 
2148
        $columnName = $this->getColumnName($fieldName);
 
2149
        // this loop is a dirty workaround to get the validators filtered out of
 
2150
        // the options, since everything is squeezed together currently
 
2151
        foreach ($this->_columns[$columnName] as $name => $args) {
 
2152
             if (empty($name)
 
2153
                    || $name == 'primary'
 
2154
                    || $name == 'protected'
 
2155
                    || $name == 'autoincrement'
 
2156
                    || $name == 'default'
 
2157
                    || $name == 'values'
 
2158
                    || $name == 'sequence'
 
2159
                    || $name == 'zerofill'
 
2160
                    || $name == 'owner'
 
2161
                    || $name == 'scale'
 
2162
                    || $name == 'type'
 
2163
                    || $name == 'length'
 
2164
                    || $name == 'fixed'
 
2165
                    || $name == 'comment') {
 
2166
                continue;
 
2167
            }
 
2168
            if ($name == 'notnull' && isset($this->_columns[$columnName]['autoincrement'])
 
2169
                    && $this->_columns[$columnName]['autoincrement'] === true) {
 
2170
                continue;
 
2171
            }
 
2172
            // skip it if it's explicitly set to FALSE (i.e. notnull => false)
 
2173
            if ($args === false) {
 
2174
                continue;
 
2175
            }
 
2176
            $validators[$name] = $args;
 
2177
        }
 
2178
 
 
2179
        return $validators;
 
2180
    }
 
2181
 
 
2182
    /**
 
2183
     * Gets the (maximum) length of a field.
 
2184
     */
 
2185
    public function getFieldLength($fieldName)
 
2186
    {
 
2187
        return $this->_columns[$this->getColumnName($fieldName)]['length'];
 
2188
    }
 
2189
 
 
2190
    /**
 
2191
     * getBoundQueryPart
 
2192
     *
 
2193
     * @param string $queryPart
 
2194
     * @return string $queryPart
 
2195
     */
 
2196
    public function getBoundQueryPart($queryPart)
 
2197
    {
 
2198
        if ( ! isset($this->_options['queryParts'][$queryPart])) {
 
2199
            return null;
 
2200
        }
 
2201
 
 
2202
        return $this->_options['queryParts'][$queryPart];
 
2203
    }
 
2204
 
 
2205
    /**
 
2206
     * unshiftFilter
 
2207
     *
 
2208
     * @param  object Doctrine_Record_Filter $filter
 
2209
     * @return object $this
 
2210
     */
 
2211
    public function unshiftFilter(Doctrine_Record_Filter $filter)
 
2212
    {
 
2213
        $filter->setTable($this);
 
2214
 
 
2215
        $filter->init();
 
2216
 
 
2217
        array_unshift($this->_filters, $filter);
 
2218
 
 
2219
        return $this;
 
2220
    }
 
2221
 
 
2222
    /**
 
2223
     * getFilters
 
2224
     *
 
2225
     * @return array $filters
 
2226
     */
 
2227
    public function getFilters()
 
2228
    {
 
2229
        return $this->_filters;
 
2230
    }
 
2231
 
 
2232
    /**
 
2233
     * returns a string representation of this object
 
2234
     *
 
2235
     * @return string
 
2236
     */
 
2237
    public function __toString()
 
2238
    {
 
2239
        return Doctrine_Lib::getTableAsString($this);
 
2240
    }
 
2241
 
 
2242
    /**
 
2243
     * Resolve the passed find by field name to the appropriate field name
 
2244
     * regardless of whether the user passes a column name, field name, or a Doctrine_Inflector::classified()
 
2245
     * version of their column name. It will be inflected with Doctrine_Inflector::tableize() 
 
2246
     * to get the column or field name
 
2247
     *
 
2248
     * @param string $name 
 
2249
     * @return string $fieldName
 
2250
     */
 
2251
    protected function _resolveFindByFieldName($name)
 
2252
    {
 
2253
        $fieldName = Doctrine_Inflector::tableize($name);
 
2254
        if ($this->hasColumn($name) || $this->hasField($name)) {
 
2255
            return $this->getFieldName($this->getColumnName($name));
 
2256
        } else if ($this->hasColumn($fieldName) || $this->hasField($fieldName)) {
 
2257
            return $this->getFieldName($this->getColumnName($fieldName));
 
2258
        } else {
 
2259
            return false;
 
2260
        }
 
2261
    }
 
2262
 
 
2263
    /**
 
2264
     * __call
 
2265
     *
 
2266
     * Adds support for magic finders.
 
2267
     * findByColumnName, findByRelationAlias
 
2268
     * findById, findByContactId, etc.
 
2269
     *
 
2270
     * @return void
 
2271
     */
 
2272
    public function __call($method, $arguments)
 
2273
    {
 
2274
        $lcMethod = strtolower($method);
 
2275
 
 
2276
        if (substr($lcMethod, 0, 6) == 'findby') {
 
2277
            $by = substr($method, 6, strlen($method));
 
2278
            $method = 'findBy';
 
2279
        } else if (substr($lcMethod, 0, 9) == 'findoneby') {
 
2280
            $by = substr($method, 9, strlen($method));
 
2281
            $method = 'findOneBy';
 
2282
        }
 
2283
 
 
2284
        if (isset($by)) {
 
2285
            if ( ! isset($arguments[0])) {
 
2286
                throw new Doctrine_Table_Exception('You must specify the value to findBy');
 
2287
            }
 
2288
 
 
2289
            $fieldName = $this->_resolveFindByFieldName($by);
 
2290
            $hydrationMode = isset($arguments[1]) ? $arguments[1]:null;
 
2291
            if ($this->hasField($fieldName)) {
 
2292
                return $this->$method($fieldName, $arguments[0], $hydrationMode);
 
2293
            } else if ($this->hasRelation($by)) {
 
2294
                $relation = $this->getRelation($by);
 
2295
 
 
2296
                if ($relation['type'] === Doctrine_Relation::MANY) {
 
2297
                    throw new Doctrine_Table_Exception('Cannot findBy many relationship.');
 
2298
                }
 
2299
 
 
2300
                return $this->$method($relation['local'], $arguments[0], $hydrationMode);
 
2301
            } else {
 
2302
                throw new Doctrine_Table_Exception('Cannot find by: ' . $by . '. Invalid column or relationship alias.');
 
2303
            }
 
2304
        }
 
2305
 
 
2306
        // Forward the method on to the record instance and see if it has anything or one of its behaviors
 
2307
        try {
 
2308
            return call_user_func_array(array($this->getRecordInstance(), $method . 'TableProxy'), $arguments);
 
2309
        } catch (Doctrine_Record_UnknownPropertyException $e) {}
 
2310
 
 
2311
        throw new Doctrine_Table_Exception(sprintf('Unknown method %s::%s', get_class($this), $method));
 
2312
    }
 
2313
}