Commit 523c93c2 authored by romanb's avatar romanb

[2.0] Refactored SQL query building process and hydration. Hydration should...

[2.0] Refactored SQL query building process and hydration. Hydration should now support result sets with any number of root components. Introducing ResultSetMapping that is used by hydration instead of queryComponents. This allows mapping of arbitrary SQL queries (NativeQuery).
parent 62204af8
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common;
/**
* Contract for classes that provide the service of notifying listeners of
* changes to their properties.
*
* @author robo
* @since 2.0
*/
interface NotifyPropertyChanged
{
public function addPropertyChangedListener(PropertyChangedListener $listener);
}
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common;
/**
* Contract for classes that are potential listeners of a <tt>NotifyPropertyChanged</tt>
* implementor.
*
* @author robo
* @since 2.0
*/
interface PropertyChangedListener
{
public function propertyChanged($sender, $propertyName, $oldValue, $newValue);
}
......@@ -35,21 +35,8 @@ use \PDO;
*/
abstract class AbstractHydrator
{
/**
* @var array $_queryComponents
*
* Two dimensional array containing the map for query aliases. Main keys are component aliases.
*
* metadata ClassMetadata object associated with given alias.
* relation Relation object owned by the parent.
* parent Alias of the parent.
* agg Aggregates of this component.
* map Name of the column / aggregate value this component is mapped to in a collection.
*/
protected $_queryComponents = array();
/** @var array Table alias map. Keys are SQL aliases and values DQL aliases. */
protected $_tableAliases = array();
/** The ResultSetMapping. */
protected $_resultSetMapping;
/** @var EntityManager The EntityManager instance. */
protected $_em;
......@@ -133,8 +120,7 @@ abstract class AbstractHydrator
*/
protected function _prepare($parserResult)
{
$this->_queryComponents = $parserResult->getQueryComponents();
$this->_tableAliases = $parserResult->getTableAliasMap();
$this->_resultSetMapping = $parserResult->getResultSetMapping();
$this->_parserResult = $parserResult;
}
......@@ -144,6 +130,7 @@ abstract class AbstractHydrator
*/
protected function _cleanup()
{
$this->_resultSetMapping = null;
$this->_parserResult = null;
$this->_stmt->closeCursor();
$this->_stmt = null;
......@@ -159,7 +146,9 @@ abstract class AbstractHydrator
* @param mixed $result The result to fill.
*/
protected function _hydrateRow(array &$data, array &$cache, &$result)
{}
{
throw new Exception("_hydrateRow() not implemented for this hydrator.");
}
/**
* Hydrates all rows from the current statement instance at once.
......@@ -193,24 +182,19 @@ abstract class AbstractHydrator
if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue;
// Cache general information like the column name <-> field name mapping
$e = explode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[
implode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $e)
];
// check whether it's an aggregate value or a regular field
if ($cache[$key]['dqlAlias'] == 'dctrn') {
$cache[$key]['fieldName'] = $columnName;
if ($this->_resultSetMapping->isScalarResult($key)) {
$cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key);
$cache[$key]['isScalar'] = true;
} else {
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
$fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$classMetadata = $this->_resultSetMapping->getOwningClass($key);
$fieldName = $this->_resultSetMapping->getFieldName($key);
$classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName);
//$fieldName = $classMetadata->getFieldNameForLowerColumnName($columnName);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['isScalar'] = false;
$cache[$key]['type'] = $classMetadata->getTypeOfColumn($columnName);
// Cache identifier information
$cache[$key]['type'] = $classMetadata->getTypeOfField($fieldName);
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_resultSetMapping->getEntityAlias($key);
}
}
......@@ -261,31 +245,27 @@ abstract class AbstractHydrator
if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue;
// cache general information like the column name <-> field name mapping
$e = explode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e);
$sqlAlias = implode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[$sqlAlias];
// check whether it's a scalar value or a regular field
if ($cache[$key]['dqlAlias'] == 'dctrn') {
$cache[$key]['fieldName'] = $columnName;
if ($this->_resultSetMapping->isScalarResult($key)) {
$cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key);
$cache[$key]['isScalar'] = true;
} else {
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
$fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$classMetadata = $this->_resultSetMapping->getOwningClass($key);
$fieldName = $this->_resultSetMapping->getFieldName($key);
$classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName);
//$fieldName = $classMetadata->getFieldNameForLowerColumnName($columnName);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['isScalar'] = false;
// cache type information
$cache[$key]['type'] = $classMetadata->getTypeOfColumn($columnName);
$cache[$key]['type'] = $classMetadata->getTypeOfField($fieldName);
$cache[$key]['dqlAlias'] = $this->_resultSetMapping->getEntityAlias($key);
}
}
$dqlAlias = $cache[$key]['dqlAlias'];
$fieldName = $cache[$key]['fieldName'];
if ($cache[$key]['isScalar']) {
$rowData[$dqlAlias . '_' . $fieldName] = $value;
$rowData[/*$dqlAlias . '_' . */$fieldName] = $value;
} else {
$dqlAlias = $cache[$key]['dqlAlias'];
$rowData[$dqlAlias . '_' . $fieldName] = $cache[$key]['type']->convertToPHPValue($value);
}
}
......@@ -301,7 +281,8 @@ abstract class AbstractHydrator
*/
protected function _getCustomIndexField($alias)
{
return isset($this->_queryComponents[$alias]['map']) ? $this->_queryComponents[$alias]['map'] : null;
return $this->_resultSetMapping->hasIndexBy($alias) ?
$this->_resultSetMapping->getIndexByField($alias) : null;
}
/**
......@@ -335,20 +316,21 @@ abstract class AbstractHydrator
* @return string The field name.
* @throws DoctrineException If the field name could not be found.
*/
private function _lookupFieldName($class, $lcColumnName)
private function _lookupDeclaringClass($class, $fieldName)
{
if ($class->hasLowerColumn($lcColumnName)) {
return $class->getFieldNameForLowerColumnName($lcColumnName);
if ($class->hasField($fieldName)) {
//return $class->getFieldNameForLowerColumnName($lcColumnName);
return $class;
}
foreach ($class->getSubclasses() as $subClass) {
$subClassMetadata = Doctrine_ORM_Mapping_ClassMetadataFactory::getInstance()
->getMetadataFor($subClass);
if ($subClassMetadata->hasLowerColumn($lcColumnName)) {
return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName);
$subClassMetadata = $this->_em->getClassMetadata($subClass);
if ($subClassMetadata->hasField($fieldName)) {
//return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName);
return $subClassMetadata;
}
}
throw DoctrineException::updateMe("No field name found for column name '$lcColumnName' during hydration.");
throw DoctrineException::updateMe("No owner found for field '$fieldName' during hydration.");
}
}
\ No newline at end of file
......@@ -30,8 +30,7 @@ use \PDO;
*/
class ArrayHydrator extends AbstractHydrator
{
private $_rootAlias;
private $_rootEntityName;
private $_rootAliases = array();
private $_isSimpleQuery = false;
private $_identifierMap = array();
private $_resultPointers = array();
......@@ -42,14 +41,12 @@ class ArrayHydrator extends AbstractHydrator
protected function _prepare($parserResult)
{
parent::_prepare($parserResult);
$this->_rootAlias = $parserResult->getDefaultQueryComponentAlias();
$this->_rootEntityName = $this->_queryComponents[$this->_rootAlias]['metadata']->getClassName();
$this->_isSimpleQuery = count($this->_queryComponents) <= 1;
$this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1;
$this->_identifierMap = array();
$this->_resultPointers = array();
$this->_idTemplate = array();
$this->_resultCounter = 0;
foreach ($this->_queryComponents as $dqlAlias => $component) {
foreach ($this->_resultSetMapping->getAliasMap() as $dqlAlias => $class) {
$this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
......@@ -80,36 +77,6 @@ class ArrayHydrator extends AbstractHydrator
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rootAlias = $this->_rootAlias;
// 2) Hydrate the data of the root entity from the current row
// Check for an existing element
$index = false;
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$rootAlias][$id[$rootAlias]])) {
$element = $rowData[$rootAlias];
if ($field = $this->_getCustomIndexField($rootAlias)) {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element[$field] => $element);
++$this->_resultCounter;
} else {
$result[$element[$field]] = $element;
}
} else {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element);
++$this->_resultCounter;
} else {
$result[] = $element;
}
}
end($result);
$this->_identifierMap[$rootAlias][$id[$rootAlias]] = key($result);
} else {
$index = $this->_identifierMap[$rootAlias][$id[$rootAlias]];
}
$this->updateResultPointer($result, $index, $rootAlias, false);
unset($rowData[$rootAlias]);
// end of hydrate data of the root component for the current row
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
......@@ -121,14 +88,17 @@ class ArrayHydrator extends AbstractHydrator
// belongs to other (related) entities.
foreach ($rowData as $dqlAlias => $data) {
$index = false;
$map = $this->_queryComponents[$dqlAlias];
$parent = $map['parent'];
$relationAlias = $map['relation']->getSourceFieldName();
if ($this->_resultSetMapping->hasParentAlias($dqlAlias)) {
$parent = $this->_resultSetMapping->getParentAlias($dqlAlias);
$relation = $this->_resultSetMapping->getRelation($dqlAlias);
$relationAlias = $relation->getSourceFieldName();
$path = $parent . '.' . $dqlAlias;
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
if ($this->_parserResult->isMixedQuery() && $parent == $rootAlias) {
if ($this->_parserResult->isMixedQuery() && isset($this->_rootAliases[$parent])) {
$key = key(reset($this->_resultPointers));
// TODO: Exception if $key === null ?
$baseElement =& $this->_resultPointers[$parent][$key];
......@@ -140,7 +110,7 @@ class ArrayHydrator extends AbstractHydrator
}
// Check the type of the relation (many or single-valued)
if ( ! $map['relation']->isOneToOne()) {
if ( ! $relation->isOneToOne()) {
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($baseElement[$relationAlias])) {
......@@ -177,6 +147,36 @@ class ArrayHydrator extends AbstractHydrator
if ($coll !== null) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
}
} else {
$this->_rootAliases[$dqlAlias] = true; // Mark as root
// 2) Hydrate the data of the root entity from the current row
// Check for an existing element
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $rowData[$dqlAlias];
if ($field = $this->_getCustomIndexField($dqlAlias)) {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element[$field] => $element);
++$this->_resultCounter;
} else {
$result[$element[$field]] = $element;
}
} else {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element);
++$this->_resultCounter;
} else {
$result[] = $element;
}
}
end($result);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
} else {
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
}
$this->updateResultPointer($result, $index, $dqlAlias, false);
//unset($rowData[$rootAlias]);
}
}
// Append scalar values to mixed result sets
......
......@@ -37,25 +37,23 @@ class ObjectHydrator extends AbstractHydrator
/** Memory for initialized relations */
private $_initializedRelations = array();
private $_metadataMap = array();
private $_rootAlias;
private $_rootEntityName;
private $_rootAliases = array();
private $_isSimpleQuery = false;
private $_identifierMap = array();
private $_resultPointers = array();
private $_idTemplate = array();
private $_resultCounter = 0;
/** @override */
protected function _prepare($parserResult)
{
parent::_prepare($parserResult);
$this->_rootAlias = $parserResult->getDefaultQueryComponentAlias();
$this->_rootEntityName = $this->_queryComponents[$this->_rootAlias]['metadata']->getClassName();
$this->_isSimpleQuery = count($this->_queryComponents) <= 1;
$this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1;
$this->_identifierMap = array();
$this->_resultPointers = array();
$this->_idTemplate = array();
$this->_resultCounter = 0;
foreach ($this->_queryComponents as $dqlAlias => $component) {
foreach ($this->_resultSetMapping->getAliasMap() as $dqlAlias => $class) {
$this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
......@@ -158,8 +156,10 @@ class ObjectHydrator extends AbstractHydrator
private function isIndexKeyInUse($entity, $assocField, $indexField)
{
return $this->_metadataMap[spl_object_hash($entity)]->getReflectionProperty($assocField)
->getValue($entity)->containsKey($indexField);
return $this->_metadataMap[spl_object_hash($entity)]
->getReflectionProperty($assocField)
->getValue($entity)
->containsKey($indexField);
}
private function getLastKey($coll)
......@@ -268,42 +268,6 @@ class ObjectHydrator extends AbstractHydrator
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rootAlias = $this->_rootAlias;
// 2) Hydrate the data of the root entity from the current row
// Check for an existing element
$index = false;
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$rootAlias][$id[$rootAlias]])) {
$element = $this->_uow->createEntity($this->_rootEntityName, $rowData[$rootAlias]);
$oid = spl_object_hash($element);
$this->_metadataMap[$oid] = $this->_em->getClassMetadata($this->_rootEntityName);
if ($field = $this->_getCustomIndexField($rootAlias)) {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array(
$this->_metadataMap[$oid]->getReflectionProperty($field)
->getValue($element) => $element
);
++$this->_resultCounter;
} else {
$result->set($element, $this->_metadataMap[$oid]
->getReflectionProperty($field)
->getValue($element));
}
} else {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element);
++$this->_resultCounter;
} else {
$result->add($element);
}
}
$this->_identifierMap[$rootAlias][$id[$rootAlias]] = $this->getLastKey($result);
} else {
$index = $this->_identifierMap[$rootAlias][$id[$rootAlias]];
}
$this->updateResultPointer($result, $index, $rootAlias, false);
unset($rowData[$rootAlias]);
// end hydrate data of the root component for the current row
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
......@@ -311,19 +275,22 @@ class ObjectHydrator extends AbstractHydrator
unset($rowData['scalars']);
}
// 3) Now hydrate the rest of the data found in the current row, that
// belongs to other (related) entities.
// Now hydrate the entity data found in the current row.
foreach ($rowData as $dqlAlias => $data) {
$index = false;
$map = $this->_queryComponents[$dqlAlias];
$entityName = $map['metadata']->getClassName();
$parent = $map['parent'];
$relationAlias = $map['relation']->getSourceFieldName();
$entityName = $this->_resultSetMapping->getClass($dqlAlias)->getClassName();
if ($this->_resultSetMapping->hasParentAlias($dqlAlias)) {
// It's a joined result
$parent = $this->_resultSetMapping->getParentAlias($dqlAlias);
$relation = $this->_resultSetMapping->getRelation($dqlAlias);
$relationAlias = $relation->getSourceFieldName();
$path = $parent . '.' . $dqlAlias;
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
if ($this->_parserResult->isMixedQuery() && $parent == $rootAlias) {
if ($this->_parserResult->isMixedQuery() && isset($this->_rootAliases[$parent])) {
$key = key(reset($this->_resultPointers));
// TODO: Exception if $key === null ?
$baseElement =& $this->_resultPointers[$parent][$key];
......@@ -337,7 +304,7 @@ class ObjectHydrator extends AbstractHydrator
$oid = spl_object_hash($baseElement);
// Check the type of the relation (many or single-valued)
if ( ! $map['relation']->isOneToOne()) {
if ( ! $relation->isOneToOne()) {
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
$this->initRelatedCollection($baseElement, $relationAlias);
......@@ -379,6 +346,43 @@ class ObjectHydrator extends AbstractHydrator
if ($coll !== null) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
}
} else {
// Its a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->_uow->createEntity($entityName, $rowData[$dqlAlias]);
$oid = spl_object_hash($element);
$this->_metadataMap[$oid] = $this->_em->getClassMetadata($entityName);
if ($field = $this->_getCustomIndexField($dqlAlias)) {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array(
$this->_metadataMap[$oid]
->getReflectionProperty($field)
->getValue($element) => $element
);
++$this->_resultCounter;
} else {
$result->set($element, $this->_metadataMap[$oid]
->getReflectionProperty($field)
->getValue($element));
}
} else {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element);
++$this->_resultCounter;
} else {
$result->add($element);
}
}
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->getLastKey($result);
} else {
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
}
$this->updateResultPointer($result, $index, $dqlAlias, false);
//unset($rowData[$dqlAlias]);
}
}
// Append scalar values to mixed result sets
......
......@@ -84,6 +84,26 @@ final class ClassMetadata
* must have a natural id.
*/
const GENERATOR_TYPE_NONE = 'none';
/**
* DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
* by doing a property-by-property comparison with the original data. This will
* be done for all entities that are in MANAGED state at commit-time.
*
* This is the default change tracking policy.
*/
const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
/**
* DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
* by doing a property-by-property comparison with the original data. This will
* be done only for entities that were explicitly saved (through save() or cascade).
*/
const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
/**
* NOTIFY means that Doctrine relies on the entities sending out notifications
* when their properties change. Such entity classes must implement
* the <tt>NotifyPropertyChanged</tt> interface.
*/
const CHANGETRACKING_NOTIFY = 3;
/**
* The name of the entity class.
......@@ -333,13 +353,6 @@ final class ClassMetadata
private $_reflectionProperties;
//private $_insertSql;
/**
* The name of the ID generator used for this class. Only used for SEQUENCE
* and TABLE generation strategies.
*
* @var string
*/
//private $_idGeneratorName;
/**
* The ID generator used for generating IDs for this class.
......@@ -364,6 +377,13 @@ final class ClassMetadata
*/
//private $_tableGeneratorDefinition;
/**
* The policy used for change-tracking on entities of this class.
*
* @var integer
*/
//private $_changeTrackingPolicy;
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
......@@ -399,6 +419,18 @@ final class ClassMetadata
return $this->_reflectionProperties;
}
/**
* INTERNAL:
* Adds a reflection property. Usually only used by the ClassMetadataFactory
* while processing inheritance mappings.
*
* @param array $props
*/
public function addReflectionProperty($propName, \ReflectionProperty $property)
{
$this->_reflectionProperties[$propName] = $property;
}
/**
* Gets a ReflectionProperty for a specific field of the mapped class.
*
......@@ -434,6 +466,23 @@ final class ClassMetadata
return $this->_entityName;
}
/**
* Gets the name of the class in the entity hierarchy that owns the field with
* the given name. The owning class is the one that defines the field.
*
* @param string $fieldName
* @return string
*/
public function getOwningClass($fieldName)
{
if ($this->_inheritanceType == self::INHERITANCE_TYPE_NONE) {
return $this->_entityName;
} else {
$mapping = $this->getFieldMapping($fieldName);
return $mapping['inherited'];
}
}
/**
* Gets the name of the root class of the mapped entity hierarchy. If the entity described
* by this ClassMetadata instance is not participating in a hierarchy, this is the same as the
......@@ -755,6 +804,13 @@ final class ClassMetadata
return $this->getColumnName($this->getSingleIdentifierFieldName());
}
/**
* INTERNAL:
* Sets the mapped identifier/primary key fields of this class.
* Mainly used by the ClassMetadataFactory to assign inherited identifiers.
*
* @param array $identifier
*/
public function setIdentifier(array $identifier)
{
$this->_identifier = $identifier;
......@@ -1176,7 +1232,7 @@ final class ClassMetadata
{
$this->_validateAndCompleteFieldMapping($mapping);
if (isset($this->_fieldMappings[$mapping['fieldName']])) {
throw MappingException::duplicateFieldMapping();
throw MappingException::duplicateFieldMapping($mapping['fieldName']);
}
$this->_fieldMappings[$mapping['fieldName']] = $mapping;
}
......@@ -1193,6 +1249,18 @@ final class ClassMetadata
$this->_storeAssociationMapping($mapping);
}
/**
* INTERNAL:
* Adds an association mapping without completing/validating it.
* This is mainly used to add inherited field mappings to derived classes.
*
* @param array $mapping
*/
public function addFieldMapping(array $fieldMapping)
{
$this->_fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
}
/**
* Adds a one-to-one mapping.
*
......
......@@ -131,6 +131,7 @@ class ClassMetadataFactory
$class->setIdGeneratorType($parent->getIdGeneratorType());
$this->_addInheritedFields($class, $parent);
$this->_addInheritedRelations($class, $parent);
$class->setIdentifier($parent->getIdentifier());
}
// Invoke driver
......@@ -176,7 +177,8 @@ class ClassMetadataFactory
if ( ! isset($mapping['inherited'])) {
$mapping['inherited'] = $parentClass->getClassName();
}
$subClass->mapField($mapping);
$subClass->addFieldMapping($mapping);
$subClass->addReflectionProperty($fieldName, $parentClass->getReflectionProperty($fieldName));
}
}
......
......@@ -77,8 +77,8 @@ class AnnotationDriver
}
// Evaluate DoctrineDiscriminatorMap annotation
if ($discrValueAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorMap')) {
$metadata->setDiscriminatorMap((array)$discrValueAnnot->value);
if ($discrMapAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorMap')) {
$metadata->setDiscriminatorMap((array)$discrMapAnnot->value);
}
// Evaluate DoctrineSubClasses annotation
......@@ -88,6 +88,10 @@ class AnnotationDriver
// Evaluate annotations on properties/fields
foreach ($annotClass->getProperties() as $property) {
if ($metadata->hasField($property->getName())) {
continue;
}
$mapping = array();
$mapping['fieldName'] = $property->getName();
......
......@@ -43,7 +43,7 @@ abstract class AbstractEntityPersister
protected $_classMetadata;
/**
* The name of the Entity the persister is used for.
* The name of the entity the persister is used for.
*
* @var string
*/
......@@ -123,6 +123,7 @@ abstract class AbstractEntityPersister
foreach ($this->_queuedInserts as $insertData) {
$stmt->execute(array_values($insertData));
}
$this->_queuedInserts = array();
}
/**
......@@ -153,7 +154,18 @@ abstract class AbstractEntityPersister
$this->_conn->delete($this->_classMetadata->getTableName(), $id);
}
public function addDelete($entity)
{
}
public function executeDeletions()
{
}
/**
* Gets the ClassMetadata instance of the entity class this persister is used for.
*
* @return Doctrine\ORM\Mapping\ClassMetadata
*/
......@@ -171,38 +183,29 @@ abstract class AbstractEntityPersister
}
/**
* Gets the name of the class in the entity hierarchy that owns the field with
* the given name. The owning class is the one that defines the field.
*
* @param string $fieldName
* @return string
* @todo Move to ClassMetadata?
* Callback that is invoked during the SQL construction process.
*/
public function getOwningClass($fieldName)
public function getCustomJoins()
{
if ($this->_classMetadata->isInheritanceTypeNone()) {
return $this->_classMetadata;
} else {
$mapping = $this->_classMetadata->getFieldMapping($fieldName);
return $mapping['inherited'];
}
\Doctrine\Common\DoctrineException::updateMe("Unable to find defining class of field '$fieldName'.");
return array();
}
/**
* Callback that is invoked during the SQL construction process.
*/
public function getCustomJoins()
public function getCustomFields()
{
return array();
}
/**
* Callback that is invoked during the SQL construction process.
* Gets all field mappings of the entire entity hierarchy.
*
* @return array
*/
public function getCustomFields()
public function getAllFieldMappingsInHierarchy()
{
return array();
return $this->_classMetadata->getFieldMappings();
}
/**
......@@ -241,13 +244,13 @@ abstract class AbstractEntityPersister
$result[$columnName] = $type->convertToDatabaseValue($newVal, $this->_conn->getDatabasePlatform());
}
}
/*
// Populate the discriminator column on insert in JOINED & SINGLE_TABLE inheritance
if ($isInsert && ($this->_classMetadata->isInheritanceTypeJoined() ||
$this->_classMetadata->isInheritanceTypeSingleTable())) {
$discColumn = $this->_classMetadata->getDiscriminatorColumn();
$discMap = $this->_classMetadata->getDiscriminatorMap();
$result[$discColumn['name']] = array_search($this->_entityName, $discMap);
}
}*/
}
}
\ No newline at end of file
......@@ -4,5 +4,37 @@ namespace Doctrine\ORM\Persisters;
class SingleTablePersister extends AbstractEntityPersister
{
//private $_selectColumnList = array();
public function insert($entity)
{
return parent::insert($entity);
}
/** @override */
protected function _prepareData($entity, array &$result, $isInsert = false)
{
parent::_prepareData($entity, $result, $isInsert);
// Populate the discriminator column
if ($isInsert) {
$discColumn = $this->_classMetadata->getDiscriminatorColumn();
$discMap = $this->_classMetadata->getDiscriminatorMap();
$result[$discColumn['name']] = array_search($this->_entityName, $discMap);
}
}
/**
* {@inheritdoc}
*/
/*public function getAllFieldMappingsInHierarchy()
{
$fieldMappings = $this->_classMetadata->getFieldMappings();
foreach ($this->_classMetadata->getSubclasses() as $subclassName) {
$fieldMappings = array_merge(
$fieldMappings,
$this->_em->getClassMetadata($subclassName)->getFieldMappings()
);
}
return $fieldMappings;
}*/
}
\ No newline at end of file
......@@ -16,7 +16,7 @@
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Persisters;
......@@ -38,22 +38,6 @@ class StandardEntityPersister extends AbstractEntityPersister
*/
protected function _doDelete($record)
{
/*try {
$this->_conn->beginInternalTransaction();
$this->_deleteComposites($record);
$record->_state(Doctrine_ORM_Entity::STATE_TDIRTY);
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $metadata);
$this->_deleteRow($metadata->getTableName(), $identifier);
$record->_state(Doctrine_ORM_Entity::STATE_TCLEAN);
$this->removeRecord($record);
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}*/
}
/**
......@@ -63,41 +47,6 @@ class StandardEntityPersister extends AbstractEntityPersister
*/
protected function _doInsert(Doctrine_ORM_Entity $record)
{
$fields = $record->getPrepared();
if (empty($fields)) {
return false;
}
$class = $this->_classMetadata;
$identifier = $class->getIdentifier();
$fields = $this->_convertFieldToColumnNames($fields, $class);
$seq = $class->getTableOption('sequenceName');
if ( ! empty($seq)) {
$id = $conn->sequence->nextId($seq);
$seqName = $identifier[0];
$fields[$seqName] = $id;
$record->assignIdentifier($id);
}
$this->_insertRow($class->getTableName(), $fields);
if (empty($seq) && count($identifier) == 1 &&
$class->getIdentifierType() != Doctrine::IDENTIFIER_NATURAL) {
if (strtolower($conn->getName()) == 'pgsql') {
$seq = $class->getTableName() . '_' . $identifier[0];
}
$id = $conn->sequence->lastInsertId($seq);
if ( ! $id) {
throw \Doctrine\Common\DoctrineException::updateMe("Couldn't get last insert identifier.");
}
$record->assignIdentifier($id);
} else {
$record->assignIdentifier(true);
}
}
/**
......@@ -105,11 +54,5 @@ class StandardEntityPersister extends AbstractEntityPersister
*/
protected function _doUpdate(Doctrine_ORM_Entity $record)
{
$conn = $this->_conn;
$classMetadata = $this->_classMetadata;
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $classMetadata);
$data = $this->_convertFieldToColumnNames($record->getPrepared(), $classMetadata);
$this->_updateRow($classMetadata->getTableName(), $data, $identifier);
$record->assignIdentifier(true);
}
}
\ No newline at end of file
......@@ -227,6 +227,10 @@ class Query extends AbstractQuery
*/
public function execute($params = array(), $hydrationMode = null)
{
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
$this->_em->flush();
}
if ($hydrationMode !== null) {
$this->_hydrationMode = $hydrationMode;
}
......@@ -497,6 +501,16 @@ class Query extends AbstractQuery
return $this;
}
/**
* Gets the hydration mode currently used by the query.
*
* @return integer
*/
public function getHydrationMode()
{
return $this->_hydrationMode;
}
/**
* Gets the list of results for the query.
*
......
......@@ -51,12 +51,12 @@ abstract class AbstractResult
* parent Alias of the parent.
* map Name of the column / aggregate value this component is mapped to a collection.
*/
protected $_queryComponents = array();
//protected $_queryComponents = array();
/**
* @var array Table alias map. Keys are SQL aliases and values DQL aliases.
*/
protected $_tableAliasMap = array();
//protected $_tableAliasMap = array();
/**
* @var array Enum params.
......@@ -66,7 +66,7 @@ abstract class AbstractResult
/**
* @var string
*/
protected $_defaultQueryComponentAlias;
//protected $_defaultQueryComponentAlias;
/**
* @var boolean
......@@ -78,10 +78,10 @@ abstract class AbstractResult
*
* @param array $queryComponents Query components.
*/
public function setQueryComponents(array $queryComponents)
/*public function setQueryComponents(array $queryComponents)
{
$this->_queryComponents = $queryComponents;
}
}*/
/**
* Sets the declaration for given component alias.
......@@ -89,38 +89,38 @@ abstract class AbstractResult
* @param string $componentAlias The component alias to set the declaration to.
* @param string $queryComponent Alias declaration.
*/
public function setQueryComponent($componentAlias, array $queryComponent)
/*public function setQueryComponent($componentAlias, array $queryComponent)
{
$this->_queryComponents[$componentAlias] = $queryComponent;
}
}*/
/**
* Gets the mapping components.
*
* @return array Query components.
*/
public function getQueryComponents()
/*public function getQueryComponents()
{
return $this->_queryComponents;
}
}*/
/**
*
*/
public function getDefaultQueryComponentAlias()
/*public function getDefaultQueryComponentAlias()
{
return $this->_defaultQueryComponentAlias;
}
}*/
/**
*
*
* @param <type> $alias
*/
public function setDefaultQueryComponentAlias($alias)
/*public function setDefaultQueryComponentAlias($alias)
{
$this->_defaultQueryComponentAlias = $alias;
}
}*/
/**
* Get the declaration for given component alias.
......@@ -128,14 +128,14 @@ abstract class AbstractResult
* @param string $componentAlias The component alias the retrieve the declaration from.
* @return array Alias declaration.
*/
public function getQueryComponent($componentAlias)
/*public function getQueryComponent($componentAlias)
{
if ( ! isset($this->_queryComponents[$componentAlias])) {
throw new DoctrineException('Unknown query component ' . $componentAlias);
}
return $this->_queryComponents[$componentAlias];
}
}*/
/**
* Get the component alias for a given query component
......@@ -143,10 +143,10 @@ abstract class AbstractResult
* @param array $queryComponent The query component
* @param string Component alias
*/
public function getComponentAlias($queryComponent)
/*public function getComponentAlias($queryComponent)
{
return array_search($queryComponent, $this->_queryComponents);;
}
}*/
/**
* Whether or not this object has a declaration for given component alias.
......@@ -154,20 +154,20 @@ abstract class AbstractResult
* @param string $componentAlias Component alias the retrieve the declaration from.
* @return boolean True if this object has given alias, otherwise false.
*/
public function hasQueryComponent($componentAlias)
/*public function hasQueryComponent($componentAlias)
{
return isset($this->_queryComponents[$componentAlias]);
}
}*/
/**
* Defines the table aliases.
*
* @param array $tableAliasMap Table aliases.
*/
public function setTableAliasMap(array $tableAliasMap)
/*public function setTableAliasMap(array $tableAliasMap)
{
$this->_tableAliasMap = $tableAliasMap;
}
}*/
/**
* Adds an SQL table alias and associates it a component alias
......@@ -175,20 +175,20 @@ abstract class AbstractResult
* @param string $tableAlias Table alias to be added.
* @param string $componentAlias Alias for the query component associated with given tableAlias.
*/
public function setTableAlias($tableAlias, $componentAlias)
/*public function setTableAlias($tableAlias, $componentAlias)
{
$this->_tableAliasMap[$tableAlias] = $componentAlias;
}
}*/
/**
* Returns all table aliases.
*
* @return array Table aliases as an array.
*/
public function getTableAliasMap()
/*public function getTableAliasMap()
{
return $this->_tableAliasMap;
}
}*/
/**
* Get DQL alias associated with given SQL table alias.
......@@ -196,14 +196,14 @@ abstract class AbstractResult
* @param string $tableAlias SQL table alias that identifies the component alias
* @return string Component alias
*/
public function getTableAlias($tableAlias)
/*public function getTableAlias($tableAlias)
{
if ( ! isset($this->_tableAliasMap[$tableAlias])) {
throw DoctrineException::updateMe('Unknown table alias ' . $tableAlias);
}
return $this->_tableAliasMap[$tableAlias];
}
}*/
/**
* Get table alias associated with given component alias.
......@@ -211,10 +211,10 @@ abstract class AbstractResult
* @param string $componentAlias Component alias that identifies the table alias
* @return string Component alias
*/
public function getTableAliasFromComponentAlias($componentAlias)
/*public function getTableAliasFromComponentAlias($componentAlias)
{
return array_search($componentAlias, $this->_tableAliasMap);
}
}*/
/**
* Whether or not this object has given tableAlias.
......@@ -222,10 +222,10 @@ abstract class AbstractResult
* @param string $tableAlias Table alias to be checked.
* @return boolean True if this object has given alias, otherwise false.
*/
public function hasTableAlias($tableAlias)
/*public function hasTableAlias($tableAlias)
{
return (isset($this->_tableAliasMap[$tableAlias]));
}
}*/
/**
* Gets whether the parsed query selects objects/arrays and scalar values
......@@ -263,7 +263,7 @@ abstract class AbstractResult
* @param string $key The key of the input parameter
* @return Doctrine_ORM_Query_AbstractResult
*/
public function addEnumParam($key, $table = null, $column = null)
/*public function addEnumParam($key, $table = null, $column = null)
{
$array = (isset($table) || isset($column)) ? array($table, $column) : array();
......@@ -274,7 +274,7 @@ abstract class AbstractResult
}
return $this;
}
}*/
/**
* Returns this object in serialized format, revertable using fromCached*.
......
......@@ -16,7 +16,7 @@
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query;
......
......@@ -121,12 +121,22 @@ class Parser
private $_resultContainsScalars = false;
/**
* Whether the query is a SELECT query and contains objects in the result list
* Whether the query is a SELECT query and contains properties in the result list
* as defined by the SelectExpressions.
*
* @var boolean
*/
private $_resultContainsObjects = false;
private $_resultContainsProperties = false;
/**
* Map of declared classes in the parsed query.
* Maps the declared DQL alias (key) to the class name (value).
*
* @var array
*/
//private $_declaredClasses = array();
private $_queryComponents = array();
/**
* Creates a new query parser object.
......@@ -204,7 +214,7 @@ class Parser
}
// Create SqlWalker who creates the SQL from the AST
$sqlWalker = new SqlWalker($this->_em, $this->_parserResult);
$sqlWalker = new SqlWalker($this->_query, $this->_parserResult, $this->_queryComponents);
// Assign an SQL executor to the parser result
$this->_parserResult->setSqlExecutor(Exec\AbstractExecutor::create($AST, $sqlWalker));
......@@ -388,7 +398,7 @@ class Parser
private function _processDeferredPathExpressionStack()
{
$exprStack = array_pop($this->_deferredPathExpressionStacks);
$qComps = $this->_parserResult->getQueryComponents();
$qComps = $this->_queryComponents;
foreach ($exprStack as $expr) {
$parts = $expr->getParts();
$numParts = count($parts);
......@@ -483,8 +493,7 @@ class Parser
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($aliasIdentificationVariable, $queryComponent);
$this->_parserResult->setDefaultQueryComponentAlias($aliasIdentificationVariable);
$this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
$updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
$updateClause->setAliasIdentificationVariable($aliasIdentificationVariable);
......@@ -504,7 +513,8 @@ class Parser
$identVariable = $this->_lexer->token['value'];
$this->match('.');
} else {
$identVariable = $this->_parserResult->getDefaultQueryComponentAlias();
//$identVariable = $this->_parserResult->getDefaultQueryComponentAlias();
throw new DoctrineException("Missing alias qualifier.");
}
$this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value'];
......@@ -578,10 +588,9 @@ class Parser
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($deleteClause->getAliasIdentificationVariable(),
$queryComponent);
$this->_parserResult->setDefaultQueryComponentAlias($deleteClause->getAliasIdentificationVariable());
$this->_queryComponents[$deleteClause->getAliasIdentificationVariable()] = $queryComponent;
//$this->_parserResult->setDefaultQueryComponentAlias($deleteClause->getAliasIdentificationVariable());
//$this->_declaredClasses[$deleteClause->getAliasIdentificationVariable()] = $classMetadata;
return $deleteClause;
}
......@@ -620,11 +629,11 @@ class Parser
$identificationVariableDeclarations[] = $this->_IdentificationVariableDeclaration();
$firstRangeDecl = $identificationVariableDeclarations[0]->getRangeVariableDeclaration();
if ($firstRangeDecl->getAliasIdentificationVariable()) {
/*if ($firstRangeDecl->getAliasIdentificationVariable()) {
$this->_parserResult->setDefaultQueryComponentAlias($firstRangeDecl->getAliasIdentificationVariable());
} else {
$this->_parserResult->setDefaultQueryComponentAlias($firstRangeDecl->getAbstractSchemaName());
}
}*/
while ($this->_lexer->isNextToken(',')) {
$this->match(',');
......@@ -647,7 +656,7 @@ class Parser
$peek = $this->_lexer->glimpse();
// First we recognize for an IdentificationVariable (DQL class alias)
if ($peek['value'] != '.' && $peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) {
$this->_resultContainsObjects = true;
$this->_resultContainsProperties = true;
if ($this->_resultContainsScalars) {
$this->_parserResult->setMixedQuery(true);
}
......@@ -672,11 +681,11 @@ class Parser
$fieldIdentificationVariable = $this->_lexer->token['value'];
}
$this->_resultContainsScalars = true;
if ($this->_resultContainsObjects) {
if ($this->_resultContainsProperties) {
$this->_parserResult->setMixedQuery(true);
}
} else {
$this->_resultContainsObjects = true;
$this->_resultContainsProperties = true;
if ($this->_resultContainsScalars) {
$this->_parserResult->setMixedQuery(true);
}
......@@ -739,7 +748,8 @@ class Parser
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($aliasIdentificationVariable, $queryComponent);
$this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
//$this->_declaredClasses[$aliasIdentificationVariable] = $classMetadata;
return new AST\RangeVariableDeclaration(
$classMetadata, $aliasIdentificationVariable
......@@ -807,8 +817,8 @@ class Parser
// Verify that the association exists, if yes update the ParserResult
// with the new component.
$parentComp = $this->_parserResult->getQueryComponent($joinPathExpression->getIdentificationVariable());
$parentClass = $parentComp['metadata'];
//$parentComp = $this->_parserResult->getQueryComponent($joinPathExpression->getIdentificationVariable());
$parentClass = $this->_queryComponents[$joinPathExpression->getIdentificationVariable()]['metadata'];
$assocField = $joinPathExpression->getAssociationField();
if ( ! $parentClass->hasAssociation($assocField)) {
$this->semanticalError("Class " . $parentClass->getClassName() .
......@@ -824,7 +834,8 @@ class Parser
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($aliasIdentificationVariable, $joinQueryComponent);
$this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
//$this->_declaredClasses[$aliasIdentificationVariable] = $this->_em->getClassMetadata($targetClassName);
// Create AST node
$join = new AST\Join($joinType, $joinPathExpression, $aliasIdentificationVariable);
......@@ -866,9 +877,7 @@ class Parser
$this->match(Lexer::T_BY);
$pathExp = $this->_SimpleStateFieldPathExpression();
// Add the INDEX BY info to the query component
$qComp = $this->_parserResult->getQueryComponent($pathExp->getIdentificationVariable());
$qComp['map'] = $pathExp->getSimpleStateField();
$this->_parserResult->setQueryComponent($pathExp->getIdentificationVariable(), $qComp);
$this->_queryComponents[$pathExp->getIdentificationVariable()]['map'] = $pathExp->getSimpleStateField();
return $pathExp;
}
......@@ -909,10 +918,10 @@ class Parser
$assocSeen = false;
$identificationVariable = $this->_IdentificationVariable();
if ( ! $this->_parserResult->hasQueryComponent($identificationVariable)) {
if ( ! isset($this->_queryComponents[$identificationVariable])) {
$this->syntaxError("Identification variable.");
}
$qComp = $this->_parserResult->getQueryComponent($identificationVariable);
$qComp = $this->_queryComponents[$identificationVariable];
$parts[] = $identificationVariable;
$class = $qComp['metadata'];
......
......@@ -45,7 +45,35 @@ class ParserResult extends AbstractResult
*
* @var array $_queryFields
*/
protected $_queryFields = array();
//protected $_queryFields = array();
protected $_resultSetMapping;
public function __construct()
{
$this->_resultSetMapping = new ResultSetMapping;
}
/**
* Gets the ResultSetMapping for the parsed query.
*
* @return ResultSetMapping The result set mapping of the parsed query or NULL
* if the query is not a SELECT query.
*/
public function getResultSetMapping()
{
return $this->_resultSetMapping;
}
/**
* Sets the ResultSetMapping of the parsed query.
*
* @param ResultSetMapping $rsm
*/
public function setResultSetMapping(ResultSetMapping $rsm)
{
$this->_resultSetMapping = $rsm;
}
/**
* Sets the Entity Manager.
......@@ -88,10 +116,10 @@ class ParserResult extends AbstractResult
*
* @param array $queryFields Query fields.
*/
public function setQueryFields(array $queryFields)
/*public function setQueryFields(array $queryFields)
{
$this->_queryFields = $queryFields;
}
}*/
/**
* Sets the declaration for given field alias.
......@@ -99,20 +127,20 @@ class ParserResult extends AbstractResult
* @param string $fieldAlias The field alias to set the declaration to.
* @param string $queryField Alias declaration.
*/
public function setQueryField($fieldAlias, $queryField)
/*public function setQueryField($fieldAlias, $queryField)
{
$this->_queryFields[$fieldAlias] = $queryField;
}
}*/
/**
* Gets the mapping fields.
*
* @return array Query fields.
*/
public function getQueryFields()
/*public function getQueryFields()
{
return $this->_queryFields;
}
}*/
/**
* Get the declaration for given field alias.
......@@ -120,14 +148,14 @@ class ParserResult extends AbstractResult
* @param string $fieldAlias The field alias the retrieve the declaration from.
* @return array Alias declaration.
*/
public function getQueryField($fieldAlias)
/*public function getQueryField($fieldAlias)
{
if ( ! isset($this->_queryFields[$fieldAlias])) {
throw DoctrineException::updateMe('Unknown query field ' . $fieldAlias);
}
return $this->_queryFields[$fieldAlias];
}
}*/
/**
* Whether or not this object has a declaration for given field alias.
......@@ -135,8 +163,8 @@ class ParserResult extends AbstractResult
* @param string $fieldAlias Field alias the retrieve the declaration from.
* @return boolean True if this object has given alias, otherwise false.
*/
public function hasQueryField($fieldAlias)
/*public function hasQueryField($fieldAlias)
{
return isset($this->_queryFields[$fieldAlias]);
}
}*/
}
\ No newline at end of file
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query;
/**
* A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class ResultSetMapping
{
/** Maps alias names to ClassMetadata descriptors. */
private $_aliasMap = array();
/** Maps alias names to related association mappings. */
private $_relationMap = array();
/** Maps alias names to parent alias names. */
private $_parentAliasMap = array();
/** Maps column names in the result set to field names for each class. */
private $_fieldMappings = array();
/** Maps column names in the result set to the alias to use in the mapped result. */
private $_scalarMappings = array();
/** Maps column names in the result set to the alias they belong to. */
private $_columnOwnerMap = array();
/** Maps discriminator columns in the result set to the class they represent. */
private $_discriminatorMap = array();
/** Maps alias names to field names that should be used for indexing. */
private $_indexByMap = array();
/**
*
* @param <type> $class
* @param <type> $alias The alias for this class. The alias must be unique within this ResultSetMapping.
* @param <type> $discriminatorColumn
*/
public function addEntityResult($class, $alias, $discriminatorColumn = null)
{
$this->_aliasMap[$alias] = $class;
if ($discriminatorColumn !== null) {
$this->_discriminatorMap[$discriminatorColumn] = $class;
}
}
public function addIndexBy($alias, $fieldName)
{
$this->_indexByMap[$alias] = $fieldName;
}
public function hasIndexBy($alias)
{
return isset($this->_indexByMap[$alias]);
}
public function getIndexByField($alias)
{
return $this->_indexByMap[$alias];
}
public function addFieldResult($alias, $columnName, $fieldName)
{
$this->_fieldMappings[$columnName] = $fieldName;
$this->_columnOwnerMap[$columnName] = $alias;
}
public function addJoinedEntityResult($class, $alias, $parentAlias, $relation, $discriminatorColumn = null)
{
$this->_aliasMap[$alias] = $class;
$this->_parentAliasMap[$alias] = $parentAlias;
$this->_relationMap[$alias] = $relation;
if ($discriminatorColumn !== null) {
$this->_discriminatorMap[$discriminatorColumn] = $class;
}
}
public function isDiscriminatorColumn($columnName)
{
return isset($this->_discriminatorMap[$columnName]);
}
public function addScalarResult($columnName, $alias)
{
$this->_scalarMappings[$columnName] = $alias;
}
/**
* @return boolean
*/
public function isScalarResult($columnName)
{
return isset($this->_scalarMappings[$columnName]);
}
/**
*
* @param <type> $alias
*/
public function getClass($alias)
{
if ( ! isset($this->_aliasMap[$alias])) {
var_dump($alias); die();
}
return $this->_aliasMap[$alias];
}
/**
* Gets the alias for a column that is mapped as a scalar value.
*
* @param string $columnName
* @return string
*/
public function getScalarAlias($columnName)
{
return $this->_scalarMappings[$columnName];
}
/**
* Gets the class that owns the specified column.
*
* @param string $columnName
*/
public function getOwningClass($columnName)
{
return $this->_aliasMap[$this->_columnOwnerMap[$columnName]];
}
public function getRelation($alias)
{
return $this->_relationMap[$alias];
}
/**
*
* @param <type> $columnName
* @return <type>
*/
public function getEntityAlias($columnName)
{
return $this->_columnOwnerMap[$columnName];
}
/**
*
* @param <type> $alias
* @return <type>
*/
public function getParentAlias($alias)
{
return $this->_parentAliasMap[$alias];
}
public function hasParentAlias($alias)
{
return isset($this->_parentAliasMap[$alias]);
}
/**
*
* @param <type> $className
* @param <type> $columnName
* @return <type>
*/
public function getFieldName($columnName)
{
return $this->_fieldMappings[$columnName];
}
public function getAliasMap()
{
return $this->_aliasMap;
}
public function getEntityResultCount()
{
return count($this->_aliasMap);
}
/* TEMP */
public function getRootAlias()
{
reset($this->_aliasMap);
return key($this->_aliasMap);
}
}
This diff is collapsed.
......@@ -21,6 +21,7 @@
namespace Doctrine\ORM\Tools;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
/**
......@@ -76,30 +77,19 @@ class SchemaTool
*/
public function getCreateSchemaSql(array $classes)
{
$processedClasses = array();
$sql = array();
$foreignKeyConstraints = array();
// First we create the tables
foreach ($classes as $class) {
$columns = array(); // table columns
$options = array(); // table options
foreach ($class->getFieldMappings() as $fieldName => $mapping) {
$column = array();
$column['name'] = $mapping['columnName'];
$column['type'] = $mapping['type'];
$column['length'] = $mapping['length'];
$column['notnull'] = ! $mapping['nullable'];
if ($class->isIdentifier($fieldName)) {
$column['primary'] = true;
$options['primary'][] = $mapping['columnName'];
if ($class->isIdGeneratorIdentity()) {
$column['autoincrement'] = true;
}
}
$columns[$mapping['columnName']] = $column;
if (isset($processedClasses[$class->getClassName()])) {
continue;
}
$columns = $this->_gatherColumns($class); // table columns
$options = array(); // table options
foreach ($class->getAssociationMappings() as $mapping) {
$foreignClass = $this->_em->getClassMetadata($mapping->getTargetEntityName());
if ($mapping->isOneToOne() && $mapping->isOwningSide()) {
......@@ -165,7 +155,28 @@ class SchemaTool
}
}
if ($class->isInheritanceTypeSingleTable()) {
// Add the discriminator column
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class);
$columns[$discrColumnDef['name']] = $discrColumnDef;
// Aggregate all the information from all classes in the hierarchy
foreach ($class->getParentClasses() as $parentClassName) {
// Parent class information is already contained in this class
$processedClasses[$parentClassName] = true;
}
foreach ($class->getSubclasses() as $subClassName) {
$columns = array_merge($columns, $this->_gatherColumns($this->_em->getClassMetadata($subClassName)));
$processedClasses[$subClassName] = true;
}
} else if ($class->isInheritanceTypeJoined()) {
//TODO
} else if ($class->isInheritanceTypeTablePerClass()) {
//TODO
}
$sql = array_merge($sql, $this->_platform->getCreateTableSql($class->getTableName(), $columns, $options));
$processedClasses[$class->getClassName()] = true;
}
// Now create the foreign key constraints
......@@ -178,6 +189,38 @@ class SchemaTool
return $sql;
}
private function _getDiscriminatorColumnDefinition($class)
{
$discrColumn = $class->getDiscriminatorColumn();
return array(
'name' => $discrColumn['name'],
'type' => Type::getType($discrColumn['type']),
'length' => $discrColumn['length'],
'notnull' => true
);
}
private function _gatherColumns($class)
{
$columns = array();
foreach ($class->getFieldMappings() as $fieldName => $mapping) {
$column = array();
$column['name'] = $mapping['columnName'];
$column['type'] = $mapping['type'];
$column['length'] = $mapping['length'];
$column['notnull'] = ! $mapping['nullable'];
if ($class->isIdentifier($fieldName)) {
$column['primary'] = true;
$options['primary'][] = $mapping['columnName'];
if ($class->isIdGeneratorIdentity()) {
$column['autoincrement'] = true;
}
}
$columns[$mapping['columnName']] = $column;
}
return $columns;
}
public function dropSchema(array $classes)
{
//TODO
......
This diff is collapsed.
......@@ -56,6 +56,11 @@ class CmsUser
$phone->user = $this;
}
public function addArticle(CmsArticle $article) {
$this->articles[] = $article;
$article->user = $this;
}
public function removePhonenumber($index) {
if (isset($this->phonenumbers[$index])) {
$ph = $this->phonenumbers[$index];
......
......@@ -32,7 +32,7 @@ class EntityPersisterTest extends \Doctrine\Tests\OrmTestCase
public function testSimpleInsert()
{
$userPersister = new \Doctrine\ORM\Persisters\StandardEntityPersister(
$userPersister = new \Doctrine\ORM\Persisters\SingleTablePersister(
$this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser"));
$avatarPersister = new \Doctrine\ORM\Persisters\StandardEntityPersister(
$this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumAvatar"));
......
......@@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsArticle;
require_once __DIR__ . '/../../TestInit.php';
......@@ -59,5 +60,38 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('GUILHERME', $query->getSingleScalarResult());
}
public function testJoinQueries()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$article1 = new CmsArticle;
$article1->topic = "Doctrine 2";
$article1->text = "This is an introduction to Doctrine 2.";
$user->addArticle($article1);
$article2 = new CmsArticle;
$article2->topic = "Symfony 2";
$article2->text = "This is an introduction to Symfony 2.";
$user->addArticle($article2);
$this->_em->save($user);
$this->_em->save($article1);
$this->_em->save($article2);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("select u, a from Doctrine\Tests\Models\CMS\CmsUser u join u.articles a");
$users = $query->getResultList();
$this->assertEquals(1, count($users));
$this->assertTrue($users[0] instanceof CmsUser);
$this->assertEquals(2, count($users[0]->articles));
$this->assertEquals('Doctrine 2', $users[0]->articles[0]->topic);
$this->assertEquals('Symfony 2', $users[0]->articles[1]->topic);
}
}
......@@ -24,6 +24,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Hydration\ArrayHydratorTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Hydration\ScalarHydratorTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Hydration\SingleScalarHydratorTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Hydration\ResultSetMappingTest');
return $suite;
}
......
......@@ -18,18 +18,12 @@ class HydrationTest extends \Doctrine\Tests\OrmTestCase
}
/** Helper method */
protected function _createParserResult($queryComponents, $tableToClassAliasMap, $isMixedQuery = false)
protected function _createParserResult($resultSetMapping, $isMixedQuery = false)
{
$parserResult = new ParserResult(
'',
array(/*queryComponent*/),
array(/*tableAliasMap*/)
);
//$parserResult = new \Doctrine\ORM\Query\ParserResult();
$parserResult->setQueryComponents($queryComponents);
$parserResult->setDefaultQueryComponentAlias(key($queryComponents));
$parserResult->setTableAliasMap($tableToClassAliasMap);
$parserResult = new ParserResult;
$parserResult->setResultSetMapping($resultSetMapping);
//$parserResult->setDefaultQueryComponentAlias(key($queryComponents));
//$parserResult->setTableAliasMap($tableToClassAliasMap);
$parserResult->setMixedQuery($isMixedQuery);
return $parserResult;
}
......
<?php
namespace Doctrine\Tests\ORM\Hydration;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Mapping\ClassMetadata;
require_once __DIR__ . '/../../TestInit.php';
/**
* Description of ResultSetMappingTest
*
* @author robo
*/
class ResultSetMappingTest extends \Doctrine\Tests\OrmTestCase
{
private $_rsm;
private $_em;
protected function setUp() {
parent::setUp();
$this->_rsm = new ResultSetMapping;
$this->_em = $this->_getTestEntityManager();
}
/**
* For SQL: SELECT id, status, username, name FROM cms_users
*/
public function testBasicResultSetMapping()
{
$this->_rsm->addEntityResult(
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'),
'u'
);
$this->_rsm->addFieldResult('u', 'id', 'id');
$this->_rsm->addFieldResult('u', 'status', 'status');
$this->_rsm->addFieldResult('u', 'username', 'username');
$this->_rsm->addFieldResult('u', 'name', 'name');
$this->assertFalse($this->_rsm->isScalarResult('id'));
$this->assertFalse($this->_rsm->isScalarResult('status'));
$this->assertFalse($this->_rsm->isScalarResult('username'));
$this->assertFalse($this->_rsm->isScalarResult('name'));
$this->assertTrue($this->_rsm->getClass('u') instanceof ClassMetadata);
$class = $this->_rsm->getOwningClass('id');
$this->assertTrue($class instanceof ClassMetadata);
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $class->getClassName());
$this->assertEquals('u', $this->_rsm->getEntityAlias('id'));
$this->assertEquals('u', $this->_rsm->getEntityAlias('status'));
$this->assertEquals('u', $this->_rsm->getEntityAlias('username'));
$this->assertEquals('u', $this->_rsm->getEntityAlias('name'));
$this->assertEquals('id', $this->_rsm->getFieldName('id'));
$this->assertEquals('status', $this->_rsm->getFieldName('status'));
$this->assertEquals('username', $this->_rsm->getFieldName('username'));
$this->assertEquals('name', $this->_rsm->getFieldName('name'));
}
}
......@@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Hydration;
use Doctrine\Tests\Mocks\HydratorMockStatement;
use Doctrine\ORM\Query\ResultSetMapping;
require_once __DIR__ . '/../../TestInit.php';
......@@ -13,20 +14,10 @@ class ScalarHydratorTest extends HydrationTest
*/
public function testNewHydrationSimpleEntityQuery()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
$rsm = new ResultSetMapping;
$rsm->addEntityResult($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), 'u');
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
// Faked result set
$resultSet = array(
......@@ -44,8 +35,7 @@ class ScalarHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ScalarHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$queryComponents, $tableAliasMap));
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm));
$this->assertTrue(is_array($result));
$this->assertEquals(2, count($result));
......
......@@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Hydration;
use Doctrine\Tests\Mocks\HydratorMockStatement;
use Doctrine\ORM\Query\ResultSetMapping;
require_once __DIR__ . '/../../TestInit.php';
......@@ -53,36 +54,23 @@ class SingleScalarHydratorTest extends HydrationTest
*/
public function testHydrateSingleScalar($name, $resultSet)
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
$rsm = new ResultSetMapping;
$rsm->addEntityResult($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), 'u');
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\SingleScalarHydrator($this->_em);
if ($name == 'result1') {
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$queryComponents, $tableAliasMap));
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm));
$this->assertEquals('romanb', $result);
} else if ($name == 'result2') {
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$queryComponents, $tableAliasMap));
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm));
$this->assertEquals(1, $result);
} else if ($name == 'result3' || $name == 'result4') {
try {
$result = $hydrator->hydrateall($stmt, $this->_createParserResult(
$queryComponents, $tableAliasMap));
$result = $hydrator->hydrateall($stmt, $this->_createParserResult($rsm));
$this->fail();
} catch (\Doctrine\ORM\Internal\Hydration\HydrationException $ex) {}
}
......
......@@ -20,7 +20,7 @@ class AllTests
$suite = new \Doctrine\Tests\DoctrineTestSuite('Doctrine Orm Mapping');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\ClassMetadataTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\ClassMetadataFactoryTest');
//$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\ClassMetadataFactoryTest');
return $suite;
}
......
......@@ -47,75 +47,6 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue($cm1->hasField('name'));
$this->assertEquals('sequence', $cm1->getIdGeneratorType());
}
public function testGetMetadataForClassInHierarchy()
{
$mockPlatform = new DatabasePlatformMock();
$mockPlatform->setPrefersIdentityColumns(true);
$mockDriver = new MetadataDriverMock();
// Self-made metadata
$cm1 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity1');
$cm1->setInheritanceType('singleTable');
// Add a mapped field
$cm1->mapField(array('fieldName' => 'name', 'type' => 'varchar'));
// Add a mapped field
$cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
// and a mapped association
$cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'Other', 'mappedBy' => 'this'));
// and an id generator type
$cm1->setIdGeneratorType('auto');
$cm2 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity2');
$cm3 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity3');
$cmf = new ClassMetadataFactoryTestSubject($mockDriver, $mockPlatform);
// Set self-made metadata
$cmf->setMetadataForClass('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm1);
$cmf->setMetadataForClass('Doctrine\Tests\ORM\Mapping\TestEntity2', $cm2);
$cmf->setMetadataForClass('Doctrine\Tests\ORM\Mapping\TestEntity3', $cm3);
// Prechecks
$this->assertEquals(array(), $cm1->getParentClasses());
$this->assertEquals(array(), $cm2->getParentClasses());
$this->assertEquals(array(), $cm3->getParentClasses());
$this->assertEquals('none', $cm2->getInheritanceType());
$this->assertEquals('none', $cm3->getInheritanceType());
$this->assertFalse($cm2->hasField('name'));
$this->assertFalse($cm3->hasField('name'));
$this->assertEquals(1, count($cm1->getAssociationMappings()));
$this->assertEquals(0, count($cm2->getAssociationMappings()));
$this->assertEquals(0, count($cm3->getAssociationMappings()));
$this->assertEquals('none', $cm2->getIdGeneratorType());
$this->assertEquals('none', $cm3->getIdGeneratorType());
// Go
$cm3 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity3');
// Metadata gathering should start at the root of the hierarchy, from there on downwards
$this->assertEquals(array('Doctrine\Tests\ORM\Mapping\TestEntity1', 'Doctrine\Tests\ORM\Mapping\TestEntity2', 'Doctrine\Tests\ORM\Mapping\TestEntity3'), $cmf->getRequestedClasses());
// Parent classes should be assigned by factory
$this->assertEquals(array('Doctrine\Tests\ORM\Mapping\TestEntity2', 'Doctrine\Tests\ORM\Mapping\TestEntity1'), $cm3->getParentClasses());
$this->assertEquals('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm3->getRootClassName());
$this->assertEquals('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm2->getRootClassName());
$this->assertEquals('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm1->getRootClassName());
// Inheritance type should be inherited to Entity2
$this->assertEquals('singleTable', $cm2->getInheritanceType());
$this->assertEquals('singleTable', $cm3->getInheritanceType());
// Field mappings should be inherited
$this->assertTrue($cm2->hasField('name'));
$this->assertTrue($cm3->hasField('name'));
// Association mappings should be inherited
$this->assertEquals(1, count($cm2->getAssociationMappings()));
$this->assertEquals(1, count($cm3->getAssociationMappings()));
$this->assertTrue($cm2->hasAssociation('other'));
$this->assertTrue($cm3->hasAssociation('other'));
// Id generator 'auto' should have been resolved to 'identity' as preferred by our
// mock platform (see above). And it should be inherited.
$this->assertEquals('identity', $cm1->getIdGeneratorType());
$this->assertEquals('identity', $cm2->getIdGeneratorType());
$this->assertEquals('identity', $cm3->getIdGeneratorType());
}
}
/* Test subject class with overriden factory method for mocking purposes */
......@@ -144,15 +75,3 @@ class ClassMetadataFactoryTestSubject extends \Doctrine\ORM\Mapping\ClassMetadat
return $this->_requestedClasses;
}
}
/* Test classes */
class TestEntity1
{
protected $id;
protected $name;
protected $other;
}
class TestEntity2 extends TestEntity1 {}
class TestEntity3 extends TestEntity2 {}
\ No newline at end of file
......@@ -19,7 +19,7 @@ class AllTests
{
$suite = new \Doctrine\Tests\DoctrineTestSuite('Doctrine Orm Query');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\IdentifierRecognitionTest');
//$suite->addTestSuite('Doctrine\Tests\ORM\Query\IdentifierRecognitionTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\SelectSqlGenerationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\LanguageRecognitionTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\LexerTest');
......
......@@ -60,11 +60,11 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u',
'DELETE FROM cms_users c0'
'DELETE FROM cms_users c0_'
);
$this->assertSqlGeneration(
'DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u',
'DELETE FROM cms_users c0'
'DELETE FROM cms_users c0_'
);
}
......@@ -72,7 +72,7 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1',
'DELETE FROM cms_users c0 WHERE c0.id = ?'
'DELETE FROM cms_users c0_ WHERE c0_.id = ?'
);
}
......@@ -80,12 +80,12 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = ?1 OR u.name = ?2',
'DELETE FROM cms_users c0 WHERE c0.username = ? OR c0.name = ?'
'DELETE FROM cms_users c0_ WHERE c0_.username = ? OR c0_.name = ?'
);
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1 OR ( u.username = ?2 OR u.name = ?3)',
'DELETE FROM cms_users c0 WHERE c0.id = ? OR (c0.username = ? OR c0.name = ?)'
'DELETE FROM cms_users c0_ WHERE c0_.id = ? OR (c0_.username = ? OR c0_.name = ?)'
);
//$this->assertSqlGeneration(
......@@ -98,7 +98,7 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
"delete from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1",
"DELETE FROM cms_users c0 WHERE c0.username = ?"
"DELETE FROM cms_users c0_ WHERE c0_.username = ?"
);
}
......@@ -106,7 +106,7 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = ?1 AND u.name = ?2",
"DELETE FROM cms_users c0 WHERE c0.username = ? AND c0.name = ?"
"DELETE FROM cms_users c0_ WHERE c0_.username = ? AND c0_.name = ?"
);
}
......@@ -114,17 +114,17 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT u.id != ?1",
"DELETE FROM cms_users c0 WHERE NOT c0.id <> ?"
"DELETE FROM cms_users c0_ WHERE NOT c0_.id <> ?"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT ( u.id != ?1 )",
"DELETE FROM cms_users c0 WHERE NOT (c0.id <> ?)"
"DELETE FROM cms_users c0_ WHERE NOT (c0_.id <> ?)"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT ( u.id != ?1 AND u.username = ?2 )",
"DELETE FROM cms_users c0 WHERE NOT (c0.id <> ? AND c0.username = ?)"
"DELETE FROM cms_users c0_ WHERE NOT (c0_.id <> ? AND c0_.username = ?)"
);
}
......@@ -135,32 +135,32 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
// id = ? was already tested (see testDeleteWithWhere())
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ?1",
"DELETE FROM cms_users c0 WHERE c0.id > ?"
"DELETE FROM cms_users c0_ WHERE c0_.id > ?"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id >= ?1",
"DELETE FROM cms_users c0 WHERE c0.id >= ?"
"DELETE FROM cms_users c0_ WHERE c0_.id >= ?"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id < ?1",
"DELETE FROM cms_users c0 WHERE c0.id < ?"
"DELETE FROM cms_users c0_ WHERE c0_.id < ?"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id <= ?1",
"DELETE FROM cms_users c0 WHERE c0.id <= ?"
"DELETE FROM cms_users c0_ WHERE c0_.id <= ?"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id <> ?1",
"DELETE FROM cms_users c0 WHERE c0.id <> ?"
"DELETE FROM cms_users c0_ WHERE c0_.id <> ?"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id != ?1",
"DELETE FROM cms_users c0 WHERE c0.id <> ?"
"DELETE FROM cms_users c0_ WHERE c0_.id <> ?"
);
}
......@@ -168,12 +168,12 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT BETWEEN ?1 AND ?2",
"DELETE FROM cms_users c0 WHERE c0.id NOT BETWEEN ? AND ?"
"DELETE FROM cms_users c0_ WHERE c0_.id NOT BETWEEN ? AND ?"
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id BETWEEN ?1 AND ?2 AND u.username != ?3",
"DELETE FROM cms_users c0 WHERE c0.id BETWEEN ? AND ? AND c0.username <> ?"
"DELETE FROM cms_users c0_ WHERE c0_.id BETWEEN ? AND ? AND c0_.username <> ?"
);
}
......@@ -182,12 +182,12 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
// "WHERE" Expression LikeExpression
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username NOT LIKE ?1',
'DELETE FROM cms_users c0 WHERE c0.username NOT LIKE ?'
'DELETE FROM cms_users c0_ WHERE c0_.username NOT LIKE ?'
);
$this->assertSqlGeneration(
"DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username LIKE ?1 ESCAPE '\\'",
"DELETE FROM cms_users c0 WHERE c0.username LIKE ? ESCAPE '\\'"
"DELETE FROM cms_users c0_ WHERE c0_.username LIKE ? ESCAPE '\\'"
);
}
......@@ -196,12 +196,12 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
// "WHERE" Expression NullComparisonExpression
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IS NULL',
'DELETE FROM cms_users c0 WHERE c0.name IS NULL'
'DELETE FROM cms_users c0_ WHERE c0_.name IS NULL'
);
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IS NOT NULL',
'DELETE FROM cms_users c0 WHERE c0.name IS NOT NULL'
'DELETE FROM cms_users c0_ WHERE c0_.name IS NOT NULL'
);
}
......@@ -209,12 +209,12 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE 1 = 1',
'DELETE FROM cms_users c0 WHERE 1 = 1'
'DELETE FROM cms_users c0_ WHERE 1 = 1'
);
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE ?1 = 1',
'DELETE FROM cms_users c0 WHERE ? = 1'
'DELETE FROM cms_users c0_ WHERE ? = 1'
);
}
......@@ -222,12 +222,12 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN ( ?1, ?2, ?3, ?4 )',
'DELETE FROM cms_users c0 WHERE c0.id IN (?, ?, ?, ?)'
'DELETE FROM cms_users c0_ WHERE c0_.id IN (?, ?, ?, ?)'
);
$this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN ( ?1, ?2 )',
'DELETE FROM cms_users c0 WHERE c0.id NOT IN (?, ?)'
'DELETE FROM cms_users c0_ WHERE c0_.id NOT IN (?, ?)'
);
}
......
......@@ -60,11 +60,11 @@ class UpdateSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1',
'UPDATE cms_users c0 SET c0.name = ?'
'UPDATE cms_users c0_ SET c0_.name = ?'
);
$this->assertSqlGeneration(
'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1, u.username = ?2',
'UPDATE cms_users c0 SET c0.name = ?, c0.username = ?'
'UPDATE cms_users c0_ SET c0_.name = ?, c0_.username = ?'
);
}
......
......@@ -30,7 +30,8 @@ class OrmFunctionalTestCase extends OrmTestCase
'Doctrine\Tests\Models\CMS\CmsUser',
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'Doctrine\Tests\Models\CMS\CmsAddress',
'Doctrine\Tests\Models\CMS\CmsGroup'
'Doctrine\Tests\Models\CMS\CmsGroup',
'Doctrine\Tests\Models\CMS\CmsArticle'
),
'forum' => array(),
'company' => array(),
......@@ -53,6 +54,7 @@ class OrmFunctionalTestCase extends OrmTestCase
$conn->exec('DELETE FROM cms_groups');
$conn->exec('DELETE FROM cms_addresses');
$conn->exec('DELETE FROM cms_phonenumbers');
$conn->exec('DELETE FROM cms_articles');
$conn->exec('DELETE FROM cms_users');
}
$this->_em->clear();
......
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