Table.php 33.3 KB
Newer Older
doctrine's avatar
doctrine committed
1
<?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.com>.
 */
21

doctrine's avatar
doctrine committed
22 23 24
/**
 * Doctrine_Table   represents a database table
 *                  each Doctrine_Table holds the information of foreignKeys and associations
25
 *
doctrine's avatar
doctrine committed
26 27 28 29 30 31 32
 *
 * @author      Konsta Vesterinen
 * @package     Doctrine ORM
 * @url         www.phpdoctrine.com
 * @license     LGPL
 * @version     1.0 alpha
 */
33
class Doctrine_Table extends Doctrine_Configurable implements Countable {
doctrine's avatar
doctrine committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
    /**
     * @var boolean $isNewEntry                         whether ot not this table created a new record or not, used only internally
     */
    private $isNewEntry       = false;
    /**
     * @var array $data                                 temporary data which is then loaded into Doctrine_Record::$data
     */
    private $data             = array();
    /**
     * @var array $relations                            an array containing all the Doctrine_Relation objects for this table
     */
    private $relations        = array();
    /**
     * @var array $primaryKeys                          an array containing all primary key column names
     */
    private $primaryKeys      = array();
    /**
     * @var mixed $identifier
     */
    private $identifier;
    /**
55 56
     * @see Doctrine_Identifier constants
     * @var integer $identifierType                     the type of identifier this table uses
doctrine's avatar
doctrine committed
57 58 59 60 61 62 63
     */
    private $identifierType;
    /**
     * @var string $query                               cached simple query
     */
    private $query;
    /**
zYne's avatar
zYne committed
64
     * @var Doctrine_Connection $connection             Doctrine_Connection object that created this table
doctrine's avatar
doctrine committed
65
     */
zYne's avatar
zYne committed
66
    private $connection;
doctrine's avatar
doctrine committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    /**
     * @var string $name                                name of the component, for example component name of the GroupTable is 'Group'
     */
    private $name;
    /**
     * @var string $tableName                           database table name, in most cases this is the same as component name but in some cases
     *                                                  where one-table-multi-class inheritance is used this will be the name of the inherited table
     */
    private $tableName;
    /**
     * @var string $sequenceName                        Some databases need sequences instead of auto incrementation primary keys, you can set specific
     *                                                  sequence for your table by calling setSequenceName()
     */
    private $sequenceName;
    /**
     * @var array $identityMap                          first level cache
     */
    private $identityMap        = array();
85 86 87
    /**
     * @var Doctrine_Repository $repository             record repository
     */
doctrine's avatar
doctrine committed
88
    private $repository;
89

doctrine's avatar
doctrine committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    /**
     * @var Doctrine_Cache $cache                       second level cache
     */
    private $cache;
    /**
     * @var array $columns                              an array of column definitions
     */
    private $columns;
    /**
     * @var array $bound                                bound relations
     */
    private $bound              = array();
    /**
     * @var array $boundAliases                         bound relation aliases
     */
    private $boundAliases       = array();
    /**
107 108
     * @var integer $columnCount                        cached column count, Doctrine_Record uses this column count in when
     *                                                  determining its state
doctrine's avatar
doctrine committed
109 110 111 112 113 114 115 116 117 118 119 120 121
     */
    private $columnCount;


    /**
     * @var array $inheritanceMap                       inheritanceMap is used for inheritance mapping, keys representing columns and values
     *                                                  the column values that should correspond to child classes
     */
    private $inheritanceMap     = array();
    /**
     * @var array $parents                              the parent classes of this component
     */
    private $parents            = array();
doctrine's avatar
doctrine committed
122 123 124 125
    /**
     * @var array $enum                                 enum value arrays
     */
    private $enum               = array();
126 127 128 129
    /**
     * @var boolean $hasDefaultValues                   whether or not this table has default values
     */
    private $hasDefaultValues;
doctrine's avatar
doctrine committed
130 131


doctrine's avatar
doctrine committed
132

zYne's avatar
zYne committed
133

doctrine's avatar
doctrine committed
134 135
    /**
     * the constructor
zYne's avatar
zYne committed
136 137
     * @throws Doctrine_Connection_Exception    if there are no opened connections
     * @throws Doctrine_Table_Exception         if there is already an instance of this table
doctrine's avatar
doctrine committed
138 139
     * @return void
     */
140 141
    public function __construct($name, Doctrine_Connection $conn) {
        $this->connection = $conn;
doctrine's avatar
doctrine committed
142

zYne's avatar
zYne committed
143
        $this->setParent($this->connection);
doctrine's avatar
doctrine committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

        $this->name = $name;

        if( ! class_exists($name) || empty($name))
            throw new Doctrine_Exception("Couldn't find class $name");

        $record = new $name($this);

        $names = array();

        $class = $name;

        // get parent classes

        do {
zYne's avatar
zYne committed
159 160
            if($class == "Doctrine_Record") 
                break;
doctrine's avatar
doctrine committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

           	$name  = $class;
            $names[] = $name;
        } while($class = get_parent_class($class));

        // reverse names
        $names = array_reverse($names);

        // create database table
        if(method_exists($record,"setTableDefinition")) {
            $record->setTableDefinition();

            $this->columnCount = count($this->columns);

            if(isset($this->columns)) {
zYne's avatar
zYne committed
176 177
                                      	
                // get the declaring class of setTableDefinition method
doctrine's avatar
doctrine committed
178 179 180 181
                $method    = new ReflectionMethod($this->name,"setTableDefinition");
                $class     = $method->getDeclaringClass();

                if( ! isset($this->tableName))
182
                    $this->tableName = Doctrine::tableize($class->getName());
doctrine's avatar
doctrine committed
183 184 185

                switch(count($this->primaryKeys)):
                    case 0:
zYne's avatar
zYne committed
186
                        $this->columns = array_merge(array("id" => array("integer", 20, array("autoincrement" => true, "primary" => true))), $this->columns);
doctrine's avatar
doctrine committed
187 188 189
                        $this->primaryKeys[] = "id";
                        $this->identifier = "id";
                        $this->identifierType = Doctrine_Identifier::AUTO_INCREMENT;
190
                        $this->columnCount++;
doctrine's avatar
doctrine committed
191 192 193 194 195
                    break;
                    default:
                        if(count($this->primaryKeys) > 1) {
                            $this->identifier = $this->primaryKeys;
                            $this->identifierType = Doctrine_Identifier::COMPOSITE;
196

doctrine's avatar
doctrine committed
197 198
                        } else {
                            foreach($this->primaryKeys as $pk) {
199
                                $e = $this->columns[$pk][2];
200

201
                                $found = false;
doctrine's avatar
doctrine committed
202

203
                                foreach($e as $option => $value) {
doctrine's avatar
doctrine committed
204 205
                                    if($found)
                                        break;
206

doctrine's avatar
doctrine committed
207
                                    $e2 = explode(":",$option);
208

doctrine's avatar
doctrine committed
209 210 211 212 213 214 215 216 217 218 219 220 221
                                    switch(strtolower($e2[0])):
                                        case "autoincrement":
                                            $this->identifierType = Doctrine_Identifier::AUTO_INCREMENT;
                                            $found = true;
                                        break;
                                        case "seq":
                                            $this->identifierType = Doctrine_Identifier::SEQUENCE;
                                            $found = true;
                                        break;
                                    endswitch;
                                }
                                if( ! isset($this->identifierType))
                                    $this->identifierType = Doctrine_Identifier::NORMAL;
222

doctrine's avatar
doctrine committed
223 224 225 226 227
                                $this->identifier = $pk;
                            }
                        }
                endswitch;

zYne's avatar
zYne committed
228 229 230 231 232
                 if($this->getAttribute(Doctrine::ATTR_CREATE_TABLES)) {
                    if(Doctrine_DataDict::isValidClassname($class->getName())) {
                        $dict      = new Doctrine_DataDict($this->getConnection()->getDBH());
                        $dict->createTable($this->tableName, $this->columns);
                    }
doctrine's avatar
doctrine committed
233 234 235 236 237 238
                }

            }
        } else {
            throw new Doctrine_Exception("Class '$name' has no table definition.");
        }
239

zYne's avatar
zYne committed
240

doctrine's avatar
doctrine committed
241 242 243 244 245 246 247 248 249
        $record->setUp();

        // save parents
        array_pop($names);
        $this->parents   = $names;

        $this->query     = "SELECT ".implode(", ",array_keys($this->columns))." FROM ".$this->getTableName();

        // check if an instance of this table is already initialized
zYne's avatar
zYne committed
250
        if( ! $this->connection->addTable($this))
doctrine's avatar
doctrine committed
251
            throw new Doctrine_Table_Exception();
252
            
doctrine's avatar
doctrine committed
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
        $this->repository = new Doctrine_Repository($this);
    }
    /**
     * @return Doctrine_Repository
     */
    public function getRepository() {
        return $this->repository;
    }
    /**
     * setColumn
     * @param string $name
     * @param string $type
     * @param integer $length
     * @param mixed $options
     * @return void
     */
269 270 271 272
    final public function setColumn($name, $type, $length, $options = array()) {
        if(is_string($options)) 
            $options = explode('|', $options);

273 274 275 276 277 278 279 280
        foreach($options as $k => $option) {
            if(is_numeric($k)) {
                if( ! empty($option))
                    $options[$option] = true;

                unset($options[$k]);
            }
        }
281
        $name = strtolower($name);
doctrine's avatar
doctrine committed
282
        $this->columns[$name] = array($type,$length,$options);
283

284
        if(isset($options['primary'])) {
doctrine's avatar
doctrine committed
285 286
            $this->primaryKeys[] = $name;
        }
287 288 289 290 291 292 293 294 295 296 297 298
        if(isset($options['default'])) {
            $this->hasDefaultValues = true;
        }
    }
    /**
     * hasDefaultValues
     * returns true if this table has default values, otherwise false
     *
     * @return boolean
     */
    public function hasDefaultValues() {
        return $this->hasDefaultValues;
doctrine's avatar
doctrine committed
299
    }
300 301 302 303 304 305 306 307
    /**
     * getDefaultValueOf
     * returns the default value(if any) for given column
     *
     * @param string $column
     * @return mixed
     */
    public function getDefaultValueOf($column) {
308
        $column = strtolower($column);
309 310
        if( ! isset($this->columns[$column]))
            throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$column." doesn't exist.");
zYne's avatar
zYne committed
311

312 313 314 315 316 317
        if(isset($this->columns[$column][2]['default'])) {

            return $this->columns[$column][2]['default'];
        } else
            return null;
    }
doctrine's avatar
doctrine committed
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
    /**
     * @return mixed
     */
    final public function getIdentifier() {
        return $this->identifier;
    }
    /**
     * @return integer
     */
    final public function getIdentifierType() {
        return $this->identifierType;
    }
    /**
     * hasColumn
     * @return boolean
     */
    final public function hasColumn($name) {
        return isset($this->columns[$name]);
    }
    /**
     * @param mixed $key
     * @return void
     */
    final public function setPrimaryKey($key) {
        switch(gettype($key)):
            case "array":
                $this->primaryKeys = array_values($key);
            break;
            case "string":
                $this->primaryKeys[] = $key;
            break;
        endswitch;
    }
    /**
     * returns all primary keys
     * @return array
     */
    final public function getPrimaryKeys() {
        return $this->primaryKeys;
    }
    /**
     * @return boolean
     */
    final public function hasPrimaryKey($key) {
        return in_array($key,$this->primaryKeys);
    }
    /**
     * @param $sequence
     * @return void
     */
    final public function setSequenceName($sequence) {
        $this->sequenceName = $sequence;
    }
    /**
     * @return string   sequence name
     */
    final public function getSequenceName() {
        return $this->sequenceName;
    }
377 378 379 380
    /**
     * getParents
     */
    final public function getParents() {
doctrine's avatar
doctrine committed
381
        return $this->parents;
382 383 384 385 386 387 388
    }
    /**
     * @return boolean
     */
    final public function hasInheritanceMap() {
        return (empty($this->inheritanceMap));
    }
doctrine's avatar
doctrine committed
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
    /**
     * setInheritanceMap
     * @param array $inheritanceMap
     * @return void
     */
    final public function setInheritanceMap(array $inheritanceMap) {
        $this->inheritanceMap = $inheritanceMap;
    }
    /**
     * @return array        inheritance map (array keys as fields)
     */
    final public function getInheritanceMap() {
        return $this->inheritanceMap;
    }
    /**
     * return all composite paths in the form [component1].[component2]. . .[componentN]
     * @return array
     */
    final public function getCompositePaths() {
        $array = array();
        $name  = $this->getComponentName();
        foreach($this->bound as $k=>$a) {
            try {
412
            $fk = $this->getRelation($k);
doctrine's avatar
doctrine committed
413 414 415 416 417 418 419 420 421 422 423 424 425
            switch($fk->getType()):
                case Doctrine_Relation::ONE_COMPOSITE:
                case Doctrine_Relation::MANY_COMPOSITE:
                    $n = $fk->getTable()->getComponentName();
                    $array[] = $name.".".$n;
                    $e = $fk->getTable()->getCompositePaths();
                    if( ! empty($e)) {
                        foreach($e as $name) {
                            $array[] = $name.".".$n.".".$name;
                        }
                    }
                break;
            endswitch;
426
            } catch(Doctrine_Table_Exception $e) {
427

doctrine's avatar
doctrine committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
            }
        }
        return $array;
    }
    /**
     * returns all bound relations
     *
     * @return array
     */
    final public function getBounds() {
        return $this->bound;
    }
    /**
     * returns a bound relation array
     *
     * @param string $name
     * @return array
     */
    final public function getBound($name) {
447
        if( ! isset($this->bound[$name]))
448
            throw new Doctrine_Table_Exception('Unknown bound '.$name);
doctrine's avatar
doctrine committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

        return $this->bound[$name];
    }
    /**
     * returns a bound relation array
     *
     * @param string $name
     * @return array
     */
    final public function getBoundForName($name) {
        foreach($this->bound as $k => $bound) {
            if($bound[3] == $name) {
                return $this->bound[$k];
            }
        }
464
        throw new Doctrine_Table_Exception('Unknown bound '.$name);
doctrine's avatar
doctrine committed
465 466 467 468 469 470 471 472 473 474
    }
    /**
     * returns the alias for given component name
     *
     * @param string $name
     * @return string
     */
    final public function getAlias($name) {
        if(isset($this->boundAliases[$name]))
            return $this->boundAliases[$name];
475

doctrine's avatar
doctrine committed
476 477 478 479
        return $name;
    }
    /**
     * returns component name for given alias
480
     *
doctrine's avatar
doctrine committed
481 482 483 484 485 486
     * @param string $alias
     * @return string
     */
    final public function getAliasName($alias) {
        if($name = array_search($this->boundAliases,$alias))
            return $name;
487

488
        throw new Doctrine_Table_Exception('Unknown alias '.$alias);
doctrine's avatar
doctrine committed
489 490 491
    }
    /**
     * unbinds all relations
492
     *
doctrine's avatar
doctrine committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506
     * @return void
     */
    final public function unbindAll() {
        $this->bound        = array();
        $this->relations    = array();
        $this->boundAliases = array();
    }
    /**
     * unbinds a relation
     * returns true on success, false on failure
     *
     * @param $name
     * @return boolean
     */
507
    final public function unbind($name) {
doctrine's avatar
doctrine committed
508 509
        if( ! isset($this->bound[$name]))
            return false;
510

doctrine's avatar
doctrine committed
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
        unset($this->bound[$name]);

        if(isset($this->relations[$name]))
            unset($this->relations[$name]);

        if(isset($this->boundAliases[$name]))
            unset($this->boundAliases[$name]);

        return true;
    }
    /**
     * binds a relation
     *
     * @param string $name
     * @param string $field
     * @return void
     */
    final public function bind($name,$field,$type,$localKey) {
        if(isset($this->relations[$name]))
530
            throw new Doctrine_Table_Exception('Relation already set for '.$name);
doctrine's avatar
doctrine committed
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551

        $e          = explode(" as ",$name);
        $name       = $e[0];

        if(isset($e[1])) {
            $alias = $e[1];
            $this->boundAliases[$name] = $alias;
        } else
            $alias = $name;


        $this->bound[$alias] = array($field,$type,$localKey,$name);
    }
    /**
     * getComponentName
     * @return string                   the component name
     */
    final public function getComponentName() {
        return $this->name;
    }
    /**
zYne's avatar
zYne committed
552
     * @return Doctrine_Connection
doctrine's avatar
doctrine committed
553
     */
zYne's avatar
zYne committed
554 555
    final public function getConnection() {
        return $this->connection;
doctrine's avatar
doctrine committed
556 557 558 559 560 561 562
    }
    /**
     * @return Doctrine_Cache
     */
    final public function getCache() {
        return $this->cache;
    }
563 564 565 566 567 568 569 570 571 572 573
    /**
     * hasRelatedComponent
     * @return boolean
     */
    final public function hasRelatedComponent($name, $component) {
         return (strpos($this->bound[$name][0], $component.'.') !== false);
    }
    /**
     * @param string $name              component name of which a foreign key object is bound
     * @return boolean
     */
574
    final public function hasRelation($name) {
575 576 577 578 579 580 581 582 583 584
        if(isset($this->bound[$name]))
            return true;

        foreach($this->bound as $k=>$v)
        {
            if($this->hasRelatedComponent($k, $name))
                return true;
        }
        return false;
    }
doctrine's avatar
doctrine committed
585 586 587 588
    /**
     * @param string $name              component name of which a foreign key object is bound
     * @return Doctrine_Relation
     */
589
    final public function getRelation($name) {
590 591
        $original = $name;

doctrine's avatar
doctrine committed
592 593 594 595 596 597 598 599 600 601
        if(isset($this->relations[$name]))
            return $this->relations[$name];

        if(isset($this->bound[$name])) {
            $type       = $this->bound[$name][1];
            $local      = $this->bound[$name][2];
            list($component, $foreign) = explode(".",$this->bound[$name][0]);
            $alias      = $name;
            $name       = $this->bound[$alias][3];

zYne's avatar
zYne committed
602
            $table      = $this->connection->getTable($name);
doctrine's avatar
doctrine committed
603 604 605 606 607 608 609 610 611

            if($component == $this->name || in_array($component, $this->parents)) {

                // ONE-TO-ONE
                if($type == Doctrine_Relation::ONE_COMPOSITE ||
                   $type == Doctrine_Relation::ONE_AGGREGATE) {
                    if( ! isset($local))
                        $local = $table->getIdentifier();

doctrine's avatar
doctrine committed
612
                    $relation = new Doctrine_LocalKey($table,$foreign,$local,$type, $alias);
doctrine's avatar
doctrine committed
613
                } else
614
                    throw new Doctrine_Table_Exception("Only one-to-one relations are possible when local reference key is used.");
doctrine's avatar
doctrine committed
615

zYne's avatar
zYne committed
616 617 618
            } elseif($component == $name || 
                    ($component == $alias && ($name == $this->name || in_array($name,$this->parents)))) {

doctrine's avatar
doctrine committed
619 620 621 622
                if( ! isset($local))
                    $local = $this->identifier;

                // ONE-TO-MANY or ONE-TO-ONE
doctrine's avatar
doctrine committed
623
                $relation = new Doctrine_ForeignKey($table, $local, $foreign, $type, $alias);
doctrine's avatar
doctrine committed
624 625 626 627 628

            } else {
                // MANY-TO-MANY
                // only aggregate relations allowed

629
                if($type != Doctrine_Relation::MANY_AGGREGATE)
630
                    throw new Doctrine_Table_Exception("Only aggregate relations are allowed for many-to-many relations");
doctrine's avatar
doctrine committed
631 632 633 634 635 636 637

                $classes = array_merge($this->parents, array($this->name));

                foreach(array_reverse($classes) as $class) {
                    try {
                        $bound = $table->getBoundForName($class);
                        break;
638
                    } catch(Doctrine_Table_Exception $exc) { }
doctrine's avatar
doctrine committed
639 640 641 642 643 644 645 646 647

                }
                if( ! isset($local))
                    $local = $this->identifier;

                $e2    = explode(".",$bound[0]);
                $fields = explode("-",$e2[1]);

                if($e2[0] != $component)
648
                    throw new Doctrine_Table_Exception($e2[0]." doesn't match ".$component);
doctrine's avatar
doctrine committed
649

zYne's avatar
zYne committed
650
                $associationTable = $this->connection->getTable($e2[0]);
doctrine's avatar
doctrine committed
651 652 653

                if(count($fields) > 1) {
                    // SELF-REFERENCING THROUGH JOIN TABLE
doctrine's avatar
doctrine committed
654
                    $this->relations[$e2[0]] = new Doctrine_ForeignKey($associationTable,$local,$fields[0],Doctrine_Relation::MANY_COMPOSITE, $e2[0]);
655

656
                    $relation = new Doctrine_Association_Self($table,$associationTable,$fields[0],$fields[1], $type, $alias);
doctrine's avatar
doctrine committed
657
                } else {
658 659

                    // auto initialize a new one-to-one relationship for association table
zYne's avatar
zYne committed
660 661
                    $associationTable->bind($this->getComponentName(),  $associationTable->getComponentName(). '.' .$e2[1], Doctrine_Relation::ONE_AGGREGATE, 'id');
                    $associationTable->bind($table->getComponentName(), $associationTable->getComponentName(). '.' .$foreign, Doctrine_Relation::ONE_AGGREGATE, 'id');
662

doctrine's avatar
doctrine committed
663
                    // NORMAL MANY-TO-MANY RELATIONSHIP
doctrine's avatar
doctrine committed
664
                    $this->relations[$e2[0]] = new Doctrine_ForeignKey($associationTable,$local,$e2[1],Doctrine_Relation::MANY_COMPOSITE, $e2[0]);
doctrine's avatar
doctrine committed
665

doctrine's avatar
doctrine committed
666
                    $relation = new Doctrine_Association($table, $associationTable, $e2[1], $foreign, $type, $alias);
doctrine's avatar
doctrine committed
667 668 669 670 671 672
                }

            }
            $this->relations[$alias] = $relation;
            return $this->relations[$alias];
        }
673 674 675 676 677
        try {
            throw new Doctrine_Table_Exception($this->name . " doesn't have a relation to " . $original);
        } catch(Exception $e) {
            print $e;
        }
doctrine's avatar
doctrine committed
678 679 680 681 682 683
    }
    /**
     * returns an array containing all foreign key objects
     *
     * @return array
     */
684
    final public function getRelations() {
doctrine's avatar
doctrine committed
685 686
        $a = array();
        foreach($this->bound as $k=>$v) {
687
            $this->getRelation($k);
doctrine's avatar
doctrine committed
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
        }

        return $this->relations;
    }
    /**
     * sets the database table name
     *
     * @param string $name              database table name
     * @return void
     */
    final public function setTableName($name) {
        $this->tableName = $name;
    }

    /**
     * returns the database table name
     *
     * @return string
     */
    final public function getTableName() {
        return $this->tableName;
    }
    /**
     * create
     * creates a new record
     *
     * @param $array                    an array where keys are field names and values representing field values
     * @return Doctrine_Record
     */
    public function create(array $array = array()) {
718
        $this->data         = $array;
doctrine's avatar
doctrine committed
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
        $this->isNewEntry   = true;
        $record = new $this->name($this);
        $this->isNewEntry   = false;
        $this->data         = array();
        return $record;
    }
    /**
     * finds a record by its identifier
     *
     * @param $id                       database row id
     * @throws Doctrine_Find_Exception
     * @return Doctrine_Record          a record for given database identifier
     */
    public function find($id) {
        if($id !== null) {
            if( ! is_array($id))
                $id = array($id);
736
            else
doctrine's avatar
doctrine committed
737 738 739 740
                $id = array_values($id);

            $query  = $this->query." WHERE ".implode(" = ? AND ",$this->primaryKeys)." = ?";
            $query  = $this->applyInheritance($query);
doctrine's avatar
doctrine committed
741 742


doctrine's avatar
doctrine committed
743 744
            $params = array_merge($id, array_values($this->inheritanceMap));

zYne's avatar
zYne committed
745
            $stmt  = $this->connection->execute($query,$params);
doctrine's avatar
doctrine committed
746 747

            $this->data = $stmt->fetch(PDO::FETCH_ASSOC);
doctrine's avatar
doctrine committed
748 749

            if($this->data === false)
750
                return false;
doctrine's avatar
doctrine committed
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
        }
        return $this->getRecord();
    }
    /**
     * applyInheritance
     * @param $where                    query where part to be modified
     * @return string                   query where part with column aggregation inheritance added
     */
    final public function applyInheritance($where) {
        if( ! empty($this->inheritanceMap)) {
            $a = array();
            foreach($this->inheritanceMap as $field => $value) {
                $a[] = $field." = ?";
            }
            $i = implode(" AND ",$a);
            $where .= " AND $i";
        }
        return $where;
    }
    /**
     * findAll
     * returns a collection of records
     *
     * @return Doctrine_Collection
     */
    public function findAll() {
zYne's avatar
zYne committed
777
        $graph = new Doctrine_Query($this->connection);
doctrine's avatar
doctrine committed
778 779 780 781
        $users = $graph->query("FROM ".$this->name);
        return $users;
    }
    /**
782 783
     * findByDql
     * finds records with given DQL where clause
doctrine's avatar
doctrine committed
784 785
     * returns a collection of records
     *
786
     * @param string $dql               DQL after WHERE clause
doctrine's avatar
doctrine committed
787 788 789
     * @param array $params             query parameters
     * @return Doctrine_Collection
     */
790
    public function findBySql($dql, array $params = array()) {
zYne's avatar
zYne committed
791
        $q = new Doctrine_Query($this->connection);
792
        $users = $q->query("FROM ".$this->name." WHERE ".$dql, $params);
doctrine's avatar
doctrine committed
793 794
        return $users;
    }
795

796 797 798
    public function findByDql($dql, array $params = array()) {
        return $this->findBySql($dql, $params);
    }
doctrine's avatar
doctrine committed
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
    /**
     * clear
     * clears the first level cache (identityMap)
     *
     * @return void
     */
    public function clear() {
        $this->identityMap = array();
    }
    /**
     * getRecord
     * first checks if record exists in identityMap, if not
     * returns a new record
     *
     * @return Doctrine_Record
     */
    public function getRecord() {
zYne's avatar
zYne committed
816 817
        $this->data = array_change_key_case($this->data, CASE_LOWER);

doctrine's avatar
doctrine committed
818 819 820 821 822 823 824
        $key = $this->getIdentifier();

        if( ! is_array($key))
            $key = array($key);

        foreach($key as $k) {
            if( ! isset($this->data[$k]))
825
                throw new Doctrine_Exception("Primary key value for $k wasn't found");
826

doctrine's avatar
doctrine committed
827 828
            $id[] = $this->data[$k];
        }
829

doctrine's avatar
doctrine committed
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
        $id = implode(' ', $id);

        if(isset($this->identityMap[$id]))
            $record = $this->identityMap[$id];
        else {
            $record = new $this->name($this);
            $this->identityMap[$id] = $record;
        }
        $this->data = array();

        return $record;
    }
    /**
     * @param $id                       database row id
     * @throws Doctrine_Find_Exception
     * @return DAOProxy                 a proxy for given identifier
     */
    final public function getProxy($id = null) {
        if($id !== null) {
            $query = "SELECT ".implode(", ",$this->primaryKeys)." FROM ".$this->getTableName()." WHERE ".implode(" = ? && ",$this->primaryKeys)." = ?";
            $query = $this->applyInheritance($query);
851

doctrine's avatar
doctrine committed
852 853
            $params = array_merge(array($id), array_values($this->inheritanceMap));

zYne's avatar
zYne committed
854
            $this->data = $this->connection->execute($query,$params)->fetch(PDO::FETCH_ASSOC);
doctrine's avatar
doctrine committed
855 856

            if($this->data === false)
857
                return false;
doctrine's avatar
doctrine committed
858 859 860 861 862
        }
        return $this->getRecord();
    }
    /**
     * getTableDescription
zYne's avatar
zYne committed
863
     * @return array
doctrine's avatar
doctrine committed
864 865 866 867
     */
    final public function getTableDescription() {
        return $this->columns;
    }
868 869
    /**
     * count
870
     *
871 872 873
     * @return integer
     */
    public function count() {
zYne's avatar
zYne committed
874
        $a = $this->connection->getDBH()->query("SELECT COUNT(1) FROM ".$this->tableName)->fetch(PDO::FETCH_NUM);
875 876
        return current($a);
    }
doctrine's avatar
doctrine committed
877 878 879 880
    /**
     * @return Doctrine_Query                           a Doctrine_Query object
     */
    public function getQueryObject() {
zYne's avatar
zYne committed
881
        $graph = new Doctrine_Query($this->getConnection());
doctrine's avatar
doctrine committed
882 883 884 885 886 887 888 889 890 891 892 893
        $graph->load($this->getComponentName());
        return $graph;
    }
    /**
     * execute
     * @param string $query
     * @param array $array
     * @param integer $limit
     * @param integer $offset
     */
    public function execute($query, array $array = array(), $limit = null, $offset = null) {
        $coll  = new Doctrine_Collection($this);
zYne's avatar
zYne committed
894
        $query = $this->connection->modifyLimitQuery($query,$limit,$offset);
doctrine's avatar
doctrine committed
895
        if( ! empty($array)) {
zYne's avatar
zYne committed
896
            $stmt = $this->connection->getDBH()->prepare($query);
doctrine's avatar
doctrine committed
897 898
            $stmt->execute($array);
        } else {
zYne's avatar
zYne committed
899
            $stmt = $this->connection->getDBH()->query($query);
doctrine's avatar
doctrine committed
900 901 902 903 904 905 906 907 908 909 910
        }
        $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $stmt->closeCursor();

        foreach($data as $row) {
            $this->data = $row;
            $record = $this->getRecord();
            $coll->add($record);
        }
        return $coll;
    }
doctrine's avatar
doctrine committed
911 912 913 914 915 916 917 918
    /**
     * sets enumerated value array for given field
     *
     * @param string $field
     * @param array $values
     * @return void
     */
    final public function setEnumValues($field, array $values) {
919
        $this->enum[strtolower($field)] = $values;
doctrine's avatar
doctrine committed
920
    }
doctrine's avatar
doctrine committed
921 922 923 924 925
    /**
     * @param string $field
     * @return array
     */
    final public function getEnumValues($field) {
926
        if(isset($this->enum[$field]))
doctrine's avatar
doctrine committed
927 928 929 930
            return $this->enum[$field];
        else
            return array();
    }
doctrine's avatar
doctrine committed
931 932
    /**
     * enumValue
933 934 935 936
     *
     * @param string $field
     * @param integer $index
     * @return mixed
doctrine's avatar
doctrine committed
937 938 939 940 941 942
     */
    final public function enumValue($field, $index) {
        return isset($this->enum[$field][$index])?$this->enum[$field][$index]:$index;
    }
    /**
     * enumIndex
943 944 945 946
     *
     * @param string $field
     * @param mixed $value
     * @return mixed
doctrine's avatar
doctrine committed
947 948
     */
    final public function enumIndex($field, $value) {
949 950 951 952 953 954
        if( ! isset($this->enum[$field])) 
            $values = array();
        else
            $values = $this->enum[$field];

        return array_search($value, $values);
doctrine's avatar
doctrine committed
955
    }
doctrine's avatar
doctrine committed
956 957 958 959
    /**
     * @return integer
     */
    final public function getColumnCount() {
960
        return $this->columnCount;
doctrine's avatar
doctrine committed
961
    }
doctrine's avatar
doctrine committed
962

doctrine's avatar
doctrine committed
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
    /**
     * returns all columns and their definitions
     *
     * @return array
     */
    final public function getColumns() {
        return $this->columns;
    }
    /**
     * returns an array containing all the column names
     *
     * @return array
     */
    public function getColumnNames() {
        return array_keys($this->columns);
    }
doctrine's avatar
doctrine committed
979 980
    /**
     * getDefinitionOf
981 982
     *
     * @return mixed        array on success, false on failure
doctrine's avatar
doctrine committed
983 984 985 986
     */
    public function getDefinitionOf($column) {
        if(isset($this->columns[$column]))
            return $this->columns[$column];
987 988

        return false;
doctrine's avatar
doctrine committed
989
    }
doctrine's avatar
doctrine committed
990 991
    /**
     * getTypeOf
992 993
     *
     * @return mixed        string on success, false on failure
doctrine's avatar
doctrine committed
994 995 996
     */
    public function getTypeOf($column) {
        if(isset($this->columns[$column]))
997
            return $this->columns[$column][0];
998 999

        return false;
doctrine's avatar
doctrine committed
1000
    }
doctrine's avatar
doctrine committed
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
    /**
     * setData
     * doctrine uses this function internally
     * users are strongly discouraged to use this function
     *
     * @param array $data               internal data
     * @return void
     */
    public function setData(array $data) {
        $this->data = $data;
    }
    /**
     * returns the maximum primary key value
     *
     * @return integer
     */
    final public function getMaxIdentifier() {
        $sql  = "SELECT MAX(".$this->getIdentifier().") FROM ".$this->getTableName();
zYne's avatar
zYne committed
1019
        $stmt = $this->connection->getDBH()->query($sql);
doctrine's avatar
doctrine committed
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
        $data = $stmt->fetch(PDO::FETCH_NUM);
        return isset($data[0])?$data[0]:1;
    }
    /**
     * return whether or not a newly created object is new or not
     *
     * @return boolean
     */
    final public function isNewEntry() {
        return $this->isNewEntry;
    }
    /**
     * returns simple cached query
     *
     * @return string
     */
    final public function getQuery() {
        return $this->query;
    }
    /**
1040
     * returns internal data, used by Doctrine_Record instances
doctrine's avatar
doctrine committed
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
     * when retrieving data from database
     *
     * @return array
     */
    final public function getData() {
        return $this->data;
    }
    /**
     * returns a string representation of this object
     *
     * @return string
     */
    public function __toString() {
        return Doctrine_Lib::getTableAsString($this);
    }
}
1057