/**
  * Validates and completes the mapping.
  *
  * @param array $mapping
  * @override
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     if ($this->isOwningSide()) {
         // owning side MUST have a join table
         if (!isset($mapping['joinTable'])) {
             throw MappingException::joinTableRequired($mapping['fieldName']);
         }
         // owning side MUST specify joinColumns
         if (!isset($mapping['joinTable']['joinColumns'])) {
             throw MappingException::invalidMapping($this->_sourceFieldName);
         }
         foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
             $this->sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name'];
             $this->joinTableColumns[] = $joinColumn['name'];
         }
         $this->sourceKeyColumns = array_keys($this->sourceToRelationKeyColumns);
         // owning side MUST specify inverseJoinColumns
         if (!isset($mapping['joinTable']['inverseJoinColumns'])) {
             throw MappingException::invalidMapping($this->_sourceFieldName);
         }
         foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
             $this->targetToRelationKeyColumns[$inverseJoinColumn['referencedColumnName']] = $inverseJoinColumn['name'];
             $this->joinTableColumns[] = $inverseJoinColumn['name'];
         }
         $this->targetKeyColumns = array_keys($this->targetToRelationKeyColumns);
     }
 }
 /**
  * Validates and completes the mapping.
  *
  * @param array $mapping The mapping to validate and complete.
  * @return array The validated and completed mapping.
  * @override
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     // one-side MUST be inverse (must have mappedBy)
     if (!isset($mapping['mappedBy'])) {
         throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
     }
     $this->deleteOrphans = isset($mapping['deleteOrphans']) ? (bool) $mapping['deleteOrphans'] : false;
 }
Beispiel #3
0
 /**
  * Validates and completes the mapping.
  *
  * @param array $mapping The mapping to validate and complete.
  * @return array The validated and completed mapping.
  * @override
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     // OneToMany-side MUST be inverse (must have mappedBy)
     if (!isset($mapping['mappedBy'])) {
         throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
     }
     //TODO: if orphanRemoval, cascade=remove is implicit!
     $this->orphanRemoval = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
     if (isset($mapping['orderBy'])) {
         if (!is_array($mapping['orderBy'])) {
             throw new \InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));
         }
         $this->orderBy = $mapping['orderBy'];
     }
 }
Beispiel #4
0
 /**
  * {@inheritdoc}
  *
  * @param array $mapping  The mapping to validate & complete.
  * @return array  The validated & completed mapping.
  * @override
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     if ($this->isOwningSide()) {
         if (!isset($mapping['joinColumns'])) {
             throw MappingException::invalidMapping($this->_sourceFieldName);
         }
         $this->joinColumns = $mapping['joinColumns'];
         foreach ($mapping['joinColumns'] as $joinColumn) {
             $this->sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
         }
         $this->targetToSourceKeyColumns = array_flip($this->sourceToTargetKeyColumns);
     }
     $this->deleteOrphans = isset($mapping['deleteOrphans']) ? (bool) $mapping['deleteOrphans'] : false;
     return $mapping;
 }
Beispiel #5
0
 /**
  * {@inheritdoc}
  *
  * @param array $mapping  The mapping to validate & complete.
  * @return array  The validated & completed mapping.
  * @override
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
         $this->isOwningSide = true;
     }
     if ($this->isOwningSide) {
         if (!isset($mapping['joinColumns']) || !$mapping['joinColumns']) {
             // Apply default join column
             $mapping['joinColumns'] = array(array('name' => $this->sourceFieldName . '_id', 'referencedColumnName' => 'id'));
         }
         foreach ($mapping['joinColumns'] as $joinColumn) {
             $this->sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
             $this->joinColumnFieldNames[$joinColumn['name']] = isset($joinColumn['fieldName']) ? $joinColumn['fieldName'] : $joinColumn['name'];
         }
         $this->joinColumns = $mapping['joinColumns'];
         $this->targetToSourceKeyColumns = array_flip($this->sourceToTargetKeyColumns);
     }
     //TODO: if orphanRemoval, cascade=remove is implicit!
     $this->orphanRemoval = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
     return $mapping;
 }
 /**
  * Validates and completes the mapping.
  *
  * @param array $mapping
  * @override
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     if ($this->isOwningSide) {
         // owning side MUST have a join table
         if (!isset($mapping['joinTable']) || !$mapping['joinTable']) {
             // Apply default join table
             $sourceShortName = substr($this->sourceEntityName, strrpos($this->sourceEntityName, '\\') + 1);
             $targetShortName = substr($this->targetEntityName, strrpos($this->targetEntityName, '\\') + 1);
             $mapping['joinTable'] = array('name' => $sourceShortName . '_' . $targetShortName, 'joinColumns' => array(array('name' => $sourceShortName . '_id', 'referencedColumnName' => 'id')), 'inverseJoinColumns' => array(array('name' => $targetShortName . '_id', 'referencedColumnName' => 'id')));
             $this->joinTable = $mapping['joinTable'];
         } else {
             if (!isset($mapping['joinTable']['joinColumns'])) {
                 throw MappingException::missingRequiredOption($this->sourceFieldName, 'joinColumns', 'Did you think of case sensitivity / plural s?');
             } else {
                 if (!isset($mapping['joinTable']['inverseJoinColumns'])) {
                     throw MappingException::missingRequiredOption($this->sourceFieldName, 'inverseJoinColumns', 'Did you think of case sensitivity / plural s?');
                 }
             }
         }
         foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
             if ($joinColumn['name'][0] == '`') {
                 $joinColumn['name'] = trim($joinColumn['name'], '`');
                 $joinColumn['quoted'] = true;
             }
             $this->relationToSourceKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
             $this->joinTableColumns[] = $joinColumn['name'];
         }
         foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
             if ($inverseJoinColumn['name'][0] == '`') {
                 $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`');
                 $inverseJoinColumn['quoted'] = true;
             }
             $this->relationToTargetKeyColumns[$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
             $this->joinTableColumns[] = $inverseJoinColumn['name'];
         }
     }
 }
 /**
  * {@inheritdoc}
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     if ($this->isOwningSide) {
         // owning side MUST have a join table
         if (!isset($mapping['joinTable']) || !$mapping['joinTable']) {
             // Apply default join table
             $sourceShortName = substr($this->sourceEntityName, strrpos($this->sourceEntityName, '\\') + 1);
             $targetShortName = substr($this->targetEntityName, strrpos($this->targetEntityName, '\\') + 1);
             $mapping['joinTable'] = array('name' => $sourceShortName . '_' . $targetShortName, 'joinColumns' => array(array('name' => $sourceShortName . '_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')), 'inverseJoinColumns' => array(array('name' => $targetShortName . '_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')));
             $this->joinTable = $mapping['joinTable'];
         } else {
             if (!isset($mapping['joinTable']['joinColumns'])) {
                 throw MappingException::missingRequiredOption($this->sourceFieldName, 'joinColumns', 'Did you think of case sensitivity / plural s?');
             } else {
                 if (!isset($mapping['joinTable']['inverseJoinColumns'])) {
                     throw MappingException::missingRequiredOption($this->sourceFieldName, 'inverseJoinColumns', 'Did you think of case sensitivity / plural s?');
                 }
             }
         }
         foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
             $this->relationToSourceKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
             $this->joinTableColumns[] = $joinColumn['name'];
         }
         foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
             $this->relationToTargetKeyColumns[$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
             $this->joinTableColumns[] = $inverseJoinColumn['name'];
         }
     }
     if (isset($mapping['orderBy'])) {
         if (!is_array($mapping['orderBy'])) {
             throw new \InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));
         }
         $this->orderBy = $mapping['orderBy'];
     }
 }
Beispiel #8
0
 /**
  * Registers the mapping as an inverse mapping, if it is a mapping on the
  * inverse side of an association mapping.
  *
  * @param AssociationMapping The mapping to register as inverse if it is a mapping
  *      for the inverse side of an association.
  */
 private function _registerMappingIfInverse(AssociationMapping $assoc)
 {
     if ($assoc->isInverseSide()) {
         $this->inverseMappings[$assoc->targetEntityName][$assoc->mappedBy] = $assoc;
     }
 }
 /**
  * Determines which fields get serialized.
  *
  * It is only serialized what is necessary for best unserialization performance.
  * That means any metadata properties that are not set or empty or simply have
  * their default value are NOT serialized.
  *
  * @return array The names of all the fields that should be serialized.
  */
 public function __sleep()
 {
     $serialized = parent::__sleep();
     $serialized[] = 'joinColumns';
     $serialized[] = 'joinColumnFieldNames';
     $serialized[] = 'sourceToTargetKeyColumns';
     $serialized[] = 'targetToSourceKeyColumns';
     if ($this->orphanRemoval) {
         $serialized[] = 'orphanRemoval';
     }
     return $serialized;
 }
Beispiel #10
0
 /**
  * Computes the changes of an association.
  *
  * @param AssociationMapping $assoc
  * @param mixed $value The value of the association.
  */
 private function _computeAssociationChanges($assoc, $value)
 {
     if ($value instanceof PersistentCollection && $value->isDirty()) {
         if ($assoc->isOwningSide) {
             $this->_collectionUpdates[] = $value;
         }
         $this->_visitedCollections[] = $value;
     }
     if (!$assoc->isCascadeSave) {
         //echo "NOT CASCADING INTO " . $assoc->getSourceFieldName() . PHP_EOL;
         return;
         // "Persistence by reachability" only if save cascade specified
     }
     // Look through the entities, and in any of their associations, for transient
     // enities, recursively. ("Persistence by reachability")
     if ($assoc->isOneToOne()) {
         $value = array($value);
     }
     $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
     foreach ($value as $entry) {
         $state = $this->getEntityState($entry);
         $oid = spl_object_hash($entry);
         if ($state == self::STATE_NEW) {
             // Get identifier, if possible (not post-insert)
             $idGen = $targetClass->getIdGenerator();
             if (!$idGen->isPostInsertGenerator()) {
                 $idValue = $idGen->generate($this->_em, $entry);
                 $this->_entityStates[$oid] = self::STATE_MANAGED;
                 if (!$idGen instanceof \Doctrine\ORM\Id\Assigned) {
                     $this->_entityIdentifiers[$oid] = array($idValue);
                     $targetClass->getSingleIdReflectionProperty()->setValue($entry, $idValue);
                 } else {
                     $this->_entityIdentifiers[$oid] = $idValue;
                 }
                 $this->addToIdentityMap($entry);
             }
             // Collect the original data and changeset, recursing into associations.
             $data = array();
             $changeSet = array();
             foreach ($targetClass->reflFields as $name => $refProp) {
                 $data[$name] = $refProp->getValue($entry);
                 $changeSet[$name] = array(null, $data[$name]);
                 if (isset($targetClass->associationMappings[$name])) {
                     //echo "RECURSING INTO $name" . PHP_EOL;
                     //TODO: Prevent infinite recursion
                     $this->_computeAssociationChanges($targetClass->associationMappings[$name], $data[$name]);
                 }
             }
             // NEW entities are INSERTed within the current unit of work.
             $this->_entityInsertions[$oid] = $entry;
             $this->_entityChangeSets[$oid] = $changeSet;
             $this->_originalEntityData[$oid] = $data;
         } else {
             if ($state == self::STATE_DELETED) {
                 throw DoctrineException::updateMe("Deleted entity in collection detected during flush." . " Make sure you properly remove deleted entities from collections.");
             }
         }
         // MANAGED associated entities are already taken into account
         // during changeset calculation anyway, since they are in the identity map.
     }
 }
Beispiel #11
0
 /**
  * Computes the changes of an association.
  *
  * @param AssociationMapping $assoc
  * @param mixed $value The value of the association.
  */
 private function _computeAssociationChanges($assoc, $value)
 {
     if ($value instanceof PersistentCollection && $value->isDirty()) {
         if ($assoc->isOwningSide) {
             $this->_collectionUpdates[] = $value;
         }
         $this->_visitedCollections[] = $value;
     }
     if (!$assoc->isCascadePersist) {
         return;
         // "Persistence by reachability" only if persist cascade specified
     }
     // Look through the entities, and in any of their associations, for transient
     // enities, recursively. ("Persistence by reachability")
     if ($assoc->isOneToOne()) {
         if ($value instanceof Proxy && !$value->__isInitialized__) {
             return;
             // Ignore uninitialized proxy objects
         }
         $value = array($value);
     } else {
         if ($value instanceof PersistentCollection) {
             $value = $value->unwrap();
         }
     }
     $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
     foreach ($value as $entry) {
         $state = $this->getEntityState($entry, self::STATE_NEW);
         $oid = spl_object_hash($entry);
         if ($state == self::STATE_NEW) {
             if (isset($targetClass->lifecycleCallbacks[Events::prePersist])) {
                 $targetClass->invokeLifecycleCallbacks(Events::prePersist, $entry);
             }
             if ($this->_evm->hasListeners(Events::prePersist)) {
                 $this->_evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($entry, $this->_em));
             }
             // Get identifier, if possible (not post-insert)
             $idGen = $targetClass->idGenerator;
             if (!$idGen->isPostInsertGenerator()) {
                 $idValue = $idGen->generate($this->_em, $entry);
                 if (!$idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
                     $this->_entityIdentifiers[$oid] = array($targetClass->identifier[0] => $idValue);
                     $targetClass->getSingleIdReflectionProperty()->setValue($entry, $idValue);
                 } else {
                     $this->_entityIdentifiers[$oid] = $idValue;
                 }
                 $this->addToIdentityMap($entry);
             }
             $this->_entityStates[$oid] = self::STATE_MANAGED;
             // NEW entities are INSERTed within the current unit of work.
             $this->_entityInsertions[$oid] = $entry;
             $this->computeChangeSet($targetClass, $entry);
         } else {
             if ($state == self::STATE_REMOVED) {
                 throw ORMException::removedEntityInCollectionDetected($entity, $assoc);
             }
         }
         // MANAGED associated entities are already taken into account
         // during changeset calculation anyway, since they are in the identity map.
     }
 }
 /**
  * Determines which fields get serialized.
  *
  * It is only serialized what is necessary for best unserialization performance.
  * That means any metadata properties that are not set or empty or simply have
  * their default value are NOT serialized.
  *
  * @return array The names of all the fields that should be serialized.
  */
 public function __sleep()
 {
     $serialized = parent::__sleep();
     $serialized[] = 'joinTableColumns';
     $serialized[] = 'relationToSourceKeyColumns';
     $serialized[] = 'relationToTargetKeyColumns';
     if ($this->isOnDeleteCascade) {
         $serialized[] = 'isOnDeleteCascade';
     }
     if ($this->orderBy) {
         $serialized[] = 'orderBy';
     }
     return $serialized;
 }
 /**
  * Determines which fields get serialized.
  *
  * It is only serialized what is necessary for best unserialization performance.
  * That means any metadata properties that are not set or empty or simply have
  * their default value are NOT serialized.
  *
  * @return array The names of all the fields that should be serialized.
  */
 public function __sleep()
 {
     $serialized = parent::__sleep();
     if ($this->orderBy) {
         $serialized[] = 'orderBy';
     }
     if ($this->orphanRemoval) {
         $serialized[] = 'orphanRemoval';
     }
     return $serialized;
 }
 /**
  * {@inheritdoc}
  *
  * @param array $mapping  The mapping to validate & complete.
  * @return array  The validated & completed mapping.
  * @override
  */
 protected function _validateAndCompleteMapping(array $mapping)
 {
     parent::_validateAndCompleteMapping($mapping);
     if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
         $this->isOwningSide = true;
     }
     if ($this->isOwningSide) {
         if (!isset($mapping['joinColumns']) || !$mapping['joinColumns']) {
             // Apply default join column
             $mapping['joinColumns'] = array(array('name' => $this->sourceFieldName . '_id', 'referencedColumnName' => 'id'));
         }
         foreach ($mapping['joinColumns'] as &$joinColumn) {
             if ($joinColumn['name'][0] == '`') {
                 $joinColumn['name'] = trim($joinColumn['name'], '`');
                 $joinColumn['quoted'] = true;
             }
             $this->sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
             $this->joinColumnFieldNames[$joinColumn['name']] = isset($joinColumn['fieldName']) ? $joinColumn['fieldName'] : $joinColumn['name'];
         }
         $this->joinColumns = $mapping['joinColumns'];
         $this->targetToSourceKeyColumns = array_flip($this->sourceToTargetKeyColumns);
     }
     $this->isOptional = isset($mapping['optional']) ? (bool) $mapping['optional'] : true;
     $this->orphanRemoval = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
     /*if ($this->isOptional) {
           $this->fetchMode = self::FETCH_EAGER;
       }*/
     return $mapping;
 }
Beispiel #15
0
 /**
  * Computes the changes of an association.
  *
  * @param AssociationMapping $assoc
  * @param mixed $value The value of the association.
  */
 private function computeAssociationChanges($assoc, $value)
 {
     if ($value instanceof PersistentCollection && $value->isDirty()) {
         if ($assoc->isOwningSide) {
             $this->collectionUpdates[] = $value;
         }
         $this->visitedCollections[] = $value;
     }
     // Look through the entities, and in any of their associations, for transient (new)
     // enities, recursively. ("Persistence by reachability")
     if ($assoc->isOneToOne()) {
         if ($value instanceof Proxy && !$value->__isInitialized__) {
             return;
             // Ignore uninitialized proxy objects
         }
         $value = array($value);
     } else {
         if ($value instanceof PersistentCollection) {
             // Unwrap. Uninitialized collections will simply be empty.
             $value = $value->unwrap();
         }
     }
     $targetClass = $this->em->getClassMetadata($assoc->targetEntityName);
     foreach ($value as $entry) {
         $state = $this->getEntityState($entry, self::STATE_NEW);
         $oid = spl_object_hash($entry);
         if ($state == self::STATE_NEW) {
             if (!$assoc->isCascadePersist) {
                 throw new InvalidArgumentException("A new entity was found through a relationship that was not" . " configured to cascade persist operations: " . self::objToStr($entry) . "." . " Explicitly persist the new entity or configure cascading persist operations" . " on the relationship.");
             }
             $this->persistNew($targetClass, $entry);
             $this->computeChangeSet($targetClass, $entry);
         } else {
             if ($state == self::STATE_REMOVED) {
                 return new InvalidArgumentException("Removed entity detected during flush: " . self::objToStr($removedEntity) . ". Remove deleted entities from associations.");
             } else {
                 if ($state == self::STATE_DETACHED) {
                     // Can actually not happen right now as we assume STATE_NEW,
                     // so the exception will be raised from the DBAL layer (constraint violation).
                     throw new InvalidArgumentException("A detached entity was found through a " . "relationship during cascading a persist operation.");
                 }
             }
         }
         // MANAGED associated entities are already taken into account
         // during changeset calculation anyway, since they are in the identity map.
     }
 }
 /**
  * Gets the conditional SQL fragment used in the WHERE clause when selecting
  * entities in this persister.
  *
  * Subclasses are supposed to override this method if they intend to change
  * or alter the criteria by which entities are selected.
  *
  * @param array $criteria
  * @param AssociationMapping $assoc
  * @return string
  */
 protected function _getSelectConditionSQL(array $criteria, $assoc = null)
 {
     $conditionSql = '';
     foreach ($criteria as $field => $value) {
         $conditionSql .= $conditionSql ? ' AND ' : '';
         if (isset($this->_class->columnNames[$field])) {
             if (isset($this->_class->fieldMappings[$field]['inherited'])) {
                 $conditionSql .= $this->_getSQLTableAlias($this->_class->fieldMappings[$field]['inherited']) . '.';
             } else {
                 $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
             }
             $conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
         } else {
             if ($assoc !== null) {
                 if ($assoc->isManyToMany()) {
                     $owningAssoc = $assoc->isOwningSide ? $assoc : $this->_em->getClassMetadata($assoc->targetEntityName)->associationMappings[$assoc->mappedBy];
                     $conditionSql .= $owningAssoc->getQuotedJoinTableName($this->_platform) . '.' . $field;
                 } else {
                     $conditionSql .= $field;
                 }
             } else {
                 throw ORMException::unrecognizedField($field);
             }
         }
         $conditionSql .= ' = ?';
     }
     return $conditionSql;
 }
 /**
  * Gets the SELECT SQL to select one or more entities by a set of field criteria.
  *
  * @param array $criteria
  * @param AssociationMapping $assoc
  * @param string $orderBy
  * @return string
  * @override
  */
 protected function _getSelectEntitiesSQL(array &$criteria, $assoc = null, $orderBy = null)
 {
     $idColumns = $this->_class->getIdentifierColumnNames();
     $baseTableAlias = $this->_getSQLTableAlias($this->_class);
     if ($this->_selectColumnListSql === null) {
         // Add regular columns
         $columnList = '';
         foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
             if ($columnList != '') {
                 $columnList .= ', ';
             }
             $columnList .= $this->_getSelectColumnSQL($fieldName, isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class);
         }
         // Add foreign key columns
         foreach ($this->_class->associationMappings as $assoc) {
             if ($assoc->isOwningSide && $assoc->isOneToOne()) {
                 $tableAlias = isset($this->_class->inheritedAssociationFields[$assoc->sourceFieldName]) ? $this->_getSQLTableAlias($this->_em->getClassMetadata($this->_class->inheritedAssociationFields[$assoc->sourceFieldName])) : $baseTableAlias;
                 foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
                     $columnAlias = $srcColumn . $this->_sqlAliasCounter++;
                     $columnList .= ", {$tableAlias}.{$srcColumn} AS {$columnAlias}";
                     $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
                     if (!isset($this->_resultColumnNames[$resultColumnName])) {
                         $this->_resultColumnNames[$resultColumnName] = $srcColumn;
                     }
                 }
             }
         }
         // Add discriminator column (DO NOT ALIAS THIS COLUMN).
         $discrColumn = $this->_class->discriminatorColumn['name'];
         if ($this->_class->rootEntityName == $this->_class->name) {
             $columnList .= ", {$baseTableAlias}.{$discrColumn}";
         } else {
             $columnList .= ', ' . $this->_getSQLTableAlias($this->_em->getClassMetadata($this->_class->rootEntityName)) . ".{$discrColumn}";
         }
         $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
         $this->_resultColumnNames[$resultColumnName] = $discrColumn;
     }
     // INNER JOIN parent tables
     $joinSql = '';
     foreach ($this->_class->parentClasses as $parentClassName) {
         $parentClass = $this->_em->getClassMetadata($parentClassName);
         $tableAlias = $this->_getSQLTableAlias($parentClass);
         $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
         $first = true;
         foreach ($idColumns as $idColumn) {
             if ($first) {
                 $first = false;
             } else {
                 $joinSql .= ' AND ';
             }
             $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
         }
     }
     // OUTER JOIN sub tables
     foreach ($this->_class->subClasses as $subClassName) {
         $subClass = $this->_em->getClassMetadata($subClassName);
         $tableAlias = $this->_getSQLTableAlias($subClass);
         if ($this->_selectColumnListSql === null) {
             // Add subclass columns
             foreach ($subClass->fieldMappings as $fieldName => $mapping) {
                 if (isset($mapping['inherited'])) {
                     continue;
                 }
                 $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
             }
             // Add join columns (foreign keys)
             foreach ($subClass->associationMappings as $assoc2) {
                 if ($assoc2->isOwningSide && $assoc2->isOneToOne() && !isset($subClass->inheritedAssociationFields[$assoc2->sourceFieldName])) {
                     foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
                         $columnAlias = $srcColumn . $this->_sqlAliasCounter++;
                         $columnList .= ', ' . $tableAlias . ".{$srcColumn} AS {$columnAlias}";
                         $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
                         if (!isset($this->_resultColumnNames[$resultColumnName])) {
                             $this->_resultColumnNames[$resultColumnName] = $srcColumn;
                         }
                     }
                 }
             }
         }
         // Add LEFT JOIN
         $joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
         $first = true;
         foreach ($idColumns as $idColumn) {
             if ($first) {
                 $first = false;
             } else {
                 $joinSql .= ' AND ';
             }
             $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
         }
     }
     $conditionSql = '';
     foreach ($criteria as $field => $value) {
         if ($conditionSql != '') {
             $conditionSql .= ' AND ';
         }
         if (isset($this->_class->fieldMappings[$field]['inherited'])) {
             $conditionSql .= $this->_getSQLTableAlias($this->_em->getClassMetadata($this->_class->fieldMappings[$field]['inherited'])) . '.';
         } else {
             $conditionSql .= $baseTableAlias . '.';
         }
         if (isset($this->_class->columnNames[$field])) {
             $conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
         } else {
             if ($assoc !== null) {
                 $conditionSql .= $field;
             } else {
                 throw ORMException::unrecognizedField($field);
             }
         }
         $conditionSql .= ' = ?';
     }
     $orderBySql = '';
     if ($orderBy !== null) {
         $orderBySql = $this->_getCollectionOrderBySQL($orderBy, $baseTableAlias);
     }
     if ($this->_selectColumnListSql === null) {
         $this->_selectColumnListSql = $columnList;
     }
     return 'SELECT ' . $this->_selectColumnListSql . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias . $joinSql . ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql;
 }