1
0
mirror of synced 2024-12-14 15:16:04 +03:00

Merge branch 'master' of github.com:doctrine/doctrine2

This commit is contained in:
Guilherme Blanco 2011-05-06 01:42:01 -03:00
commit bffca232e2
28 changed files with 632 additions and 157 deletions

View File

@ -272,7 +272,7 @@ abstract class AbstractQuery
* @param boolean $bool
* @param integer $timeToLive
* @param string $resultCacheId
* @return This query instance.
* @return Doctrine\ORM\AbstractQuery This query instance.
*/
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
{

View File

@ -160,11 +160,14 @@ class EntityRepository implements ObjectRepository
* Finds entities by a set of criteria.
*
* @param array $criteria
* @return array
* @param array|null $orderBy
* @param int|null $limit
* @param int|null $offset
* @return array The objects.
*/
public function findBy(array $criteria)
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
{
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria);
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria, $orderBy, $limit, $offset);
}
/**

View File

@ -75,6 +75,10 @@ class ObjectHydrator extends AbstractHydrator
// Remember which associations are "fetch joined", so that we know where to inject
// collection stubs or proxies and where not.
if (isset($this->_rsm->relationMap[$dqlAlias])) {
if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
}
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$sourceClass = $this->_getClassMetadata($sourceClassName);
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];

View File

@ -1628,6 +1628,7 @@ class ClassMetadataInfo implements ClassMetadata
if (strpos($className, '\\') === false && strlen($this->namespace)) {
$className = $this->namespace . '\\' . $className;
}
$className = ltrim($className, '\\');
$this->discriminatorMap[$value] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $value;

View File

@ -67,10 +67,10 @@ class AnnotationDriver implements Driver
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations.
*
* @param $reader The AnnotationReader to use.
* @param AnnotationReader $reader The AnnotationReader to use, duck-typed.
* @param string|array $paths One or multiple paths where mapping classes can be found.
*/
public function __construct(AnnotationReader $reader, $paths = null)
public function __construct($reader, $paths = null)
{
$this->_reader = $reader;
if ($paths) {

View File

@ -67,6 +67,26 @@ class DatabaseDriver implements Driver
$this->_sm = $schemaManager;
}
/**
* Set tables manually instead of relying on the reverse engeneering capabilities of SchemaManager.
*
* @param array $entityTables
* @param array $manyToManyTables
* @return void
*/
public function setTables($entityTables, $manyToManyTables)
{
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($entityTables AS $table) {
$className = Inflector::classify(strtolower($table->getName()));
$this->classToTableNames[$className] = $table->getName();
$this->tables[$table->getName()] = $table;
}
foreach ($manyToManyTables AS $table) {
$this->manyToManyTables[$table->getName()] = $table;
}
}
private function reverseEngineerMappingFromDatabase()
{
if ($this->tables !== null) {
@ -77,7 +97,7 @@ class DatabaseDriver implements Driver
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = array();
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($tables AS $tableName => $table) {
/* @var $table Table */
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
@ -95,11 +115,7 @@ class DatabaseDriver implements Driver
sort($pkColumns);
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns) {
if (count($table->getForeignKeys()) > 2) {
throw new \InvalidArgumentException("ManyToMany table '" . $tableName . "' with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver.");
}
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
$this->manyToManyTables[$tableName] = $table;
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
@ -191,8 +207,10 @@ class DatabaseDriver implements Driver
foreach ($this->manyToManyTables AS $manyTable) {
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
// foreign key maps to the table of the current entity, many to many association probably exists
if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) {
$myFk = $foreignKey;
$otherFk = null;
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
if ($foreignKey != $myFk) {
$otherFk = $foreignKey;
@ -200,6 +218,12 @@ class DatabaseDriver implements Driver
}
}
if (!$otherFk) {
// the definition of this many to many table does not contain
// enough foreign key information to continue reverse engeneering.
continue;
}
$localColumn = current($myFk->getColumns());
$associationMapping = array();
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower(current($otherFk->getColumns()))));

View File

@ -680,12 +680,15 @@ class BasicEntityPersister
* Loads a list of entities by a list of field criteria.
*
* @param array $criteria
* @param array $orderBy
* @param int $limit
* @param int $offset
* @return array
*/
public function loadAll(array $criteria = array())
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
{
$entities = array();
$sql = $this->_getSelectEntitiesSQL($criteria);
$sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy);
list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
@ -831,19 +834,21 @@ class BasicEntityPersister
* @param AssociationMapping $assoc
* @param string $orderBy
* @param int $lockMode
* @param int $limit
* @param int $offset
* @param array $orderBy
* @return string
* @todo Refactor: _getSelectSQL(...)
*/
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null)
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
{
$joinSql = $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ?
$this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBySql = $assoc !== null && isset($assoc['orderBy']) ?
$this->_getCollectionOrderBySQL($assoc['orderBy'], $this->_getSQLTableAlias($this->_class->name))
: '';
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : '';
$lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) {
@ -869,12 +874,12 @@ class BasicEntityPersister
* @return string
* @todo Rename: _getOrderBySQL
*/
protected final function _getCollectionOrderBySQL(array $orderBy, $baseTableAlias)
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
{
$orderBySql = '';
foreach ($orderBy as $fieldName => $orientation) {
if ( ! isset($this->_class->fieldMappings[$fieldName])) {
ORMException::unrecognizedField($fieldName);
throw ORMException::unrecognizedField($fieldName);
}
$tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ?

View File

@ -238,7 +238,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
/**
* {@inheritdoc}
*/
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null)
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
{
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
@ -343,10 +343,8 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBySql = '';
if ($assoc != null && isset($assoc['orderBy'])) {
$orderBySql = $this->_getCollectionOrderBySQL($assoc['orderBy'], $baseTableAlias);
}
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $baseTableAlias) : '';
if ($this->_selectColumnListSql === null) {
$this->_selectColumnListSql = $columnList;

View File

@ -126,7 +126,7 @@ class Lexer extends \Doctrine\Common\Lexer
'[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}',
'(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?',
"'(?:[^']|'')*'",
'\?[1-9][0-9]*|:[a-z][a-z0-9_]+'
'\?[1-9][0-9]*|:[a-z]{1}[a-z0-9_]{0,}'
);
}

View File

@ -234,7 +234,7 @@ class Parser
* If they match, updates the lookahead token; otherwise raises a syntax
* error.
*
* @param int|string token type or value
* @param int token type
* @return void
* @throws QueryException If the tokens dont match.
*/
@ -2308,7 +2308,8 @@ class Parser
if ($peek['value'] == '.') {
return $this->StateFieldPathExpression();
} else if ($peek['value'] == '(') {
return $this->FunctionsReturningStrings();
// do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
return $this->FunctionDeclaration();
} else {
$this->syntaxError("'.' or '('");
}

View File

@ -1,7 +1,5 @@
<?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
@ -393,4 +391,3 @@ class ResultSetMapping
$this->columnOwnerMap[$columnName] = $alias;
}
}

View File

@ -0,0 +1,100 @@
<?php
/*
* 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;
use Doctrine\ORM\EntityManager;
/**
* A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields
*
* @author Michael Ridgway <mcridgway@gmail.com>
* @since 2.1
*/
class ResultSetMappingBuilder extends ResultSetMapping
{
/**
* @var EntityManager
*/
private $em;
/**
* @param EntityManager
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Adds a root entity and all of its fields to the result set.
*
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
*/
public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array())
{
$this->addEntityResult($class, $alias);
$classMetadata = $this->em->getClassMetadata($class);
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
}
$platform = $this->em->getConnection()->getDatabasePlatform();
foreach ($classMetadata->getColumnNames() AS $columnName) {
$propertyName = $classMetadata->getFieldName($columnName);
if (isset($renamedColumns[$columnName])) {
$columnName = $renamedColumns[$columnName];
}
if (isset($this->fieldMappings[$columnName])) {
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
}
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
}
}
/**
* Adds a joined entity and all of its fields to the result set.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param object $relation The association field that connects the parent entity result with the joined entity result.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
*/
public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array())
{
$this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);
$classMetadata = $this->em->getClassMetadata($class);
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
}
$platform = $this->em->getConnection()->getDatabasePlatform();
foreach ($classMetadata->getColumnNames() AS $columnName) {
$propertyName = $classMetadata->getFieldName($columnName);
if (isset($renamedColumns[$columnName])) {
$columnName = $renamedColumns[$columnName];
}
if (isset($this->fieldMappings[$columnName])) {
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
}
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
}
}
}

View File

@ -65,7 +65,7 @@ EOT
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
$output->write('ATTENTION: This operation should not be executed in a production enviroment.' . PHP_EOL . PHP_EOL);
if ($input->getOption('dump-sql') === true) {
$sqls = $schemaTool->getCreateSchemaSql($metadatas);

View File

@ -92,7 +92,7 @@ EOT
}
$output->write('Database schema dropped successfully!' . PHP_EOL);
} else {
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
$output->write('ATTENTION: This operation should not be executed in a production enviroment.' . PHP_EOL . PHP_EOL);
if ($isFullDatabaseDrop) {
$sqls = $schemaTool->getDropDatabaseSQL();

View File

@ -86,7 +86,7 @@ EOT
$schemaTool->updateSchema($metadatas, $saveMode);
$output->write('Database schema updated successfully!' . PHP_EOL);
} else {
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL);
$output->write('ATTENTION: This operation should not be executed in a production enviroment.' . PHP_EOL);
$output->write('Use the incremental update to detect changes during development and use' . PHP_EOL);
$output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL);

View File

@ -179,7 +179,7 @@ public function <methodName>()
$this->_isNew = !file_exists($path) || (file_exists($path) && $this->_regenerateEntityIfExists);
if ( ! $this->_isNew) {
$this->_parseTokensInEntityFile($path);
$this->_parseTokensInEntityFile(file_get_contents($path));
}
if ($this->_backupExisting && file_exists($path)) {
@ -400,24 +400,42 @@ public function <methodName>()
/**
* @todo this won't work if there is a namespace in brackets and a class outside of it.
* @param string $path
* @param string $src
*/
private function _parseTokensInEntityFile($path)
private function _parseTokensInEntityFile($src)
{
$tokens = token_get_all(file_get_contents($path));
$tokens = token_get_all($src);
$lastSeenNamespace = "";
$lastSeenClass = false;
$inNamespace = false;
$inClass = false;
for ($i = 0; $i < count($tokens); $i++) {
$token = $tokens[$i];
if ($token[0] == T_NAMESPACE) {
$lastSeenNamespace = $tokens[$i+2][1] . "\\";
} else if ($token[0] == T_NS_SEPARATOR) {
$lastSeenNamespace .= $tokens[$i+1][1] . "\\";
} else if ($token[0] == T_CLASS) {
$lastSeenClass = $lastSeenNamespace . $tokens[$i+2][1];
if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
continue;
}
if ($inNamespace) {
if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
$lastSeenNamespace .= $token[1];
} else if (is_string($token) && in_array($token, array(';', '{'))) {
$inNamespace = false;
}
}
if ($inClass) {
$inClass = false;
$lastSeenClass = $lastSeenNamespace . '\\' . $token[1];
$this->_staticReflection[$lastSeenClass]['properties'] = array();
$this->_staticReflection[$lastSeenClass]['methods'] = array();
}
if ($token[0] == T_NAMESPACE) {
$lastSeenNamespace = "";
$inNamespace = true;
} else if ($token[0] == T_CLASS) {
$inClass = true;
} else if ($token[0] == T_FUNCTION) {
if ($tokens[$i+2][0] == T_STRING) {
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
@ -502,7 +520,7 @@ public function <methodName>()
}
if ($metadata->isMappedSuperclass) {
$lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSupperClass';
$lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSuperClass';
} else {
$lines[] = ' * @' . $this->_annotationsPrefix . 'Entity';
}

View File

@ -592,74 +592,44 @@ class SchemaTool
}
/**
* Get SQL to drop the tables defined by the passed classes.
*
* @param array $classes
* @return array
*/
public function getDropSchemaSQL(array $classes)
{
/* @var $conn \Doctrine\DBAL\Connection */
$conn = $this->_em->getConnection();
$visitor = new \Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector($this->_platform);
$schema = $this->getSchemaFromMetadata($classes);
/* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */
$sm = $conn->getSchemaManager();
$sql = array();
$orderedTables = array();
foreach ($classes AS $class) {
if ($class->isIdGeneratorSequence() && !$class->isMappedSuperclass && $class->name == $class->rootEntityName && $this->_platform->supportsSequences()) {
$sql[] = $this->_platform->getDropSequenceSQL($class->sequenceGeneratorDefinition['sequenceName']);
$sm = $this->_em->getConnection()->getSchemaManager();
$fullSchema = $sm->createSchema();
foreach ($fullSchema->getTables() AS $table) {
if (!$schema->hasTable($table->getName())) {
foreach ($table->getForeignKeys() AS $foreignKey) {
/* @var $foreignKey \Doctrine\DBAL\Schema\ForeignKeyConstraint */
if ($schema->hasTable($foreignKey->getForeignTableName())) {
$visitor->acceptForeignKey($table, $foreignKey);
}
}
} else {
$visitor->acceptTable($table);
foreach ($table->getForeignKeys() AS $foreignKey) {
$visitor->acceptForeignKey($table, $foreignKey);
}
}
}
$commitOrder = $this->_getCommitOrder($classes);
$associationTables = $this->_getAssociationTables($commitOrder);
// Drop association tables first
foreach ($associationTables as $associationTable) {
if (!in_array($associationTable, $orderedTables)) {
$orderedTables[] = $associationTable;
}
}
// Drop tables in reverse commit order
for ($i = count($commitOrder) - 1; $i >= 0; --$i) {
$class = $commitOrder[$i];
if (($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName)
|| $class->isMappedSuperclass) {
continue;
}
if (!in_array($class->getTableName(), $orderedTables)) {
$orderedTables[] = $class->getTableName();
}
}
$supportsForeignKeyConstraints = $conn->getDatabasePlatform()->supportsForeignKeyConstraints();
$dropTablesSql = array();
foreach ($orderedTables AS $tableName) {
if ($supportsForeignKeyConstraints) {
$foreignKeys = $sm->listTableForeignKeys($tableName);
foreach ($foreignKeys AS $foreignKey) {
$sql[] = $this->_platform->getDropForeignKeySQL($foreignKey, $tableName);
}
}
$dropTablesSql[] = $this->_platform->getDropTableSQL($tableName);
}
return array_merge($sql, $dropTablesSql);
return $visitor->getQueries();
}
/**
* Updates the database schema of the given classes by comparing the ClassMetadata
* ins$tableNametances to the current database schema that is inspected.
* instances to the current database schema that is inspected. If $saveMode is set
* to true the command is executed in the Database, else SQL is returned.
*
* @param array $classes
* @param boolean $saveMode
* @return void
*/
public function updateSchema(array $classes, $saveMode=false)
@ -675,8 +645,11 @@ class SchemaTool
/**
* Gets the sequence of SQL statements that need to be performed in order
* to bring the given class mappings in-synch with the relational schema.
* If $saveMode is set to true the command is executed in the Database,
* else SQL is returned.
*
* @param array $classes The classes to consider.
* @param boolean $saveMode True for writing to DB, false for SQL string
* @return array The sequence of SQL statements.
*/
public function getUpdateSchemaSql(array $classes, $saveMode=false)
@ -695,44 +668,4 @@ class SchemaTool
return $schemaDiff->toSql($this->_platform);
}
}
private function _getCommitOrder(array $classes)
{
$calc = new CommitOrderCalculator;
// Calculate dependencies
foreach ($classes as $class) {
$calc->addClass($class);
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide']) {
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
if ( ! $calc->hasClass($targetClass->name)) {
$calc->addClass($targetClass);
}
// add dependency ($targetClass before $class)
$calc->addDependency($targetClass, $class);
}
}
}
return $calc->getCommitOrder();
}
private function _getAssociationTables(array $classes)
{
$associationTables = array();
foreach ($classes as $class) {
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] == ClassMetadata::MANY_TO_MANY) {
$associationTables[] = $assoc['joinTable']['name'];
}
}
}
return $associationTables;
}
}

View File

@ -406,8 +406,11 @@ class UnitOfWork implements PropertyChangedListener
$actualData = array();
foreach ($class->reflFields as $name => $refProp) {
$value = $refProp->getValue($entity);
if ($class->isCollectionValuedAssociation($name) && $value !== null
if (isset($class->associationMappings[$name])
&& ($class->associationMappings[$name]['type'] & ClassMetadata::TO_MANY)
&& $value !== null
&& ! ($value instanceof PersistentCollection)) {
// If $value is not a Collection then use an ArrayCollection.
if ( ! $value instanceof Collection) {
$value = new ArrayCollection($value);
@ -426,7 +429,7 @@ class UnitOfWork implements PropertyChangedListener
$coll->setDirty( ! $coll->isEmpty());
$class->reflFields[$name]->setValue($entity, $coll);
$actualData[$name] = $coll;
} else if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) {
} else if ( (! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField) ) {
$actualData[$name] = $value;
}
}
@ -474,9 +477,7 @@ class UnitOfWork implements PropertyChangedListener
}
} else if ($isChangeTrackingNotify) {
continue;
} else if (is_object($orgValue) && $orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue, $actualValue);
} else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) {
} else if ($orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue, $actualValue);
}
}

@ -1 +1 @@
Subproject commit ba63ae0f0b6b62a2a8617f01386698730ff2b713
Subproject commit 076a03f8f40b6e08f0ae2f4ee2678474e64b6f59

View File

@ -104,9 +104,42 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey('user', $metadatas['CmsGroups']->associationMappings);
}
public function testIgnoreManyToManyTableWithoutFurtherForeignKeyDetails()
{
$tableB = new \Doctrine\DBAL\Schema\Table("dbdriver_bar");
$tableB->addColumn('id', 'integer');
$tableB->setPrimaryKey(array('id'));
$tableA = new \Doctrine\DBAL\Schema\Table("dbdriver_baz");
$tableA->addColumn('id', 'integer');
$tableA->setPrimaryKey(array('id'));
$tableMany = new \Doctrine\DBAL\Schema\Table("dbdriver_bar_baz");
$tableMany->addColumn('bar_id', 'integer');
$tableMany->addColumn('baz_id', 'integer');
$tableMany->addForeignKeyConstraint('dbdriver_bar', array('bar_id'), array('id'));
$metadatas = $this->convertToClassMetadata(array($tableA, $tableB), array($tableMany));
$this->assertEquals(0, count($metadatas['DbdriverBaz']->associationMappings), "no association mappings should be detected.");
}
protected function convertToClassMetadata(array $entityTables, array $manyTables = array())
{
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($this->_sm);
$driver->setTables($entityTables, $manyTables);
$metadatas = array();
foreach ($driver->getAllClassNames() AS $className) {
$class = new ClassMetadataInfo($className);
$driver->loadMetadataForClass($className, $class);
$metadatas[$className] = $class;
}
return $metadatas;
}
/**
*
* @param string $className
* @return ClassMetadata
*/

View File

@ -320,5 +320,40 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(1, count($params), "Should only execute with one parameter.");
$this->assertEquals(array('romanb'), $params);
}
/**
* @group DDC-1094
*/
public function testFindByLimitOffset()
{
$this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$users1 = $repos->findBy(array(), null, 1, 0);
$users2 = $repos->findBy(array(), null, 1, 1);
$this->assertEquals(2, count($repos->findBy(array())));
$this->assertEquals(1, count($users1));
$this->assertEquals(1, count($users2));
$this->assertNotSame($users1[0], $users2[0]);
}
/**
* @group DDC-1094
*/
public function testFindByOrderBy()
{
$this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$usersAsc = $repos->findBy(array(), array("username" => "ASC"));
$usersDesc = $repos->findBy(array(), array("username" => "DESC"));
$this->assertEquals(2, count($usersAsc), "Pre-condition: only two users in fixture");
$this->assertEquals(2, count($usersDesc), "Pre-condition: only two users in fixture");
$this->assertSame($usersAsc[0], $usersDesc[1]);
$this->assertSame($usersAsc[1], $usersDesc[0]);
}
}

View File

@ -3,9 +3,12 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\Company\CompanyFixContract;
use Doctrine\Tests\Models\Company\CompanyEmployee;
require_once __DIR__ . '/../../TestInit.php';
@ -156,5 +159,111 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertSame($q, $q2);
}
public function testJoinedOneToManyNativeQueryWithRSMBuilder()
{
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'dev';
$phone = new CmsPhonenumber;
$phone->phonenumber = 424242;
$user->addPhonenumber($phone);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', 'phonenumbers');
$query = $this->_em->createNativeQuery('SELECT u.*, p.* FROM cms_users u LEFT JOIN cms_phonenumbers p ON u.id = p.user_id WHERE username = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
$this->assertEquals(1, count($users));
$this->assertTrue($users[0] instanceof CmsUser);
$this->assertEquals('Roman', $users[0]->name);
$this->assertTrue($users[0]->getPhonenumbers() instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($users[0]->getPhonenumbers()->isInitialized());
$this->assertEquals(1, count($users[0]->getPhonenumbers()));
$phones = $users[0]->getPhonenumbers();
$this->assertEquals(424242, $phones[0]->phonenumber);
$this->assertTrue($phones[0]->getUser() === $users[0]);
}
public function testJoinedOneToOneNativeQueryWithRSMBuilder()
{
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'dev';
$addr = new CmsAddress;
$addr->country = 'germany';
$addr->zip = 10827;
$addr->city = 'Berlin';
$user->setAddress($addr);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'u', 'address', array('id' => 'a_id'));
$query = $this->_em->createNativeQuery('SELECT u.*, a.*, a.id AS a_id FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
$this->assertEquals(1, count($users));
$this->assertTrue($users[0] instanceof CmsUser);
$this->assertEquals('Roman', $users[0]->name);
$this->assertTrue($users[0]->getPhonenumbers() instanceof \Doctrine\ORM\PersistentCollection);
$this->assertFalse($users[0]->getPhonenumbers()->isInitialized());
$this->assertTrue($users[0]->getAddress() instanceof CmsAddress);
$this->assertTrue($users[0]->getAddress()->getUser() == $users[0]);
$this->assertEquals('germany', $users[0]->getAddress()->getCountry());
$this->assertEquals(10827, $users[0]->getAddress()->getZipCode());
$this->assertEquals('Berlin', $users[0]->getAddress()->getCity());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRSMBuilderThrowsExceptionOnColumnConflict()
{
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'u', 'address');
}
/**
* @group PR-39
*/
public function testUnknownParentAliasThrowsException()
{
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'un', 'address', array('id' => 'a_id'));
$query = $this->_em->createNativeQuery('SELECT u.*, a.*, a.id AS a_id FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm);
$query->setParameter(1, 'romanb');
$this->setExpectedException(
"Doctrine\ORM\Internal\Hydration\HydrationException",
"The parent object of entity result with alias 'a' was not found. The parent alias is 'un'."
);
$users = $query->getResult();
}
}

View File

@ -50,4 +50,19 @@ class CompanySchemaTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($table->getColumn('pricePerHour')->getNotnull());
$this->assertFalse($table->getColumn('maxPrice')->getNotnull());
}
/**
* @group DBAL-115
*/
public function testDropPartSchemaWithForeignKeys()
{
if (!$this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$this->markTestSkipped("Foreign Key test");
}
$sql = $this->_schemaTool->getDropSchemaSQL(array(
$this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyManager'),
));
$this->assertEquals(3, count($sql));
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1043
*/
class DDC1043Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testChangeSetPlusWeirdPHPCastingIntCastingRule()
{
$user = new \Doctrine\Tests\Models\CMS\CmsUser();
$user->name = "John Galt";
$user->username = "jgalt";
$user->status = "+44";
$this->_em->persist($user);
$this->_em->flush();
$user->status = "44";
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->find("Doctrine\Tests\Models\CMS\CmsUser", $user->id);
$this->assertSame("44", $user->status);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1129
*/
class DDC1129Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testVersionFieldIgnoredInChangesetComputation()
{
$article = new \Doctrine\Tests\Models\CMS\CmsArticle();
$article->text = "I don't know.";
$article->topic = "Who is John Galt?";
$this->_em->persist($article);
$this->_em->flush();
$this->assertEquals(1, $article->version);
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsArticle');
$uow = $this->_em->getUnitOfWork();
$uow->computeChangeSet($class, $article);
$changeSet = $uow->getEntityChangeSet($article);
$this->assertEquals(0, count($changeSet), "No changesets should be computed.");
$article->text = "This is John Galt speaking.";
$this->_em->flush();
$this->assertEquals(2, $article->version);
$uow->computeChangeSet($class, $article);
$changeSet = $uow->getEntityChangeSet($article);
$this->assertEquals(0, count($changeSet), "No changesets should be computed.");
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace Doctrine\Tests\ORM\Performance;
require_once __DIR__ . '/../../TestInit.php';
use Doctrine\Tests\Models\CMS\CmsUser;
/**
* Description of InsertPerformanceTest
*
* @author robo
*/
class UnitOfWorkPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
{
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testComputeChanges()
{
$n = 100;
$users = array();
for ($i=1; $i<=$n; ++$i) {
$user = new CmsUser;
$user->status = 'user';
$user->username = 'user' . $i;
$user->name = 'Mr.Smith-' . $i;
$this->_em->persist($user);
$users[] = $user;
}
$this->_em->flush();
foreach ($users AS $user) {
$user->status = 'other';
$user->username = $user->username . '++';
$user->name = str_replace('Mr.', 'Mrs.', $user->name);
}
$s = microtime(true);
$this->_em->flush();
$e = microtime(true);
echo ' Compute ChangeSet '.$n.' objects in ' . ($e - $s) . ' seconds' . PHP_EOL;
}
}

View File

@ -482,6 +482,16 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$this->assertValidDQL('SELECT u, u.id + ?1 AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u');
}
/**
* @group DDC-1091
*/
public function testCustomFunctionsReturningStringInStringPrimary()
{
$this->_em->getConfiguration()->addCustomStringFunction('CC', 'Doctrine\ORM\Query\AST\Functions\ConcatFunction');
$this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CC('%', u.name) LIKE '%foo%'", true);
}
/**
* @group DDC-505
*/
@ -513,6 +523,14 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$this->assertInvalidDQL('SELECT g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g');
}
/**
* @group DDC-1108
*/
public function testInputParameterSingleChar()
{
$this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :q');
}
/**
* @group DDC-1053
*/

View File

@ -200,6 +200,54 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals($cm->idGenerator, $metadata->idGenerator);
$this->assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName);
}
/**
* @dataProvider getParseTokensInEntityFileData
*/
public function testParseTokensInEntityFile($php, $classes)
{
$r = new \ReflectionObject($this->_generator);
$m = $r->getMethod('_parseTokensInEntityFile');
$m->setAccessible(true);
$p = $r->getProperty('_staticReflection');
$p->setAccessible(true);
$ret = $m->invoke($this->_generator, $php);
$this->assertEquals($classes, array_keys($p->getValue($this->_generator)));
}
public function getParseTokensInEntityFileData()
{
return array(
array(
'<?php namespace Foo\Bar; class Baz {}',
array('Foo\Bar\Baz'),
),
array(
'<?php namespace Foo\Bar; use Foo; class Baz {}',
array('Foo\Bar\Baz'),
),
array(
'<?php namespace /*Comment*/ Foo\Bar; /** Foo */class /* Comment */ Baz {}',
array('Foo\Bar\Baz'),
),
array(
'
<?php namespace
/*Comment*/
Foo\Bar
;
/** Foo */
class
/* Comment */
Baz {}
',
array('Foo\Bar\Baz'),
),
);
}
}
class EntityGeneratorAuthor {}