Source for file Export.php
Documentation is available at Export.php
* $Id: Export.php 2288 2007-08-29 21:51:49Z Jonathan.Wage $
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>.
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @category Object Relational Mapping
* @link www.phpdoctrine.com
* @version $Revision: 2288 $
'timestamp' =>
'1970-01-01 00:00:00',
* drop an existing database
* (this method is implemented by the drivers)
* @param string $name name of the database that should be dropped
* drop an existing database
* (this method is implemented by the drivers)
* @param string $name name of the database that should be dropped
* @param string $table name of table that should be dropped from the database
return 'DROP TABLE ' .
$this->conn->quoteIdentifier($table);
* @param string $table name of table that should be dropped from the database
* @param string $table name of table that should be used in method
* @param string $name name of the index to be dropped
* @param string $table name of table that should be used in method
* @param string $name name of the index to be dropped
* @return string SQL that is used for dropping an index
$name =
$this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
return 'DROP INDEX ' .
$name;
* drop existing constraint
* @param string $table name of table that should be used in method
* @param string $name name of the constraint to be dropped
* @param string $primary hint if the constraint is primary
$table =
$this->conn->quoteIdentifier($table);
$name =
$this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
return $this->conn->exec('ALTER TABLE ' .
$table .
' DROP CONSTRAINT ' .
$name);
* (this method is implemented by the drivers)
* @throws Doctrine_Connection_Exception if something fails at database level
* @param string $sequenceName name of the sequence to be dropped
* @throws Doctrine_Connection_Exception if something fails at database level
* @param string $sequenceName name of the sequence to be dropped
* (this method is implemented by the drivers)
* @param string $name name of the database that should be created
* (this method is implemented by the drivers)
* @param string $name name of the database that should be created
* @param string $name Name of the database that should be created
* @param array $fields Associative array that contains the definition of each field of the new table
* The indexes of the array entries are the names of the fields of the table an
* the array entry values are associative arrays like those that are meant to be
* passed with the field definitions to get[Type]Declaration() functions.
* @param array $options An associative array of table options:
public function createTableSql($name, array $fields, array $options =
array())
if (isset
($options['primary']) &&
! empty($options['primary'])) {
if (isset
($options['indexes']) &&
! empty($options['indexes'])) {
foreach($options['indexes'] as $index =>
$definition) {
$query =
'CREATE TABLE ' .
$this->conn->quoteIdentifier($name, true) .
' (' .
$queryFields;
if (isset
($options['foreignKeys'])) {
foreach ((array)
$options['foreignKeys'] as $k =>
$definition) {
* @param string $name Name of the database that should be created
* @param array $fields Associative array that contains the definition of each field of the new table
* @param array $options An associative array of table options:
* @see Doctrine_Export::createTableSql()
public function createTable($name, array $fields, array $options =
array())
foreach ($sql as $query) {
$this->conn->execute($query);
* @throws Doctrine_Connection_Exception if something fails at database level
* @param string $seqName name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @param array $options An associative array of table options:
* 'collate' => 'utf8_unicode_ci',
public function createSequence($seqName, $start =
1, array $options =
array())
* return RDBMS specific create sequence statement
* (this method is implemented by the drivers)
* @throws Doctrine_Connection_Exception if something fails at database level
* @param string $seqName name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @param array $options An associative array of table options:
* 'collate' => 'utf8_unicode_ci',
* create a constraint on a table
* @param string $table name of the table on which the constraint is to be created
* @param string $name name of the constraint to be created
* @param array $definition associative array that defines properties of the constraint to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the constraint fields as array
* constraints. Each entry of this array is set to another type of associative
* array that specifies properties of the constraint that are specific to
* 'user_name' => array(),
* 'last_login' => array()
* create a constraint on a table
* @param string $table name of the table on which the constraint is to be created
* @param string $name name of the constraint to be created
* @param array $definition associative array that defines properties of the constraint to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the constraint fields as array
* constraints. Each entry of this array is set to another type of associative
* array that specifies properties of the constraint that are specific to
* 'user_name' => array(),
* 'last_login' => array()
$table =
$this->conn->quoteIdentifier($table);
$name =
$this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
$query =
'ALTER TABLE ' .
$table .
' ADD CONSTRAINT ' .
$name;
if (isset
($definition['primary']) &&
$definition['primary']) {
$query .=
' PRIMARY KEY';
} elseif (isset
($definition['unique']) &&
$definition['unique']) {
foreach (array_keys($definition['fields']) as $field) {
$fields[] =
$this->conn->quoteIdentifier($field, true);
$query .=
' ('.
implode(', ', $fields) .
')';
* Get the stucture of a field into an array
* @param string $table name of the table on which the index is to be created
* @param string $name name of the index to be created
* @param array $definition associative array that defines properties of the index to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the index fields as array
* indexes. Each entry of this array is set to another type of associative
* array that specifies properties of the index that are specific to
* Currently, only the sorting property is supported. It should be used
* to define the sorting direction of the index. It may be set to either
* ascending or descending.
* Not all DBMS support index sorting direction configuration. The DBMS
* drivers of those that do not support it ignore this property. Use the
* function supports() to determine whether the DBMS driver can manage indexes.
* 'sorting' => 'ascending'
* 'last_login' => array()
public function createIndex($table, $name, array $definition)
return $this->conn->execute($this->createIndexSql($table, $name, $definition));
* Get the stucture of a field into an array
* @param string $table name of the table on which the index is to be created
* @param string $name name of the index to be created
* @param array $definition associative array that defines properties of the index to be created.
* @see Doctrine_Export::createIndex()
$table =
$this->conn->quoteIdentifier($table);
$name =
$this->conn->quoteIdentifier($name);
if(isset
($definition['type'])) {
$query =
'CREATE ' .
$type .
'INDEX ' .
$name .
' ON ' .
$table;
foreach ($definition['fields'] as $field) {
$fields[] =
$this->conn->quoteIdentifier($field);
$query .=
' (' .
implode(', ', $fields) .
')';
* @param string $table name of the table on which the foreign key is to be created
* @param array $definition associative array that defines properties of the foreign key to be created.
$table =
$this->conn->quoteIdentifier($table);
* alter an existing table
* (this method is implemented by the drivers)
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
* New name for the table.
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
* 'file_limit' => array(),
* 'time_limit' => array()
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
public function alterTable($name, array $changes, $check)
$this->conn->execute($this->alterTableSql($name, $changes, $check));
* generates the sql for altering an existing table
* (this method is implemented by the drivers)
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type *
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @see Doctrine_Export::alterTable()
throw
new Doctrine_Export_Exception('Alter table not supported by this driver.');
* Get declaration of a number of field in bulk
* @param array $fields a multidimensional associative array.
* The first dimension determines the field name, while the second
* dimension is keyed with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
* Text value to be used as default for this field.
* Boolean flag that indicates whether this field is constrained
* Text value with the default CHARACTER SET for this field.
* Text value with the default COLLATION for this field.
foreach ($fields as $fieldName =>
$field) {
return implode(', ', $queryFields);
* Obtain DBMS specific SQL code portion needed to declare a generic type
* field to be used in statements like CREATE TABLE.
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
* Text value to be used as default for this field.
* Boolean flag that indicates whether this field is constrained
* Text value with the default CHARACTER SET for this field.
* Text value with the default COLLATION for this field.
* column check constraint
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
$default =
$this->getDefaultFieldDeclaration($field);
$charset =
(isset
($field['charset']) &&
$field['charset']) ?
$collation =
(isset
($field['collation']) &&
$field['collation']) ?
$notnull =
(isset
($field['notnull']) &&
$field['notnull']) ?
' NOT NULL' :
'';
$unique =
(isset
($field['unique']) &&
$field['unique']) ?
$check =
(isset
($field['check']) &&
$field['check']) ?
' ' .
$field['check'] :
'';
$method =
'get' .
$field['type'] .
'Declaration';
if (method_exists($this->conn->dataDict, $method)) {
return $this->conn->dataDict->$method($name, $field);
$dec =
$this->conn->dataDict->getNativeDeclaration($field);
return $this->conn->quoteIdentifier($name, true) .
' ' .
$dec .
$charset .
$default .
$notnull .
$unique .
$check .
$collation;
* Obtain DBMS specific SQL code portion needed to set a default value
* declaration to be used in statements like CREATE TABLE.
* @param array $field field definition array
* @return string DBMS specific SQL code portion needed to set a default value
if (isset
($field['default'])) {
if ($field['default'] ===
'') {
$field['default'] =
empty($field['notnull'])
if ($field['default'] ===
'' &&
$field['default'] =
null;
if ($field['type'] ===
'boolean') {
$fields['default'] =
$this->conn->convertBooleans($field['default']);
$default =
' DEFAULT ' .
$this->conn->quote($field['default'], $field['type']);
* Obtain DBMS specific SQL code portion needed to set a CHECK constraint
* declaration to be used in statements like CREATE TABLE.
* @param array $definition check definition
* @return string DBMS specific SQL code portion needed to set a CHECK constraint
foreach ($definition as $field =>
$def) {
$constraints[] =
'CHECK (' .
$def .
')';
if (isset
($def['min'])) {
$constraints[] =
'CHECK (' .
$field .
' >= ' .
$def['min'] .
')';
if (isset
($def['max'])) {
$constraints[] =
'CHECK (' .
$field .
' <= ' .
$def['max'] .
')';
return implode(', ', $constraints);
* Obtain DBMS specific SQL code portion needed to set an index
* declaration to be used in statements like CREATE TABLE.
* @param string $name name of the index
* @param array $definition index definition
* @return string DBMS specific SQL code portion needed to set an index
$name =
$this->conn->quoteIdentifier($name);
if (isset
($definition['type'])) {
if (strtolower($definition['type']) ==
'unique') {
if ( ! isset
($definition['fields']) ||
! is_array($definition['fields'])) {
$query =
$type .
'INDEX ' .
$name;
* getIndexFieldDeclarationList
* Obtain DBMS specific SQL code portion needed to set an index
* declaration to be used in statements like CREATE TABLE.
foreach ($fields as $field =>
$definition) {
$ret[] =
$this->conn->quoteIdentifier($field);
$ret[] =
$this->conn->quoteIdentifier($definition);
* A method to return the required SQL string that fits between CREATE ... TABLE
* to create the table as a temporary table.
* Should be overridden in driver classes to return the correct string for the
* specific database type.
* The default is to return the string "TEMPORARY" - this will result in a
* SQL error for any database that does not support temporary tables, or that
* requires a different SQL command from "CREATE TEMPORARY TABLE".
* @return string The string required to be placed between "CREATE" and "TABLE"
* to generate a temporary table, if possible.
* getForeignKeyDeclaration
* Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
* of a field declaration to be used in statements like CREATE TABLE.
* @param array $definition an associative array with the following structure:
* name optional constraint name
* local the local field(s)
* foreign the foreign reference field(s)
* foreignTable the name of the foreign table
* onDelete referential delete action
* onUpdate referential update action
* deferred deferred constraint checking
* The onDelete and onUpdate keys accept the following values:
* CASCADE: Delete or update the row from the parent table and automatically delete or
* update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported.
* Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column
* in the parent table or in the child table.
* SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the
* child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier
* specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported.
* NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary
* key value is not allowed to proceed if there is a related foreign key value in the referenced table.
* RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as
* omitting the ON DELETE or ON UPDATE clause.
* @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
* of a field declaration.
$sql =
$this->getForeignKeyBaseDeclaration($definition);
* getAdvancedForeignKeyOptions
* Return the FOREIGN KEY query section dealing with non-standard options
* as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
* @param array $definition foreign key definition
if ( ! empty($definition['onUpdate'])) {
$query .=
' ON UPDATE ' .
$this->getForeignKeyRefentialAction($definition['onUpdate']);
if ( ! empty($definition['onDelete'])) {
$query .=
' ON DELETE ' .
$this->getForeignKeyRefentialAction($definition['onDelete']);
* getForeignKeyReferentialAction
* returns given referential action in uppercase if valid, otherwise throws
* @throws Doctrine_Exception_Exception if unknown referential action given
* @param string $action foreign key referential action
* @param string foreign key referential action in uppercase
* getForeignKeyBaseDeclaration
* Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
* of a field declaration to be used in statements like CREATE TABLE.
* @param array $definition
if (isset
($definition['name'])) {
$sql .=
'CONSTRAINT ' .
$this->conn->quoteIdentifier($definition['name']) .
' ';
if ( ! isset
($definition['local'])) {
if ( ! isset
($definition['foreign'])) {
if ( ! isset
($definition['foreignTable'])) {
if ( ! is_array($definition['local'])) {
$definition['local'] =
array($definition['local']);
if ( ! is_array($definition['foreign'])) {
$definition['foreign'] =
array($definition['foreign']);
.
$definition['foreignTable'] .
'('
* Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint
* of a field declaration to be used in statements like CREATE TABLE.
* @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
* of a field declaration.
* Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration to be used in statements like CREATE TABLE.
* @param string $charset name of the charset
* @return string DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration.
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
* @param string $collation name of the collation
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
* method for exporting Doctrine_Record classes to a schema
* if the directory parameter is given this method first iterates
* recursively trhough the given directory in order to find any model classes
* Then it iterates through all declared classes and creates tables for the ones
* that extend Doctrine_Record and are not abstract classes
* @throws Doctrine_Connection_Exception if some error other than Doctrine::ERR_ALREADY_EXISTS
* occurred during the create table operation
* @param string $directory optional directory parameter
$this->conn->beginTransaction();
foreach ($sql as $query) {
// we only want to silence table already exists errors
if($e->getPortableCode() !==
Doctrine::ERR_ALREADY_EXISTS) {
* method for exporting Doctrine_Record classes to a schema
* @throws Doctrine_Connection_Exception if some error other than Doctrine::ERR_ALREADY_EXISTS
* occurred during the create table operation
$sql =
$this->exportClassesSql($classes);
$this->conn->beginTransaction();
foreach ($sql as $query) {
// we only want to silence table already exists errors
if($e->getPortableCode() !==
Doctrine::ERR_ALREADY_EXISTS) {
* method for exporting Doctrine_Record classes to a schema
* @throws Doctrine_Connection_Exception if some error other than Doctrine::ERR_ALREADY_EXISTS
* occurred during the create table operation
$parent =
new ReflectionClass('Doctrine_Record');
// we iterate trhough the diff of previously declared classes
// and currently declared classes
foreach ($classes as $name) {
$class =
new ReflectionClass($name);
// check if class is an instance of Doctrine_Record and not abstract
// class must have method setTableDefinition (to avoid non-Record subclasses like symfony's sfDoctrineRecord)
if ($class->isSubclassOf($parent) &&
! $class->isAbstract() &&
$class->hasMethod('setTableDefinition')
&&
$class->getMethod('setTableDefinition')->getDeclaringClass()->getName() ==
$class->getName()) {
$table =
$record->getTable();
$data =
$table->getExportableFormat();
$query =
$this->conn->export->createTableSql($data['tableName'], $data['columns'], $data['options']);
* returns the sql for exporting Doctrine_Record classes to a schema
* if the directory parameter is given this method first iterates
* recursively trhough the given directory in order to find any model classes
* Then it iterates through all declared classes and creates tables for the ones
* that extend Doctrine_Record and are not abstract classes
* @throws Doctrine_Connection_Exception if some error other than Doctrine::ERR_ALREADY_EXISTS
* occurred during the create table operation
* @param string $directory optional directory parameter
if ($directory !==
null) {
foreach ((array)
$directory as $dir) {
$it =
new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::LEAVES_ONLY);
$e =
explode('.', $file->getFileName());
if (end($e) ===
'php' &&
strpos($file->getFileName(), '.inc') ===
false) {
require_once $file->getPathName();
* exports given table into database based on column and option definitions
* @throws Doctrine_Connection_Exception if some error other than Doctrine::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
TODO: maybe there should be portability option for the following check
if ( ! Doctrine::isValidClassname($table->getOption('declaringClass')->getName())) {
throw new Doctrine_Export_Exception('Class name not valid.');
$data =
$table->getExportableFormat();
$this->conn->export->createTable($data['tableName'], $data['columns'], $data['options']);
// we only want to silence table already exists errors
if($e->getPortableCode() !==
Doctrine::ERR_ALREADY_EXISTS) {