Commit 55d70248 authored by romanb's avatar romanb

[2.0] Implemented class table inheritance (no DQL bulk UPDATE/DELETE support yet)

parent eb6c6b2d
...@@ -515,7 +515,7 @@ class Connection ...@@ -515,7 +515,7 @@ class Connection
{ {
$this->connect(); $this->connect();
try { try {
//echo "DBAL:" . $query . PHP_EOL; echo "DBAL:" . $query . PHP_EOL;
if ( ! empty($params)) { if ( ! empty($params)) {
$stmt = $this->prepare($query); $stmt = $this->prepare($query);
$stmt->execute($params); $stmt->execute($params);
...@@ -542,9 +542,9 @@ class Connection ...@@ -542,9 +542,9 @@ class Connection
public function exec($query, array $params = array()) { public function exec($query, array $params = array()) {
$this->connect(); $this->connect();
try { try {
//echo $query . PHP_EOL; echo "DBAL:" . $query . PHP_EOL;
if ( ! empty($params)) { if ( ! empty($params)) {
//var_dump($params); var_dump($params);
$stmt = $this->prepare($query); $stmt = $this->prepare($query);
$stmt->execute($params); $stmt->execute($params);
return $stmt->rowCount(); return $stmt->rowCount();
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
namespace Doctrine\DBAL\Platforms; namespace Doctrine\DBAL\Platforms;
use Doctrine\Common\DoctrineException;
/** /**
* The SqlitePlatform class describes the specifics and dialects of the SQLite * The SqlitePlatform class describes the specifics and dialects of the SQLite
* database platform. * database platform.
...@@ -492,11 +494,11 @@ class SqlitePlatform extends AbstractPlatform ...@@ -492,11 +494,11 @@ class SqlitePlatform extends AbstractPlatform
public function getCreateTableSql($name, array $fields, array $options = array()) public function getCreateTableSql($name, array $fields, array $options = array())
{ {
if ( ! $name) { if ( ! $name) {
throw ConnectionException::invalidTableName($name); throw DoctrineException::invalidTableName($name);
} }
if (empty($fields)) { if (empty($fields)) {
throw ConnectionException::noFieldsSpecifiedForTable($name); throw DoctrineException::noFieldsSpecifiedForTable($name);
} }
$queryFields = $this->getColumnDeclarationListSql($fields); $queryFields = $this->getColumnDeclarationListSql($fields);
......
...@@ -50,11 +50,6 @@ abstract class Type ...@@ -50,11 +50,6 @@ abstract class Type
*/ */
public static function getType($name) public static function getType($name)
{ {
if (is_object($name)) {
try { throw new \Exception(); }
catch (\Exception $e) { echo $e->getTraceAsString(); }
die();
}
if ( ! isset(self::$_typeObjects[$name])) { if ( ! isset(self::$_typeObjects[$name])) {
if ( ! isset(self::$_typesMap[$name])) { if ( ! isset(self::$_typesMap[$name])) {
throw DoctrineException::updateMe("Unknown type: $name"); throw DoctrineException::updateMe("Unknown type: $name");
......
...@@ -297,7 +297,7 @@ final class ClassMetadata ...@@ -297,7 +297,7 @@ final class ClassMetadata
* *
* name => <tableName> * name => <tableName>
* schema => <schemaName> * schema => <schemaName>
* catalog => <catalogName> * catalog => <catalogName> //TODO: remove catalog? needed?
* *
* @var array * @var array
*/ */
...@@ -397,6 +397,15 @@ final class ClassMetadata ...@@ -397,6 +397,15 @@ final class ClassMetadata
*/ */
public $insertSql; public $insertSql;
/**
* A map of field names to class names, where the field names are association
* fields that have been inherited from another class and values are the names
* of the classes that define the association.
*
* @var array
*/
public $inheritedAssociationFields = array();
/** /**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping * Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name. * metadata of the class with the given name.
...@@ -1362,10 +1371,18 @@ final class ClassMetadata ...@@ -1362,10 +1371,18 @@ final class ClassMetadata
* This is mainly used to add inherited association mappings to derived classes. * This is mainly used to add inherited association mappings to derived classes.
* *
* @param AssociationMapping $mapping * @param AssociationMapping $mapping
* @param string $owningClassName The name of the class that defined this mapping.
* @todo Rename: addInheritedAssociationMapping
*/ */
public function addAssociationMapping(AssociationMapping $mapping) public function addAssociationMapping(AssociationMapping $mapping, $owningClassName)
{ {
$this->_storeAssociationMapping($mapping); $sourceFieldName = $mapping->sourceFieldName;
if (isset($this->associationMappings[$sourceFieldName])) {
throw MappingException::duplicateFieldMapping();
}
$this->associationMappings[$sourceFieldName] = $mapping;
$this->inheritedAssociationFields[$sourceFieldName] = $owningClassName;
$this->_registerMappingIfInverse($mapping);
} }
/** /**
...@@ -1374,6 +1391,7 @@ final class ClassMetadata ...@@ -1374,6 +1391,7 @@ final class ClassMetadata
* This is mainly used to add inherited field mappings to derived classes. * This is mainly used to add inherited field mappings to derived classes.
* *
* @param array $mapping * @param array $mapping
* @todo Rename: addInheritedFieldMapping
*/ */
public function addFieldMapping(array $fieldMapping) public function addFieldMapping(array $fieldMapping)
{ {
...@@ -1761,46 +1779,6 @@ final class ClassMetadata ...@@ -1761,46 +1779,6 @@ final class ClassMetadata
$this->sequenceGeneratorDefinition = $definition; $this->sequenceGeneratorDefinition = $definition;
} }
/**
* INTERNAL: Called by ClassMetadataFactory.
*
* Tells this class descriptor to finish the mapping definition, making any
* final adjustments, i.e. generating some SQL strings.
*/
public function finishMapping()
{
$columns = $values = array();
if ($this->inheritanceType == self::INHERITANCE_TYPE_JOINED) {
//TODO
} else {
foreach ($this->reflFields as $name => $field) {
if (isset($this->associationMappings[$name])) {
$assoc = $this->associationMappings[$name];
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
foreach ($assoc->targetToSourceKeyColumns as $sourceCol) {
$columns[] = $sourceCol;
$values[] = '?';
}
}
} else if ($this->generatorType != self::GENERATOR_TYPE_IDENTITY || $this->identifier[0] != $name) {
$columns[] = $this->columnNames[$name];
$values[] = '?';
}
}
}
if ($this->discriminatorColumn) {
$columns[] = $this->discriminatorColumn['name'];
$values[] = '?';
}
$this->insertSql = 'INSERT INTO ' . $this->primaryTable['name']
. ' (' . implode(', ', $columns) . ') '
. 'VALUES (' . implode(', ', $values) . ')';
}
/** /**
* Creates a string representation of this instance. * Creates a string representation of this instance.
* *
......
...@@ -34,6 +34,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; ...@@ -34,6 +34,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
* @version $Revision$ * @version $Revision$
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @todo Support for mapped superclasses (@DoctrineMappedSuperclass)
*/ */
class ClassMetadataFactory class ClassMetadataFactory
{ {
...@@ -43,6 +44,7 @@ class ClassMetadataFactory ...@@ -43,6 +44,7 @@ class ClassMetadataFactory
private $_driver; private $_driver;
/** The used cache driver. */ /** The used cache driver. */
private $_cacheDriver; private $_cacheDriver;
private $_loadedMetadata = array();
/** /**
* Creates a new factory instance that uses the given metadata driver implementation. * Creates a new factory instance that uses the given metadata driver implementation.
...@@ -100,9 +102,11 @@ class ClassMetadataFactory ...@@ -100,9 +102,11 @@ class ClassMetadataFactory
} }
/** /**
* Sets the metadata descriptor for a specific class.
* This is only useful in very special cases, like when generating proxy classes.
* *
* @param <type> $className * @param string $className
* @param <type> $class * @param ClassMetadata $class
*/ */
public function setMetadataFor($className, $class) public function setMetadataFor($className, $class)
{ {
...@@ -113,7 +117,7 @@ class ClassMetadataFactory ...@@ -113,7 +117,7 @@ class ClassMetadataFactory
* Loads the metadata of the class in question and all it's ancestors whose metadata * Loads the metadata of the class in question and all it's ancestors whose metadata
* is still not loaded. * is still not loaded.
* *
* @param string $name The name of the class for which the metadata should get loaded. * @param string $name The name of the class for which the metadata should get loaded.
* @param array $tables The metadata collection to which the loaded metadata is added. * @param array $tables The metadata collection to which the loaded metadata is added.
*/ */
protected function _loadMetadata($name) protected function _loadMetadata($name)
...@@ -133,6 +137,12 @@ class ClassMetadataFactory ...@@ -133,6 +137,12 @@ class ClassMetadataFactory
$parent = null; $parent = null;
$visited = array(); $visited = array();
foreach ($parentClasses as $className) { foreach ($parentClasses as $className) {
if (isset($this->_loadedMetadata[$className])) {
$parent = $this->_loadedMetadata[$className];
array_unshift($visited, $className);
continue;
}
$class = $this->_newClassMetadataInstance($className); $class = $this->_newClassMetadataInstance($className);
if ($parent) { if ($parent) {
$class->setInheritanceType($parent->inheritanceType); $class->setInheritanceType($parent->inheritanceType);
...@@ -167,7 +177,8 @@ class ClassMetadataFactory ...@@ -167,7 +177,8 @@ class ClassMetadataFactory
} }
$class->setParentClasses($visited); $class->setParentClasses($visited);
$class->finishMapping();
$this->_generateStaticSql($class);
$this->_loadedMetadata[$className] = $class; $this->_loadedMetadata[$className] = $class;
...@@ -200,7 +211,9 @@ class ClassMetadataFactory ...@@ -200,7 +211,9 @@ class ClassMetadataFactory
$mapping['inherited'] = $parentClass->name; $mapping['inherited'] = $parentClass->name;
} }
$subClass->addFieldMapping($mapping); $subClass->addFieldMapping($mapping);
$subClass->addReflectionProperty($fieldName, $parentClass->getReflectionProperty($fieldName)); }
foreach ($parentClass->reflFields as $name => $field) {
$subClass->reflFields[$name] = $field;
} }
} }
...@@ -213,8 +226,70 @@ class ClassMetadataFactory ...@@ -213,8 +226,70 @@ class ClassMetadataFactory
private function _addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) private function _addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
{ {
foreach ($parentClass->associationMappings as $mapping) { foreach ($parentClass->associationMappings as $mapping) {
$subClass->addAssociationMapping($mapping); if (isset($parentClass->inheritedAssociationFields[$mapping->sourceFieldName])) {
// parent class also inherited that one
$subClass->addAssociationMapping($mapping, $parentClass->inheritedAssociationFields[$mapping->sourceFieldName]);
} else {
// parent class defined that one
$subClass->addAssociationMapping($mapping, $parentClass->name);
}
}
}
/**
* Generates any static SQL strings for a class and stores them in the descriptor.
*
* @param ClassMetadata $class
*/
private function _generateStaticSql($class)
{
// Generate INSERT SQL
$columns = $values = array();
if ($class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_JOINED) {
foreach ($class->reflFields as $name => $field) {
if (isset($class->fieldMappings[$name]['inherited']) && ! isset($class->fieldMappings[$name]['id'])
|| isset($class->inheritedAssociationFields[$name])) {
continue;
}
if (isset($class->associationMappings[$name])) {
$assoc = $class->associationMappings[$name];
if ($assoc->isOneToOne() && $assoc->isOwningSide) {
foreach ($assoc->targetToSourceKeyColumns as $sourceCol) {
$columns[] = $this->_targetPlatform->quoteIdentifier($sourceCol);
$values[] = '?';
}
}
} else if ($class->name != $class->rootEntityName || ! $class->isIdGeneratorIdentity() || $class->identifier[0] != $name) {
$columns[] = $this->_targetPlatform->quoteIdentifier($class->columnNames[$name]);
$values[] = '?';
}
}
} else {
foreach ($class->reflFields as $name => $field) {
if (isset($class->associationMappings[$name])) {
$assoc = $class->associationMappings[$name];
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
foreach ($assoc->targetToSourceKeyColumns as $sourceCol) {
$columns[] = $this->_targetPlatform->quoteIdentifier($sourceCol);
$values[] = '?';
}
}
} else if ($class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $class->identifier[0] != $name) {
$columns[] = $this->_targetPlatform->quoteIdentifier($class->columnNames[$name]);
$values[] = '?';
}
}
} }
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined() && $class->name == $class->rootEntityName) {
$columns[] = $class->discriminatorColumn['name'];
$values[] = '?';
}
$class->insertSql = 'INSERT INTO ' .
$this->_targetPlatform->quoteIdentifier($class->primaryTable['name'])
. ' (' . implode(', ', $columns) . ') '
. 'VALUES (' . implode(', ', $values) . ')';
} }
/** /**
......
...@@ -40,7 +40,8 @@ class SingleTablePersister extends StandardEntityPersister ...@@ -40,7 +40,8 @@ class SingleTablePersister extends StandardEntityPersister
// Populate the discriminator column // Populate the discriminator column
if ($isInsert) { if ($isInsert) {
$discColumn = $this->_class->discriminatorColumn; $discColumn = $this->_class->discriminatorColumn;
$result[$discColumn['name']] = $this->_class->discriminatorValue; $result[$this->_class->primaryTable['name']][$discColumn['name']] =
$this->_class->discriminatorValue;
} }
} }
} }
\ No newline at end of file
...@@ -84,29 +84,6 @@ class StandardEntityPersister ...@@ -84,29 +84,6 @@ class StandardEntityPersister
$this->_conn = $em->getConnection(); $this->_conn = $em->getConnection();
$this->_class = $class; $this->_class = $class;
} }
/**
* Inserts an entity.
*
* @param object $entity The entity to insert.
* @return mixed If the entity uses a post-insert ID generator, the generated
* ID is returned, NULL otherwise.
*/
public function insert($entity)
{
$insertData = array();
$this->_prepareData($entity, $insertData, true);
$stmt = $this->_conn->prepare($this->_class->insertSql);
$stmt->execute(array_values($insertData));
$stmt->closeCursor();
$idGen = $this->_class->getIdGenerator();
if ($idGen->isPostInsertGenerator()) {
return $idGen->generate($this->_em, $entity);
}
return null;
}
/** /**
* Adds an entity to the queued inserts. * Adds an entity to the queued inserts.
...@@ -120,6 +97,8 @@ class StandardEntityPersister ...@@ -120,6 +97,8 @@ class StandardEntityPersister
/** /**
* Executes all queued inserts. * Executes all queued inserts.
*
* @return array An array of any generated post-insert IDs.
*/ */
public function executeInserts() public function executeInserts()
{ {
...@@ -128,14 +107,21 @@ class StandardEntityPersister ...@@ -128,14 +107,21 @@ class StandardEntityPersister
} }
$postInsertIds = array(); $postInsertIds = array();
$idGen = $this->_class->getIdGenerator(); $idGen = $this->_class->idGenerator;
$isPostInsertId = $idGen->isPostInsertGenerator(); $isPostInsertId = $idGen->isPostInsertGenerator();
$stmt = $this->_conn->prepare($this->_class->insertSql); $stmt = $this->_conn->prepare($this->_class->insertSql);
$primaryTableName = $this->_class->primaryTable['name'];
foreach ($this->_queuedInserts as $entity) { foreach ($this->_queuedInserts as $entity) {
$insertData = array(); $insertData = array();
$this->_prepareData($entity, $insertData, true); $this->_prepareData($entity, $insertData, true);
$stmt->execute(array_values($insertData));
$paramIndex = 1;
foreach ($insertData[$primaryTableName] as $value) {
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE */);
}
$stmt->execute();
if ($isPostInsertId) { if ($isPostInsertId) {
$postInsertIds[$idGen->generate($this->_em, $entity)] = $entity; $postInsertIds[$idGen->generate($this->_em, $entity)] = $entity;
} }
...@@ -157,7 +143,8 @@ class StandardEntityPersister ...@@ -157,7 +143,8 @@ class StandardEntityPersister
$this->_prepareData($entity, $updateData); $this->_prepareData($entity, $updateData);
$id = array_combine($this->_class->getIdentifierFieldNames(), $id = array_combine($this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)); $this->_em->getUnitOfWork()->getEntityIdentifier($entity));
$this->_conn->update($this->_class->getTableName(), $updateData, $id); $tableName = $this->_class->primaryTable['name'];
$this->_conn->update($tableName, $updateData[$tableName], $id);
} }
/** /**
...@@ -171,7 +158,7 @@ class StandardEntityPersister ...@@ -171,7 +158,7 @@ class StandardEntityPersister
$this->_class->getIdentifierFieldNames(), $this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity) $this->_em->getUnitOfWork()->getEntityIdentifier($entity)
); );
$this->_conn->delete($this->_class->getTableName(), $id); $this->_conn->delete($this->_class->primaryTable['name'], $id);
} }
/** /**
...@@ -229,7 +216,9 @@ class StandardEntityPersister ...@@ -229,7 +216,9 @@ class StandardEntityPersister
} }
/** /**
* Prepares the data of an entity for an insert/update operation. * Prepares the data changeset of an entity for database insertion.
* The array that is passed as the second parameter is filled with
* <columnName> => <value> pairs during this preparation.
* *
* @param object $entity * @param object $entity
* @param array $result The reference to the data array. * @param array $result The reference to the data array.
...@@ -237,6 +226,7 @@ class StandardEntityPersister ...@@ -237,6 +226,7 @@ class StandardEntityPersister
*/ */
protected function _prepareData($entity, array &$result, $isInsert = false) protected function _prepareData($entity, array &$result, $isInsert = false)
{ {
$platform = $this->_conn->getDatabasePlatform();
foreach ($this->_em->getUnitOfWork()->getEntityChangeSet($entity) as $field => $change) { foreach ($this->_em->getUnitOfWork()->getEntityChangeSet($entity) as $field => $change) {
$oldVal = $change[0]; $oldVal = $change[0];
$newVal = $change[1]; $newVal = $change[1];
...@@ -245,27 +235,41 @@ class StandardEntityPersister ...@@ -245,27 +235,41 @@ class StandardEntityPersister
if (isset($this->_class->associationMappings[$field])) { if (isset($this->_class->associationMappings[$field])) {
$assocMapping = $this->_class->associationMappings[$field]; $assocMapping = $this->_class->associationMappings[$field];
// Only owning side of 1-1 associations can have a FK column.
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) { if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
continue; continue;
} }
foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName); $otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
if ($newVal === null) { if ($newVal === null) {
$result[$sourceColumn] = null; $result[$this->getOwningTable($field)][$sourceColumn] = null;
} else { } else {
$result[$sourceColumn] = $otherClass->getReflectionProperty( $result[$this->getOwningTable($field)][$sourceColumn] =
$otherClass->getFieldName($targetColumn))->getValue($newVal); $otherClass->reflFields[$otherClass->fieldNames[$targetColumn]]
->getValue($newVal);
} }
} }
} else if ($newVal === null) { } else if ($newVal === null) {
$result[$columnName] = null; $result[$this->getOwningTable($field)][$columnName] = null;
} else { } else {
$result[$columnName] = Type::getType($this->_class->getTypeOfField($field)) $result[$this->getOwningTable($field)][$columnName] = Type::getType(
->convertToDatabaseValue($newVal, $this->_conn->getDatabasePlatform()); $this->_class->fieldMappings[$field]['type'])
->convertToDatabaseValue($newVal, $platform);
} }
} }
} }
/**
* Gets the name of the table that owns the column the given field is mapped to.
*
* @param string $fieldName
* @return string
*/
public function getOwningTable($fieldName)
{
return $this->_class->primaryTable['name'];
}
/** /**
* Loads an entity by a list of field criteria. * Loads an entity by a list of field criteria.
* *
...@@ -292,7 +296,7 @@ class StandardEntityPersister ...@@ -292,7 +296,7 @@ class StandardEntityPersister
$this->_class->reflFields[$field]->setValue($entity, $value); $this->_class->reflFields[$field]->setValue($entity, $value);
} }
$id = array(); $id = array();
if ($this->_class->isIdentifierComposite()) { if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) { foreach ($this->_class->identifier as $fieldName) {
$id[] = $data[$fieldName]; $id[] = $data[$fieldName];
} }
...@@ -329,7 +333,8 @@ class StandardEntityPersister ...@@ -329,7 +333,8 @@ class StandardEntityPersister
* Gets the SELECT SQL to select a single entity by a set of field criteria. * Gets the SELECT SQL to select a single entity by a set of field criteria.
* *
* @param array $criteria * @param array $criteria
* @return string * @return string The SQL.
* @todo Quote identifier.
*/ */
protected function _getSelectSingleEntitySql(array $criteria) protected function _getSelectSingleEntitySql(array $criteria)
{ {
......
...@@ -31,7 +31,7 @@ use Doctrine\Common\DoctrineException; ...@@ -31,7 +31,7 @@ use Doctrine\Common\DoctrineException;
* node. Therefore it is possible to only generate SQL parts by simply walking over * node. Therefore it is possible to only generate SQL parts by simply walking over
* certain subtrees of the AST. * certain subtrees of the AST.
* *
* @author robo * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
*/ */
class SqlWalker class SqlWalker
...@@ -130,8 +130,9 @@ class SqlWalker ...@@ -130,8 +130,9 @@ class SqlWalker
} }
//if ($this->_query->getHydrationMode() == \Doctrine\ORM\Query::HYDRATE_OBJECT) { //if ($this->_query->getHydrationMode() == \Doctrine\ORM\Query::HYDRATE_OBJECT) {
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
$tblAlias = $this->getSqlTableAlias($class->getTableName() . $dqlAlias); $rootClass = $this->_em->getClassMetadata($class->rootEntityName);
$discrColumn = $class->discriminatorColumn; $tblAlias = $this->getSqlTableAlias($rootClass->getTableName() . $dqlAlias);
$discrColumn = $rootClass->discriminatorColumn;
$columnAlias = $this->getSqlColumnAlias($discrColumn['name']); $columnAlias = $this->getSqlColumnAlias($discrColumn['name']);
$sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias; $sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias;
$this->_resultSetMapping->setDiscriminatorColumn($class->name, $dqlAlias, $columnAlias); $this->_resultSetMapping->setDiscriminatorColumn($class->name, $dqlAlias, $columnAlias);
...@@ -156,9 +157,13 @@ class SqlWalker ...@@ -156,9 +157,13 @@ class SqlWalker
$dqlAlias = $rangeDecl->getAliasIdentificationVariable(); $dqlAlias = $rangeDecl->getAliasIdentificationVariable();
$this->_currentRootAlias = $dqlAlias; $this->_currentRootAlias = $dqlAlias;
$class = $rangeDecl->getClassMetadata();
$sql .= $rangeDecl->getClassMetadata()->getTableName() . ' ' $sql .= $class->getTableName() . ' ' . $this->getSqlTableAlias($class->getTableName() . $dqlAlias);
. $this->getSqlTableAlias($rangeDecl->getClassMetadata()->getTableName() . $dqlAlias);
if ($class->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
}
foreach ($firstIdentificationVarDecl->getJoinVariableDeclarations() as $joinVarDecl) { foreach ($firstIdentificationVarDecl->getJoinVariableDeclarations() as $joinVarDecl) {
$sql .= $this->walkJoinVariableDeclaration($joinVarDecl); $sql .= $this->walkJoinVariableDeclaration($joinVarDecl);
...@@ -275,7 +280,7 @@ class SqlWalker ...@@ -275,7 +280,7 @@ class SqlWalker
$joinTable = $assoc->getJoinTable(); $joinTable = $assoc->getJoinTable();
$joinTableAlias = $this->getSqlTableAlias($joinTable['name']); $joinTableAlias = $this->getSqlTableAlias($joinTable['name']);
$sql .= $joinTable['name'] . ' ' . $joinTableAlias . ' ON '; $sql .= $joinTable['name'] . ' ' . $joinTableAlias . ' ON ';
if ($targetQComp['relation']->isOwningSide()) { if ($targetQComp['relation']->isOwningSide) {
$sourceToRelationJoinColumns = $assoc->getSourceToRelationKeyColumns(); $sourceToRelationJoinColumns = $assoc->getSourceToRelationKeyColumns();
foreach ($sourceToRelationJoinColumns as $sourceColumn => $relationColumn) { foreach ($sourceToRelationJoinColumns as $sourceColumn => $relationColumn) {
$sql .= "$sourceTableAlias.$sourceColumn = $joinTableAlias.$relationColumn"; $sql .= "$sourceTableAlias.$sourceColumn = $joinTableAlias.$relationColumn";
...@@ -296,7 +301,7 @@ class SqlWalker ...@@ -296,7 +301,7 @@ class SqlWalker
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
if ($targetQComp['relation']->isOwningSide()) { if ($targetQComp['relation']->isOwningSide) {
$targetToRelationJoinColumns = $assoc->getTargetToRelationKeyColumns(); $targetToRelationJoinColumns = $assoc->getTargetToRelationKeyColumns();
foreach ($targetToRelationJoinColumns as $targetColumn => $relationColumn) { foreach ($targetToRelationJoinColumns as $targetColumn => $relationColumn) {
$sql .= "$targetTableAlias.$targetColumn = $joinTableAlias.$relationColumn"; $sql .= "$targetTableAlias.$targetColumn = $joinTableAlias.$relationColumn";
...@@ -313,6 +318,10 @@ class SqlWalker ...@@ -313,6 +318,10 @@ class SqlWalker
if ($discrSql) { if ($discrSql) {
$sql .= ' AND ' . $discrSql; $sql .= ' AND ' . $discrSql;
} }
if ($targetQComp['metadata']->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($targetQComp['metadata'], $joinedDqlAlias);
}
return $sql; return $sql;
} }
...@@ -379,7 +388,7 @@ class SqlWalker ...@@ -379,7 +388,7 @@ class SqlWalker
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias; $sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
$this->_resultSetMapping->addScalarResult($columnAlias, $resultAlias); $this->_resultSetMapping->addScalarResult($columnAlias, $resultAlias);
} else { } else {
// $expr is an IdentificationVariable // IdentificationVariable
$dqlAlias = $expr; $dqlAlias = $expr;
$queryComp = $this->_queryComponents[$dqlAlias]; $queryComp = $this->_queryComponents[$dqlAlias];
...@@ -389,29 +398,54 @@ class SqlWalker ...@@ -389,29 +398,54 @@ class SqlWalker
$this->_selectedClasses[$dqlAlias] = $class; $this->_selectedClasses[$dqlAlias] = $class;
} }
$sqlTableAlias = $this->getSqlTableAlias($class->getTableName() . $dqlAlias); $beginning = true;
if ($class->isInheritanceTypeJoined()) {
// Select all fields from the queried class
foreach ($class->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited'])) {
$tableName = $this->_em->getClassMetadata($mapping['inherited'])->primaryTable['name'];
} else {
$tableName = $class->primaryTable['name'];
}
if ($beginning) $beginning = false; else $sql .= ', ';
$sqlTableAlias = $this->getSqlTableAlias($tableName . $dqlAlias);
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
$sql .= $sqlTableAlias . '.' . $mapping['columnName'] . ' AS ' . $columnAlias;
$this->_resultSetMapping->addFieldResult($dqlAlias, $columnAlias, $fieldName);
}
// Gather all fields // Add any additional fields of subclasses (not inherited fields)
$fieldMappings = $class->fieldMappings; foreach ($class->subClasses as $subClassName) {
foreach ($class->subClasses as $subclassName) { $subClass = $this->_em->getClassMetadata($subClassName);
$fieldMappings = array_merge( foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited'])) {
continue;
}
if ($beginning) $beginning = false; else $sql .= ', ';
$sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'] . $dqlAlias);
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
$sql .= $sqlTableAlias . '.' . $mapping['columnName'] . ' AS ' . $columnAlias;
$this->_resultSetMapping->addFieldResult($dqlAlias, $columnAlias, $fieldName);
}
}
} else {
$fieldMappings = $class->fieldMappings;
foreach ($class->subClasses as $subclassName) {
$fieldMappings = array_merge(
$fieldMappings, $fieldMappings,
$this->_em->getClassMetadata($subclassName)->fieldMappings $this->_em->getClassMetadata($subclassName)->fieldMappings
); );
} }
$sqlTableAlias = $this->getSqlTableAlias($class->getTableName() . $dqlAlias);
$beginning = true; foreach ($fieldMappings as $fieldName => $mapping) {
foreach ($fieldMappings as $fieldName => $fieldMapping) { if ($beginning) $beginning = false; else $sql .= ', ';
if ($beginning) { $columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
$beginning = false; $sql .= $sqlTableAlias . '.' . $mapping['columnName'] . ' AS ' . $columnAlias;
} else { $this->_resultSetMapping->addFieldResult($dqlAlias, $columnAlias, $fieldName);
$sql .= ', ';
} }
$columnAlias = $this->getSqlColumnAlias($fieldMapping['columnName']);
$sql .= $sqlTableAlias . '.' . $fieldMapping['columnName'] . ' AS ' . $columnAlias;
$this->_resultSetMapping->addFieldResult($dqlAlias, $columnAlias, $fieldName);
} }
} }
return $sql; return $sql;
} }
...@@ -1075,4 +1109,45 @@ class SqlWalker ...@@ -1075,4 +1109,45 @@ class SqlWalker
{ {
return $columnName . $this->_aliasCounter++; return $columnName . $this->_aliasCounter++;
} }
/**
* Generates the SQL JOINs, that are necessary for Class Table Inheritance,
* for the given class.
*
* @param ClassMetadata $class
* @param string $dqlAlias
*/
private function _generateClassTableInheritanceJoins($class, $dqlAlias)
{
$sql = '';
$baseTableAlias = $this->getSqlTableAlias($class->primaryTable['name'] . $dqlAlias);
$idColumns = $class->getIdentifierColumnNames();
// INNER JOIN parent class tables
foreach ($class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->getSqlTableAlias($parentClass->primaryTable['name'] . $dqlAlias);
$sql .= ' INNER JOIN ' . $parentClass->primaryTable['name'] . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $sql .= ' AND ';
$sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
}
}
// LEFT JOIN subclass tables
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$tableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'] . $dqlAlias);
$sql .= ' LEFT JOIN ' . $subClass->primaryTable['name'] . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $sql .= ' AND ';
$sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
}
}
return $sql;
}
} }
\ No newline at end of file
...@@ -89,11 +89,12 @@ class SchemaTool ...@@ -89,11 +89,12 @@ class SchemaTool
} }
$options = array(); // table options $options = array(); // table options
$columns = $this->_gatherColumns($class, $options); // table columns $columns = array(); // table columns
$this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints);
if ($class->isInheritanceTypeSingleTable()) { if ($class->isInheritanceTypeSingleTable()) {
$columns = $this->_gatherColumns($class, $options);
$this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints);
// Add the discriminator column // Add the discriminator column
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class); $discrColumnDef = $this->_getDiscriminatorColumnDefinition($class);
$columns[$discrColumnDef['name']] = $discrColumnDef; $columns[$discrColumnDef['name']] = $discrColumnDef;
...@@ -110,15 +111,46 @@ class SchemaTool ...@@ -110,15 +111,46 @@ class SchemaTool
$processedClasses[$subClassName] = true; $processedClasses[$subClassName] = true;
} }
} else if ($class->isInheritanceTypeJoined()) { } else if ($class->isInheritanceTypeJoined()) {
//TODO // Add all non-inherited fields as columns
foreach ($class->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) {
$columns[$mapping['columnName']] = $this->_gatherColumn($class, $mapping, $options);
}
}
$this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints);
// Add the discriminator column only to the root table
if ($class->name == $class->rootEntityName) {
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class);
$columns[$discrColumnDef['name']] = $discrColumnDef;
} else {
// Add an ID FK column to child tables
$idMapping = $class->fieldMappings[$class->identifier[0]];
$idColumn = $this->_gatherColumn($class, $idMapping, $options);
unset($idColumn['autoincrement']);
$columns[$idMapping['columnName']] = $idColumn;
// Add a FK constraint on the ID column
$constraint = array();
$constraint['tableName'] = $class->getTableName();
$constraint['foreignTable'] = $this->_em->getClassMetadata($class->rootEntityName)->getTableName();
$constraint['local'] = array($idMapping['columnName']);
$constraint['foreign'] = array($idMapping['columnName']);
$constraint['onDelete'] = 'CASCADE';
$foreignKeyConstraints[] = $constraint;
}
} else if ($class->isInheritanceTypeTablePerClass()) { } else if ($class->isInheritanceTypeTablePerClass()) {
//TODO //TODO
} else {
$columns = $this->_gatherColumns($class, $options);
$this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints);
} }
$sql = array_merge($sql, $this->_platform->getCreateTableSql($class->getTableName(), $columns, $options)); $sql = array_merge($sql, $this->_platform->getCreateTableSql($class->getTableName(), $columns, $options));
$processedClasses[$class->name] = true; $processedClasses[$class->name] = true;
if ($class->isIdGeneratorSequence()) { if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) {
$seqDef = $class->getSequenceGeneratorDefinition(); $seqDef = $class->getSequenceGeneratorDefinition();
$sequences[] = $this->_platform->getCreateSequenceSql( $sequences[] = $this->_platform->getCreateSequenceSql(
$seqDef['sequenceName'], $seqDef['sequenceName'],
...@@ -152,33 +184,49 @@ class SchemaTool ...@@ -152,33 +184,49 @@ class SchemaTool
); );
} }
/**
* Gathers the column definitions of all field mappings found in the given class.
*
* @param ClassMetadata $class
* @param array $options
* @return array
*/
private function _gatherColumns($class, array &$options) private function _gatherColumns($class, array &$options)
{ {
$columns = array(); $columns = array();
foreach ($class->fieldMappings as $fieldName => $mapping) { foreach ($class->fieldMappings as $fieldName => $mapping) {
$column = array(); $columns[$mapping['columnName']] = $this->_gatherColumn($class, $mapping, $options);
$column['name'] = $mapping['columnName'];
$column['type'] = Type::getType($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;
} }
return $columns; return $columns;
} }
private function _gatherColumn($class, array $mapping, array &$options)
{
$column = array();
$column['name'] = $mapping['columnName'];
$column['type'] = Type::getType($mapping['type']);
$column['length'] = $mapping['length'];
$column['notnull'] = ! $mapping['nullable'];
if ($class->isIdentifier($mapping['fieldName'])) {
$column['primary'] = true;
$options['primary'][] = $mapping['columnName'];
if ($class->isIdGeneratorIdentity()) {
$column['autoincrement'] = true;
}
}
return $column;
}
private function _gatherRelationsSql($class, array &$sql, array &$columns, array &$constraints) private function _gatherRelationsSql($class, array &$sql, array &$columns, array &$constraints)
{ {
foreach ($class->associationMappings as $mapping) { foreach ($class->associationMappings as $fieldName => $mapping) {
$foreignClass = $this->_em->getClassMetadata($mapping->getTargetEntityName()); if (isset($class->inheritedAssociationFields[$fieldName])) {
if ($mapping->isOneToOne() && $mapping->isOwningSide()) { continue;
}
$foreignClass = $this->_em->getClassMetadata($mapping->targetEntityName);
if ($mapping->isOneToOne() && $mapping->isOwningSide) {
$constraint = array(); $constraint = array();
$constraint['tableName'] = $class->getTableName(); $constraint['tableName'] = $class->getTableName();
$constraint['foreignTable'] = $foreignClass->getTableName(); $constraint['foreignTable'] = $foreignClass->getTableName();
...@@ -193,10 +241,10 @@ class SchemaTool ...@@ -193,10 +241,10 @@ class SchemaTool
$constraint['foreign'][] = $joinColumn['referencedColumnName']; $constraint['foreign'][] = $joinColumn['referencedColumnName'];
} }
$constraints[] = $constraint; $constraints[] = $constraint;
} else if ($mapping->isOneToMany() && $mapping->isOwningSide()) { } else if ($mapping->isOneToMany() && $mapping->isOwningSide) {
//... create join table, one-many through join table supported later //... create join table, one-many through join table supported later
throw DoctrineException::updateMe("Not yet implemented."); throw DoctrineException::updateMe("Not yet implemented.");
} else if ($mapping->isManyToMany() && $mapping->isOwningSide()) { } else if ($mapping->isManyToMany() && $mapping->isOwningSide) {
// create join table // create join table
$joinTableColumns = array(); $joinTableColumns = array();
$joinTableOptions = array(); $joinTableOptions = array();
......
...@@ -4,30 +4,35 @@ namespace Doctrine\Tests\Models\Company; ...@@ -4,30 +4,35 @@ namespace Doctrine\Tests\Models\Company;
/** /**
* @DoctrineEntity * @DoctrineEntity
* @DoctrineTable(name="company_employee") * @DoctrineTable(name="company_employees")
* @DoctrineInheritanceType("joined") * @DoctrineDiscriminatorValue("employee")
* @DoctrineDiscriminatorColumn(name="dtype", type="string", length=20) * @DoctrineSubClasses({"Doctrine\Tests\Models\Company\CompanyManager"})
* @DoctrineDiscriminatorMap({
"emp" = "Doctrine\Tests\Models\Company\CompanyEmployee",
"man" = "Doctrine\Tests\Models\Company\CompanyManager"})
* @DoctrineSubclasses({"Doctrine\Tests\Models\Company\CompanyManager"})
*/ */
class CompanyEmployee class CompanyEmployee extends CompanyPerson
{ {
/** /**
* @DoctrineId
* @DoctrineColumn(type="integer") * @DoctrineColumn(type="integer")
* @DoctrineGeneratedValue(strategy="auto")
*/ */
public $id; private $salary;
/**
* @DoctrineColumn(type="double")
*/
public $salary;
/** /**
* @DoctrineColumn(type="string", length=255) * @DoctrineColumn(type="string", length=255)
*/ */
public $department; private $department;
public function getSalary() {
return $this->salary;
}
public function setSalary($salary) {
$this->salary = $salary;
}
public function getDepartment() {
return $this->department;
}
public function setDepartment($dep) {
$this->department = $dep;
}
} }
\ No newline at end of file
...@@ -4,11 +4,21 @@ namespace Doctrine\Tests\Models\Company; ...@@ -4,11 +4,21 @@ namespace Doctrine\Tests\Models\Company;
/** /**
* @DoctrineEntity * @DoctrineEntity
* @DoctrineTable(name="company_managers")
* @DoctrineDiscriminatorValue("manager")
*/ */
class CompanyManager extends CompanyEmployee class CompanyManager extends CompanyEmployee
{ {
/* /**
* @DoctrineColumn(type="string", length="255") * @DoctrineColumn(type="string", length="250")
*/ */
public $title; private $title;
public function getTitle() {
return $this->title;
}
public function setTitle($title) {
$this->title = $title;
}
} }
\ No newline at end of file
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\Tests\Models\Company;
/**
* Description of CompanyPerson
*
* @author robo
* @DoctrineEntity
* @DoctrineTable(name="company_persons")
* @DoctrineDiscriminatorValue("person")
* @DoctrineInheritanceType("joined")
* @DoctrineDiscriminatorColumn(name="discr", type="string")
* @DoctrineSubClasses({"Doctrine\Tests\Models\Company\CompanyEmployee",
"Doctrine\Tests\Models\Company\CompanyManager"})
*/
class CompanyPerson
{
/**
* @DoctrineId
* @DoctrineColumn(type="integer")
* @DoctrineGeneratedValue(strategy="auto")
*/
private $id;
/**
* @DoctrineColumn(type="string")
*/
private $name;
/**
* @DoctrineOneToOne(targetEntity="CompanyPerson")
* @DoctrineJoinColumn(name="spouse_id", referencedColumnName="id")
*/
private $spouse;
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getSpouse() {
return $this->spouse;
}
public function setSpouse(CompanyPerson $spouse) {
if ($spouse !== $this->spouse) {
$this->spouse = $spouse;
$this->spouse->setSpouse($this);
}
}
}
...@@ -22,6 +22,7 @@ class AllTests ...@@ -22,6 +22,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\DetachedEntityTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\DetachedEntityTest');
return $suite; return $suite;
......
<?php
namespace Doctrine\Tests\ORM\Functional;
require_once __DIR__ . '/../../TestInit.php';
use Doctrine\Tests\Models\Company\CompanyPerson;
use Doctrine\Tests\Models\Company\CompanyEmployee;
use Doctrine\Tests\Models\Company\CompanyManager;
/**
* Functional tests for the Single Table Inheritance mapping strategy.
*
* @author robo
*/
class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
$this->useModelSet('company');
parent::setUp();
}
public function testCRUD()
{
$person = new CompanyPerson;
$person->setName('Roman S. Borschel');
$this->_em->save($person);
$employee = new CompanyEmployee;
$employee->setName('Roman S. Borschel');
$employee->setSalary(100000);
$employee->setDepartment('IT');
$this->_em->save($employee);
$employee->setName('Guilherme Blanco');
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("select p from Doctrine\Tests\Models\Company\CompanyPerson p order by p.id asc");
$entities = $query->getResultList();
$this->assertEquals(2, count($entities));
$this->assertTrue($entities[0] instanceof CompanyPerson);
$this->assertTrue($entities[1] instanceof CompanyEmployee);
$this->assertTrue(is_numeric($entities[0]->getId()));
$this->assertTrue(is_numeric($entities[1]->getId()));
$this->assertEquals('Roman S. Borschel', $entities[0]->getName());
$this->assertEquals('Guilherme Blanco', $entities[1]->getName());
$this->assertEquals(100000, $entities[1]->getSalary());
$this->_em->clear();
$query = $this->_em->createQuery("select p from Doctrine\Tests\Models\Company\CompanyEmployee p");
$entities = $query->getResultList();
$this->assertEquals(1, count($entities));
$this->assertTrue($entities[0] instanceof CompanyEmployee);
$this->assertTrue(is_numeric($entities[0]->getId()));
$this->assertEquals('Guilherme Blanco', $entities[0]->getName());
$this->assertEquals(100000, $entities[0]->getSalary());
$this->_em->clear();
/*
$query = $this->_em->createQuery("select r,o from Doctrine\Tests\ORM\Functional\RelatedEntity r join r.owner o");
$entities = $query->getResultList();
$this->assertEquals(1, count($entities));
$this->assertTrue($entities[0] instanceof RelatedEntity);
$this->assertTrue(is_numeric($entities[0]->getId()));
$this->assertEquals('theRelatedOne', $entities[0]->getName());
$this->assertTrue($entities[0]->getOwner() instanceof ChildEntity);
$this->assertEquals('thedata', $entities[0]->getOwner()->getData());
$this->assertSame($entities[0], $entities[0]->getOwner()->getRelatedEntity());
$query = $this->_em->createQuery("update Doctrine\Tests\ORM\Functional\ChildEntity e set e.data = 'newdata'");
$affected = $query->execute();
$this->assertEquals(1, $affected);
$query = $this->_em->createQuery("delete Doctrine\Tests\ORM\Functional\ParentEntity e");
$affected = $query->execute();
$this->assertEquals(2, $affected);
*/
}
}
...@@ -34,13 +34,17 @@ class OrmFunctionalTestCase extends OrmTestCase ...@@ -34,13 +34,17 @@ class OrmFunctionalTestCase extends OrmTestCase
'Doctrine\Tests\Models\CMS\CmsArticle' 'Doctrine\Tests\Models\CMS\CmsArticle'
), ),
'forum' => array(), 'forum' => array(),
'company' => array(), 'company' => array(
'Doctrine\Tests\Models\Company\CompanyPerson',
'Doctrine\Tests\Models\Company\CompanyEmployee',
'Doctrine\Tests\Models\Company\CompanyManager'
),
'ecommerce' => array() 'ecommerce' => array()
); );
protected function useModelSet($setName) protected function useModelSet($setName)
{ {
$this->_usedModelSets[] = $setName; $this->_usedModelSets[$setName] = true;
} }
/** /**
...@@ -49,7 +53,7 @@ class OrmFunctionalTestCase extends OrmTestCase ...@@ -49,7 +53,7 @@ class OrmFunctionalTestCase extends OrmTestCase
protected function tearDown() protected function tearDown()
{ {
$conn = $this->sharedFixture['conn']; $conn = $this->sharedFixture['conn'];
if (in_array('cms', $this->_usedModelSets)) { if (isset($this->_usedModelSets['cms'])) {
$conn->exec('DELETE FROM cms_users_groups'); $conn->exec('DELETE FROM cms_users_groups');
$conn->exec('DELETE FROM cms_groups'); $conn->exec('DELETE FROM cms_groups');
$conn->exec('DELETE FROM cms_addresses'); $conn->exec('DELETE FROM cms_addresses');
...@@ -57,6 +61,12 @@ class OrmFunctionalTestCase extends OrmTestCase ...@@ -57,6 +61,12 @@ class OrmFunctionalTestCase extends OrmTestCase
$conn->exec('DELETE FROM cms_articles'); $conn->exec('DELETE FROM cms_articles');
$conn->exec('DELETE FROM cms_users'); $conn->exec('DELETE FROM cms_users');
} }
if (isset($this->_usedModelSets['company'])) {
$conn->exec('DELETE FROM company_managers');
$conn->exec('DELETE FROM company_employees');
$conn->exec('DELETE FROM company_persons');
}
$this->_em->clear(); $this->_em->clear();
} }
...@@ -79,7 +89,7 @@ class OrmFunctionalTestCase extends OrmTestCase ...@@ -79,7 +89,7 @@ class OrmFunctionalTestCase extends OrmTestCase
} }
$classes = array(); $classes = array();
foreach ($this->_usedModelSets as $setName) { foreach ($this->_usedModelSets as $setName => $bool) {
if ( ! isset(self::$_tablesCreated[$setName]) || $forceCreateTables) { if ( ! isset(self::$_tablesCreated[$setName]) || $forceCreateTables) {
foreach (self::$_modelSets[$setName] as $className) { foreach (self::$_modelSets[$setName] as $className) {
$classes[] = $this->_em->getClassMetadata($className); $classes[] = $this->_em->getClassMetadata($className);
...@@ -88,7 +98,14 @@ class OrmFunctionalTestCase extends OrmTestCase ...@@ -88,7 +98,14 @@ class OrmFunctionalTestCase extends OrmTestCase
} }
} }
if ($classes) { if ($classes) {
$this->_schemaTool->createSchema($classes); try {
$this->_schemaTool->createSchema($classes);
} catch (\Exception $e) {
// Suppress "xxx already exists" messages
if (stripos($e->getMessage(), 'already exists') === false) {
throw $e;
}
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment