diff --git a/lib/Doctrine/Common/DoctrineException.php b/lib/Doctrine/Common/DoctrineException.php index 0c36f720f..4774c3bd5 100644 --- a/lib/Doctrine/Common/DoctrineException.php +++ b/lib/Doctrine/Common/DoctrineException.php @@ -25,13 +25,16 @@ class DoctrineException extends \Exception public static function __callStatic($method, $arguments) { + $class = get_called_class(); + $messageKey = substr($class, strrpos($class, '\\') + 1) . "#$method"; + $end = end($arguments); if ($end instanceof Exception) { $this->_innerException = $end; unset($arguments[count($arguments) - 1]); } - if ($message = self::getExceptionMessage($method)) { + if ($message = self::getExceptionMessage($messageKey)) { $message = sprintf($message, $arguments); } else { $message = strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $method)); @@ -42,22 +45,25 @@ class DoctrineException extends \Exception } $message .= ' (' . implode(', ', $args) . ')'; } - $class = get_called_class(); + return new $class($message); } - public static function getExceptionMessage($method) + public static function getExceptionMessage($messageKey) { if ( ! self::$_messages) { + // Lazy-init messages self::$_messages = array( - 'partialObjectsAreDangerous' => + '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." + "set the doctrine.forcePartialLoad query hint to TRUE.", + 'QueryException#nonUniqueResult' => + "The query contains more than one result." ); } - if (isset(self::$_messages[$method])) { - return self::$_messages[$method]; + if (isset(self::$_messages[$messageKey])) { + return self::$_messages[$messageKey]; } return false; } diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 14fc3c046..13b8383fe 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -515,7 +515,7 @@ class Connection { $this->connect(); try { - echo $query . PHP_EOL; + echo "DBAL:" . $query . PHP_EOL; if ( ! empty($params)) { $stmt = $this->prepare($query); $stmt->execute($params); @@ -756,6 +756,7 @@ class Connection */ public function getWrappedConnection() { + $this->connect(); return $this->_conn; } diff --git a/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php index e383e2067..2435bd077 100644 --- a/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -28,7 +28,18 @@ class Driver implements \Doctrine\DBAL\Driver */ private function _constructPdoDsn(array $params) { - //TODO + $dsn = 'pgsql:'; + if (isset($params['host'])) { + $dsn .= 'host=' . $params['host'] . ' '; + } + if (isset($params['port'])) { + $dsn .= 'port=' . $params['port'] . ' '; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ' '; + } + + return $dsn; } public function getDatabasePlatform() diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 4b755a38b..cfce0eef1 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -108,13 +108,6 @@ class EntityManager */ private $_eventManager; - /** - * The maintained (cached) Id generators. - * - * @var array - */ - private $_idGenerators = array(); - /** * The maintained (cached) hydrators. One instance per type. * @@ -202,38 +195,6 @@ class EntityManager { return $this->_metadataFactory->getMetadataFor($className); } - - /** - * Gets an IdGenerator that can be used to generate identifiers for the specified - * class. - */ - public function getIdGenerator($className) - { - if (!isset($this->_idGenerators[$className])) { - $this->_idGenerators[$className] = $this->_createIdGenerator( - $this->getClassMetadata($className)->getIdGeneratorType()); - } - return $this->_idGenerators[$className]; - } - - /** - * Used to lazily create an ID generator. - * - * @param string $generatorType - * @return object - */ - protected function _createIdGenerator($generatorType) - { - if ($generatorType == ClassMetadata::GENERATOR_TYPE_IDENTITY) { - return new \Doctrine\ORM\Id\IdentityGenerator($this); - } else if ($generatorType == ClassMetadata::GENERATOR_TYPE_SEQUENCE) { - return new \Doctrine\ORM\Id\SequenceGenerator($this); - } else if ($generatorType == ClassMetadata::GENERATOR_TYPE_TABLE) { - return new \Doctrine\ORM\Id\TableGenerator($this); - } else { - return new \Doctrine\ORM\Id\Assigned($this); - } - } /** * Creates a new Query object. @@ -265,7 +226,7 @@ class EntityManager * Creates a query with the specified name. * * @todo Implementation. - * @throws SomeException If there is no query registered with the given name. + * @throws DoctrineException If there is no query registered with the given name. */ public function createNamedQuery($name) { diff --git a/lib/Doctrine/ORM/Export/ClassExporter.php b/lib/Doctrine/ORM/Export/ClassExporter.php deleted file mode 100644 index 108aefd36..000000000 --- a/lib/Doctrine/ORM/Export/ClassExporter.php +++ /dev/null @@ -1,182 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Export; - -use Doctrine\ORM\EntityManager; - -/** - * The ClassExporter can generate database schemas/structures from ClassMetadata - * class descriptors. - * - * @author Konsta Vesterinen - * @author Lukas Smith (PEAR MDB2 library) - * @author Roman Borschel - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.org - * @since 2.0 - * @version $Revision: 4805 $ - */ -class ClassExporter -{ - /** The SchemaManager */ - private $_sm; - /** The EntityManager */ - private $_em; - /** The DatabasePlatform */ - private $_platform; - - /** - * Initializes a new ClassExporter instance that uses the connection of the - * provided EntityManager. - * - * @param Doctrine\ORM\EntityManager $em - */ - public function __construct(EntityManager $em) - { - $this->_em = $em; - $this->_sm = $em->getConnection()->getSchemaManager(); - $this->_platform = $em->getConnection()->getDatabasePlatform(); - } - - /** - * Exports an array of class meta data instances to your database - * - * @param array $classes - */ - public function exportClasses(array $classes) - { - $exportClassesSql = $this->getExportClassesSql($classes); - foreach ($exportClassesSql as $sql) { - $this->_em->getConnection()->execute($sql); - } - } - - /** - * Get an array of sql statements for the specified array of class meta data instances - * - * @param array $classes - * @return array $sql - */ - public function getExportClassesSql(array $classes) - { - $sql = array(); - $foreignKeyConstraints = array(); - - // First we create the tables - foreach ($classes as $class) { - $columns = array(); - $options = array(); - - foreach ($class->getFieldMappings() as $fieldName => $mapping) { - $column = array(); - $column['name'] = $mapping['columnName']; - $column['type'] = $mapping['type']; - $column['length'] = $mapping['length']; - $column['notnull'] = ! $mapping['nullable']; - if ($class->isIdentifier($fieldName)) { - $column['primary'] = true; - $options['primary'][] = $mapping['columnName']; - if ($class->isIdGeneratorIdentity()) { - $column['autoincrement'] = true; - } - } - $columns[$mapping['columnName']] = $column; - } - - foreach ($class->getAssociationMappings() as $mapping) { - $foreignClass = $this->_em->getClassMetadata($mapping->getTargetEntityName()); - 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'] = $foreignClass->getTypeOfColumn($joinColumn['referencedColumnName']); - $columns[$joinColumn['name']] = $column; - $constraint['local'][] = $joinColumn['name']; - $constraint['foreign'][] = $joinColumn['referencedColumnName']; - } - $foreignKeyConstraints[] = $constraint; - } else if ($mapping->isOneToMany() && $mapping->isOwningSide()) { - //... create join table, one-many through join table supported later - \Doctrine\Common\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'] = $class->getTypeOfColumn($joinColumn['referencedColumnName']); - $joinTableColumns[$joinColumn['name']] = $column; - $constraint1['local'][] = $joinColumn['name']; - $constraint1['foreign'][] = $joinColumn['referencedColumnName']; - } - $foreignKeyConstraints[] = $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'] = $this->_em->getClassMetadata($mapping->getTargetEntityName()) - ->getTypeOfColumn($inverseJoinColumn['referencedColumnName']); - $joinTableColumns[$inverseJoinColumn['name']] = $column; - $constraint2['local'][] = $inverseJoinColumn['name']; - $constraint2['foreign'][] = $inverseJoinColumn['referencedColumnName']; - } - $foreignKeyConstraints[] = $constraint2; - - $sql = array_merge($sql, $this->_platform->getCreateTableSql( - $joinTable['name'], $joinTableColumns, $joinTableOptions)); - } - } - - $sql = array_merge($sql, $this->_platform->getCreateTableSql($class->getTableName(), $columns, $options)); - } - - // Now create the foreign key constraints - if ($this->_platform->supportsForeignKeyConstraints()) { - foreach ($foreignKeyConstraints as $fkConstraint) { - $sql = array_merge($sql, (array)$this->_platform->getCreateForeignKeySql($fkConstraint['tableName'], $fkConstraint)); - } - } - - return $sql; - } -} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Id/AbstractIdGenerator.php b/lib/Doctrine/ORM/Id/AbstractIdGenerator.php index 625826ffb..f7d8441c6 100644 --- a/lib/Doctrine/ORM/Id/AbstractIdGenerator.php +++ b/lib/Doctrine/ORM/Id/AbstractIdGenerator.php @@ -8,21 +8,14 @@ use Doctrine\ORM\EntityManager; * Enter description here... */ abstract class AbstractIdGenerator -{ - protected $_em; - - public function __construct(EntityManager $em) - { - $this->_em = $em; - } - +{ /** * Generates an identifier for an entity. * * @param Doctrine\ORM\Entity $entity * @return mixed */ - abstract public function generate($entity); + abstract public function generate(EntityManager $em, $entity); /** * Gets whether this generator is a post-insert generator which means that diff --git a/lib/Doctrine/ORM/Id/Assigned.php b/lib/Doctrine/ORM/Id/Assigned.php index ca2c49568..3311084bc 100644 --- a/lib/Doctrine/ORM/Id/Assigned.php +++ b/lib/Doctrine/ORM/Id/Assigned.php @@ -21,6 +21,7 @@ namespace Doctrine\ORM\Id; +use Doctrine\ORM\EntityManager; use Doctrine\Common\DoctrineException; /** @@ -38,9 +39,9 @@ class Assigned extends AbstractIdGenerator * @return mixed * @override */ - public function generate($entity) + public function generate(EntityManager $em, $entity) { - $class = $this->_em->getClassMetadata(get_class($entity)); + $class = $em->getClassMetadata(get_class($entity)); $identifier = null; if ($class->isIdentifierComposite()) { $identifier = array(); @@ -61,7 +62,7 @@ class Assigned extends AbstractIdGenerator } if ( ! $identifier) { - \Doctrine\Common\DoctrineException::updateMe("Entity of type '" . get_class($entity) . "' is missing an assigned ID."); + throw DoctrineException::updateMe("Entity of type '" . get_class($entity) . "' is missing an assigned ID."); } return $identifier; diff --git a/lib/Doctrine/ORM/Id/IdentityGenerator.php b/lib/Doctrine/ORM/Id/IdentityGenerator.php index f6913343f..e2c3bf423 100644 --- a/lib/Doctrine/ORM/Id/IdentityGenerator.php +++ b/lib/Doctrine/ORM/Id/IdentityGenerator.php @@ -2,18 +2,20 @@ namespace Doctrine\ORM\Id; +use Doctrine\ORM\EntityManager; + class IdentityGenerator extends AbstractIdGenerator { /** - * Enter description here... + * Generates an ID for the given entity. * - * @param Doctrine_ORM_Entity $entity - * @return unknown + * @param object $entity + * @return integer|float * @override */ - public function generate($entity) + public function generate(EntityManager $em, $entity) { - return $this->_em->getConnection()->lastInsertId(); + return $em->getConnection()->lastInsertId(); } /** diff --git a/lib/Doctrine/ORM/Id/SequenceGenerator.php b/lib/Doctrine/ORM/Id/SequenceGenerator.php index 2d8ecb13e..4aaec13f4 100644 --- a/lib/Doctrine/ORM/Id/SequenceGenerator.php +++ b/lib/Doctrine/ORM/Id/SequenceGenerator.php @@ -1,19 +1,50 @@ . + */ namespace Doctrine\ORM\Id; use Doctrine\ORM\EntityManager; -class SequenceGenerator extends AbstractIdGenerator +/** + * Represents an ID generator that uses a database sequence. + * + * @since 2.0 + * @author Roman Borschel + */ +class SequenceGenerator extends AbstractIdGenerator implements \Serializable { private $_allocationSize; private $_sequenceName; private $_nextValue = 0; private $_maxValue = null; - - public function __construct(EntityManager $em, $sequenceName, $allocationSize = 20) + + /** + * Initializes a new sequence generator. + * + * @param Doctrine\ORM\EntityManager $em The EntityManager to use. + * @param string $sequenceName The name of the sequence. + * @param integer $allocationSize The allocation size of the sequence. + */ + public function __construct($sequenceName, $allocationSize) { - parent::__construct($em); $this->_sequenceName = $sequenceName; $this->_allocationSize = $allocationSize; } @@ -25,11 +56,11 @@ class SequenceGenerator extends AbstractIdGenerator * @return integer|float The generated value. * @override */ - public function generate($entity) + public function generate(EntityManager $em, $entity) { if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) { // Allocate new values - $conn = $this->_em->getConnection(); + $conn = $em->getConnection(); $sql = $conn->getDatabasePlatform()->getSequenceNextValSql($this->_sequenceName); $this->_maxValue = $conn->fetchOne($sql); $this->_nextValue = $this->_maxValue - $this->_allocationSize; @@ -37,13 +68,38 @@ class SequenceGenerator extends AbstractIdGenerator return $this->_nextValue++; } + /** + * Gets the maximum value of the currently allocated bag of values. + * + * @return integer|float + */ public function getCurrentMaxValue() { return $this->_maxValue; } - + + /** + * Gets the next value that will be returned by generate(). + * + * @return integer|float + */ public function getNextValue() { return $this->_nextValue; } + + public function serialize() + { + return serialize(array( + 'allocationSize' => $this->_allocationSize, + 'sequenceName' => $this->_sequenceName + )); + } + + public function unserialize($serialized) + { + $array = unserialize($serialized); + $this->_sequenceName = $array['sequenceName']; + $this->_allocationSize = $array['allocationSize']; + } } diff --git a/lib/Doctrine/ORM/Id/SequenceIdentityGenerator.php b/lib/Doctrine/ORM/Id/SequenceIdentityGenerator.php index b3c187e4c..758d73474 100644 --- a/lib/Doctrine/ORM/Id/SequenceIdentityGenerator.php +++ b/lib/Doctrine/ORM/Id/SequenceIdentityGenerator.php @@ -2,6 +2,8 @@ namespace Doctrine\ORM\Id; +use Doctrine\ORM\EntityManager; + class SequenceIdentityGenerator extends IdentityGenerator { private $_sequenceName; @@ -10,15 +12,18 @@ class SequenceIdentityGenerator extends IdentityGenerator { $this->_sequenceName = $sequenceName; } + + public function generate(EntityManager $em, $entity) + { + return $em->getConnection()->lastInsertId($this->_sequenceName); + } /** - * Enter description here... - * - * @param Doctrine_Connection $conn + * @return boolean * @override */ - public function getPostInsertId() + public function isPostInsertGenerator() { - return $this->_em->getConnection()->lastInsertId($this->_sequenceName); + return true; } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Id/TableGenerator.php b/lib/Doctrine/ORM/Id/TableGenerator.php index 9221eb75b..d9700dfa3 100644 --- a/lib/Doctrine/ORM/Id/TableGenerator.php +++ b/lib/Doctrine/ORM/Id/TableGenerator.php @@ -2,6 +2,8 @@ namespace Doctrine\ORM\Id; +use Doctrine\ORM\EntityManager; + /** * Id generator that uses a single-row database table and a hi/lo algorithm. * @@ -9,7 +11,7 @@ namespace Doctrine\ORM\Id; */ class TableGenerator extends AbstractIdGenerator { - public function generate($entity) + public function generate(EntityManager $em, $entity) { throw \Doctrine\Common\DoctrineException::updateMe("Not implemented"); } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 6ac0f71ea..c9c7ef682 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -333,6 +333,36 @@ final class ClassMetadata private $_reflectionProperties; //private $_insertSql; + /** + * The name of the ID generator used for this class. Only used for SEQUENCE + * and TABLE generation strategies. + * + * @var string + */ + //private $_idGeneratorName; + + /** + * The ID generator used for generating IDs for this class. + * + * @var AbstractIdGenerator + */ + private $_idGenerator; + + /** + * The definition of the sequence generator of this class. Only used for the + * SEQUENCE generation strategy. + * + * @var array + */ + private $_sequenceGeneratorDefinition; + + /** + * The definition of the table generator of this class. Only used for the + * TABLE generation strategy. + * + * @var array + */ + //private $_tableGeneratorDefinition; /** * Initializes a new ClassMetadata instance that will hold the object-relational mapping @@ -389,7 +419,7 @@ final class ClassMetadata public function getSingleIdReflectionProperty() { if ($this->_isIdentifierComposite) { - \Doctrine\Common\DoctrineException::updateMe("getSingleIdReflectionProperty called on entity with composite key."); + throw DoctrineException::updateMe("getSingleIdReflectionProperty called on entity with composite key."); } return $this->_reflectionProperties[$this->_identifier[0]]; } @@ -635,17 +665,6 @@ final class ClassMetadata if ( ! in_array($mapping['fieldName'], $this->_identifier)) { $this->_identifier[] = $mapping['fieldName']; } - if (isset($mapping['idGenerator'])) { - if ( ! $this->_isIdGeneratorType($mapping['idGenerator'])) { - //TODO: check if the idGenerator specifies an existing generator by name - throw MappingException::invalidGeneratorType($mapping['idGenerator']); - } else if (count($this->_identifier) > 1) { - throw MappingException::generatorNotAllowedWithCompositeId(); - } - $this->_generatorType = $mapping['idGenerator']; - } - // TODO: validate/complete 'tableGenerator' and 'sequenceGenerator' mappings - // Check for composite key if ( ! $this->_isIdentifierComposite && count($this->_identifier) > 1) { $this->_isIdentifierComposite = true; @@ -719,12 +738,23 @@ final class ClassMetadata public function getSingleIdentifierFieldName() { if ($this->_isIdentifierComposite) { - throw \Doctrine\Common\DoctrineException::updateMe("Calling getSingleIdentifierFieldName " + throw DoctrineException::updateMe("Calling getSingleIdentifierFieldName " . "on a class that uses a composite identifier is not allowed."); } return $this->_identifier[0]; } + /** + * Gets the column name of the single id column. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + */ + public function getSingleIdentifierColumnName() + { + return $this->getColumnName($this->getSingleIdentifierFieldName()); + } + public function setIdentifier(array $identifier) { $this->_identifier = $identifier; @@ -1067,7 +1097,7 @@ final class ClassMetadata } /** - * Sets the primary table definition. The provided array must have th + * Sets the primary table definition. The provided array must have the * following structure: * * name => @@ -1471,7 +1501,82 @@ final class ClassMetadata ! $this->_associationMappings[$fieldName]->isOneToOne(); } - /** Creates a string representation of the instance. */ + /** + * Gets the name of the ID generator used for this class. + * Only classes that use a SEQUENCE or TABLE ID generation strategy have a generator name. + * + * @return string|null The name of the ID generator or NULL if this class does not + * use a named ID generator. + */ + /*public function getIdGeneratorName() + { + return $this->_idGeneratorName; + }*/ + + /** + * Sets the ID generator used to generate IDs for instances of this class. + * + * @param AbstractIdGenerator $generator + */ + public function setIdGenerator($generator) + { + $this->_idGenerator = $generator; + } + + /** + * Gets the ID generator used to generate IDs for instances of this class. + * + * @return AbstractIdGenerator + */ + public function getIdGenerator() + { + return $this->_idGenerator; + } + + /** + * Gets the definition of the sequence ID generator for this class. + * + * The definition has the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @return array|null An array with the generator definition or NULL if this class + * has no sequence generator definition. + */ + public function getSequenceGeneratorDefinition() + { + return $this->_sequenceGeneratorDefinition; + } + + /** + * Sets the definition of the sequence ID generator for this class. + * + * The definition must have the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @param array $definition + */ + public function setSequenceGeneratorDefinition(array $definition) + { + $this->_sequenceGeneratorDefinition = $definition; + } + + /** + * Creates a string representation of this instance. + * + * @return string The string representation of this instance. + */ public function __toString() { return __CLASS__ . '@' . spl_object_hash($this); diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index a3b2a3b91..7083d086e 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -21,6 +21,7 @@ namespace Doctrine\ORM\Mapping; +use Doctrine\Common\DoctrineException; use Doctrine\DBAL\Platforms\AbstractPlatform; /** @@ -134,6 +135,11 @@ class ClassMetadataFactory // Invoke driver $this->_driver->loadMetadataForClass($className, $class); + + // Verify & complete identifier mapping + if ( ! $class->getIdentifier()) { + throw MappingException::identifierRequired($className); + } $this->_completeIdGeneratorMapping($class); if ($parent && $parent->isInheritanceTypeSingleTable()) { @@ -195,7 +201,8 @@ class ClassMetadataFactory */ private function _completeIdGeneratorMapping(ClassMetadata $class) { - if ($class->getIdGeneratorType() == ClassMetadata::GENERATOR_TYPE_AUTO) { + $idGenType = $class->getIdGeneratorType(); + if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) { if ($this->_targetPlatform->prefersSequences()) { $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); } else if ($this->_targetPlatform->prefersIdentityColumns()) { @@ -204,5 +211,35 @@ class ClassMetadataFactory $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE); } } + + // Create & assign an appropriate ID generator instance + switch ($class->getIdGeneratorType()) { + case ClassMetadata::GENERATOR_TYPE_IDENTITY: + $class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator()); + break; + case ClassMetadata::GENERATOR_TYPE_SEQUENCE: + // If there is no sequence definition yet, create a default definition + $definition = $class->getSequenceGeneratorDefinition(); + if ( ! $definition) { + $definition['sequenceName'] = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq'; + $definition['allocationSize'] = 20; + $definition['initialValue'] = 1; + $class->setSequenceGeneratorDefinition($definition); + } + $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator( + $definition['sequenceName'], + $definition['allocationSize'] + ); + $class->setIdGenerator($sequenceGenerator); + break; + case ClassMetadata::GENERATOR_TYPE_NONE: + $class->setIdGenerator(new \Doctrine\ORM\Id\Assigned()); + break; + case ClassMetadata::GENERATOR_TYPE_TABLE: + throw new DoctrineException("DoctrineTableGenerator not yet implemented."); + break; + default: + throw new DoctrineException("Unexhaustive match."); + } } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 2c5640eec..9908d89db 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -21,6 +21,7 @@ namespace Doctrine\ORM\Mapping\Driver; +use Doctrine\Common\DoctrineException; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\MappingException; @@ -48,7 +49,7 @@ class AnnotationDriver // Evaluate DoctrineEntity annotation if (($entityAnnot = $annotClass->getAnnotation('DoctrineEntity')) === false) { - throw \Doctrine\Common\DoctrineException::updateMe("$className is no entity."); + throw DoctrineException::updateMe("$className is no entity."); } $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); @@ -109,7 +110,7 @@ class AnnotationDriver // DoctrineOneToOne, DoctrineOneToMany, DoctrineManyToOne, DoctrineManyToMany if ($columnAnnot = $property->getAnnotation('DoctrineColumn')) { if ($columnAnnot->type == null) { - throw \Doctrine\Common\DoctrineException::updateMe("Missing type on property " . $property->getName()); + throw DoctrineException::updateMe("Missing type on property " . $property->getName()); } $mapping['type'] = $columnAnnot->type; $mapping['length'] = $columnAnnot->length; @@ -117,10 +118,22 @@ class AnnotationDriver if ($idAnnot = $property->getAnnotation('DoctrineId')) { $mapping['id'] = true; } - if ($idGeneratorAnnot = $property->getAnnotation('DoctrineIdGenerator')) { - $mapping['idGenerator'] = $idGeneratorAnnot->value; + if ($generatedValueAnnot = $property->getAnnotation('DoctrineGeneratedValue')) { + $metadata->setIdGeneratorType($generatedValueAnnot->strategy); } $metadata->mapField($mapping); + + // Check for SequenceGenerator/TableGenerator definition + if ($seqGeneratorAnnot = $property->getAnnotation('DoctrineSequenceGenerator')) { + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => $seqGeneratorAnnot->sequenceName, + 'allocationSize' => $seqGeneratorAnnot->allocationSize, + 'initialValue' => $seqGeneratorAnnot->initialValue + )); + } else if ($tblGeneratorAnnot = $property->getAnnotation('DoctrineTableGenerator')) { + throw new DoctrineException("DoctrineTableGenerator not yet implemented."); + } + } else if ($oneToOneAnnot = $property->getAnnotation('DoctrineOneToOne')) { $mapping['targetEntity'] = $oneToOneAnnot->targetEntity; $mapping['joinColumns'] = $joinColumns; diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index f93802a7d..b3ade22ab 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -33,7 +33,10 @@ final class DoctrineDiscriminatorColumn extends \Addendum\Annotation { final class DoctrineDiscriminatorMap extends \Addendum\Annotation {} final class DoctrineSubClasses extends \Addendum\Annotation {} final class DoctrineId extends \Addendum\Annotation {} -final class DoctrineIdGenerator extends \Addendum\Annotation {} +final class DoctrineGeneratedValue extends \Addendum\Annotation { + public $strategy; + //public $generator; +} final class DoctrineVersion extends \Addendum\Annotation {} final class DoctrineJoinColumn extends \Addendum\Annotation { public $name; @@ -85,7 +88,10 @@ final class DoctrineJoinTable extends \Addendum\Annotation { public $inverseJoinColumns; } final class DoctrineSequenceGenerator extends \Addendum\Annotation { - public $name; + //public $name; + public $sequenceName; public $allocationSize = 20; - public $initialValue; + public $initialValue = 1; + /** The name of the class that defines the generator. */ + //public $definingClass; } diff --git a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php index 57f4776dd..7d145ed23 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php @@ -94,9 +94,9 @@ abstract class AbstractEntityPersister $insertData = array(); $this->_prepareData($entity, $insertData, true); $this->_conn->insert($this->_classMetadata->getTableName(), $insertData); - $idGen = $this->_em->getIdGenerator($this->_classMetadata->getClassName()); + $idGen = $this->_classMetadata->getIdGenerator(); if ($idGen->isPostInsertGenerator()) { - return $idGen->generate($entity); + return $idGen->generate($this->_em, $entity); } return null; } diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 0982ace80..d0cf56b3c 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -21,6 +21,7 @@ namespace Doctrine\ORM; +use Doctrine\ORM\Query\CacheHandler; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\QueryException; @@ -129,7 +130,7 @@ class Query extends AbstractQuery } /** - * Retrieves the assocated EntityManager to this Query instance. + * Retrieves the associated EntityManager of this Query instance. * * @return Doctrine\ORM\EntityManager */ @@ -243,13 +244,13 @@ class Query extends AbstractQuery if ($cached === false) { // Cache does not exist, we have to create it. $result = $this->_execute($params, self::HYDRATE_ARRAY); - $queryResult = \Doctrine\ORM\Query\CacheHandler::fromResultSet($this, $result); + $queryResult = CacheHandler::fromResultSet($this, $result); $cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL); return $result; } else { // Cache exists, recover it and return the results. - $queryResult = \Doctrine\ORM\Query\CacheHandler::fromCachedResult($this, $cached); + $queryResult = CacheHandler::fromCachedResult($this, $cached); return $queryResult->getResultSet(); } @@ -288,7 +289,7 @@ class Query extends AbstractQuery $cacheDriver->save($hash, $this->_parserResult->toCachedForm(), $this->_queryCacheTTL); } else { // Cache exists, recover it and return the results. - $this->_parserResult = Doctrine\ORM\Query\CacheHandler::fromCachedQuery($this, $cached); + $this->_parserResult = CacheHandler::fromCachedQuery($this, $cached); $executor = $this->_parserResult->getSqlExecutor(); } @@ -327,7 +328,7 @@ class Query extends AbstractQuery public function setResultCache($resultCache) { if ($resultCache !== null && ! ($resultCache instanceof \Doctrine\ORM\Cache\Cache)) { - \Doctrine\Common\DoctrineException::updateMe( + throw DoctrineException::updateMe( 'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.' ); } @@ -409,7 +410,7 @@ class Query extends AbstractQuery public function setQueryCache($queryCache) { if ($queryCache !== null && ! ($queryCache instanceof \Doctrine\ORM\Cache\Cache)) { - \Doctrine\Common\DoctrineException::updateMe( + throw DoctrineException::updateMe( 'Method setResultCache() accepts only an instance of Doctrine_ORM_Cache_Interface or null.' ); } @@ -510,7 +511,6 @@ class Query extends AbstractQuery /** * Gets the array of results for the query. - * Object graphs are represented as nested array structures. * * Alias for execute(array(), HYDRATE_ARRAY). * @@ -575,9 +575,11 @@ class Query extends AbstractQuery } /** - * Executes the query and returns an IterableResult that can be iterated over. - * Objects in the result are initialized on-demand. + * Executes the query and returns an IterableResult that can be used to incrementally + * iterated over the result. * + * @param array $params The query parameters. + * @param integer $hydrationMode The hydratio mode to use. * @return IterableResult */ public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) diff --git a/lib/Doctrine/ORM/Query/AbstractResult.php b/lib/Doctrine/ORM/Query/AbstractResult.php index 33f1aa3b0..a2d6ce358 100644 --- a/lib/Doctrine/ORM/Query/AbstractResult.php +++ b/lib/Doctrine/ORM/Query/AbstractResult.php @@ -32,6 +32,7 @@ use Doctrine\Common\DoctrineException; * @version $Revision: 1393 $ * @author Guilherme Blanco * @author Konsta Vesterinen + * @author Roman Borschel */ abstract class AbstractResult { diff --git a/lib/Doctrine/ORM/Query/QueryException.php b/lib/Doctrine/ORM/Query/QueryException.php index 95e8dd657..a42dad959 100644 --- a/lib/Doctrine/ORM/Query/QueryException.php +++ b/lib/Doctrine/ORM/Query/QueryException.php @@ -11,10 +11,4 @@ namespace Doctrine\ORM\Query; * * @author robo */ -class QueryException extends \Doctrine\Common\DoctrineException -{ - public static function nonUniqueResult() - { - return new self("The query contains more than one result."); - } -} \ No newline at end of file +class QueryException extends \Doctrine\Common\DoctrineException {} \ No newline at end of file diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 25899c39a..c7fcbe6c5 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -28,7 +28,6 @@ use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Mapping; use Doctrine\ORM\Persisters; use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Exceptions\UnitOfWorkException; /** * The UnitOfWork is responsible for tracking changes to objects during an @@ -440,9 +439,9 @@ class UnitOfWork $oid = spl_object_hash($entry); if ($state == self::STATE_NEW) { // Get identifier, if possible (not post-insert) - $idGen = $this->_em->getIdGenerator($targetClass->getClassName()); + $idGen = $targetClass->getIdGenerator(); if ( ! $idGen->isPostInsertGenerator()) { - $idValue = $idGen->generate($entry); + $idValue = $idGen->generate($this->_em, $entry); $this->_entityStates[$oid] = self::STATE_MANAGED; if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) { $this->_entityIdentifiers[$oid] = array($idValue); @@ -828,7 +827,7 @@ class UnitOfWork $classMetadata = $this->_em->getClassMetadata(get_class($entity)); $idHash = $this->getIdentifierHash($this->_entityIdentifiers[$oid]); if ($idHash === '') { - \Doctrine\Common\DoctrineException::updateMe("Entity with oid '" . spl_object_hash($entity) + throw DoctrineException::updateMe("Entity with oid '" . spl_object_hash($entity) . "' has no identity and therefore can't be removed from the identity map."); } $className = $classMetadata->getRootClassName(); @@ -974,11 +973,11 @@ class UnitOfWork } break; case self::STATE_NEW: - $idGen = $this->_em->getIdGenerator($class->getClassName()); + $idGen = $class->getIdGenerator(); if ($idGen->isPostInsertGenerator()) { $insertNow[$oid] = $entity; } else { - $idValue = $idGen->generate($entity); + $idValue = $idGen->generate($this->_em, $entity); $this->_entityStates[$oid] = self::STATE_MANAGED; if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) { $this->_entityIdentifiers[$oid] = array($idValue); @@ -990,7 +989,7 @@ class UnitOfWork $this->registerNew($entity); break; case self::STATE_DETACHED: - \Doctrine\Common\DoctrineException::updateMe("Behavior of save() for a detached entity " + throw DoctrineException::updateMe("Behavior of save() for a detached entity " . "is not yet defined."); case self::STATE_DELETED: // entity becomes managed again @@ -1004,7 +1003,7 @@ class UnitOfWork break; default: //TODO: throw UnitOfWorkException::invalidEntityState() - \Doctrine\Common\DoctrineException::updateMe("Encountered invalid entity state."); + throw DoctrineException::updateMe("Encountered invalid entity state."); } $this->_cascadeSave($entity, $visited, $insertNow); } @@ -1046,9 +1045,9 @@ class UnitOfWork $this->registerDeleted($entity); break; case self::STATE_DETACHED: - \Doctrine\Common\DoctrineException::updateMe("A detached entity can't be deleted."); + throw DoctrineException::updateMe("A detached entity can't be deleted."); default: - \Doctrine\Common\DoctrineException::updateMe("Encountered invalid entity state."); + throw DoctrineException::updateMe("Encountered invalid entity state."); } $this->_cascadeDelete($entity, $visited); } @@ -1298,7 +1297,7 @@ class UnitOfWork /** * Gets the identifier of an entity. * The returned value is always an array of identifier values. If the entity - * has a composite primary key then the identifier values are in the same + * has a composite identifier then the identifier values are in the same * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames(). * * @param object $entity @@ -1343,8 +1342,6 @@ class UnitOfWork /** * Gets the EntityPersister for an Entity. * - * This is usually not of interest for users, mainly for internal use. - * * @param string $entityName The name of the Entity. * @return Doctrine\ORM\Persister\AbstractEntityPersister */ diff --git a/tests/Doctrine/Tests/Mocks/DatabasePlatformMock.php b/tests/Doctrine/Tests/Mocks/DatabasePlatformMock.php index 9fb5d2763..4f9503f92 100644 --- a/tests/Doctrine/Tests/Mocks/DatabasePlatformMock.php +++ b/tests/Doctrine/Tests/Mocks/DatabasePlatformMock.php @@ -5,7 +5,8 @@ namespace Doctrine\Tests\Mocks; class DatabasePlatformMock extends \Doctrine\DBAL\Platforms\AbstractPlatform { private $_sequenceNextValSql = ""; - private $_prefersIdentityColumns = false; + private $_prefersIdentityColumns = true; + private $_prefersSequences = false; /** * @override @@ -25,6 +26,14 @@ class DatabasePlatformMock extends \Doctrine\DBAL\Platforms\AbstractPlatform return $this->_prefersIdentityColumns; } + /** + * @override + */ + public function prefersSequences() + { + return $this->_prefersSequences; + } + /** @override */ public function getSequenceNextValSql($sequenceName) { @@ -37,15 +46,9 @@ class DatabasePlatformMock extends \Doctrine\DBAL\Platforms\AbstractPlatform /** @override */ public function getBigIntTypeDeclarationSql(array $field) {} - /** @override */ - public function getTinyIntTypeDeclarationSql(array $field) {} - /** @override */ public function getSmallIntTypeDeclarationSql(array $field) {} - /** @override */ - public function getMediumIntTypeDeclarationSql(array $field) {} - /** @override */ protected function _getCommonIntegerTypeDeclarationSql(array $columnDef) {} @@ -56,7 +59,12 @@ class DatabasePlatformMock extends \Doctrine\DBAL\Platforms\AbstractPlatform public function setPrefersIdentityColumns($bool) { - $this->_prefersIdentityColumns = (bool)$bool; + $this->_prefersIdentityColumns = $bool; + } + + public function setPrefersSequences($bool) + { + $this->_prefersSequences = $bool; } public function setSequenceNextValSql($sql) diff --git a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php index 3ab2dcdcf..d0259bb3a 100644 --- a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php +++ b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php @@ -70,14 +70,14 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager return new EntityManagerMock($conn, $config, $eventManager); } - +/* public function setIdGenerator($className, $generator) { $this->_idGenerators[$className] = $generator; } - +*/ /** @override */ - public function getIdGenerator($className) +/* public function getIdGenerator($className) { if (isset($this->_idGenerators[$className])) { @@ -86,4 +86,5 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager return parent::getIdGenerator($className); } + */ } \ No newline at end of file diff --git a/tests/Doctrine/Tests/Mocks/SequenceMock.php b/tests/Doctrine/Tests/Mocks/SequenceMock.php index 92b256744..69579ae6d 100644 --- a/tests/Doctrine/Tests/Mocks/SequenceMock.php +++ b/tests/Doctrine/Tests/Mocks/SequenceMock.php @@ -2,6 +2,8 @@ namespace Doctrine\Tests\Mocks; +use Doctrine\ORM\EntityManager; + class SequenceMock extends \Doctrine\ORM\Id\SequenceGenerator { private $_sequenceNumber = 0; @@ -9,10 +11,10 @@ class SequenceMock extends \Doctrine\ORM\Id\SequenceGenerator /** * Enter description here... * - * @param Doctrine_Entity $entity + * @param object $entity * @override */ - public function generate($entity) + public function generate(EntityManager $em, $entity) { return $this->_sequenceNumber++; } diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php index 726d79da0..dd967888f 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php @@ -14,7 +14,7 @@ class CmsAddress /** * @DoctrineColumn(type="integer") * @DoctrineId - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; diff --git a/tests/Doctrine/Tests/Models/CMS/CmsArticle.php b/tests/Doctrine/Tests/Models/CMS/CmsArticle.php index 88de6b36e..b2da30408 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsArticle.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsArticle.php @@ -11,7 +11,7 @@ class CmsArticle /** * @DoctrineId * @DoctrineColumn(type="integer") - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsComment.php b/tests/Doctrine/Tests/Models/CMS/CmsComment.php index f45629cb5..1e5eef550 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsComment.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsComment.php @@ -11,7 +11,7 @@ class CmsComment /** * @DoctrineColumn(type="integer") * @DoctrineId - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsGroup.php b/tests/Doctrine/Tests/Models/CMS/CmsGroup.php index 43f6d12a9..b762fe2be 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsGroup.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsGroup.php @@ -18,7 +18,7 @@ class CmsGroup /** * @DoctrineId * @DoctrineColumn(type="integer") - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index c0c57c28f..fe5e472e9 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -11,7 +11,7 @@ class CmsUser /** * @DoctrineId * @DoctrineColumn(type="integer") - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; /** diff --git a/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php b/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php index e667fed3c..3d3fca0f8 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php @@ -17,7 +17,7 @@ class CompanyEmployee /** * @DoctrineId * @DoctrineColumn(type="integer") - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; diff --git a/tests/Doctrine/Tests/Models/Forum/ForumAvatar.php b/tests/Doctrine/Tests/Models/Forum/ForumAvatar.php index 5cdec764f..47a09148f 100644 --- a/tests/Doctrine/Tests/Models/Forum/ForumAvatar.php +++ b/tests/Doctrine/Tests/Models/Forum/ForumAvatar.php @@ -11,7 +11,7 @@ class ForumAvatar /** * @DoctrineId * @DoctrineColumn(type="integer") - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; } diff --git a/tests/Doctrine/Tests/Models/Forum/ForumEntry.php b/tests/Doctrine/Tests/Models/Forum/ForumEntry.php index f8693c3b6..9cb33b670 100644 --- a/tests/Doctrine/Tests/Models/Forum/ForumEntry.php +++ b/tests/Doctrine/Tests/Models/Forum/ForumEntry.php @@ -11,7 +11,7 @@ class ForumEntry /** * @DoctrineId * @DoctrineColumn(type="integer") - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; /** diff --git a/tests/Doctrine/Tests/Models/Forum/ForumUser.php b/tests/Doctrine/Tests/Models/Forum/ForumUser.php index ed29e6515..9b51eaa7e 100644 --- a/tests/Doctrine/Tests/Models/Forum/ForumUser.php +++ b/tests/Doctrine/Tests/Models/Forum/ForumUser.php @@ -17,7 +17,7 @@ class ForumUser /** * @DoctrineColumn(type="integer") * @DoctrineId - * @DoctrineIdGenerator("auto") + * @DoctrineGeneratedValue(strategy="auto") */ public $id; /** diff --git a/tests/Doctrine/Tests/ORM/AllTests.php b/tests/Doctrine/Tests/ORM/AllTests.php index 2c3299517..0d5caef85 100644 --- a/tests/Doctrine/Tests/ORM/AllTests.php +++ b/tests/Doctrine/Tests/ORM/AllTests.php @@ -36,7 +36,7 @@ class AllTests $suite->addTest(Query\AllTests::suite()); $suite->addTest(Hydration\AllTests::suite()); $suite->addTest(Entity\AllTests::suite()); - $suite->addTest(Export\AllTests::suite()); + $suite->addTest(Tools\AllTests::suite()); $suite->addTest(Associations\AllTests::suite()); $suite->addTest(Mapping\AllTests::suite()); $suite->addTest(Functional\AllTests::suite()); diff --git a/tests/Doctrine/Tests/ORM/EntityPersisterTest.php b/tests/Doctrine/Tests/ORM/EntityPersisterTest.php index 1eea09ee9..01f773e29 100644 --- a/tests/Doctrine/Tests/ORM/EntityPersisterTest.php +++ b/tests/Doctrine/Tests/ORM/EntityPersisterTest.php @@ -26,8 +26,8 @@ class EntityPersisterTest extends \Doctrine\Tests\OrmTestCase $this->_emMock = EntityManagerMock::create($this->_connMock); $this->_uowMock = new UnitOfWorkMock($this->_emMock); $this->_emMock->setUnitOfWork($this->_uowMock); - $this->_idGenMock = new SequenceMock($this->_emMock, 'seq'); - $this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $this->_idGenMock); + $this->_idGenMock = new SequenceMock($this->_emMock, 'seq', 20); + //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $this->_idGenMock); } public function testSimpleInsert() diff --git a/tests/Doctrine/Tests/ORM/Export/ClassExporterTest.php b/tests/Doctrine/Tests/ORM/Export/ClassExporterTest.php deleted file mode 100644 index 3d1c7035d..000000000 --- a/tests/Doctrine/Tests/ORM/Export/ClassExporterTest.php +++ /dev/null @@ -1,29 +0,0 @@ -setDatabasePlatform(new \Doctrine\DBAL\Platforms\MySqlPlatform()); - - $em = $this->_getTestEntityManager($conn); - - $classes = array( - $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'), - $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), - $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), - ); - - $exporter = new ClassExporter($em); - $sql = $exporter->getExportClassesSql($classes); - $this->assertEquals(count($sql), 8); - } -} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/AllTests.php b/tests/Doctrine/Tests/ORM/Functional/AllTests.php index d5ab54a9c..4c40cd882 100644 --- a/tests/Doctrine/Tests/ORM/Functional/AllTests.php +++ b/tests/Doctrine/Tests/ORM/Functional/AllTests.php @@ -19,7 +19,7 @@ class AllTests { $suite = new \Doctrine\Tests\OrmFunctionalTestSuite('Doctrine Orm Functional'); - $suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicCRUDTest'); + $suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest'); return $suite; } diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php similarity index 88% rename from tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php rename to tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 130c150a1..a36565b4b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\ORM\Export\ClassExporter; +use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsAddress; @@ -10,27 +10,16 @@ use Doctrine\Tests\Models\CMS\CmsGroup; require_once __DIR__ . '/../../TestInit.php'; -class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase +class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase { - protected function tearDown() + protected function setUp() { - $conn = $this->_em->getConnection(); - $conn->exec('DELETE FROM cms_users_groups'); - $conn->exec('DELETE FROM cms_groups'); - $conn->exec('DELETE FROM cms_addresses'); - $conn->exec('DELETE FROM cms_phonenumbers'); - $conn->exec('DELETE FROM cms_users'); + $this->useModelSet('cms'); + parent::setUp(); } public function testBasicUnitsOfWorkWithOneToManyAssociation() { - $this->_exporter->exportClasses(array( - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'), - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup') - )); - // Create $user = new CmsUser; $user->name = 'Roman'; @@ -175,7 +164,7 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase $group->users[] = $user; } - $this->_em->save($user); // Saves the user, cause of post-insert ID + $this->_em->save($user); // Saves the user, 'cause of post-insert ID $this->_em->flush(); @@ -215,6 +204,22 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals('developer', $users[0]->status); $this->assertNull($users[0]->phonenumbers); $this->assertNull($users[0]->articles); + + $usersArray = $query->getResultArray(); + + $this->assertTrue(is_array($usersArray)); + $this->assertEquals(1, count($usersArray)); + $this->assertEquals('Guilherme', $usersArray[0]['name']); + $this->assertEquals('gblanco', $usersArray[0]['username']); + $this->assertEquals('developer', $usersArray[0]['status']); + + $usersScalar = $query->getScalarResult(); + + $this->assertTrue(is_array($usersScalar)); + $this->assertEquals(1, count($usersScalar)); + $this->assertEquals('Guilherme', $usersScalar[0]['u_name']); + $this->assertEquals('gblanco', $usersScalar[0]['u_username']); + $this->assertEquals('developer', $usersScalar[0]['u_status']); } public function testBasicInnerJoin() diff --git a/tests/Doctrine/Tests/ORM/Export/AllTests.php b/tests/Doctrine/Tests/ORM/Id/AllTests.php similarity index 59% rename from tests/Doctrine/Tests/ORM/Export/AllTests.php rename to tests/Doctrine/Tests/ORM/Id/AllTests.php index 1aa66c202..a9826ff12 100644 --- a/tests/Doctrine/Tests/ORM/Export/AllTests.php +++ b/tests/Doctrine/Tests/ORM/Id/AllTests.php @@ -1,11 +1,9 @@ addTestSuite('Doctrine\Tests\ORM\Export\ClassExporterTest'); + $suite->addTestSuite('Doctrine\Tests\ORM\Id\SequenceGeneratorTest'); return $suite; } } -if (PHPUnit_MAIN_METHOD == 'Orm_Export_AllTests::main') { +if (PHPUnit_MAIN_METHOD == 'Orm_Id_AllTests::main') { AllTests::main(); } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Id/SequenceGeneratorTest.php b/tests/Doctrine/Tests/ORM/Id/SequenceGeneratorTest.php new file mode 100644 index 000000000..0f6307566 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Id/SequenceGeneratorTest.php @@ -0,0 +1,40 @@ +_em = $this->_getTestEntityManager(); + $this->_seqGen = new SequenceGenerator('seq', 10); + } + + public function testGeneration() + { + for ($i=0; $i < 42; ++$i) { + if ($i % 10 == 0) { + $this->_em->getConnection()->setFetchOneResult((int)($i / 10) * 10 + 10); + } + $id = $this->_seqGen->generate($this->_em, null); + $this->assertEquals($i, $id); + $this->assertEquals((int)($i / 10) * 10 + 10, $this->_seqGen->getCurrentMaxValue()); + $this->assertEquals($i + 1, $this->_seqGen->getNextValue()); + } + + + } +} + diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php index febcbbb5f..0eaafe9df 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -15,11 +15,15 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase { $mockPlatform = new DatabasePlatformMock(); $mockDriver = new MetadataDriverMock(); + $mockPlatform->setPrefersSequences(true); + $mockPlatform->setPrefersIdentityColumns(false); // Self-made metadata $cm1 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity1'); // Add a mapped field $cm1->mapField(array('fieldName' => 'name', 'type' => 'varchar')); + // Add a mapped field + $cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true)); // and a mapped association $cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'Other', 'mappedBy' => 'this')); // and an id generator type @@ -41,8 +45,7 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array(), $cm1->getParentClasses()); $this->assertTrue($cm1->hasField('name')); - // The default fallback for id generation is the table strategy - $this->assertEquals('table', $cm1->getIdGeneratorType()); + $this->assertEquals('sequence', $cm1->getIdGeneratorType()); } public function testGetMetadataForClassInHierarchy() @@ -56,6 +59,8 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase $cm1->setInheritanceType('singleTable'); // Add a mapped field $cm1->mapField(array('fieldName' => 'name', 'type' => 'varchar')); + // Add a mapped field + $cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true)); // and a mapped association $cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'Other', 'mappedBy' => 'this')); // and an id generator type @@ -144,6 +149,7 @@ class ClassMetadataFactoryTestSubject extends \Doctrine\ORM\Mapping\ClassMetadat class TestEntity1 { + protected $id; protected $name; protected $other; } diff --git a/tests/Doctrine/Tests/ORM/Tools/AllTests.php b/tests/Doctrine/Tests/ORM/Tools/AllTests.php new file mode 100644 index 000000000..3e83ef963 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Tools/AllTests.php @@ -0,0 +1,30 @@ +addTestSuite('Doctrine\Tests\ORM\Tools\SchemaToolTest'); + + return $suite; + } +} + +if (PHPUnit_MAIN_METHOD == 'Orm_Tools_AllTests::main') { + AllTests::main(); +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index 95f5de772..a2d455c0b 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -54,8 +54,8 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase // Setup fake persister and id generator for identity generation $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser")); $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister); - $idGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); - $this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $idGeneratorMock); + //$idGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); + //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $idGeneratorMock); $userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY); // Test @@ -95,14 +95,14 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase //ForumUser $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser")); $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister); - $userIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); - $this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $userIdGeneratorMock); + //$userIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); + //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $userIdGeneratorMock); $userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY); // ForumAvatar $avatarPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumAvatar")); $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarPersister); - $avatarIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); - $this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarIdGeneratorMock); + //$avatarIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); + //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarIdGeneratorMock); $avatarPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY); // Test diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 5bda8472f..348f2cdac 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests; /** - * Base testcase class for all orm testcases. + * Base testcase class for all functional ORM testcases. * * @since 2.0 */ @@ -15,119 +15,88 @@ class OrmFunctionalTestCase extends OrmTestCase /** The EntityManager for this testcase. */ protected $_em; - /** The ClassExporter for this testcase. */ - protected $_exporter; + /** The SchemaTool. */ + protected $_schemaTool; - /** - * The currently loaded model names of the fixtures for the testcase. - */ - private $_loadedFixtures = array(); - - /** - * All loaded fixtures during test execution. Common fixture cache. - */ - private static $_fixtures = array(); - - /** - * The names of all tables that were already exported. Each table is exported - * only once. Then it's just filled & erased for each testmethod in a testcase - * that uses one or more fixtures. - */ - private static $_exportedTables = array(); - - /** - * Loads a data fixture into the database. This method must only be called - * from within the setUp() method of testcases. The database will then be - * populated with fresh data of all loaded fixtures for each test method. - * - * WARNING: A single testcase should never load fixtures from different scenarios of - * the same package as the concistency and uniqueness of keys is not guaranteed. - * - * @param string $package The package name. Must be one of Doctrine's test model packages - * (forum, cms or ecommerce). - * @param string $scenario The fixture scenario. A model package can have many fixture - * scenarios. Within a scenario all primary keys and foreign keys - * of fixtures are consistent and unique. - * @param string $name The name of the fixture to load from the specified package. - */ - protected function loadFixture($package, $scenario, $name) + /** The names of the model sets used in this testcase. */ + private $_usedModelSets = array(); + + /** Whether the database schema has already been created. */ + private static $_tablesCreated = array(); + + /** List of model sets and their classes. */ + private static $_modelSets = array( + 'cms' => array( + 'Doctrine\Tests\Models\CMS\CmsUser', + 'Doctrine\Tests\Models\CMS\CmsPhonenumber', + 'Doctrine\Tests\Models\CMS\CmsAddress', + 'Doctrine\Tests\Models\CMS\CmsGroup' + ), + 'forum' => array(), + 'company' => array(), + 'ecommerce' => array() + ); + + protected function useModelSet($setName) { - $uniqueName = $package . '/' . $scenario . '/' . $name; - - if ( ! isset(self::$_fixtures[$uniqueName])) { - // load fixture file - $fixtureFile = 'fixtures' - . DIRECTORY_SEPARATOR . $package - . DIRECTORY_SEPARATOR . $scenario - . DIRECTORY_SEPARATOR . $name - . '.php'; - require $fixtureFile; - self::$_fixtures[$uniqueName] = $fixture; - } - - $fixture = self::$_fixtures[$uniqueName]; - $this->_loadedFixtures[] = $fixture['table']; - - $conn = $this->sharedFixture['conn']; - $tableName = $fixture['table']; - - if ( ! in_array($tableName, self::$_exportedTables)) { - $conn->getSchemaManager()->exportClasses(array($fixture['model'])); - self::$_exportedTables[] = $tableName; - } - - foreach ($fixture['rows'] as $row) { - $conn->insert($tableName, $row); - } + $this->_usedModelSets[] = $setName; } /** - * Loads multiple fixtures of the same package and scenario. - * This method must only be called from within the setUp() method of testcases. - * The database will then be populated with fresh data of all loaded fixtures for each - * test method. - * - * WARNING: A single testcase should never load fixtures from different scenarios of - * the same package as the concistency and uniqueness of keys is not guaranteed. - * - * @param string $package The package name. Must be one of Doctrine's test model packages - * (forum, cms or ecommerce). - * @param string $scenario The fixture scenario. A model package can have many fixture - * scenarios. Within a scenario all primary keys and foreign keys - * of fixtures are consistent and unique. - * @param array $names The names of the fixtures to load from the specified package. - */ - protected function loadFixtures($package, $scenario, array $names) - { - foreach ($names as $name) { - $this->loadFixture($package, $scenario, $name); - } - } - - /** - * Sweeps the database tables of all used fixtures and clears the EntityManager. + * Sweeps the database tables and clears the EntityManager. */ protected function tearDown() { $conn = $this->sharedFixture['conn']; - foreach (array_reverse($this->_loadedFixtures) as $table) { - $conn->exec("DELETE FROM " . $table); + if (in_array('cms', $this->_usedModelSets)) { + $conn->exec('DELETE FROM cms_users_groups'); + $conn->exec('DELETE FROM cms_groups'); + $conn->exec('DELETE FROM cms_addresses'); + $conn->exec('DELETE FROM cms_phonenumbers'); + $conn->exec('DELETE FROM cms_users'); } $this->_em->clear(); } + /** + * Creates a connection to the test database, if there is none yet, and + * creates the necessary tables. + */ protected function setUp() { + $forceCreateTables = false; if ( ! isset($this->sharedFixture['conn'])) { - echo PHP_EOL . " --- CREATE CONNECTION ----" . PHP_EOL; $this->sharedFixture['conn'] = TestUtil::getConnection(); + if ($this->sharedFixture['conn']->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { + $forceCreateTables = true; + } } if ( ! $this->_em) { $this->_em = $this->_getEntityManager(); - $this->_exporter = new \Doctrine\ORM\Export\ClassExporter($this->_em); + $this->_schemaTool = new \Doctrine\ORM\Tools\SchemaTool($this->_em); + } + + $classes = array(); + foreach ($this->_usedModelSets as $setName) { + if ( ! isset(self::$_tablesCreated[$setName]) || $forceCreateTables) { + foreach (self::$_modelSets[$setName] as $className) { + $classes[] = $this->_em->getClassMetadata($className); + } + self::$_tablesCreated[$setName] = true; + } + } + if ($classes) { + $this->_schemaTool->createSchema($classes); } } + /** + * Gets an EntityManager for testing purposes. + * + * @param Configuration $config The Configuration to pass to the EntityManager. + * @param EventManager $eventManager The EventManager to pass to the EntityManager. + * @return EntityManager + */ protected function _getEntityManager($config = null, $eventManager = null) { // NOTE: Functional tests use their own shared metadata cache, because // the actual database platform used during execution has effect on some diff --git a/tests/fixtures/forum/common/admins.php b/tests/fixtures/forum/common/admins.php deleted file mode 100644 index 3f0dc40ff..000000000 --- a/tests/fixtures/forum/common/admins.php +++ /dev/null @@ -1,10 +0,0 @@ - 'ForumAdministrator', - 'rows' => array( - array( - 'id' => 1, - 'access_level' => 4 - ) - ) -); \ No newline at end of file diff --git a/tests/fixtures/forum/common/users.php b/tests/fixtures/forum/common/users.php deleted file mode 100644 index c908bed23..000000000 --- a/tests/fixtures/forum/common/users.php +++ /dev/null @@ -1,16 +0,0 @@ - 'ForumUser', - 'rows' => array( - array( - 'id' => 1, - 'username' => 'romanb', - 'dtype' => 'admin' - ), - array( - 'id' => 2, - 'username' => 'jwage', - 'dtype' => 'user' - ) - ) -); \ No newline at end of file