Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/** $Id: Table.php 7681 2010-08-24 15:55:34Z jwage $** 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.doctrine-project.org>.*//*** Doctrine_Table represents a database table* each Doctrine_Table holds the information of foreignKeys and associations*** @author Konsta Vesterinen <kvesteri@cc.hut.fi>* @package Doctrine* @subpackage Table* @license http://www.opensource.org/licenses/lgpl-license.php LGPL* @version $Revision: 7681 $* @link www.doctrine-project.org* @since 1.0* @method mixed findBy*(mixed $value) magic finders; @see __call()* @method mixed findOneBy*(mixed $value) magic finders; @see __call()*/class Doctrine_Table extends Doctrine_Configurable implements Countable{/*** @var array $data temporary data which is then loaded into Doctrine_Record::$_data*/protected $_data = array();/*** @var mixed $identifier The field names of all fields that are part of the identifier/primary key*/protected $_identifier = array();/*** @see Doctrine_Identifier constants* @var integer $identifierType the type of identifier this table uses*/protected $_identifierType;/*** @var Doctrine_Connection $conn Doctrine_Connection object that created this table*/protected $_conn;/*** @var array $identityMap first level cache*/protected $_identityMap = array();/*** @var Doctrine_Table_Repository $repository record repository*/protected $_repository;/*** @var array $columns an array of column definitions,* keys are column names and values are column definitions** the definition array has atleast the following values:** -- type the column type, eg. 'integer'* -- length the column length, eg. 11** additional keys:* -- notnull whether or not the column is marked as notnull* -- values enum values* -- notblank notblank validator + notnull constraint* ... many more*/protected $_columns = array();/*** Array of unique sets of fields. These values are validated on save** @var array $_uniques*/protected $_uniques = array();/*** @var array $_fieldNames an array of field names, used to look up field names* from column names. Keys are column* names and values are field names.* Alias for columns are here.*/protected $_fieldNames = array();/**** @var array $_columnNames an array of column names* keys are field names and values column names.* used to look up column names from field names.* this is the reverse lookup map of $_fieldNames.*/protected $_columnNames = array();/*** @var integer $columnCount cached column count, Doctrine_Record uses this column count in when* determining its state*/protected $columnCount;/*** @var boolean $hasDefaultValues whether or not this table has default values*/protected $hasDefaultValues;/*** @var array $options an array containing all options** -- name name of the component, for example component name of the GroupTable is 'Group'** -- parents the parent classes of this component** -- declaringClass name of the table definition declaring class (when using inheritance the class* that defines the table structure can be any class in the inheritance hierarchy,* hence we need reflection to check out which class actually calls setTableDefinition)** -- 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** -- sequenceName Some databases need sequences instead of auto incrementation primary keys,* you can set specific sequence for your table by calling setOption('sequenceName', $seqName)* where $seqName is the name of the desired sequence** -- enumMap enum value arrays** -- inheritanceMap inheritanceMap is used for inheritance mapping, keys representing columns and values* the column values that should correspond to child classes** -- type table type (mysql example: INNODB)** -- charset character set** -- foreignKeys the foreign keys of this table** -- checks the check constraints of this table, eg. 'price > dicounted_price'** -- collate collate attribute** -- indexes the index definitions of this table** -- treeImpl the tree implementation of this table (if any)** -- treeOptions the tree options** -- queryParts the bound query parts** -- versioning*/protected $_options = array('name' => null,'tableName' => null,'sequenceName' => null,'inheritanceMap' => array(),'enumMap' => array(),'type' => null,'charset' => null,'collate' => null,'treeImpl' => null,'treeOptions' => array(),'indexes' => array(),'parents' => array(),'joinedParents' => array(),'queryParts' => array(),'versioning' => null,'subclasses' => array(),'orderBy' => null);/*** @var Doctrine_Tree $tree tree object associated with this table*/protected $_tree;/*** @var Doctrine_Relation_Parser $_parser relation parser object*/protected $_parser;/*** @see Doctrine_Template* @var array $_templates an array containing all templates attached to this table*/protected $_templates = array();/*** @see Doctrine_Record_Filter* @var array $_filters an array containing all record filters attached to this table*/protected $_filters = array();/*** @see Doctrine_Record_Generator* @var array $_generators an array containing all generators attached to this table*/protected $_generators = array();/*** Generator instance responsible for constructing this table** @see Doctrine_Record_Generator* @var Doctrine_Record_Generator $generator*/protected $_generator;/*** @var array $_invokedMethods method invoker cache*/protected $_invokedMethods = array();/*** @var Doctrine_Record $record empty instance of the given model*/protected $record;/*** the constructor** @throws Doctrine_Connection_Exception if there are no opened connections* @param string $name the name of the component* @param Doctrine_Connection $conn the connection associated with this table* @param boolean $initDefinition whether to init the in-memory schema*/public function __construct($name, Doctrine_Connection $conn, $initDefinition = false){$this->_conn = $conn;$this->_options['name'] = $name;$this->setParent($this->_conn);$this->_conn->addTable($this);$this->_parser = new Doctrine_Relation_Parser($this);if ($charset = $this->getAttribute(Doctrine_Core::ATTR_DEFAULT_TABLE_CHARSET)) {$this->_options['charset'] = $charset;}if ($collate = $this->getAttribute(Doctrine_Core::ATTR_DEFAULT_TABLE_COLLATE)) {$this->_options['collate'] = $collate;}if ($initDefinition) {$this->record = $this->initDefinition();$this->initIdentifier();$this->record->setUp();// if tree, set up treeif ($this->isTree()) {$this->getTree()->setUp();}} else {if ( ! isset($this->_options['tableName'])) {$this->setTableName(Doctrine_Inflector::tableize($this->_options['name']));}}$this->_filters[] = new Doctrine_Record_Filter_Standard();$this->_repository = new Doctrine_Table_Repository($this);$this->construct();}/*** Construct template method.** This method provides concrete Table classes with the possibility* to hook into the constructor procedure. It is called after the* Doctrine_Table construction process is finished.** @return void*/public function construct(){ }/*** Initializes the in-memory table definition.** @param string $name*/public function initDefinition(){$name = $this->_options['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 classesdo {if ($class === 'Doctrine_Record') {break;}$name = $class;$names[] = $name;} while ($class = get_parent_class($class));if ($class === false) {throw new Doctrine_Table_Exception('Class "' . $name . '" must be a child class of Doctrine_Record');}// reverse names$names = array_reverse($names);// save parentsarray_pop($names);$this->_options['parents'] = $names;// create database tableif (method_exists($record, 'setTableDefinition')) {$record->setTableDefinition();// get the declaring class of setTableDefinition method$method = new ReflectionMethod($this->_options['name'], 'setTableDefinition');$class = $method->getDeclaringClass();} else {$class = new ReflectionClass($class);}$this->_options['joinedParents'] = array();foreach (array_reverse($this->_options['parents']) as $parent) {if ($parent === $class->getName()) {continue;}$ref = new ReflectionClass($parent);if ($ref->isAbstract() || ! $class->isSubClassOf($parent)) {continue;}$parentTable = $this->_conn->getTable($parent);$found = false;$parentColumns = $parentTable->getColumns();foreach ($parentColumns as $columnName => $definition) {if ( ! isset($definition['primary']) || $definition['primary'] === false) {if (isset($this->_columns[$columnName])) {$found = true;break;} else {if ( ! isset($parentColumns[$columnName]['owner'])) {$parentColumns[$columnName]['owner'] = $parentTable->getComponentName();}$this->_options['joinedParents'][] = $parentColumns[$columnName]['owner'];}} else {unset($parentColumns[$columnName]);}}if ($found) {continue;}foreach ($parentColumns as $columnName => $definition) {$fullName = $columnName . ' as ' . $parentTable->getFieldName($columnName);$this->setColumn($fullName, $definition['type'], $definition['length'], $definition, true);}break;}$this->_options['joinedParents'] = array_values(array_unique($this->_options['joinedParents']));$this->_options['declaringClass'] = $class;// set the table definition for the given tree implementationif ($this->isTree()) {$this->getTree()->setTableDefinition();}$this->columnCount = count($this->_columns);if ( ! isset($this->_options['tableName'])) {$this->setTableName(Doctrine_Inflector::tableize($class->getName()));}return $record;}/*** Initializes the primary key.** Called in the construction process, builds the identifier definition* copying in the schema the list of the fields which constitutes* the primary key.** @return void*/public function initIdentifier(){switch (count($this->_identifier)) {case 0:if ( ! empty($this->_options['joinedParents'])) {$root = current($this->_options['joinedParents']);$table = $this->_conn->getTable($root);$this->_identifier = $table->getIdentifier();$this->_identifierType = ($table->getIdentifierType() !== Doctrine_Core::IDENTIFIER_AUTOINC)? $table->getIdentifierType() : Doctrine_Core::IDENTIFIER_NATURAL;// add all inherited primary keysforeach ((array) $this->_identifier as $id) {$definition = $table->getDefinitionOf($id);// inherited primary keys shouldn't contain autoinc// and sequence definitionsunset($definition['autoincrement']);unset($definition['sequence']);// add the inherited primary key column$fullName = $id . ' as ' . $table->getFieldName($id);$this->setColumn($fullName, $definition['type'], $definition['length'],$definition, true);}} else {$identifierOptions = $this->getAttribute(Doctrine_Core::ATTR_DEFAULT_IDENTIFIER_OPTIONS);$name = (isset($identifierOptions['name']) && $identifierOptions['name']) ? $identifierOptions['name']:'id';$name = sprintf($name, $this->getTableName());$definition = array('type' => (isset($identifierOptions['type']) && $identifierOptions['type']) ? $identifierOptions['type']:'integer','length' => (isset($identifierOptions['length']) && $identifierOptions['length']) ? $identifierOptions['length']:8,'autoincrement' => isset($identifierOptions['autoincrement']) ? $identifierOptions['autoincrement']:true,'primary' => isset($identifierOptions['primary']) ? $identifierOptions['primary']:true);unset($identifierOptions['name'], $identifierOptions['type'], $identifierOptions['length']);foreach ($identifierOptions as $key => $value) {if ( ! isset($definition[$key]) || ! $definition[$key]) {$definition[$key] = $value;}}$this->setColumn($name, $definition['type'], $definition['length'], $definition, true);$this->_identifier = $name;$this->_identifierType = Doctrine_Core::IDENTIFIER_AUTOINC;}$this->columnCount++;break;case 1:foreach ($this->_identifier as $pk) {$e = $this->getDefinitionOf($pk);$found = false;foreach ($e as $option => $value) {if ($found) {break;}$e2 = explode(':', $option);switch (strtolower($e2[0])) {case 'autoincrement':case 'autoinc':if ($value !== false) {$this->_identifierType = Doctrine_Core::IDENTIFIER_AUTOINC;$found = true;}break;case 'seq':case 'sequence':$this->_identifierType = Doctrine_Core::IDENTIFIER_SEQUENCE;$found = true;if (is_string($value)) {$this->_options['sequenceName'] = $value;} else {if (($sequence = $this->getAttribute(Doctrine_Core::ATTR_DEFAULT_SEQUENCE)) !== null) {$this->_options['sequenceName'] = $sequence;} else {$this->_options['sequenceName'] = $this->_conn->formatter->getSequenceName($this->_options['tableName']);}}break;}}if ( ! isset($this->_identifierType)) {$this->_identifierType = Doctrine_Core::IDENTIFIER_NATURAL;}}$this->_identifier = $pk;break;default:$this->_identifierType = Doctrine_Core::IDENTIFIER_COMPOSITE;}}/*** Gets the owner of a column.** The owner of a column is the name of the component in a hierarchy that* defines the column.** @param string $columnName the column name* @return string the name of the owning/defining component*/public function getColumnOwner($columnName){if (isset($this->_columns[$columnName]['owner'])) {return $this->_columns[$columnName]['owner'];} else {return $this->getComponentName();}}/*** Gets the record instance for this table.** The Doctrine_Table instance always holds at least one* instance of a model so that it can be reused for several things,* but primarily it is first used to instantiate all the internal* in memory schema definition.** @return Doctrine_Record Empty instance of the record*/public function getRecordInstance(){if ( ! $this->record) {$this->record = new $this->_options['name'];}return $this->record;}/*** Checks whether a column is inherited from a component further up in the hierarchy.** @param $columnName The column name* @return boolean TRUE if column is inherited, FALSE otherwise.*/public function isInheritedColumn($columnName){return (isset($this->_columns[$columnName]['owner']));}/*** Checks whether a field is in the primary key.** Checks if $fieldName is part of the table identifier, which defines* the one-column or multi-column primary key.** @param string $fieldName The field name* @return boolean TRUE if the field is part of the table identifier/primary key field(s),*/public function isIdentifier($fieldName){return ($fieldName === $this->getIdentifier() ||in_array($fieldName, (array) $this->getIdentifier()));}/*** Checks whether a field identifier is of type autoincrement.** This method checks if the primary key is a AUTOINCREMENT column or* if the table uses a natural key.** @return boolean TRUE if the identifier is autoincrement* FALSE otherwise*/public function isIdentifierAutoincrement(){return $this->getIdentifierType() === Doctrine_Core::IDENTIFIER_AUTOINC;}/*** Checks whether a field identifier is a composite key.** @return boolean TRUE if the identifier is a composite key,* FALSE otherwise*/public function isIdentifierComposite(){return $this->getIdentifierType() === Doctrine_Core::IDENTIFIER_COMPOSITE;}/*** getMethodOwner** @param string $method* @return void*/public function getMethodOwner($method){return (isset($this->_invokedMethods[$method])) ?$this->_invokedMethods[$method] : false;}/*** setMethodOwner** @param string $method* @param string $class*/public function setMethodOwner($method, $class){$this->_invokedMethods[$method] = $class;}/*** Exports this table to database based on the schema definition.** This method create a physical table in the database, using the* definition that comes from the component Doctrine_Record instance.** @throws Doctrine_Connection_Exception if some error other than Doctrine_Core::ERR_ALREADY_EXISTS* occurred during the create table operation* @return boolean whether or not the export operation was successful* false if table already existed in the database*/public function export(){$this->_conn->export->exportTable($this);}/*** Returns an exportable representation of this object.** This method produces a array representation of the table schema, where* keys are tableName, columns (@see $_columns) and options.* The options subarray contains 'primary' and 'foreignKeys'.** @param boolean $parseForeignKeys whether to include foreign keys definition in the options* @return array*/public function getExportableFormat($parseForeignKeys = true){$columns = array();$primary = array();foreach ($this->getColumns() as $name => $definition) {if (isset($definition['owner'])) {continue;}switch ($definition['type']) {case 'boolean':if (isset($definition['default'])) {$definition['default'] = $this->getConnection()->convertBooleans($definition['default']);}break;}$columns[$name] = $definition;if (isset($definition['primary']) && $definition['primary']) {$primary[] = $name;}}$options['foreignKeys'] = isset($this->_options['foreignKeys']) ?$this->_options['foreignKeys'] : array();if ($parseForeignKeys && $this->getAttribute(Doctrine_Core::ATTR_EXPORT) & Doctrine_Core::EXPORT_CONSTRAINTS) {$constraints = array();$emptyIntegrity = array('onUpdate' => null,'onDelete' => null);foreach ($this->getRelations() as $name => $relation) {$fk = $relation->toArray();$fk['foreignTable'] = $relation->getTable()->getTableName();// do not touch tables that have EXPORT_NONE attributeif ($relation->getTable()->getAttribute(Doctrine_Core::ATTR_EXPORT) === Doctrine_Core::EXPORT_NONE) {continue;}if ($relation->getTable() === $this && in_array($relation->getLocal(), $primary)) {if ($relation->hasConstraint()) {throw new Doctrine_Table_Exception("Badly constructed integrity constraints. Cannot define constraint of different fields in the same table.");}continue;}$integrity = array('onUpdate' => $fk['onUpdate'],'onDelete' => $fk['onDelete']);$fkName = $relation->getForeignKeyName();if ($relation instanceof Doctrine_Relation_LocalKey) {$def = array('name' => $fkName,'local' => $relation->getLocalColumnName(),'foreign' => $relation->getForeignColumnName(),'foreignTable' => $relation->getTable()->getTableName());if ($integrity !== $emptyIntegrity) {$def = array_merge($def, $integrity);}if (($key = $this->_checkForeignKeyExists($def, $options['foreignKeys'])) === false) {$options['foreignKeys'][$fkName] = $def;} else {unset($def['name']);$options['foreignKeys'][$key] = array_merge($options['foreignKeys'][$key], $def);}}}}$options['primary'] = $primary;return array('tableName' => $this->getOption('tableName'),'columns' => $columns,'options' => array_merge($this->getOptions(), $options));}/*** Check if a foreign definition already exists in the fks array for a* foreign table, local and foreign key** @param array $def Foreign key definition to check for* @param array $foreignKeys Array of existing foreign key definitions to check in* @return boolean $result Whether or not the foreign key was found*/protected function _checkForeignKeyExists($def, $foreignKeys){foreach ($foreignKeys as $key => $foreignKey) {if ($def['local'] == $foreignKey['local'] && $def['foreign'] == $foreignKey['foreign'] && $def['foreignTable'] == $foreignKey['foreignTable']) {return $key;}}return false;}/*** Retrieves the relation parser associated with this table.** @return Doctrine_Relation_Parser relation parser object*/public function getRelationParser(){return $this->_parser;}/*** Magic method for accessing to object properties.** This method is an alias for getOption.* <code>* foreach ($table->indexes as $name => $definition) {* // ...* }* </code>** @param string $option* @return mixed*/public function __get($option){if (isset($this->_options[$option])) {return $this->_options[$option];}return null;}/*** Magic method for testing object properties existence.** This method tests if an option exists.* <code>* if (isset($table->tableName)) {* // ...* }* </code>** @param string $option*/public function __isset($option){return isset($this->_options[$option]);}/*** Retrieves all options of this table and the associated values.** @return array all options and their values*/public function getOptions(){return $this->_options;}/*** Sets all the options.** This method sets options of the table that are specified in the argument.* It has no effect on other options.** @param array $options keys are option names* @return void*/public function setOptions($options){foreach ($options as $key => $value) {$this->setOption($key, $value);}}/*** Adds a foreignKey to the table in-memory definition.** This method adds a foreign key to the schema definition.* It does not add the key to the physical table in the db; @see export().** @param array $definition definition of the foreign key* @return void*/public function addForeignKey(array $definition){$this->_options['foreignKeys'][] = $definition;}/*** Adds a check constraint to the table in-memory definition.** This method adds a CHECK constraint to the schema definition.* It does not add the constraint to the physical table in the* db; @see export().** @param $definition* @param mixed $name if string used as name for the constraint.* Otherwise it is indexed numerically.* @return void*/public function addCheckConstraint($definition, $name){if (is_string($name)) {$this->_options['checks'][$name] = $definition;} else {$this->_options['checks'][] = $definition;}return $this;}/*** Adds an index to this table in-memory definition.** This method adds an INDEX to the schema definition.* It does not add the index to the physical table in the db; @see export().** @param string $index index name* @param array $definition keys are type, fields* @return void*/public function addIndex($index, array $definition){if (isset($definition['fields'])) {foreach ((array) $definition['fields'] as $key => $field) {if (is_numeric($key)) {$definition['fields'][$key] = $this->getColumnName($field);} else {$columnName = $this->getColumnName($key);unset($definition['fields'][$key]);$definition['fields'][$columnName] = $field;}}}$this->_options['indexes'][$index] = $definition;}/*** Retrieves an index definition.** This method returns a given index definition: @see addIndex().** @param string $index index name; @see addIndex()* @return array|boolean array on success, FALSE on failure*/public function getIndex($index){if (isset($this->_options['indexes'][$index])) {return $this->_options['indexes'][$index];}return false;}/*** Defines a n-uple of fields that must be unique for every record.** This method Will automatically add UNIQUE index definition* and validate the values on save. The UNIQUE index is not created in the* database until you use @see export().** @param array $fields values are fieldnames* @param array $options array of options for unique validator* @param bool $createUniqueIndex Whether or not to create a unique index in the database* @return void*/public function unique($fields, $options = array(), $createdUniqueIndex = true){if ($createdUniqueIndex) {$name = implode('_', $fields) . '_unqidx';$definition = array('type' => 'unique', 'fields' => $fields);$this->addIndex($name, $definition);}$this->_uniques[] = array($fields, $options);}/*** Adds a relation to the table.** This method defines a relation on this table, that will be present on* every record belonging to this component.** @param array $args first value is a string, name of related component;* second value is array, options for the relation.* @see Doctrine_Relation::_$definition* @param integer $type Doctrine_Relation::ONE or Doctrine_Relation::MANY* @return void* @todo Name proposal: addRelation*/public function bind($args, $type){$options = ( ! isset($args[1])) ? array() : $args[1];$options['type'] = $type;$this->_parser->bind($args[0], $options);}/*** Binds One-to-One aggregate relation** @param string $componentName the name of the related component* @param string $options relation options* @see Doctrine_Relation::_$definition* @return Doctrine_Record this object*/public function hasOne(){$this->bind(func_get_args(), Doctrine_Relation::ONE);}/*** Binds One-to-Many / Many-to-Many aggregate relation** @param string $componentName the name of the related component* @param string $options relation options* @see Doctrine_Relation::_$definition* @return Doctrine_Record this object*/public function hasMany(){$this->bind(func_get_args(), Doctrine_Relation::MANY);}/*** Tests if a relation exists.** This method queries the table definition to find out if a relation* is defined for this component. Alias defined with foreignAlias are not* recognized as there's only one Doctrine_Relation object on the owning* side.** @param string $alias the relation alias to search for.* @return boolean true if the relation exists. Otherwise false.*/public function hasRelation($alias){return $this->_parser->hasRelation($alias);}/*** Retrieves a relation object for this component.** @param string $alias relation alias; @see hasRelation()* @return Doctrine_Relation*/public function getRelation($alias, $recursive = true){return $this->_parser->getRelation($alias, $recursive);}/*** Retrieves all relation objects defined on this table.** @return array*/public function getRelations(){return $this->_parser->getRelations();}/*** Creates a query on this table.** This method returns a new Doctrine_Query object and adds the component* name of this table as the query 'from' part.* <code>* $table = Doctrine_Core::getTable('User');* $table->createQuery('myuser')* ->where('myuser.Phonenumber = ?', '5551234');* </code>** @param string $alias name for component aliasing* @return Doctrine_Query*/public function createQuery($alias = ''){if ( ! empty($alias)) {$alias = ' ' . trim($alias);}$class = $this->getAttribute(Doctrine_Core::ATTR_QUERY_CLASS);return Doctrine_Query::create(null, $class)->from($this->getComponentName() . $alias);}/*** Gets the internal record repository.** @return Doctrine_Table_Repository*/public function getRepository(){return $this->_repository;}/*** Sets an option for the table.** This method sets an option and returns this object in order to* allow flexible method chaining.** @see Doctrine_Table::$_options for available options* @param string $name the name of the option to set* @param mixed $value the value of the option* @return Doctrine_Table this object*/public function setOption($name, $value){switch ($name) {case 'name':case 'tableName':break;case 'enumMap':case 'inheritanceMap':case 'index':case 'treeOptions':if ( ! is_array($value)) {throw new Doctrine_Table_Exception($name . ' should be an array.');}break;}$this->_options[$name] = $value;}/*** Returns the value of a given option.** @see Doctrine_Table::$_options for available options* @param string $name the name of the option* @return mixed the value of given option*/public function getOption($name){if (isset($this->_options[$name])) {return $this->_options[$name];}return null;}/*** Get the table orderby statement** @param string $alias The alias to use* @param boolean $columnNames Whether or not to use column names instead of field names* @return string $orderByStatement*/public function getOrderByStatement($alias = null, $columnNames = false){if (isset($this->_options['orderBy'])) {return $this->processOrderBy($alias, $this->_options['orderBy']);}}/*** Process an order by statement to be prefixed with the passed alias and* field names converted to column names if the 3rd argument is true.** @param string $alias The alias to prefix columns with* @param string $orderBy The order by to process* @param string $columnNames Whether or not to convert field names to column names* @return string $orderBy*/public function processOrderBy($alias, $orderBy, $columnNames = false){if ( ! $alias) {$alias = $this->getComponentName();}if ( ! is_array($orderBy)) {$e1 = explode(',', $orderBy);} else {$e1 = $orderBy;}$e1 = array_map('trim', $e1);foreach ($e1 as $k => $v) {$e2 = explode(' ', $v);if ($columnNames) {$e2[0] = $this->getColumnName($e2[0]);}if ($this->hasField($this->getFieldName($e2[0]))) {$e1[$k] = $alias . '.' . $e2[0];} else {$e1[$k] = $e2[0];}if (isset($e2[1])) {$e1[$k] .= ' ' . $e2[1];}}return implode(', ', $e1);}/*** Returns a column name for a column alias.** If the actual name for the alias cannot be found* this method returns the given alias.** @param string $alias column alias* @return string column name*/public function getColumnName($fieldName){// FIX ME: This is being used in places where an array is passed, but it should not be an array// For example in places where Doctrine should support composite foreign/primary keys$fieldName = is_array($fieldName) ? $fieldName[0]:$fieldName;if (isset($this->_columnNames[$fieldName])) {return $this->_columnNames[$fieldName];}return strtolower($fieldName);}/*** Retrieves a column definition from this table schema.** @param string $columnName* @return array column definition; @see $_columns*/public function getColumnDefinition($columnName){if ( ! isset($this->_columns[$columnName])) {return false;}return $this->_columns[$columnName];}/*** Returns a column alias for a column name.** If no alias can be found the column name is returned.** @param string $columnName column name* @return string column alias*/public function getFieldName($columnName){if (isset($this->_fieldNames[$columnName])) {return $this->_fieldNames[$columnName];}return $columnName;}/*** Customize the array of options for a column or multiple columns. First* argument can be a single field/column name or an array of them. The second* argument is an array of options.** [php]* public function setTableDefinition()* {* parent::setTableDefinition();* $this->setColumnOptions('username', array(* 'unique' => true* ));* }** @param string $columnName* @param array $validators* @return void*/public function setColumnOptions($columnName, array $options){if (is_array($columnName)) {foreach ($columnName as $name) {$this->setColumnOptions($name, $options);}} else {foreach ($options as $option => $value) {$this->setColumnOption($columnName, $option, $value);}}}/*** Set an individual column option** @param string $columnName* @param string $option* @param string $value* @return void*/public function setColumnOption($columnName, $option, $value){if ($option == 'primary') {if (isset($this->_identifier)) {$this->_identifier = (array) $this->_identifier;}if ($value && ! in_array($columnName, $this->_identifier)) {$this->_identifier[] = $columnName;} else if (!$value && in_array($columnName, $this->_identifier)) {$key = array_search($columnName, $this->_identifier);unset($this->_identifier[$key]);}}$columnName = $this->getColumnName($columnName);$this->_columns[$columnName][$option] = $value;}/*** Set multiple column definitions at once** @param array $definitions* @return void*/public function setColumns(array $definitions){foreach ($definitions as $name => $options) {$this->setColumn($name, $options['type'], $options['length'], $options);}}/*** Adds a column to the schema.** This method does not alter the database table; @see export();** @see $_columns;* @param string $name column physical name* @param string $type type of data* @param integer $length maximum length* @param mixed $options* @param boolean $prepend Whether to prepend or append the new column to the column list.* By default the column gets appended.* @throws Doctrine_Table_Exception if trying use wrongly typed parameter* @return void*/public function setColumn($name, $type = null, $length = null, $options = array(), $prepend = false){if (is_string($options)) {$options = explode('|', $options);}foreach ($options as $k => $option) {if (is_numeric($k)) {if ( ! empty($option)) {$options[$option] = true;}unset($options[$k]);}}// extract column name & field nameif (stripos($name, ' as ')){if (strpos($name, ' as ')) {$parts = explode(' as ', $name);} else {$parts = explode(' AS ', $name);}if (count($parts) > 1) {$fieldName = $parts[1];} else {$fieldName = $parts[0];}$name = strtolower($parts[0]);} else {$fieldName = $name;$name = strtolower($name);}$name = trim($name);$fieldName = trim($fieldName);if ($prepend) {$this->_columnNames = array_merge(array($fieldName => $name), $this->_columnNames);$this->_fieldNames = array_merge(array($name => $fieldName), $this->_fieldNames);} else {$this->_columnNames[$fieldName] = $name;$this->_fieldNames[$name] = $fieldName;}$defaultOptions = $this->getAttribute(Doctrine_Core::ATTR_DEFAULT_COLUMN_OPTIONS);if (isset($defaultOptions['length']) && $defaultOptions['length'] && $length == null) {$length = $defaultOptions['length'];}if ($length == null) {switch ($type) {case 'integer':$length = 8;break;case 'decimal':$length = 18;break;case 'string':case 'clob':case 'float':case 'integer':case 'array':case 'object':case 'blob':case 'gzip'://$length = 2147483647;//All the DataDict driver classes have work-arounds to deal//with unset lengths.$length = null;break;case 'boolean':$length = 1;case 'date':// YYYY-MM-DD ISO 8601$length = 10;case 'time':// HH:NN:SS+00:00 ISO 8601$length = 14;case 'timestamp':// YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601$length = 25;}}$options['type'] = $type;$options['length'] = $length;if (strtolower($fieldName) != $name) {$options['alias'] = $fieldName;}foreach ($defaultOptions as $key => $value) {if ( ! array_key_exists($key, $options) || is_null($options[$key])) {$options[$key] = $value;}}if ($prepend) {$this->_columns = array_merge(array($name => $options), $this->_columns);} else {$this->_columns[$name] = $options;}if (isset($options['primary']) && $options['primary']) {if (isset($this->_identifier)) {$this->_identifier = (array) $this->_identifier;}if ( ! in_array($fieldName, $this->_identifier)) {$this->_identifier[] = $fieldName;}}if (isset($options['default'])) {$this->hasDefaultValues = true;}}/*** Finds out whether this table has default values for columns.** @return boolean*/public function hasDefaultValues(){return $this->hasDefaultValues;}/*** Retrieves the default value (if any) for a given column.** @param string $fieldName column name* @return mixed default value as set in definition*/public function getDefaultValueOf($fieldName){$columnName = $this->getColumnName($fieldName);if ( ! isset($this->_columns[$columnName])) {throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$columnName." doesn't exist.");}if (isset($this->_columns[$columnName]['default'])) {return $this->_columns[$columnName]['default'];} else {return null;}}/*** Returns the definition of the identifier key.* @return string can be array if a multi-column primary key is used.*/public function getIdentifier(){return $this->_identifier;}/*** Retrieves the type of primary key.** This method finds out if the primary key is multifield.* @see Doctrine_Identifier constants* @return integer*/public function getIdentifierType(){return $this->_identifierType;}/*** Finds out whether the table definition contains a given column.* @param string $columnName* @return boolean*/public function hasColumn($columnName){return isset($this->_columns[strtolower($columnName)]);}/*** Finds out whether the table definition has a given field.** This method returns true if @see hasColumn() returns true or if an alias* named $fieldName exists.* @param string $fieldName* @return boolean*/public function hasField($fieldName){return isset($this->_columnNames[$fieldName]);}/*** Sets the default connection for this table.** This method assign the connection which this table will use* to create queries.** @params Doctrine_Connection a connection object* @return Doctrine_Table this object; fluent interface*/public function setConnection(Doctrine_Connection $conn){$this->_conn = $conn;$this->setParent($this->_conn);return $this;}/*** Returns the connection associated with this table (if any).** @return Doctrine_Connection|null the connection object*/public function getConnection(){return $this->_conn;}/*** Creates a new record.** This method create a new instance of the model defined by this table.* The class of this record is the subclass of Doctrine_Record defined by* this component. The record is not created in the database until you* call @save().** @param $array an array where keys are field names and* values representing field values. Can contain* also related components;* @see Doctrine_Record::fromArray()* @return Doctrine_Record the created record object*/public function create(array $array = array()){$record = new $this->_options['name']($this, true);$record->fromArray($array);return $record;}/*** Adds a named query in the query registry.** This methods register a query object with a name to use in the future.* @see createNamedQuery()* @param $queryKey query key name to use for storage* @param string|Doctrine_Query $query DQL string or object* @return void*/public function addNamedQuery($queryKey, $query){$registry = Doctrine_Manager::getInstance()->getQueryRegistry();$registry->add($this->getComponentName() . '/' . $queryKey, $query);}/*** Creates a named query from one in the query registry.** This method clones a new query object from a previously registered one.** @see addNamedQuery()* @param string $queryKey query key name* @return Doctrine_Query*/public function createNamedQuery($queryKey){$queryRegistry = Doctrine_Manager::getInstance()->getQueryRegistry();if (strpos($queryKey, '/') !== false) {$e = explode('/', $queryKey);return $queryRegistry->get($e[1], $e[0]);}return $queryRegistry->get($queryKey, $this->getComponentName());}/*** Finds a record by its identifier.** <code>* $table->find(11);* $table->find(11, Doctrine_Core::HYDRATE_RECORD);* $table->find('namedQueryForYearArchive', array(2009), Doctrine_Core::HYDRATE_ARRAY);* </code>** @param mixed $name Database Row ID or Query Name defined previously as a NamedQuery* @param mixed $params This argument is the hydration mode (Doctrine_Core::HYDRATE_ARRAY or* Doctrine_Core::HYDRATE_RECORD) if first param is a Database Row ID.* Otherwise this argument expect an array of query params.* @param int $hydrationMode Optional Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD if* first argument is a NamedQuery* @return mixed Doctrine_Collection, array, Doctrine_Record or false if no result*/public function find(){$num_args = func_num_args();// Named Query or IDs$name = func_get_arg(0);if (is_null($name)) {return false;}$ns = $this->getComponentName();$m = $name;// Check for possible cross-accessif ( ! is_array($name) && strpos($name, '/') !== false) {list($ns, $m) = explode('/', $name);}// Define query to be usedif (! is_array($name) &&Doctrine_Manager::getInstance()->getQueryRegistry()->has($m, $ns)) {// We're dealing with a named query$q = $this->createNamedQuery($name);// Parameters construction$params = ($num_args >= 2) ? func_get_arg(1) : array();// Hydration mode$hydrationMode = ($num_args == 3) ? func_get_arg(2) : null;// Executing query$res = $q->execute($params, $hydrationMode);} else {// We're passing a single ID or an array of IDs$q = $this->createQuery('dctrn_find')->where('dctrn_find.' . implode(' = ? AND dctrn_find.', (array) $this->getIdentifier()) . ' = ?')->limit(1);// Parameters construction$params = is_array($name) ? array_values($name) : array($name);// Hydration mode$hydrationMode = ($num_args == 2) ? func_get_arg(1) : null;// Executing query$res = $q->fetchOne($params, $hydrationMode);}$q->free();return $res;}/*** Retrieves all the records stored in this table.** @param int $hydrationMode Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD* @return Doctrine_Collection|array*/public function findAll($hydrationMode = null){return $this->createQuery('dctrn_find')->execute(array(), $hydrationMode);}/*** Finds records in this table with a given SQL where clause.** @param string $dql DQL WHERE clause to use* @param array $params query parameters (a la PDO)* @param int $hydrationMode Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD* @return Doctrine_Collection|array** @todo This actually takes DQL, not SQL, but it requires column names* instead of field names. This should be fixed to use raw SQL instead.*/public function findBySql($dql, $params = array(), $hydrationMode = null){return $this->createQuery('dctrn_find')->where($dql)->execute($params, $hydrationMode);}/*** Finds records in this table with a given DQL where clause.** @param string $dql DQL WHERE clause* @param array $params preparated statement parameters* @param int $hydrationMode Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD* @return Doctrine_Collection|array*/public function findByDql($dql, $params = array(), $hydrationMode = null){$parser = $this->createQuery();$query = 'FROM ' . $this->getComponentName() . ' dctrn_find WHERE ' . $dql;return $parser->query($query, $params, $hydrationMode);}/*** Find records basing on a field.** @param string $column field for the WHERE clause* @param string $value prepared statement parameter* @param int $hydrationMode Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD* @return Doctrine_Collection|array*/public function findBy($fieldName, $value, $hydrationMode = null){return $this->createQuery('dctrn_find')->where($this->buildFindByWhere($fieldName), (array) $value)->execute(array(), $hydrationMode);}/*** Finds the first record that satisfy the clause.** @param string $column field for the WHERE clause* @param string $value prepared statement parameter* @param int $hydrationMode Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD* @return Doctrine_Record*/public function findOneBy($fieldName, $value, $hydrationMode = null){return $this->createQuery('dctrn_find')->where($this->buildFindByWhere($fieldName), (array) $value)->limit(1)->fetchOne(array(), $hydrationMode);}/*** Finds result of a named query.** This method fetches data using the provided $queryKey to choose a named* query in the query registry.** @param string $queryKey the query key* @param array $params prepared statement params (if any)* @param int $hydrationMode Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD* @throws Doctrine_Query_Registry if no query for given queryKey is found* @return Doctrine_Collection|array*/public function execute($queryKey, $params = array(), $hydrationMode = Doctrine_Core::HYDRATE_RECORD){return $this->createNamedQuery($queryKey)->execute($params, $hydrationMode);}/*** Fetches one record with a named query.** This method uses the provided $queryKey to clone and execute* the associated named query in the query registry.** @param string $queryKey the query key* @param array $params prepared statement params (if any)* @param int $hydrationMode Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD* @throws Doctrine_Query_Registry if no query for given queryKey is found* @return Doctrine_Record|array*/public function executeOne($queryKey, $params = array(), $hydrationMode = Doctrine_Core::HYDRATE_RECORD){return $this->createNamedQuery($queryKey)->fetchOne($params, $hydrationMode);}/*** Clears the first level cache (identityMap).** This method ensures that records are reloaded from the db.** @return void* @todo what about a more descriptive name? clearIdentityMap?*/public function clear(){$this->_identityMap = array();}/*** Adds a record to the first level cache (identity map).** This method is used internally to cache records, ensuring that only one* object that represents a sql record exists in all scopes.** @param Doctrine_Record $record record to be added* @return boolean true if record was not present in the map* @todo Better name? registerRecord?*/public function addRecord(Doctrine_Record $record){$id = implode(' ', $record->identifier());if (isset($this->_identityMap[$id])) {return false;}$this->_identityMap[$id] = $record;return true;}/*** Removes a record from the identity map.** This method deletes from the cache the given record; can be used to* force reloading of an object from database.** @param Doctrine_Record $record record to remove from cache* @return boolean true if the record was found and removed,* false if the record wasn't found.*/public function removeRecord(Doctrine_Record $record){$id = implode(' ', $record->identifier());if (isset($this->_identityMap[$id])) {unset($this->_identityMap[$id]);return true;}return false;}/*** Returns a new record.** This method checks if a internal record exists in identityMap, if does* not exist it creates a new one.** @return Doctrine_Record*/public function getRecord(){if ( ! empty($this->_data)) {$identifierFieldNames = $this->getIdentifier();if ( ! is_array($identifierFieldNames)) {$identifierFieldNames = array($identifierFieldNames);}$found = false;foreach ($identifierFieldNames as $fieldName) {if ( ! isset($this->_data[$fieldName])) {// primary key column not found return new record$found = true;break;}$id[] = $this->_data[$fieldName];}if ($found) {$recordName = $this->getComponentName();$record = new $recordName($this, true);$this->_data = array();return $record;}$id = implode(' ', $id);if (isset($this->_identityMap[$id])) {$record = $this->_identityMap[$id];if ($record->getTable()->getAttribute(Doctrine_Core::ATTR_HYDRATE_OVERWRITE)) {$record->hydrate($this->_data);if ($record->state() == Doctrine_Record::STATE_PROXY) {if (!$record->isInProxyState()) {$record->state(Doctrine_Record::STATE_CLEAN);}}} else {$record->hydrate($this->_data, false);}} else {$recordName = $this->getComponentName();$record = new $recordName($this);$this->_identityMap[$id] = $record;}$this->_data = array();} else {$recordName = $this->getComponentName();$record = new $recordName($this, true);}return $record;}/*** Get the classname to return. Most often this is just the options['name'].** Check the subclasses option and the inheritanceMap for each subclass to see* if all the maps in a subclass is met. If this is the case return that* subclass name. If no subclasses match or if there are no subclasses defined* return the name of the class for this tables record.** @todo this function could use reflection to check the first time it runs* if the subclassing option is not set.** @return string The name of the class to create* @deprecated*/public function getClassnameToReturn(){if ( ! isset($this->_options['subclasses'])) {return $this->_options['name'];}foreach ($this->_options['subclasses'] as $subclass) {$table = $this->_conn->getTable($subclass);$inheritanceMap = $table->getOption('inheritanceMap');$nomatch = false;foreach ($inheritanceMap as $key => $value) {if ( ! isset($this->_data[$key]) || $this->_data[$key] != $value) {$nomatch = true;break;}}if ( ! $nomatch) {return $table->getComponentName();}}return $this->_options['name'];}/*** @param $id database row id* @throws Doctrine_Find_Exception* @return Doctrine_Record*/final public function getProxy($id = null){if ($id !== null) {$identifierColumnNames = $this->getIdentifierColumnNames();$query = 'SELECT ' . implode(', ', (array) $identifierColumnNames). ' FROM ' . $this->getTableName(). ' WHERE ' . implode(' = ? && ', (array) $identifierColumnNames) . ' = ?';$query = $this->applyInheritance($query);$params = array_merge(array($id), array_values($this->_options['inheritanceMap']));$this->_data = $this->_conn->execute($query, $params)->fetch(PDO::FETCH_ASSOC);if ($this->_data === false)return false;}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->_options['inheritanceMap'])) {$a = array();foreach ($this->_options['inheritanceMap'] as $field => $value) {$a[] = $this->getColumnName($field) . ' = ?';}$i = implode(' AND ', $a);$where .= ' AND ' . $i;}return $where;}/*** Implements Countable interface.** @return integer number of records in the table*/public function count(){return $this->createQuery()->count();}/*** @return Doctrine_Query a Doctrine_Query object*/public function getQueryObject(){$graph = $this->createQuery();$graph->load($this->getComponentName());return $graph;}/*** Retrieves the enum values for a given field.** @param string $fieldName* @return array*/public function getEnumValues($fieldName){$columnName = $this->getColumnName($fieldName);if (isset($this->_columns[$columnName]['values'])) {return $this->_columns[$columnName]['values'];} else {return array();}}/*** Retrieves an enum value.** This method finds a enum string value. If ATTR_USE_NATIVE_ENUM is set* on the connection, index and value are the same thing.** @param string $fieldName* @param integer $index numeric index of the enum* @return mixed*/public function enumValue($fieldName, $index){if ($index instanceof Doctrine_Null) {return false;}if ($this->_conn->getAttribute(Doctrine_Core::ATTR_USE_NATIVE_ENUM)) {return $index;}$columnName = $this->getColumnName($fieldName);return isset($this->_columns[$columnName]['values'][$index]) ? $this->_columns[$columnName]['values'][$index] : false;}/*** Retrieves an enum index.* @see enumValue()** @param string $fieldName* @param mixed $value value of the enum considered* @return integer can be string if native enums are used.*/public function enumIndex($fieldName, $value){$values = $this->getEnumValues($fieldName);if ($this->_conn->getAttribute(Doctrine_Core::ATTR_USE_NATIVE_ENUM)) {return $value;}return array_search($value, $values);}/*** Validates a given field using table ATTR_VALIDATE rules.* @see Doctrine_Core::ATTR_VALIDATE** @param string $fieldName* @param string $value* @param Doctrine_Record $record record to consider; if it does not exists, it is created* @return Doctrine_Validator_ErrorStack $errorStack*/public function validateField($fieldName, $value, Doctrine_Record $record = null){if ($record instanceof Doctrine_Record) {$errorStack = $record->getErrorStack();} else {$record = $this->create();$errorStack = new Doctrine_Validator_ErrorStack($this->getOption('name'));}if ($value === self::$_null) {$value = null;} else if ($value instanceof Doctrine_Record && $value->exists()) {$value = $value->getIncremented();} else if ($value instanceof Doctrine_Record && ! $value->exists()) {foreach($this->getRelations() as $relation) {if ($fieldName == $relation->getLocalFieldName() && (get_class($value) == $relation->getClass() || is_subclass_of($value, $relation->getClass()))) {return $errorStack;}}}$dataType = $this->getTypeOf($fieldName);// Validate field type, if type validation is enabledif ($this->getAttribute(Doctrine_Core::ATTR_VALIDATE) & Doctrine_Core::VALIDATE_TYPES) {if ( ! Doctrine_Validator::isValidType($value, $dataType)) {$errorStack->add($fieldName, 'type');}if ($dataType == 'enum') {$enumIndex = $this->enumIndex($fieldName, $value);if ($enumIndex === false && $value !== null) {$errorStack->add($fieldName, 'enum');}}if ($dataType == 'set') {$values = $this->_columns[$fieldName]['values'];// Convert string to arrayif (is_string($value)) {$value = explode(',', $value);$value = array_map('trim', $value);$record->set($fieldName, $value);}// Make sure each set value is validforeach ($value as $k => $v) {if ( ! in_array($v, $values)) {$errorStack->add($fieldName, 'set');}}}}// Validate field length, if length validation is enabledif ($this->getAttribute(Doctrine_Core::ATTR_VALIDATE) & Doctrine_Core::VALIDATE_LENGTHS) {if ( ! Doctrine_Validator::validateLength($value, $dataType, $this->getFieldLength($fieldName))) {$errorStack->add($fieldName, 'length');}}// Run all custom validatorsforeach ($this->getFieldValidators($fieldName) as $validatorName => $args) {if ( ! is_string($validatorName)) {$validatorName = $args;$args = array();}$validator = Doctrine_Validator::getValidator($validatorName);$validator->invoker = $record;$validator->field = $fieldName;$validator->args = $args;if ( ! $validator->validate($value)) {$errorStack->add($fieldName, $validator);}}return $errorStack;}/*** Validates all the unique indexes.** This methods validates 'unique' sets of fields for the given Doctrine_Record instance.* Pushes error to the record error stack if they are generated.** @param Doctrine_Record $record*/public function validateUniques(Doctrine_Record $record){$errorStack = $record->getErrorStack();$validator = Doctrine_Validator::getValidator('unique');$validator->invoker = $record;foreach ($this->_uniques as $unique){list($fields, $options) = $unique;$validator->args = $options;$validator->field = $fields;$values = array();foreach ($fields as $field) {$values[] = $record->$field;}if ( ! $validator->validate($values)) {foreach ($fields as $field) {$errorStack->add($field, $validator);}}}}/*** @return integer the number of columns in this table*/public function getColumnCount(){return $this->columnCount;}/*** Retrieves all columns of the table.** @see $_columns;* @return array keys are column names and values are definition*/public function getColumns(){return $this->_columns;}/*** Removes a field name from the table schema information.** @param string $fieldName* @return boolean true if the field is found and removed.* False otherwise.*/public function removeColumn($fieldName){if ( ! $this->hasField($fieldName)) {return false;}$columnName = $this->getColumnName($fieldName);unset($this->_columnNames[$fieldName], $this->_fieldNames[$columnName], $this->_columns[$columnName]);$this->columnCount = count($this->_columns);return true;}/*** Returns an array containing all the column names.** @return array numeric array*/public function getColumnNames(array $fieldNames = null){if ($fieldNames === null) {return array_keys($this->_columns);} else {$columnNames = array();foreach ($fieldNames as $fieldName) {$columnNames[] = $this->getColumnName($fieldName);}return $columnNames;}}/*** Returns an array with all the identifier column names.** @return array numeric array*/public function getIdentifierColumnNames(){return $this->getColumnNames((array) $this->getIdentifier());}/*** Gets the array of unique fields sets.* @see $_uniques;** @return array numeric array*/public function getUniques(){return $this->_uniques;}/*** Returns an array containing all the field names.** @return array numeric array*/public function getFieldNames(){return array_values($this->_fieldNames);}/*** Retrieves the definition of a field.** This method retrieves the definition of the column, basing of $fieldName* which can be a column name or a field name (alias).** @param string $fieldName* @return array false on failure*/public function getDefinitionOf($fieldName){$columnName = $this->getColumnName($fieldName);return $this->getColumnDefinition($columnName);}/*** Retrieves the type of a field.** @param string $fieldName* @return string false on failure*/public function getTypeOf($fieldName){return $this->getTypeOfColumn($this->getColumnName($fieldName));}/*** Retrieves the type of a column.** @param string $columnName* @return string false if column is not found*/public function getTypeOfColumn($columnName){return isset($this->_columns[$columnName]) ? $this->_columns[$columnName]['type'] : false;}/*** Doctrine uses this function internally.* Users are strongly discouraged to use this function.** @access private* @param array $data internal data* @return void*/public function setData(array $data){$this->_data = $data;}/*** Returns internal data.** This method is used by Doctrine_Record instances* when retrieving data from database.** @return array*/public function getData(){return $this->_data;}/*** Performs special data preparation.** This method returns a representation of a field data, depending on* the type of the given column.** 1. It unserializes array and object typed columns* 2. Uncompresses gzip typed columns* 3. Initializes special null object pointer for null values (for fast column existence checking purposes)** example:* <code type='php'>* $field = 'name';* $value = null;* $table->prepareValue($field, $value); // Doctrine_Null* </code>** @throws Doctrine_Table_Exception if unserialization of array/object typed column fails or* @throws Doctrine_Table_Exception if uncompression of gzip typed column fails ** @param string $field the name of the field* @param string $value field value* @param string $typeHint Type hint used to pass in the type of the value to prepare* if it is already known. This enables the method to skip* the type determination. Used i.e. during hydration.* @return mixed prepared value*/public function prepareValue($fieldName, $value, $typeHint = null){if ($value === self::$_null) {return self::$_null;} else if ($value === null) {return null;} else {$type = is_null($typeHint) ? $this->getTypeOf($fieldName) : $typeHint;switch ($type) {case 'enum':case 'integer':case 'string';// don't do any casting here PHP INT_MAX is smaller than what the databases supportbreak;case 'set':return explode(',', $value);break;case 'boolean':return (boolean) $value;break;case 'array':case 'object':if (is_string($value)) {$value = empty($value) ? null:unserialize($value);if ($value === false) {throw new Doctrine_Table_Exception('Unserialization of ' . $fieldName . ' failed.');}return $value;}break;case 'gzip':$value = gzuncompress($value);if ($value === false) {throw new Doctrine_Table_Exception('Uncompressing of ' . $fieldName . ' failed.');}return $value;break;}}return $value;}/*** Gets associated tree.* This method returns the associated Tree object (if any exists).* Normally implemented by NestedSet behavior.** @return Doctrine_Tree false if not a tree*/public function getTree(){if (isset($this->_options['treeImpl'])) {if ( ! $this->_tree) {$options = isset($this->_options['treeOptions']) ? $this->_options['treeOptions'] : array();$this->_tree = Doctrine_Tree::factory($this,$this->_options['treeImpl'],$options);}return $this->_tree;}return false;}/*** Gets the subclass of Doctrine_Record that belongs to this table.** @return string*/public function getComponentName(){return $this->_options['name'];}/*** Gets the table name in the db.** @return string*/public function getTableName(){return $this->_options['tableName'];}/*** sets the table name in the schema definition.** @param string $tableName* @return void*/public function setTableName($tableName){$this->setOption('tableName', $this->_conn->formatter->getTableName($tableName));}/*** Determines if table acts as tree.** @return boolean if tree return true, otherwise returns false*/public function isTree(){return ( ! is_null($this->_options['treeImpl'])) ? true : false;}/*** Retrieves all templates (behaviors) attached to this table.** @return array an array containing all templates*/public function getTemplates(){return $this->_templates;}/*** Retrieves a particular template by class name.** This method retrieves a behavior/template object attached to the table.* For Doctrine_Template_* classes, the base name can be used.** @param string $template name of the behavior* @throws Doctrine_Table_Exception if the given template is* not set on this table* @return Doctrine_Template*/public function getTemplate($template){if (isset($this->_templates['Doctrine_Template_' . $template])) {return $this->_templates['Doctrine_Template_' . $template];} else if (isset($this->_templates[$template])) {return $this->_templates[$template];}throw new Doctrine_Table_Exception('Template ' . $template . ' not loaded');}/*** Checks if the table has a given template.** @param string $template name of template; @see getTemplate()* @return boolean*/public function hasTemplate($template){return isset($this->_templates[$template]) || isset($this->_templates['Doctrine_Template_' . $template]);}/*** Adds a template to this table.** @param string $template template name* @param Doctrine_Template $impl behavior to attach* @return Doctrine_Table*/public function addTemplate($template, Doctrine_Template $impl){$this->_templates[$template] = $impl;return $this;}/*** Gets all the generators for this table.** @return array $generators*/public function getGenerators(){return $this->_generators;}/*** Gets generator instance for a passed name.** @param string $generator* @return Doctrine_Record_Generator $generator*/public function getGenerator($generator){if ( ! isset($this->_generators[$generator])) {throw new Doctrine_Table_Exception('Generator ' . $generator . ' not loaded');}return $this->_generators[$generator];}/*** Checks if a generator name exists.** @param string $generator* @return void*/public function hasGenerator($generator){return isset($this->_generators[$generator]);}/*** Adds a generate to the table instance.** @param Doctrine_Record_Generator $generator* @param string $name* @return Doctrine_Table*/public function addGenerator(Doctrine_Record_Generator $generator, $name = null){if ($name === null) {$this->_generators[] = $generator;} else {$this->_generators[$name] = $generator;}return $this;}/*** Set the generator responsible for creating this table** @param Doctrine_Record_Generator $generator* @return void*/public function setGenerator(Doctrine_Record_Generator $generator){$this->_generator = $generator;}/*** Check whether this table was created by a record generator or not** @return boolean*/public function isGenerator(){return isset($this->_generator) ? true : false;}/*** Get the parent generator responsible for this table instance** @return Doctrine_Record_Generator*/public function getParentGenerator(){return $this->_generator;}/*** Binds query parts to this component.* @see bindQueryPart()** @param array $queryParts an array of pre-bound query parts* @return Doctrine_Table this object*/public function bindQueryParts(array $queryParts){$this->_options['queryParts'] = $queryParts;return $this;}/*** Adds default query parts to the selects executed on this table.** This method binds given value to given query part.* Every query created by this table will have this part set by default.** @param string $queryPart* @param mixed $value* @return Doctrine_Record this object*/public function bindQueryPart($queryPart, $value){$this->_options['queryParts'][$queryPart] = $value;return $this;}/*** Gets the names of all validators being applied on a field.** @param string $fieldName* @return array names of validators*/public function getFieldValidators($fieldName){$validators = array();$columnName = $this->getColumnName($fieldName);// this loop is a dirty workaround to get the validators filtered out of// the options, since everything is squeezed together currentlyforeach ($this->_columns[$columnName] as $name => $args) {if (empty($name)|| $name == 'primary'|| $name == 'protected'|| $name == 'autoincrement'|| $name == 'default'|| $name == 'values'|| $name == 'sequence'|| $name == 'zerofill'|| $name == 'owner'|| $name == 'scale'|| $name == 'type'|| $name == 'length'|| $name == 'fixed'|| $name == 'comment'|| $name == 'alias'|| $name == 'extra') {continue;}if ($name == 'notnull' && isset($this->_columns[$columnName]['autoincrement'])&& $this->_columns[$columnName]['autoincrement'] === true) {continue;}// skip it if it's explicitly set to FALSE (i.e. notnull => false)if ($args === false) {continue;}$validators[$name] = $args;}return $validators;}/*** Gets the maximum length of a field.* For integer fields, length is bytes occupied.* For decimal fields, it is the total number of cyphers** @param string $fieldName* @return integer*/public function getFieldLength($fieldName){return $this->_columns[$this->getColumnName($fieldName)]['length'];}/*** Retrieves a bound query part.* @see bindQueryPart()** @param string $queryPart field interested* @return string value of the bind*/public function getBoundQueryPart($queryPart){if ( ! isset($this->_options['queryParts'][$queryPart])) {return null;}return $this->_options['queryParts'][$queryPart];}/*** unshiftFilter** @param Doctrine_Record_Filter $filter* @return Doctrine_Table this object (provides a fluent interface)*/public function unshiftFilter(Doctrine_Record_Filter $filter){$filter->setTable($this);$filter->init();array_unshift($this->_filters, $filter);return $this;}/*** getFilters** @return array $filters*/public function getFilters(){return $this->_filters;}/*** Generates a string representation of this object.** This method is useful for debugging purposes, or it can be overriden in* Doctrine_Record to provide a value when Record is casted to (string).** @return string*/public function __toString(){return Doctrine_Lib::getTableAsString($this);}/*** Helper method for buildFindByWhere to decide if a string is greater than another** @param string $a* @param string $b*/private function isGreaterThan($a, $b){if (strlen($a) == strlen($b)) return 0;return (strlen($a) > strlen($b)) ? 1 : -1;}public function buildFindByWhere($fieldName){// Get all variations of possible field names$fields = array_merge($this->getFieldNames(), $this->getColumnNames());$fields = array_merge($fields, array_map(array('Doctrine_Inflector', 'classify'), $fields));$fields = array_merge($fields, array_map('ucfirst', $fields));// Sort field names by length - smallest first// and then reverse so that largest is firstusort($fields, array($this, 'isGreaterThan'));$fields = array_reverse(array_unique($fields));// Identify fields and operatorspreg_match_all('/(' . implode('|', $fields) . ')(Or|And)?/', $fieldName, $matches);$fieldsFound = $matches[1];$operatorFound = array_map('strtoupper', $matches[2]);// Check if $fieldName has unidentified parts leftif (strlen(implode('', $fieldsFound) . implode('', $operatorFound)) !== strlen($fieldName)) {$expression = preg_replace('/(' . implode('|', $fields) . ')(Or|And)?/', '($1)$2', $fieldName);throw new Doctrine_Table_Exception('Invalid expression found: ' . $expression);}// Build result$where = $lastOperator = '';$bracketOpen = false;foreach ($fieldsFound as $index => $field) {$field = $this->_resolveFindByFieldName($field);if (!$field) {throw new Doctrine_Table_Exception('Invalid field name to find by: ' . $field);}if ($operatorFound[$index] == 'OR' && !$bracketOpen) {$where .= '(';$bracketOpen = true;}$where .= 'dctrn_find.' . $field . ' = ?';if ($operatorFound[$index] != 'OR' && $lastOperator == 'OR') {$where .= ')';$bracketOpen = false;}$where .= ' ' . strtoupper($operatorFound[$index]) . ' ';$lastOperator = $operatorFound[$index];}return trim($where);}/*** Resolves the passed find by field name inflecting the parameter.** This method resolves the appropriate field name* regardless of whether the user passes a column name, field name, or a Doctrine_Inflector::classified()* version of their column name. It will be inflected with Doctrine_Inflector::tableize()* to get the column or field name.** @param string $name* @return string $fieldName*/protected function _resolveFindByFieldName($name){$fieldName = Doctrine_Inflector::tableize($name);if ($this->hasColumn($name) || $this->hasField($name)) {return $this->getFieldName($this->getColumnName($name));} else if ($this->hasColumn($fieldName) || $this->hasField($fieldName)) {return $this->getFieldName($this->getColumnName($fieldName));} else {return false;}}/*** Adds support for magic finders.** This method add support for calling methods not defined in code, such as:* findByColumnName, findByRelationAlias* findById, findByContactId, etc.** @return the result of the finder*/public function __call($method, $arguments){$lcMethod = strtolower($method);if (substr($lcMethod, 0, 6) == 'findby') {$by = substr($method, 6, strlen($method));$method = 'findBy';} else if (substr($lcMethod, 0, 9) == 'findoneby') {$by = substr($method, 9, strlen($method));$method = 'findOneBy';}if (isset($by)) {if ( ! isset($arguments[0])) {throw new Doctrine_Table_Exception('You must specify the value to ' . $method);}$fieldName = $this->_resolveFindByFieldName($by);$count = count(explode('Or', $by)) + (count(explode('And', $by)) - 1);if (count($arguments) > $count){$hydrationMode = end($arguments);unset($arguments[count($arguments) - 1]);} else {$hydrationMode = null;}if ($this->hasField($fieldName)) {return $this->$method($fieldName, $arguments[0], $hydrationMode);} else if ($this->hasRelation($by)) {$relation = $this->getRelation($by);if ($relation['type'] === Doctrine_Relation::MANY) {throw new Doctrine_Table_Exception('Cannot findBy many relationship.');}return $this->$method($relation['local'], $arguments[0], $hydrationMode);} else {return $this->$method($by, $arguments, $hydrationMode);}}// Forward the method on to the record instance and see if it has anything or one of its behaviorstry {return call_user_func_array(array($this->getRecordInstance(), $method . 'TableProxy'), $arguments);} catch (Doctrine_Record_UnknownPropertyException $e) {}throw new Doctrine_Table_Exception(sprintf('Unknown method %s::%s', get_class($this), $method));}}