Source for file Table.php

Documentation is available at Table.php

  1. <?php
  2. /*
  3.  *  $Id: Table.php 2279 2007-08-27 15:04:32Z Jonathan.Wage $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information, see
  19.  * <http://www.phpdoctrine.com>.
  20.  */
  21. /**
  22.  * Doctrine_Table   represents a database table
  23.  *                  each Doctrine_Table holds the information of foreignKeys and associations
  24.  *
  25.  *
  26.  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
  27.  * @package     Doctrine
  28.  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  29.  * @version     $Revision: 2279 $
  30.  * @category    Object Relational Mapping
  31.  * @link        www.phpdoctrine.com
  32.  * @since       1.0
  33.  */
  34. class Doctrine_Table extends Doctrine_Configurable implements Countable
  35. {
  36.     /**
  37.      * @var array $data                                 temporary data which is then loaded into Doctrine_Record::$data
  38.      */
  39.     private $data             = array();
  40.     /**
  41.      * @var array $primaryKeys                          an array containing all primary key column names
  42.      */
  43.     private $primaryKeys      = array();
  44.     /**
  45.      * @var mixed $identifier 
  46.      */
  47.     private $identifier;
  48.     /**
  49.      * @see Doctrine_Identifier constants
  50.      * @var integer $identifierType                     the type of identifier this table uses
  51.      */
  52.     private $identifierType;
  53.     /**
  54.      * @var Doctrine_Connection $conn                   Doctrine_Connection object that created this table
  55.      */
  56.     private $conn;
  57.     /**
  58.      * @var array $identityMap                          first level cache
  59.      */
  60.     private $identityMap        = array();
  61.     /**
  62.      * @var Doctrine_Table_Repository $repository       record repository
  63.      */
  64.     private $repository;
  65.     /**
  66.      * @var array $columns                  an array of column definitions,
  67.      *                                       keys as column names and values as column definitions
  68.      *
  69.      *                                       the definition array has atleast the following values:
  70.      *
  71.      *                                       -- type         the column type, eg. 'integer'
  72.      *                                       -- length       the column length, eg. 11
  73.      *
  74.      *                                       additional keys:
  75.      *                                       -- notnull      whether or not the column is marked as notnull
  76.      *                                       -- values       enum values
  77.      *                                       -- notblank     notblank validator + notnull constraint
  78.      *                                       ... many more
  79.      */
  80.     protected $columns          = array();
  81.     /**
  82.      * @var array $columnAliases            an array of column aliases
  83.      *                                       keys as column aliases and values as column names
  84.      */
  85.     protected $columnAliases    = array();
  86.     /**
  87.      * @var integer $columnCount            cached column count, Doctrine_Record uses this column count in when
  88.      *                                       determining its state
  89.      */
  90.     private $columnCount;
  91.     /**
  92.      * @var boolean $hasDefaultValues       whether or not this table has default values
  93.      */
  94.     private $hasDefaultValues;
  95.     /**
  96.      * @var array $options                  an array containing all options
  97.      *
  98.      *       -- name                         name of the component, for example component name of the GroupTable is 'Group'
  99.      *
  100.      *       -- parents                      the parent classes of this component
  101.      *
  102.      *       -- declaringClass               name of the table definition declaring class (when using inheritance the class
  103.      *                                       that defines the table structure can be any class in the inheritance hierarchy,
  104.      *                                       hence we need reflection to check out which class actually calls setTableDefinition)
  105.      *
  106.      *       -- tableName                    database table name, in most cases this is the same as component name but in some cases
  107.      *                                       where one-table-multi-class inheritance is used this will be the name of the inherited table
  108.      *
  109.      *       -- sequenceName                 Some databases need sequences instead of auto incrementation primary keys,
  110.      *                                       you can set specific sequence for your table by calling setOption('sequenceName', $seqName)
  111.      *                                       where $seqName is the name of the desired sequence
  112.      *
  113.      *       -- enumMap                      enum value arrays
  114.      *
  115.      *       -- inheritanceMap               inheritanceMap is used for inheritance mapping, keys representing columns and values
  116.      *                                       the column values that should correspond to child classes
  117.      *
  118.      *       -- type                         table type (mysql example: INNODB)
  119.      *
  120.      *       -- charset                      character set
  121.      *
  122.      *       -- foreignKeys                  the foreign keys of this table
  123.      *
  124.      *       -- checks                       the check constraints of this table, eg. 'price > dicounted_price'
  125.      *
  126.      *       -- collation                    collation attribute
  127.      *
  128.      *       -- indexes                      the index definitions of this table
  129.      *
  130.      *       -- treeImpl                     the tree implementation of this table (if any)
  131.      *
  132.      *       -- treeOptions                  the tree options
  133.      *
  134.      *       -- versioning
  135.      */
  136.     protected $options          = array('name'           => null,
  137.                                         'tableName'      => null,
  138.                                         'sequenceName'   => null,
  139.                                         'inheritanceMap' => array(),
  140.                                         'enumMap'        => array(),
  141.                                         'engine'         => null,
  142.                                         'charset'        => null,
  143.                                         'collation'      => null,
  144.                                         'treeImpl'       => null,
  145.                                         'treeOptions'    => null,
  146.                                         'indexes'        => array(),
  147.                                         'parents'        => array(),
  148.                                         'versioning'     => null,
  149.                                         );
  150.     /**
  151.      * @var Doctrine_Tree $tree                 tree object associated with this table
  152.      */
  153.     protected $tree;
  154.     /**
  155.      * @var Doctrine_Relation_Parser $_parser   relation parser object
  156.      */
  157.     protected $_parser;
  158.     /**
  159.      * @var array $_templates                   an array containing all templates attached to this table
  160.      */
  161.     protected $_templates;
  162.  
  163.  
  164.     /**
  165.      * the constructor
  166.      * @throws Doctrine_Connection_Exception    if there are no opened connections
  167.      * @throws Doctrine_Table_Exception         if there is already an instance of this table
  168.      * @return void 
  169.      */
  170.     public function __construct($nameDoctrine_Connection $conn)
  171.     {
  172.         $this->conn = $conn;
  173.  
  174.         $this->setParent($this->conn);
  175.  
  176.         $this->options['name'$name;
  177.         $this->_parser = new Doctrine_Relation_Parser($this);
  178.  
  179.         if class_exists($name|| empty($name)) {
  180.             throw new Doctrine_Exception("Couldn't find class " $name);
  181.         }
  182.         $record new $name($this);
  183.  
  184.         $names array();
  185.  
  186.         $class $name;
  187.  
  188.         // get parent classes
  189.  
  190.         do {
  191.             if ($class == "Doctrine_Record"{
  192.                 break;                        
  193.             }
  194.  
  195.             $name  $class;
  196.             $names[$name;
  197.         while ($class get_parent_class($class));
  198.  
  199.         // reverse names
  200.         $names array_reverse($names);
  201.         // save parents
  202.         array_pop($names);
  203.         $this->options['parents'$names;
  204.  
  205.         // create database table
  206.         if (method_exists($record'setTableDefinition')) {
  207.             $record->setTableDefinition();
  208.             // get the declaring class of setTableDefinition method
  209.             $method new ReflectionMethod($this->options['name']'setTableDefinition');
  210.             $class  $method->getDeclaringClass();
  211.         else {
  212.             $class new ReflectionClass($class);
  213.         }
  214.         $this->options['declaringClass'$class;
  215.  
  216.         // set the table definition for the given tree implementation
  217.         if ($this->isTree()) {
  218.             $this->getTree()->setTableDefinition();
  219.         }
  220.         
  221.         $this->columnCount = count($this->columns);
  222.  
  223.         if isset($this->options['tableName'])) {
  224.             $this->options['tableName'Doctrine::tableize($class->getName());
  225.         }
  226.  
  227.         switch (count($this->primaryKeys)) {
  228.             case 0:
  229.                 $this->columns = array_merge(array('id' =>
  230.                                               array('type'          => 'integer',
  231.                                                     'length'        => 20,
  232.                                                     'autoincrement' => true,
  233.                                                     'primary'       => true))$this->columns);
  234.     
  235.                 $this->primaryKeys['id';
  236.                 $this->identifier = 'id';
  237.                 $this->identifierType = Doctrine::IDENTIFIER_AUTOINC;
  238.                 $this->columnCount++;
  239.                 break;
  240.             default:
  241.                 if (count($this->primaryKeys1{
  242.                     $this->identifier = $this->primaryKeys;
  243.                     $this->identifierType = Doctrine::IDENTIFIER_COMPOSITE;
  244.     
  245.                 else {
  246.                     foreach ($this->primaryKeys as $pk{
  247.                         $e $this->columns[$pk];
  248.     
  249.                         $found false;
  250.     
  251.                         foreach ($e as $option => $value{
  252.                             if ($found)
  253.                                 break;
  254.     
  255.                             $e2 explode(':'$option);
  256.     
  257.                             switch (strtolower($e2[0])) {
  258.                                 case 'autoincrement':
  259.                                 case 'autoinc':
  260.                                     $this->identifierType = Doctrine::IDENTIFIER_AUTOINC;
  261.                                     $found true;
  262.                                     break;
  263.                                 case 'seq':
  264.                                 case 'sequence':
  265.                                     $this->identifierType = Doctrine::IDENTIFIER_SEQUENCE;
  266.                                     $found true;
  267.     
  268.                                     if ($value{
  269.                                         $this->options['sequenceName'$value;
  270.                                     else {
  271.                                         if (($sequence $this->getAttribute(Doctrine::ATTR_DEFAULT_SEQUENCE)) !== null{
  272.                                             $this->options['sequenceName'$sequence;
  273.                                         else {
  274.                                             $this->options['sequenceName'$this->conn->getSequenceName($this->options['tableName']);
  275.                                         }
  276.                                     }
  277.                                     break;
  278.                             }
  279.                         }
  280.                         if isset($this->identifierType)) {
  281.                             $this->identifierType = Doctrine::IDENTIFIER_NATURAL;
  282.                         }
  283.                         $this->identifier = $pk;
  284.                     }
  285.             }
  286.         }
  287.  
  288.  
  289.         $record->setUp();
  290.  
  291.         // if tree, set up tree
  292.         if ($this->isTree()) {
  293.             $this->getTree()->setUp();
  294.         }
  295.         $this->repository = new Doctrine_Table_Repository($this);
  296.     }
  297.     /**
  298.      * getTemplates
  299.      * returns all templates attached to this table
  300.      *
  301.      * @return array     an array containing all templates
  302.      */
  303.     public function getTemplates()
  304.     {
  305.         return $this->_templates;
  306.     }
  307.     /**
  308.      * export
  309.      * exports this table to database based on column and option definitions
  310.      *
  311.      * @throws Doctrine_Connection_Exception    if some error other than Doctrine::ERR_ALREADY_EXISTS
  312.      *                                           occurred during the create table operation
  313.      * @return boolean                          whether or not the export operation was successful
  314.      *                                           false if table already existed in the database
  315.      */
  316.     public function export(
  317.     {
  318.         $this->conn->export->exportTable($this);
  319.     }
  320.     /**
  321.      * getExportableFormat
  322.      * returns exportable presentation of this object
  323.      *
  324.      * @return array 
  325.      */
  326.     public function getExportableFormat($parseForeignKeys true)
  327.     {
  328.         $columns array();
  329.         $primary array();
  330.  
  331.         foreach ($this->getColumns(as $name => $column{
  332.             $definition $column;
  333.  
  334.             switch ($definition['type']{
  335.                 case 'enum':
  336.                     if (isset($definition['default'])) {
  337.                         $definition['default'$this->enumIndex($name$definition['default']);
  338.                     }
  339.                     break;
  340.                 case 'boolean':
  341.                     if (isset($definition['default'])) {
  342.                         $definition['default'$this->getConnection()->convertBooleans($definition['default']);
  343.                     }
  344.                     break;
  345.             }
  346.             $columns[$name$definition;
  347.  
  348.             if(isset($definition['primary']&& $definition['primary']{
  349.                 $primary[$name;
  350.             }
  351.         }
  352.         $options['foreignKeys'array();
  353.  
  354.         if ($parseForeignKeys{
  355.             if ($this->getAttribute(Doctrine::ATTR_EXPORTDoctrine::EXPORT_CONSTRAINTS{
  356.     
  357.                 $constraints array();
  358.  
  359.                 $emptyIntegrity array('onUpdate' => null,
  360.                                         'onDelete' => null);
  361.  
  362.                 foreach ($this->getRelations(as $name => $relation{
  363.                     $fk $relation->toArray();
  364.                     $fk['foreignTable'$relation->getTable()->getTableName();
  365.  
  366.                     if ($relation->getTable(=== $this && in_array($relation->getLocal()$primary)) {
  367.                         if ($relation->hasConstraint()) {
  368.                             throw new Doctrine_Table_Exception("Badly constructed integrity constraints.");
  369.                         }
  370.                         
  371.                         continue;
  372.                     }
  373.  
  374.                     $integrity array('onUpdate' => $fk['onUpdate'],
  375.                                        'onDelete' => $fk['onDelete']);
  376.                     
  377.                     if ($relation instanceof Doctrine_Relation_LocalKey{
  378.                         $def array('local'        => $relation->getLocal(),
  379.                                      'foreign'      => $relation->getForeign(),
  380.                                      'foreignTable' => $relation->getTable()->getTableName());
  381.  
  382.                         if (($key array_search($def$options['foreignKeys'])) === false{
  383.                             $options['foreignKeys'][$def;
  384.  
  385.                             $constraints[$integrity;
  386.                         else {
  387.                             if ($integrity !== $emptyIntegrity{
  388.                                 $constraints[$key$integrity;
  389.                             }
  390.                         }
  391.                     }
  392.                 }
  393.  
  394.                 foreach ($constraints as $k => $def{
  395.                     $options['foreignKeys'][$karray_merge($options['foreignKeys'][$k]$def);
  396.                 }
  397.  
  398.             }
  399.         }
  400.         $options['primary'$primary;
  401.         
  402.         return array('tableName' => $this->getOption('tableName')
  403.                      'columns'   => $columns
  404.                      'options'   => array_merge($this->getOptions()$options));
  405.     }
  406.     /**
  407.      * exportConstraints
  408.      * exports the constraints of this table into database based on option definitions
  409.      *
  410.      * @throws Doctrine_Connection_Exception    if something went wrong on db level
  411.      * @return void 
  412.      */
  413.     public function exportConstraints()
  414.     {
  415.         try {
  416.             $this->conn->beginTransaction();
  417.  
  418.             foreach ($this->options['index'as $index => $definition{
  419.                 $this->conn->export->createIndex($this->options['tableName']$index$definition);
  420.             }
  421.             $this->conn->commit();
  422.         catch(Doctrine_Connection_Exception $e{
  423.             $this->conn->rollback();
  424.  
  425.             throw $e;
  426.         }
  427.     }
  428.     /**
  429.      * getRelationParser
  430.      * return the relation parser associated with this table
  431.      *
  432.      * @return Doctrine_Relation_Parser     relation parser object
  433.      */
  434.     public function getRelationParser()
  435.     {
  436.         return $this->_parser;
  437.     }
  438.     /**
  439.      * __get
  440.      * an alias for getOption
  441.      *
  442.      * @param string $option 
  443.      */
  444.     public function __get($option)
  445.     {
  446.         if (isset($this->options[$option])) {
  447.             return $this->options[$option];
  448.         }
  449.         return null;
  450.     }
  451.     /**
  452.      * __isset
  453.      *
  454.      * @param string $option 
  455.      */
  456.     public function __isset($option
  457.     {
  458.         return isset($this->options[$option]);
  459.     }
  460.     /**
  461.      * getOptions
  462.      * returns all options of this table and the associated values
  463.      *
  464.      * @return array    all options and their values
  465.      */
  466.     public function getOptions()
  467.     {
  468.         return $this->options;
  469.     }
  470.     /**
  471.      * addForeignKey
  472.      *
  473.      * adds a foreignKey to this table
  474.      *
  475.      * @return void 
  476.      */
  477.     public function addForeignKey(array $definition)
  478.     {
  479.         $this->options['foreignKeys'][$definition;
  480.     }
  481.     /**
  482. /**
  483.      * addCheckConstraint
  484.      * 
  485.      * adds a check constraint to this table
  486.      *
  487.      * @return void 
  488.      */
  489.     public function addCheckConstraint($definition$name)
  490.     {
  491.         if (is_string($name)) {
  492.             $this->options['checks'][$name$definition;
  493.         else {
  494.             $this->options['checks'][$definition;
  495.         }
  496.         
  497.         return $this;
  498.     }
  499.     /**
  500.      * addIndex
  501.      * 
  502.      * adds an index to this table
  503.      *
  504.      * @return void 
  505.      */
  506.     public function addIndex($indexarray $definition)
  507.     {
  508.         $this->options['indexes'][$index$definition;
  509.     }
  510.     /**
  511. /**
  512.      * getIndex
  513.      *
  514.      * @return array|boolean       array on success, FALSE on failure
  515.      */
  516.     public function getIndex($index
  517.     {
  518.         if (isset($this->options['indexes'][$index])) {
  519.             return $this->options['indexes'][$index];
  520.         }
  521.  
  522.         return false;
  523.     }
  524.     public function bind($args$type)
  525.     {
  526.         $options array();
  527.         $options['type'$type;
  528.         
  529.         if isset($args[1])) {
  530.             $args[1array();
  531.         }
  532.         
  533.         // the following is needed for backwards compatibility
  534.         if (is_string($args[1])) {
  535.             if isset($args[2])) {
  536.                 $args[2array();
  537.             elseif (is_string($args[2])) {
  538.                 $args[2= (array) $args[2];
  539.             }
  540.  
  541.             $classes array_merge($this->options['parents']array($this->getComponentName()));
  542.  
  543.  
  544.             $e explode('.'$args[1]);
  545.             if (in_array($e[0]$classes)) {
  546.                 if ($options['type'>= Doctrine_Relation::MANY{
  547.                     $options['foreign'$e[1];                                                 
  548.                 else {
  549.                     $options['local'$e[1];
  550.                 }
  551.             else {
  552.                 $e2 explode(' as '$args[0]);
  553.                 if ($e[0!== $e2[0&& isset($e2[1]|| $e[0!== $e2[1])) {
  554.                     $options['refClass'$e[0];
  555.                 }
  556.  
  557.                 $options['foreign'$e[1];
  558.             }
  559.  
  560.             $options array_merge($args[2]$options);
  561.  
  562.             $this->_parser->bind($args[0]$options);
  563.         else 
  564.             $options array_merge($args[1]$options);
  565.             $this->_parser->bind($args[0]$options);
  566.         }
  567.     }
  568.  
  569.     /** 
  570.      * hasRelation
  571.      *
  572.      * @param string $alias      the relation to check if exists
  573.      * @return boolean           true if the relation exists otherwise false
  574.      */
  575.     public function hasRelation($alias)
  576.     {
  577.         return $this->_parser->hasRelation($alias);
  578.     }    
  579.  
  580.     /**
  581.      * getRelation
  582.      *
  583.      * @param string $alias      relation alias
  584.      */
  585.     public function getRelation($alias$recursive true)
  586.     {
  587.         return $this->_parser->getRelation($alias$recursive);
  588.     }
  589.     /**
  590.      * getRelations
  591.      * returns an array containing all relation objects
  592.      *
  593.      * @return array        an array of Doctrine_Relation objects
  594.      */
  595.     public function getRelations()
  596.     {
  597.         return $this->_parser->getRelations();
  598.     }
  599.     /**
  600.      * createQuery
  601.      * creates a new Doctrine_Query object and adds the component name
  602.      * of this table as the query 'from' part
  603.      *
  604.      * @return Doctrine_Query 
  605.      */
  606.     public function createQuery()
  607.     {
  608.         return Doctrine_Query::create()->from($this->getComponentName());
  609.     }
  610.     /**
  611.      * getRepository
  612.      *
  613.      * @return Doctrine_Table_Repository 
  614.      */
  615.     public function getRepository()
  616.     {
  617.         return $this->repository;
  618.     }
  619.     /**
  620.      * setOption
  621.      * sets an option and returns this object in order to
  622.      * allow flexible method chaining
  623.      *
  624.      * @see Doctrine_Table::$_options   for available options
  625.      * @param string $name              the name of the option to set
  626.      * @param mixed $value              the value of the option
  627.      * @return Doctrine_Table           this object
  628.      */
  629.     public function setOption($name$value)
  630.     {
  631.         switch ($name{
  632.         case 'name':
  633.         case 'tableName':
  634.             break;
  635.         case 'enumMap':
  636.         case 'inheritanceMap':
  637.         case 'index':
  638.         case 'treeOptions':
  639.             if is_array($value)) {
  640.                 throw new Doctrine_Table_Exception($name ' should be an array.');
  641.             }
  642.             break;
  643.         }
  644.         $this->options[$name$value;
  645.     }
  646.     /**
  647.      * getOption
  648.      * returns the value of given option
  649.      *
  650.      * @param string $name  the name of the option
  651.      * @return mixed        the value of given option
  652.      */
  653.     public function getOption($name)
  654.     {
  655.         if (isset($this->options[$name])) {
  656.             return $this->options[$name];
  657.         }
  658.         return null;
  659.     }
  660.     /**
  661.      * getColumnName
  662.      *
  663.      * returns a column name for column alias
  664.      * if the actual name for the alias cannot be found
  665.      * this method returns the given alias
  666.      *
  667.      * @param string $alias         column alias
  668.      * @return string               column name
  669.      */
  670.     public function getColumnName($alias)
  671.     {
  672.         $alias strtolower($alias);
  673.         if(isset($this->columnAliases[$alias])) {
  674.             return $this->columnAliases[$alias];
  675.         }
  676.  
  677.         return $alias;
  678.     }
  679.     /**
  680.      * setColumn
  681.      *
  682.      * @param string $name 
  683.      * @param string $type 
  684.      * @param integer $length 
  685.      * @param mixed $options 
  686.      * @throws Doctrine_Table_Exception     if trying use wrongly typed parameter
  687.      * @return void 
  688.      */
  689.     public function setColumn($name$type$length null$options array())
  690.     {
  691.         if (is_string($options)) {
  692.             $options explode('|'$options);
  693.         }
  694.  
  695.         foreach ($options as $k => $option{
  696.             if (is_numeric($k)) {
  697.                 if empty($option)) {
  698.                     $options[$optiontrue;
  699.                 }
  700.                 unset($options[$k]);
  701.             }
  702.         }
  703.  
  704.         $name  strtolower($name);
  705.         $parts explode(' as '$name);
  706.  
  707.         if (count($parts1{
  708.             $this->columnAliases[$parts[1]] $parts[0];
  709.             $name $parts[0];
  710.         }
  711.  
  712.  
  713.  
  714.         if ($length == null{
  715.             switch ($type{
  716.                 case 'string':
  717.                 case 'clob':
  718.                 case 'float':
  719.                 case 'integer':
  720.                 case 'array':
  721.                 case 'object':
  722.                 case 'blob':
  723.                 case 'gzip':
  724.                     // use php int max
  725.                     $length 2147483647;
  726.                 break;
  727.                 case 'boolean':
  728.                     $length 1;
  729.                 case 'date':
  730.                     // YYYY-MM-DD ISO 8601
  731.                     $length 10;
  732.                 case 'time':
  733.                     // HH:NN:SS+00:00 ISO 8601
  734.                     $length 14;
  735.                 case 'timestamp':
  736.                     // YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601
  737.                     $length 25;
  738.                 break;
  739.             }
  740.         }
  741.  
  742.         $this->columns[$name$options;
  743.         $this->columns[$name]['type'$type;
  744.         $this->columns[$name]['length'$length;
  745.  
  746.         if (isset($options['primary'])) {
  747.             $this->primaryKeys[$name;
  748.         }
  749.         if (isset($options['default'])) {
  750.             $this->hasDefaultValues = true;
  751.         }
  752.     }
  753.     /**
  754.      * hasDefaultValues
  755.      * returns true if this table has default values, otherwise false
  756.      *
  757.      * @return boolean 
  758.      */
  759.     public function hasDefaultValues()
  760.     {
  761.         return $this->hasDefaultValues;
  762.     }
  763.     /**
  764.      * getDefaultValueOf
  765.      * returns the default value(if any) for given column
  766.      *
  767.      * @param string $column 
  768.      * @return mixed 
  769.      */
  770.     public function getDefaultValueOf($column)
  771.     {
  772.         $column strtolower($column);
  773.         if isset($this->columns[$column])) {
  774.             throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$column." doesn't exist.");
  775.         }
  776.         if (isset($this->columns[$column]['default'])) {
  777.             return $this->columns[$column]['default'];
  778.         else {
  779.             return null;
  780.         }
  781.     }
  782.     /**
  783.      * @return mixed 
  784.      */
  785.     public function getIdentifier()
  786.     {
  787.         return $this->identifier;
  788.     }
  789.     /**
  790.      * @return integer 
  791.      */
  792.     public function getIdentifierType()
  793.     {
  794.         return $this->identifierType;
  795.     }
  796.     /**
  797.      * hasColumn
  798.      * @return boolean 
  799.      */
  800.     public function hasColumn($name)
  801.     {
  802.         return isset($this->columns[$name]);
  803.     }
  804.     /**
  805.      * @param mixed $key 
  806.      * @return void 
  807.      */
  808.     public function setPrimaryKey($key)
  809.     {
  810.         switch (gettype($key)) {
  811.         case "array":
  812.             $this->primaryKeys = array_values($key);
  813.             break;
  814.         case "string":
  815.             $this->primaryKeys[$key;
  816.             break;
  817.         };
  818.     }
  819.     /**
  820.      * returns all primary keys
  821.      * @return array 
  822.      */
  823.     public function getPrimaryKeys()
  824.     {
  825.         return $this->primaryKeys;
  826.     }
  827.     /**
  828.      * @return boolean 
  829.      */
  830.     public function hasPrimaryKey($key)
  831.     {
  832.         return in_array($key,$this->primaryKeys);
  833.     }
  834.     /**
  835.      * @return Doctrine_Connection 
  836.      */
  837.     public function getConnection()
  838.     {
  839.         return $this->conn;
  840.     }
  841.     /**
  842.      * create
  843.      * creates a new record
  844.      *
  845.      * @param $array                    an array where keys are field names and values representing field values
  846.      * @return Doctrine_Record 
  847.      */
  848.     public function create(array $array array()) {
  849.         $this->data         = $array;
  850.         $record new $this->options['name']($thistrue);
  851.         $this->data         = array();
  852.         return $record;
  853.     }
  854.     /**
  855.      * finds a record by its identifier
  856.      *
  857.      * @param $id                       database row id
  858.      * @return Doctrine_Record|false   a record for given database identifier
  859.      */
  860.     public function find($id)
  861.     {
  862.         if ($id !== null{
  863.             if is_array($id)) {
  864.                 $id array($id);
  865.             else {
  866.                 $id array_values($id);
  867.             }
  868.  
  869.             $records Doctrine_Query::create()
  870.                        ->from($this->getComponentName())
  871.                        ->where(implode(' = ? AND '$this->primaryKeys' = ?')
  872.                        ->execute($id);
  873.  
  874.             if (count($records=== 0{
  875.                 return false;
  876.             }
  877.  
  878.             return $records->getFirst();
  879.         }
  880.         return false;
  881.     }
  882.     /**
  883.      * applyInheritance
  884.      * @param $where                    query where part to be modified
  885.      * @return string                   query where part with column aggregation inheritance added
  886.      */
  887.     final public function applyInheritance($where)
  888.     {
  889.         if empty($this->options['inheritanceMap'])) {
  890.             $a array();
  891.             foreach ($this->options['inheritanceMap'as $field => $value{
  892.                 $a[$field ' = ?';
  893.             }
  894.             $i implode(' AND '$a);
  895.             $where .= ' AND ' $i;
  896.         }
  897.         return $where;
  898.     }
  899.     /**
  900.      * findAll
  901.      * returns a collection of records
  902.      *
  903.      * @return Doctrine_Collection 
  904.      */
  905.     public function findAll()
  906.     {
  907.         $graph new Doctrine_Query($this->conn);
  908.         $users $graph->query('FROM ' $this->options['name']);
  909.         return $users;
  910.     }
  911.     /**
  912.      * findByDql
  913.      * finds records with given DQL where clause
  914.      * returns a collection of records
  915.      *
  916.      * @param string $dql               DQL after WHERE clause
  917.      * @param array $params             query parameters
  918.      * @return Doctrine_Collection 
  919.      */
  920.     public function findBySql($dqlarray $params array()) {
  921.         $q new Doctrine_Query($this->conn);
  922.         $users $q->query('FROM ' $this->options['name'' WHERE ' $dql$params);
  923.         return $users;
  924.     }
  925.  
  926.     public function findByDql($dqlarray $params array()) {
  927.         return $this->findBySql($dql$params);
  928.     }
  929.     /**
  930.      * clear
  931.      * clears the first level cache (identityMap)
  932.      *
  933.      * @return void 
  934.      */
  935.     public function clear()
  936.     {
  937.         $this->identityMap = array();
  938.     }
  939.     /**
  940.      * addRecord
  941.      * adds a record to identity map
  942.      *
  943.      * @param Doctrine_Record $record       record to be added
  944.      * @return boolean 
  945.      */
  946.     public function addRecord(Doctrine_Record $record)
  947.     {
  948.         $id implode(' '$record->identifier());
  949.         
  950.         if (isset($this->identityMap[$id])) {
  951.             return false;
  952.         }
  953.         
  954.         $this->identityMap[$id$record;
  955.         
  956.         return true;
  957.     }
  958.     /**
  959.      * getRecord
  960.      * first checks if record exists in identityMap, if not
  961.      * returns a new record
  962.      *
  963.      * @return Doctrine_Record 
  964.      */
  965.     public function getRecord()
  966.     {
  967.         if empty($this->data)) {
  968.             $this->data = array_change_key_case($this->dataCASE_LOWER);
  969.     
  970.             $key $this->getIdentifier();
  971.     
  972.             if is_array($key)) {
  973.                 $key array($key);
  974.             }
  975.     
  976.             $found false;
  977.             foreach ($key as $k{
  978.                 if isset($this->data[$k])) {
  979.                     // primary key column not found return new record
  980.                     $found true;
  981.                     break;
  982.                 }
  983.                 $id[$this->data[$k];
  984.             }
  985.             
  986.             if ($found{
  987.                 $recordName $this->getClassnameToReturn();
  988.                 $record new $recordName($thistrue);
  989.                 $this->data = array();
  990.  
  991.                 return $record;
  992.             }
  993.  
  994.  
  995.             $id implode(' '$id);
  996.     
  997.             if (isset($this->identityMap[$id])) {
  998.                 $record $this->identityMap[$id];
  999.                 $record->hydrate($this->data);
  1000.             else {
  1001.                 $recordName $this->getClassnameToReturn();
  1002.                 $record new $recordName($this);
  1003.                 $this->identityMap[$id$record;
  1004.             }
  1005.             $this->data = array();
  1006.         else {
  1007.             $recordName $this->getClassnameToReturn();
  1008.             $record new $recordName($thistrue);
  1009.         }
  1010.  
  1011.  
  1012.         return $record;
  1013.     }
  1014.  
  1015.     /**
  1016.      * Get the classname to return. Most often this is just the options['name']
  1017.      *
  1018.      * Check the subclasses option and the inheritanceMap for each subclass to see
  1019.      * if all the maps in a subclass is met. If this is the case return that
  1020.      * subclass name. If no subclasses match or if there are no subclasses defined
  1021.      * return the name of the class for this tables record.
  1022.      *
  1023.      * @todo this function could use reflection to check the first time it runs
  1024.      *  if the subclassing option is not set.
  1025.      *
  1026.      * @return string The name of the class to create
  1027.      *
  1028.      */ 
  1029.     public function getClassnameToReturn()
  1030.     {
  1031.         if (!isset($this->options['subclasses'])) {
  1032.             return $this->options['name'];
  1033.         }
  1034.         foreach ($this->options['subclasses'as $subclass{
  1035.             $table $this->conn->getTable($subclass);
  1036.             $inheritanceMap $table->getOption('inheritanceMap');
  1037.             $nomatch false;
  1038.             foreach ($inheritanceMap as $key => $value{
  1039.                 if isset($this->data[$key]|| $this->data[$key!= $value{
  1040.                     $nomatch true;
  1041.                     break;
  1042.                 }
  1043.             }
  1044.             if $nomatch{
  1045.                 return $table->getComponentName();
  1046.             }
  1047.         }
  1048.         return $this->options['name'];
  1049.     }
  1050.  
  1051.     /**
  1052.      * @param $id                       database row id
  1053.      * @throws Doctrine_Find_Exception
  1054.      */
  1055.     final public function getProxy($id null)
  1056.     {
  1057.         if ($id !== null{
  1058.             $query 'SELECT ' implode(', ',$this->primaryKeys
  1059.                 . ' FROM ' $this->getTableName(
  1060.                 . ' WHERE ' implode(' = ? && ',$this->primaryKeys' = ?';
  1061.             $query $this->applyInheritance($query);
  1062.  
  1063.             $params array_merge(array($id)array_values($this->options['inheritanceMap']));
  1064.  
  1065.             $this->data = $this->conn->execute($query$params)->fetch(PDO::FETCH_ASSOC);
  1066.  
  1067.             if ($this->data === false)
  1068.                 return false;
  1069.         }
  1070.         return $this->getRecord();
  1071.     }
  1072.     /**
  1073.      * count
  1074.      *
  1075.      * @return integer 
  1076.      */
  1077.     public function count()
  1078.     {
  1079.         $a $this->conn->execute('SELECT COUNT(1) FROM ' $this->options['tableName'])->fetch(Doctrine::FETCH_NUM);
  1080.         return current($a);
  1081.     }
  1082.     /**
  1083.      * @return Doctrine_Query                           a Doctrine_Query object
  1084.      */
  1085.     public function getQueryObject()
  1086.     {
  1087.         $graph new Doctrine_Query($this->getConnection());
  1088.         $graph->load($this->getComponentName());
  1089.         return $graph;
  1090.     }
  1091.     /**
  1092.      * @param string $field 
  1093.      * @return array 
  1094.      */
  1095.     public function getEnumValues($field)
  1096.     {
  1097.         if (isset($this->columns[$field]['values'])) {
  1098.             return $this->columns[$field]['values'];
  1099.         else {
  1100.             return array();
  1101.         }
  1102.     }
  1103.     /**
  1104.      * enumValue
  1105.      *
  1106.      * @param string $field 
  1107.      * @param integer $index 
  1108.      * @return mixed 
  1109.      */
  1110.     public function enumValue($field$index)
  1111.     {
  1112.         if ($index instanceof Doctrine_Null)
  1113.             return $index;
  1114.  
  1115.         return isset($this->columns[$field]['values'][$index]$this->columns[$field]['values'][$index$index;
  1116.     }
  1117.     /**
  1118.      * enumIndex
  1119.      *
  1120.      * @param string $field 
  1121.      * @param mixed $value 
  1122.      * @return mixed 
  1123.      */
  1124.     public function enumIndex($field$value)
  1125.     {
  1126.         $values $this->getEnumValues($field);
  1127.  
  1128.         return array_search($value$values);
  1129.     }
  1130.     /**
  1131.      * getColumnCount
  1132.      *
  1133.      * @return integer      the number of columns in this table
  1134.      */
  1135.     public function getColumnCount()
  1136.     {
  1137.         return $this->columnCount;
  1138.     }
  1139.  
  1140.     /**
  1141.      * returns all columns and their definitions
  1142.      *
  1143.      * @return array 
  1144.      */
  1145.     public function getColumns()
  1146.     {
  1147.         return $this->columns;
  1148.     }
  1149.     /**
  1150.      * returns an array containing all the column names
  1151.      *
  1152.      * @return array 
  1153.      */
  1154.     public function getColumnNames()
  1155.     {
  1156.         return array_keys($this->columns);
  1157.     }
  1158.     /**
  1159.      * getDefinitionOf
  1160.      *
  1161.      * @return mixed        array on success, false on failure
  1162.      */
  1163.     public function getDefinitionOf($column)
  1164.     {
  1165.         if (isset($this->columns[$column])) {
  1166.             return $this->columns[$column];
  1167.         }
  1168.         return false;
  1169.     }
  1170.     /**
  1171.      * getTypeOf
  1172.      *
  1173.      * @return mixed        string on success, false on failure
  1174.      */
  1175.     public function getTypeOf($column)
  1176.     {
  1177.         if (isset($this->columns[$column])) {
  1178.             return $this->columns[$column]['type'];
  1179.         }
  1180.         return false;
  1181.     }
  1182.     /**
  1183.      * setData
  1184.      * doctrine uses this function internally
  1185.      * users are strongly discouraged to use this function
  1186.      *
  1187.      * @param array $data               internal data
  1188.      * @return void 
  1189.      */
  1190.     public function setData(array $data)
  1191.     {
  1192.         $this->data $data;
  1193.     }
  1194.     /**
  1195. /**
  1196.      * returns internal data, used by Doctrine_Record instances
  1197.      * when retrieving data from database
  1198.      *
  1199.      * @return array 
  1200.      */
  1201.     public function getData()
  1202.     {
  1203.         return $this->data;
  1204.     }
  1205.     /**
  1206.      * prepareValue
  1207.      * this method performs special data preparation depending on
  1208.      * the type of the given column
  1209.      *
  1210.      * 1. It unserializes array and object typed columns
  1211.      * 2. Uncompresses gzip typed columns
  1212.      * 3. Gets the appropriate enum values for enum typed columns
  1213.      * 4. Initializes special null object pointer for null values (for fast column existence checking purposes)
  1214.      *
  1215.      * example:
  1216.      * <code type='php'>
  1217.      * $field = 'name';
  1218.      * $value = null;
  1219.      * $table->prepareValue($field, $value); // Doctrine_Null
  1220.      * </code>
  1221.      *
  1222.      * @throws Doctrine_Table_Exception     if unserialization of array/object typed column fails or
  1223.      * @throws Doctrine_Table_Exception     if uncompression of gzip typed column fails         *
  1224.      * @param string $field     the name of the field
  1225.      * @param string $value     field value
  1226.      * @return mixed            prepared value
  1227.      */
  1228.     public function prepareValue($field$value)
  1229.     {
  1230.         if ($value === self::$_null{
  1231.             return self::$_null;
  1232.         else if ($value === null{
  1233.             return null;
  1234.         else {
  1235.             $type $this->getTypeOf($field);
  1236.  
  1237.             switch ($type{
  1238.                 case 'array':
  1239.                 case 'object':
  1240.                     if (is_string($value)) {
  1241.                         $value unserialize($value);
  1242.  
  1243.                         if ($value === false{
  1244.                             throw new Doctrine_Table_Exception('Unserialization of ' $field ' failed.');
  1245.                         }
  1246.                         return $value;
  1247.                     }
  1248.                 break;
  1249.                 case 'gzip':
  1250.                     $value gzuncompress($value);
  1251.  
  1252.                     if ($value === false{
  1253.                         throw new Doctrine_Table_Exception('Uncompressing of ' $field ' failed.');
  1254.                     }
  1255.                     return $value;
  1256.                 break;
  1257.                 case 'enum':
  1258.                     return $this->enumValue($field$value);
  1259.                 break;
  1260.                 case 'boolean':
  1261.                     return (boolean) $value;
  1262.                 break;
  1263.                 case 'integer':
  1264.                     // don't do any casting here PHP INT_MAX is smaller than what the databases support
  1265.                 break;
  1266.             }
  1267.         }
  1268.         return $value;
  1269.     }
  1270.     /**
  1271.      * getter for associated tree
  1272.      *
  1273.      * @return mixed  if tree return instance of Doctrine_Tree, otherwise returns false
  1274.      */    
  1275.     public function getTree({
  1276.         if (isset($this->options['treeImpl'])) {
  1277.             if $this->tree{
  1278.                 $options = isset($this->options['treeOptions']$this->options['treeOptions'array();
  1279.                 $this->tree = Doctrine_Tree::factory($this
  1280.                     $this->options['treeImpl']
  1281.                     $options
  1282.                 );
  1283.             }
  1284.             return $this->tree;
  1285.         }
  1286.         return false;
  1287.     }
  1288.     public function getComponentName(
  1289.     {
  1290.         return $this->options['name'];
  1291.     }
  1292.     public function getTableName()
  1293.     {
  1294.         return $this->options['tableName'];
  1295.     }
  1296.     public function setTableName($tableName)
  1297.     {
  1298.         $this->options['tableName'$tableName;    
  1299.     }
  1300.     /**
  1301.      * determine if table acts as tree
  1302.      *
  1303.      * @return mixed  if tree return true, otherwise returns false
  1304.      */    
  1305.     public function isTree({
  1306.         return is_null($this->options['treeImpl'])) true false;
  1307.     }
  1308.  
  1309.     public function getTemplate($template)
  1310.     {
  1311.         if isset($this->_templates[$template])) {
  1312.             throw new Doctrine_Table_Exception('Template ' $template ' not loaded');
  1313.         }
  1314.         
  1315.         return $this->_templates[$template];
  1316.     }
  1317.     
  1318.     public function addTemplate($templateDoctrine_Template $impl)
  1319.     {
  1320.         $this->_templates[$template$impl;
  1321.     }
  1322.     /**
  1323.      * returns a string representation of this object
  1324.      *
  1325.      * @return string 
  1326.      */
  1327.     public function __toString()
  1328.     {
  1329.         return Doctrine_Lib::getTableAsString($this);
  1330.     }
  1331. }