[2.0] Implemented class table inheritance (no DQL bulk UPDATE/DELETE support yet)
This commit is contained in:
parent
eb6c6b2d16
commit
55d70248a9
@ -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
|
|||||||
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 @@
|
|||||||
|
|
||||||
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
|
|||||||
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
|
|||||||
*/
|
*/
|
||||||
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
|
|||||||
*
|
*
|
||||||
* 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
|
|||||||
*/
|
*/
|
||||||
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
|
|||||||
* 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
|
|||||||
* 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
|
|||||||
$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;
|
|||||||
* @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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 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
|
|||||||
* 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
|
|||||||
$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
|
|||||||
}
|
}
|
||||||
|
|
||||||
$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
|
|||||||
$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,10 +226,72 @@ 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) . ')';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completes the ID generator mapping. If "auto" is specified we choose the generator
|
* Completes the ID generator mapping. If "auto" is specified we choose the generator
|
||||||
* most appropriate for the targeted database platform.
|
* most appropriate for the targeted database platform.
|
||||||
|
@ -34,129 +34,169 @@ use Doctrine\Common\DoctrineException;
|
|||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @todo Reimplement.
|
* @todo Reimplement.
|
||||||
*/
|
*/
|
||||||
class JoinedSubclassPersister extends AbstractEntityPersister
|
class JoinedSubclassPersister extends StandardEntityPersister
|
||||||
{
|
{
|
||||||
|
/** Map that maps column names to the table names that own them.
|
||||||
|
* This is mainly a temporary cache, used during a single request.
|
||||||
|
*/
|
||||||
|
private $_owningTableMap = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts an entity that is part of a Class Table Inheritance hierarchy.
|
* {@inheritdoc}
|
||||||
*
|
*
|
||||||
* @param object $record record to be inserted
|
|
||||||
* @return boolean
|
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
/*public function insert($entity)
|
protected function _prepareData($entity, array &$result, $isInsert = false)
|
||||||
{
|
{
|
||||||
$class = $entity->getClass();
|
parent::_prepareData($entity, $result, $isInsert);
|
||||||
|
// Populate the discriminator column
|
||||||
$dataSet = array();
|
if ($isInsert) {
|
||||||
|
$discColumn = $this->_class->discriminatorColumn;
|
||||||
$this->_prepareData($entity, $dataSet, true);
|
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
|
||||||
|
$result[$rootClass->primaryTable['name']][$discColumn['name']] =
|
||||||
$dataSet = $this->_groupFieldsByDefiningClass($class, $dataSet);
|
$this->_class->discriminatorValue;
|
||||||
|
}
|
||||||
$component = $class->name;
|
}
|
||||||
$classes = $class->parentClasses;
|
|
||||||
array_unshift($classes, $component);
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
$identifier = null;
|
*
|
||||||
foreach (array_reverse($classes) as $k => $parent) {
|
* @override
|
||||||
$parentClass = $this->_em->getClassMetadata($parent);
|
*/
|
||||||
if ($k == 0) {
|
public function getOwningTable($fieldName)
|
||||||
if ($parentClass->isIdGeneratorIdentity()) {
|
{
|
||||||
$this->_insertRow($parentClass->getTableName(), $dataSet[$parent]);
|
if ( ! isset($this->_owningTableMap[$fieldName])) {
|
||||||
$identifier = $this->_conn->lastInsertId();
|
if (isset($this->_class->associationMappings[$fieldName])) {
|
||||||
} else if ($parentClass->isIdGeneratorSequence()) {
|
if (isset($this->_class->inheritedAssociationFields[$fieldName])) {
|
||||||
$seq = $entity->getClassMetadata()->getTableOption('sequenceName');
|
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
|
||||||
if ( ! empty($seq)) {
|
$this->_class->inheritedAssociationFields[$fieldName])->primaryTable['name'];
|
||||||
$id = $this->_conn->getSequenceManager()->nextId($seq);
|
|
||||||
$identifierFields = $parentClass->identifier;
|
|
||||||
$dataSet[$parent][$identifierFields[0]] = $id;
|
|
||||||
$this->_insertRow($parentClass->getTableName(), $dataSet[$parent]);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw DoctrineException::updateMe("Unsupported identifier type '$identifierType'.");
|
$this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name'];
|
||||||
}
|
}
|
||||||
$entity->_assignIdentifier($identifier);
|
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
|
||||||
|
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
|
||||||
|
$this->_class->fieldMappings[$fieldName]['inherited'])->primaryTable['name'];
|
||||||
} else {
|
} else {
|
||||||
foreach ($entity->_identifier() as $id => $value) {
|
$this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name'];
|
||||||
$dataSet[$parent][$parentClass->getColumnName($id)] = $value;
|
|
||||||
}
|
|
||||||
$this->_insertRow($parentClass->getTableName(), $dataSet[$parent]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $this->_owningTableMap[$fieldName];
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an entity that is part of a Class Table Inheritance hierarchy.
|
* {@inheritdoc}
|
||||||
*
|
*
|
||||||
* @param Doctrine_Entity $record record to be updated
|
* @override
|
||||||
* @return boolean whether or not the update was successful
|
|
||||||
*/
|
*/
|
||||||
/*protected function _doUpdate($entity)
|
public function executeInserts()
|
||||||
{
|
{
|
||||||
$conn = $this->_conn;
|
if ( ! $this->_queuedInserts) {
|
||||||
$classMetadata = $this->_classMetadata;
|
return;
|
||||||
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $classMetadata);
|
}
|
||||||
$dataSet = $this->_groupFieldsByDefiningClass($record);
|
|
||||||
$component = $classMetadata->name;
|
|
||||||
$classes = $classMetadata->parentClasses;
|
|
||||||
array_unshift($classes, $component);
|
|
||||||
|
|
||||||
foreach ($record as $field => $value) {
|
$postInsertIds = array();
|
||||||
if ($value instanceof Doctrine_ORM_Entity) {
|
$idGen = $this->_class->idGenerator;
|
||||||
if ( ! $value->exists()) {
|
$isPostInsertId = $idGen->isPostInsertGenerator();
|
||||||
$value->save();
|
|
||||||
|
// Prepare statements for all tables
|
||||||
|
$stmts = $classes = array();
|
||||||
|
$stmts[$this->_class->primaryTable['name']] = $this->_conn->prepare($this->_class->insertSql);
|
||||||
|
$classes[$this->_class->name] = $this->_class;
|
||||||
|
foreach ($this->_class->parentClasses as $parentClass) {
|
||||||
|
$classes[$parentClass] = $this->_em->getClassMetadata($parentClass);
|
||||||
|
$stmts[$classes[$parentClass]->primaryTable['name']] = $this->_conn->prepare($classes[$parentClass]->insertSql);
|
||||||
|
}
|
||||||
|
$rootTableName = $classes[$this->_class->rootEntityName]->primaryTable['name'];
|
||||||
|
|
||||||
|
foreach ($this->_queuedInserts as $entity) {
|
||||||
|
$insertData = array();
|
||||||
|
$this->_prepareData($entity, $insertData, true);
|
||||||
|
|
||||||
|
// Execute insert on root table
|
||||||
|
$paramIndex = 1;
|
||||||
|
$stmt = $stmts[$rootTableName];
|
||||||
|
foreach ($insertData[$rootTableName] as $columnName => $value) {
|
||||||
|
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
|
||||||
|
}
|
||||||
|
$stmt->execute();
|
||||||
|
unset($insertData[$rootTableName]);
|
||||||
|
|
||||||
|
if ($isPostInsertId) {
|
||||||
|
$id = $idGen->generate($this->_em, $entity);
|
||||||
|
$postInsertIds[$id] = $entity;
|
||||||
|
} else {
|
||||||
|
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute inserts on subtables
|
||||||
|
foreach ($insertData as $tableName => $data) {
|
||||||
|
$stmt = $stmts[$tableName];
|
||||||
|
$paramIndex = 1;
|
||||||
|
foreach ((array)$id as $idVal) {
|
||||||
|
$stmt->bindValue($paramIndex++, $idVal/*, TODO: TYPE*/);
|
||||||
}
|
}
|
||||||
$idValues = $value->identifier();
|
foreach ($data as $columnName => $value) {
|
||||||
$record->set($field, $idValues[0]);
|
$stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/);
|
||||||
|
}
|
||||||
|
$stmt->execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (array_reverse($classes) as $class) {
|
foreach ($stmts as $stmt)
|
||||||
$parentTable = $conn->getClassMetadata($class);
|
$stmt->closeCursor();
|
||||||
$this->_updateRow($parentTable->getTableName(), $dataSet[$class], $identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->assignIdentifier(true);
|
|
||||||
|
|
||||||
return true;
|
$this->_queuedInserts = array();
|
||||||
}*/
|
|
||||||
|
return $postInsertIds;
|
||||||
/**
|
}
|
||||||
* Deletes an entity that is part of a Class Table Inheritance hierarchy.
|
|
||||||
|
/**
|
||||||
|
* Updates an entity.
|
||||||
*
|
*
|
||||||
|
* @param object $entity The entity to update.
|
||||||
|
* @override
|
||||||
*/
|
*/
|
||||||
/*protected function _doDelete(Doctrine_ORM_Entity $record)
|
public function update($entity)
|
||||||
{
|
{
|
||||||
$conn = $this->_conn;
|
$updateData = array();
|
||||||
try {
|
$this->_prepareData($entity, $updateData);
|
||||||
$class = $this->_classMetadata;
|
|
||||||
$conn->beginInternalTransaction();
|
|
||||||
$this->_deleteComposites($record);
|
|
||||||
|
|
||||||
$record->_state(Doctrine_ORM_Entity::STATE_TDIRTY);
|
$id = array_combine(
|
||||||
|
$this->_class->getIdentifierFieldNames(),
|
||||||
|
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)
|
||||||
|
);
|
||||||
|
|
||||||
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $class);
|
foreach ($updateData as $tableName => $data) {
|
||||||
|
$this->_conn->update($tableName, $updateData[$tableName], $id);
|
||||||
// run deletions, starting from the class, upwards the hierarchy
|
|
||||||
$conn->delete($class->getTableName(), $identifier);
|
|
||||||
foreach ($class->parentClasses as $parent) {
|
|
||||||
$parentClass = $conn->getClassMetadata($parent);
|
|
||||||
$this->_deleteRow($parentClass->getTableName(), $identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->_state(Doctrine_ORM_Entity::STATE_TCLEAN);
|
|
||||||
|
|
||||||
$this->removeRecord($record); // @todo should be done in the unitofwork
|
|
||||||
$conn->commit();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$conn->rollback();
|
|
||||||
throw $e;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
/**
|
||||||
}*/
|
* Deletes an entity.
|
||||||
|
*
|
||||||
|
* @param object $entity The entity to delete.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
public function delete($entity)
|
||||||
|
{
|
||||||
|
$id = array_combine(
|
||||||
|
$this->_class->getIdentifierFieldNames(),
|
||||||
|
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the database platform supports FKs, just
|
||||||
|
// delete the row from the root table. Cascades do the rest.
|
||||||
|
if ($this->_conn->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
||||||
|
$this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName)
|
||||||
|
->primaryTable['name'], $id);
|
||||||
|
} else {
|
||||||
|
// Delete the parent tables, starting from this class' table up to the root table
|
||||||
|
$this->_conn->delete($this->_class->primaryTable['name'], $id);
|
||||||
|
foreach ($this->_class->parentClasses as $parentClass) {
|
||||||
|
$this->_conn->delete($this->_em->getClassMetadata($parentClass)->primaryTable['name'], $id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all parent classes as INNER JOINs and subclasses as OUTER JOINs
|
* Adds all parent classes as INNER JOINs and subclasses as OUTER JOINs
|
||||||
@ -205,21 +245,6 @@ class JoinedSubclassPersister extends AbstractEntityPersister
|
|||||||
return array_unique($fields);
|
return array_unique($fields);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/*public function getFieldNames()
|
|
||||||
{
|
|
||||||
if ($this->_fieldNames) {
|
|
||||||
return $this->_fieldNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fieldNames = $this->_classMetadata->fieldNames;
|
|
||||||
$this->_fieldNames = array_unique($fieldNames);
|
|
||||||
|
|
||||||
return $fieldNames;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @todo Looks like this better belongs into the ClassMetadata class.
|
* @todo Looks like this better belongs into the ClassMetadata class.
|
||||||
@ -248,36 +273,4 @@ class JoinedSubclassPersister extends AbstractEntityPersister
|
|||||||
|
|
||||||
throw \Doctrine\Common\DoctrineException::updateMe("Unable to find defining class of field '$fieldName'.");
|
throw \Doctrine\Common\DoctrineException::updateMe("Unable to find defining class of field '$fieldName'.");
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Analyzes the fields of the entity and creates a map in which the field names
|
|
||||||
* are grouped by the class names they belong to.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
/*protected function _groupFieldsByDefiningClass(Doctrine_ClassMetadata $class, array $fields)
|
|
||||||
{
|
|
||||||
$dataSet = array();
|
|
||||||
$component = $class->name;
|
|
||||||
|
|
||||||
$classes = array_merge(array($component), $class->parentClasses);
|
|
||||||
|
|
||||||
foreach ($classes as $class) {
|
|
||||||
$dataSet[$class] = array();
|
|
||||||
$parentClassMetadata = $this->_em->getClassMetadata($class);
|
|
||||||
foreach ($parentClassMetadata->fieldMappings as $fieldName => $mapping) {
|
|
||||||
if ((isset($mapping['id']) && $mapping['id'] === true) ||
|
|
||||||
(isset($mapping['inherited']) && $mapping['inherited'] === true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( ! array_key_exists($fieldName, $fields)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$columnName = $parentClassMetadata->getColumnName($fieldName);
|
|
||||||
$dataSet[$class][$columnName] = $fields[$fieldName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dataSet;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
|||||||
}
|
}
|
||||||
|
|
||||||
$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
|
|||||||
$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
|
|||||||
$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
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
|||||||
*/
|
*/
|
||||||
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
|
|||||||
|
|
||||||
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
|
|||||||
$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
|
|||||||
* 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;
|
|||||||
* 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
|
|||||||
}
|
}
|
||||||
//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
|
|||||||
$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
|
|||||||
$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
|
|||||||
|
|
||||||
$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
|
|||||||
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
|
|||||||
$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
|
|||||||
$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
|
|||||||
{
|
{
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -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
|
|||||||
$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
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
|||||||
$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;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
62
tests/Doctrine/Tests/Models/Company/CompanyPerson.php
Normal file
62
tests/Doctrine/Tests/Models/Company/CompanyPerson.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?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
|
|||||||
$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;
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
<?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
|
|||||||
'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
|
|||||||
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
|
|||||||
$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
|
|||||||
}
|
}
|
||||||
|
|
||||||
$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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user