/** * 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; }
/** * 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']; } }
/** * {@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; }
/** * {@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']; } }
/** * 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; }
/** * 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. } }
/** * 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; }
/** * 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; }