private function _gatherRelationsSql($class, array &$sql, array &$columns, array &$constraints) { foreach ($class->associationMappings as $fieldName => $mapping) { if (isset($class->inheritedAssociationFields[$fieldName])) { continue; } $foreignClass = $this->_em->getClassMetadata($mapping->targetEntityName); if ($mapping->isOneToOne() && $mapping->isOwningSide) { $constraint = array(); $constraint['tableName'] = $class->getTableName(); $constraint['foreignTable'] = $foreignClass->getTableName(); $constraint['local'] = array(); $constraint['foreign'] = array(); foreach ($mapping->getJoinColumns() as $joinColumn) { $column = array(); $column['name'] = $joinColumn['name']; $column['type'] = Type::getType($foreignClass->getTypeOfColumn($joinColumn['referencedColumnName'])); $columns[$joinColumn['name']] = $column; $constraint['local'][] = $joinColumn['name']; $constraint['foreign'][] = $joinColumn['referencedColumnName']; } $constraints[] = $constraint; } else { if ($mapping->isOneToMany() && $mapping->isOwningSide) { //... create join table, one-many through join table supported later throw DoctrineException::updateMe("Not yet implemented."); } else { if ($mapping->isManyToMany() && $mapping->isOwningSide) { // create join table $joinTableColumns = array(); $joinTableOptions = array(); $joinTable = $mapping->getJoinTable(); $constraint1 = array(); $constraint1['tableName'] = $joinTable['name']; $constraint1['foreignTable'] = $class->getTableName(); $constraint1['local'] = array(); $constraint1['foreign'] = array(); foreach ($joinTable['joinColumns'] as $joinColumn) { $column = array(); $column['primary'] = true; $joinTableOptions['primary'][] = $joinColumn['name']; $column['name'] = $joinColumn['name']; $column['type'] = Type::getType($class->getTypeOfColumn($joinColumn['referencedColumnName'])); $joinTableColumns[$joinColumn['name']] = $column; $constraint1['local'][] = $joinColumn['name']; $constraint1['foreign'][] = $joinColumn['referencedColumnName']; } $constraints[] = $constraint1; $constraint2 = array(); $constraint2['tableName'] = $joinTable['name']; $constraint2['foreignTable'] = $foreignClass->getTableName(); $constraint2['local'] = array(); $constraint2['foreign'] = array(); foreach ($joinTable['inverseJoinColumns'] as $inverseJoinColumn) { $column = array(); $column['primary'] = true; $joinTableOptions['primary'][] = $inverseJoinColumn['name']; $column['name'] = $inverseJoinColumn['name']; $column['type'] = Type::getType($this->_em->getClassMetadata($mapping->getTargetEntityName())->getTypeOfColumn($inverseJoinColumn['referencedColumnName'])); $joinTableColumns[$inverseJoinColumn['name']] = $column; $constraint2['local'][] = $inverseJoinColumn['name']; $constraint2['foreign'][] = $inverseJoinColumn['referencedColumnName']; } $constraints[] = $constraint2; $sql = array_merge($sql, $this->_platform->getCreateTableSql($joinTable['name'], $joinTableColumns, $joinTableOptions)); } } } } }
/** * Obtain DBMS specific SQL code portion needed to set an index * declaration to be used in statements like CREATE TABLE. * * @return string * @override */ public function getIndexFieldDeclarationListSql(array $fields) { $declFields = array(); foreach ($fields as $fieldName => $field) { $fieldString = '`' . $fieldName . '`'; if (is_array($field)) { if (isset($field['length'])) { $fieldString .= '(' . $field['length'] . ')'; } if (isset($field['sorting'])) { $sort = strtoupper($field['sorting']); switch ($sort) { case 'ASC': case 'DESC': $fieldString .= ' ' . $sort; break; default: throw DoctrineException::unknownIndexSortingOption($sort); } } } else { $fieldString = '`' . $field . '`'; } $declFields[] = $fieldString; } return implode(', ', $declFields); }
/** * Overrides an already defined type to use a different implementation. * * @static * @param string $name * @param string $className * @throws DoctrineException */ public static function overrideType($name, $className) { if (!isset(self::$_typesMap[$name])) { throw DoctrineException::typeNotFound($name); } self::$_typesMap[$name] = $className; }
/** * Defines a cache driver to be used for caching result sets. * * @param Doctrine\Common\Cache\Cache $driver Cache driver * @return Doctrine\ORM\AbstractQuery */ public function setResultCacheDriver($resultCacheDriver = null) { if ($resultCacheDriver !== null && !$resultCacheDriver instanceof \Doctrine\Common\Cache\Cache) { throw DoctrineException::invalidResultCacheObject($resultCacheDriver); } $this->_resultCacheDriver = $resultCacheDriver; if ($resultCacheDriver) { $this->_useResultCache = true; } return $this; }
/** * Gathers the SQL for properly setting up the relations of the given class. * This includes the SQL for foreign key constraints and join tables. * * @param ClassMetadata $class * @param \Doctrine\DBAL\Schema\Table $table * @param \Doctrine\DBAL\Schema\Schema $schema * @return void */ private function _gatherRelationsSql($class, $table, $schema) { foreach ($class->associationMappings as $fieldName => $mapping) { if (isset($class->inheritedAssociationFields[$fieldName])) { continue; } $foreignClass = $this->_em->getClassMetadata($mapping->targetEntityName); if ($mapping->isOneToOne() && $mapping->isOwningSide) { $primaryKeyColumns = $uniqueConstraints = array(); // unnecessary for this relation-type $this->_gatherRelationJoinColumns($mapping->getJoinColumns(), $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); } else { if ($mapping->isOneToMany() && $mapping->isOwningSide) { //... create join table, one-many through join table supported later throw DoctrineException::notSupported(); } else { if ($mapping->isManyToMany() && $mapping->isOwningSide) { // create join table $joinTable = $mapping->getJoinTable(); $theJoinTable = $schema->createTable($mapping->getQuotedJoinTableName($this->_platform)); $primaryKeyColumns = $uniqueConstraints = array(); // Build first FK constraint (relation table => source table) $this->_gatherRelationJoinColumns($joinTable['joinColumns'], $theJoinTable, $class, $mapping, $primaryKeyColumns, $uniqueConstraints); // Build second FK constraint (relation table => target table) $this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); foreach ($uniqueConstraints as $indexName => $unique) { $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); } $theJoinTable->setPrimaryKey($primaryKeyColumns); } } } } }
/** * Retrieves error string given a message key for lookup * * @static * @param string $messageKey * @return string|false Returns the error string if found; FALSE otherwise */ public static function getExceptionMessage($messageKey) { if (!self::$_messages) { // Lazy-init messages self::$_messages = array('DoctrineException#partialObjectsAreDangerous' => "Loading partial objects is dangerous. Fetch full objects or consider " . "using a different fetch mode. If you really want partial objects, " . "set the doctrine.forcePartialLoad query hint to TRUE.", 'QueryException#nonUniqueResult' => "The query contains more than one result."); } if (isset(self::$_messages[$messageKey])) { return self::$_messages[$messageKey]; } return false; }
/** * Defines a cache driver to be used for caching result sets. * * @param Doctrine\ORM\Cache\Cache $driver Cache driver * @return Doctrine\ORM\Query */ public function setResultCache($resultCache = null) { if ($resultCache !== null && !$resultCache instanceof \Doctrine\ORM\Cache\Cache) { throw DoctrineException::updateMe('Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'); } $this->_resultCache = $resultCache; return $this; }
/** * Obtain DBMS specific SQL to be used to create time fields in statements * like CREATE TABLE. * * @param array $fieldDeclaration * @return string */ public function getTimeTypeDeclarationSql(array $fieldDeclaration) { throw DoctrineException::getTimeTypeDeclarationNotSupported($this); }
/** * Add a single task to CLI Namespace. * Example of inclusion support to a single task: * * [php] * $cliOrmNamespace->addTask('my-custom-task', 'MyProject\Cli\Tasks\MyCustomTask'); * * @param string $name CLI Task name * @param string $class CLI Task class (FQCN - Fully Qualified Class Name) * * @return TaskNamespace This object instance */ public function addTask($name, $class) { $name = self::formatName($name); if ($this->hasTask($name)) { throw DoctrineException::cannotOverrideTask($name); } return $this->overrideTask($name, $class); }
/** * Adds support for magic finders. * * @return array|object The found entity/entities. * @throws BadMethodCallException If the method called is an invalid find* method * or no find* method at all and therefore an invalid * method call. */ public function __call($method, $arguments) { if (substr($method, 0, 6) == 'findBy') { $by = substr($method, 6, strlen($method)); $method = 'findBy'; } else { if (substr($method, 0, 9) == 'findOneBy') { $by = substr($method, 9, strlen($method)); $method = 'findOneBy'; } else { throw new \BadMethodCallException("Undefined method '{$method}'."); } } if (!isset($arguments[0])) { throw DoctrineException::findByNameRequired(); } $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); if ($this->_class->hasField($fieldName)) { return $this->{$method}(array($fieldName => $arguments[0])); } else { throw \Doctrine\Common\DoctrineException::invalidFindBy($by); } }
/** * generates the sql for altering an existing table on postgresql * * @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() * @return array * @override */ public function getAlterTableSql($name, array $changes, $check = false) { foreach ($changes as $changeName => $change) { switch ($changeName) { case 'add': case 'remove': case 'change': case 'name': case 'rename': break; default: throw DoctrineException::updateMe('change type "' . $changeName . '\\" not yet supported'); } } if ($check) { return true; } $sql = array(); if (isset($changes['add']) && is_array($changes['add'])) { foreach ($changes['add'] as $fieldName => $field) { $query = 'ADD ' . $this->getColumnDeclarationSql($fieldName, $field); $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; } } if (isset($changes['remove']) && is_array($changes['remove'])) { foreach ($changes['remove'] as $fieldName => $field) { $fieldName = $this->quoteIdentifier($fieldName, true); $query = 'DROP ' . $fieldName; $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; } } if (isset($changes['change']) && is_array($changes['change'])) { foreach ($changes['change'] as $fieldName => $field) { $fieldName = $this->quoteIdentifier($fieldName, true); if (isset($field['type'])) { $serverInfo = $this->getServerVersion(); if (is_array($serverInfo) && $serverInfo['major'] < 8) { throw DoctrineException::updateMe('changing column type for "' . $field['type'] . '\\" requires PostgreSQL 8.0 or above'); } $query = 'ALTER ' . $fieldName . ' TYPE ' . $this->getTypeDeclarationSql($field['definition']); $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; } if (array_key_exists('default', $field)) { $query = 'ALTER ' . $fieldName . ' SET DEFAULT ' . $this->quote($field['definition']['default'], $field['definition']['type']); $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; } if (!empty($field['notnull'])) { $query = 'ALTER ' . $fieldName . ' ' . ($field['definition']['notnull'] ? 'SET' : 'DROP') . ' NOT NULL'; $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; } } } if (isset($changes['rename']) && is_array($changes['rename'])) { foreach ($changes['rename'] as $fieldName => $field) { $fieldName = $this->quoteIdentifier($fieldName, true); $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName . ' TO ' . $this->quoteIdentifier($field['name'], true); } } $name = $this->quoteIdentifier($name, true); if (isset($changes['name'])) { $changeName = $this->quoteIdentifier($changes['name'], true); $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changeName; } return $sql; }
/** * create a new table * * @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. * array( * 'id' => array( * 'type' => 'integer', * 'unsigned' => 1 * 'notnull' => 1 * 'default' => 0 * ), * 'name' => array( * 'type' => 'text', * 'length' => 12 * ), * 'password' => array( * 'type' => 'text', * 'length' => 12 * ) * ); * @param array $options An associative array of table options: * * @return void * @override */ public function getCreateTableSql($name, array $fields, array $options = array()) { if (!$name) { throw DoctrineException::invalidTableName($name); } if (empty($fields)) { throw DoctrineException::noFieldsSpecifiedForTable($name); } $queryFields = $this->getColumnDeclarationListSql($fields); $autoinc = false; foreach ($fields as $field) { if (isset($field['autoincrement']) && $field['autoincrement']) { $autoinc = true; break; } } if (!$autoinc && isset($options['primary']) && !empty($options['primary'])) { $keyColumns = array_unique(array_values($options['primary'])); $keyColumns = array_map(array($this, 'quoteIdentifier'), $keyColumns); $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } $sql = 'CREATE TABLE ' . $name . ' (' . $queryFields; /*if ($check = $this->getCheckDeclarationSql($fields)) { $sql .= ', ' . $check; } if (isset($options['checks']) && $check = $this->getCheckDeclarationSql($options['checks'])) { $sql .= ', ' . $check; }*/ $sql .= ')'; $query[] = $sql; if (isset($options['indexes']) && !empty($options['indexes'])) { foreach ($options['indexes'] as $index => $definition) { $query[] = $this->getCreateIndexSql($name, $index, $definition); } } return $query; }
/** * build a pattern matching string * * EXPERIMENTAL * * WARNING: this function is experimental and may change signature at * any time until labelled as non-experimental * * @access public * * @param array $pattern even keys are strings, odd are patterns (% and _) * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future) * @param string $field optional field name that is being matched against * (might be required when emulating ILIKE) * * @return string SQL pattern * @override */ public function getMatchPatternExpression($pattern, $operator = null, $field = null) { $match = ''; if (!is_null($operator)) { $field = is_null($field) ? '' : $field . ' '; $operator = strtoupper($operator); switch ($operator) { // case insensitive case 'ILIKE': $match = $field . 'ILIKE '; break; // case sensitive // case sensitive case 'LIKE': $match = $field . 'LIKE '; break; default: throw DoctrineException::operatorNotSupported($operator); } } $match .= "'"; foreach ($pattern as $key => $value) { if ($key % 2) { $match .= $value; } else { $match .= $this->conn->escapePattern($this->conn->escape($value)); } } $match .= "'"; $match .= $this->patternEscapeString(); return $match; }
/** * Adds support for magic finders. * findByColumnName, findByRelationAlias * findById, findByContactId, etc. * * @return void * @throws BadMethodCallException If the method called is an invalid find* method * or no find* method at all and therefore an invalid * method call. */ public function __call($method, $arguments) { if (substr($method, 0, 6) == 'findBy') { $by = substr($method, 6, strlen($method)); $method = 'findBy'; } else { if (substr($method, 0, 9) == 'findOneBy') { $by = substr($method, 9, strlen($method)); $method = 'findOneBy'; } else { throw new BadMethodCallException("Undefined method '{$method}'."); } } if (isset($by)) { if (!isset($arguments[0])) { throw DoctrineException::updateMe('You must specify the value to findBy.'); } $fieldName = Doctrine::tableize($by); $hydrationMode = isset($arguments[1]) ? $arguments[1] : null; if ($this->_classMetadata->hasField($fieldName)) { return $this->{$method}($fieldName, $arguments[0], $hydrationMode); } else { if ($this->_classMetadata->hasRelation($by)) { $relation = $this->_classMetadata->getRelation($by); if ($relation['type'] === Doctrine_Relation::MANY) { throw DoctrineException::updateMe('Cannot findBy many relationship.'); } return $this->{$method}($relation['local'], $arguments[0], $hydrationMode); } else { throw DoctrineException::updateMe('Cannot find by: ' . $by . '. Invalid field or relationship alias.'); } } } }