Commit e578bad6 authored by romanb's avatar romanb

[2.0] Fixed several referential integrity issues. Fixed critical issue with...

[2.0] Fixed several referential integrity issues. Fixed critical issue with inserts being run twice on postgresql/oracle. Added support for additional tree walkers that modify the AST prior to SQL construction and started to play with it in a testcase.
parent 8452108e
......@@ -97,7 +97,7 @@
</fileset>
</batchtest>
</phpunit>
<!-- <phpunitreport infile="build/logs/testsuites.xml" format="frames" todir="reports/tests" />-->
<phpunitreport infile="build/logs/testsuites.xml" format="frames" todir="reports/tests" />
</target>
<!--
......
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.doctrine-project.org/orm/doctrine-mapping"
xmlns:orm="http://schemas.doctrine-project.org/orm/doctrine-mapping"
targetNamespace="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:orm="http://doctrine-project.org/schemas/orm/doctrine-mapping"
elementFormDefault="qualified">
<xs:annotation>
......
This diff is collapsed.
......@@ -87,7 +87,7 @@ class Configuration
public function setCustomTypes(array $types, $override = false)
{
foreach ($types as $name => $typeClassName) {
$method = (Type::hasType($name) ? 'override' : 'add') . 'Type';
$method = (Type::hasType($name) && $override ? 'override' : 'add') . 'Type';
Type::$method($name, $typeClassName);
}
......
......@@ -47,7 +47,6 @@ class Driver implements \Doctrine\DBAL\Driver
$password,
$driverOptions
);
$conn->setAttribute(Connection::ATTR_AUTOCOMMIT, false);
return $conn;
}
......
......@@ -141,7 +141,7 @@ interface Statement
* bound parameters in the SQL statement being executed.
* @return boolean Returns TRUE on success or FALSE on failure.
*/
function execute($params = null);
function execute($params = array());
/**
* fetch
......
......@@ -585,19 +585,18 @@ class EntityManager
*/
public static function create($conn, Configuration $config = null, EventManager $eventManager = null)
{
$config = $config ?: new Configuration();
if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager);
} else if ( ! $conn instanceof Connection) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager()));
} else if ($conn instanceof Connection) {
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw DoctrineException::updateMe("Cannot use different EventManagers for EntityManager and Connection.");
}
} else {
throw DoctrineException::updateMe("Invalid parameter '$conn'.");
}
if ($config === null) {
$config = new Configuration();
}
if ($eventManager === null) {
$eventManager = new EventManager();
}
return new EntityManager($conn, $config, $eventManager);
return new EntityManager($conn, $config, $conn->getEventManager());
}
}
......@@ -26,13 +26,12 @@ namespace Doctrine\ORM;
* business specific methods for retrieving entities.
*
* This class is designed for inheritance and users can subclass this class to
* write their own repositories.
* write their own repositories with business-specific methods to locate entities.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityRepository
{
......@@ -53,21 +52,6 @@ class EntityRepository
$this->_class = $class;
}
/**
* Creates a new Doctrine_Query object and adds the component name
* of this table as the query 'from' part.
*
* @param string Optional alias name for component aliasing.
* @return Doctrine_Query
*/
protected function _createQuery($alias = '')
{
if ( ! empty($alias)) {
$alias = ' ' . trim($alias);
}
return $this->_em->createQuery()->from($this->_entityName . $alias);
}
/**
* Clears the repository, causing all managed entities to become detached.
*/
......@@ -81,9 +65,9 @@ class EntityRepository
*
* @param $id The identifier.
* @param int $hydrationMode The hydration mode to use.
* @return mixed Array or Object or false if no result.
* @return object The entity.
*/
public function find($id, $hydrationMode = null)
public function find($id)
{
// Check identity map first
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
......@@ -102,48 +86,41 @@ class EntityRepository
* Finds all entities in the repository.
*
* @param int $hydrationMode
* @return mixed
* @return array The entities.
*/
public function findAll($hydrationMode = null)
public function findAll()
{
return $this->_createQuery()->execute(array(), $hydrationMode);
return $this->findBy(array());
}
/**
* findBy
* Finds entities by a set of criteria.
*
* @param string $column
* @param string $value
* @param string $hydrationMode
* @return void
* @return array
*/
protected function findBy($fieldName, $value, $hydrationMode = null)
public function findBy(array $criteria)
{
return $this->_createQuery()->where($fieldName . ' = ?')->execute(array($value), $hydrationMode);
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria);
}
/**
* findOneBy
* Finds a single entity by a set of criteria.
*
* @param string $column
* @param string $value
* @param string $hydrationMode
* @return void
* @param string $value
* @return object
*/
protected function findOneBy($fieldName, $value, $hydrationMode = null)
public function findOneBy(array $criteria)
{
$results = $this->_createQuery()->where($fieldName . ' = ?')
->setMaxResults(1)
->execute(array($value), $hydrationMode);
return $hydrationMode === Query::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst();
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria);
}
/**
* Adds support for magic finders.
* findByColumnName, findByRelationAlias
* findById, findByContactId, etc.
*
* @return void
* @return array|object The found entity/entities.
* @throws BadMethodCallException If the method called is an invalid find* method
* or no find* method at all and therefore an invalid
* method call.
......@@ -160,25 +137,16 @@ class EntityRepository
throw new BadMethodCallException("Undefined method '$method'.");
}
if (isset($by)) {
if ( ! isset($arguments[0])) {
throw DoctrineException::updateMe('You must specify the value to findBy.');
}
$fieldName = Doctrine::tableize($by);
$hydrationMode = isset($arguments[1]) ? $arguments[1]:null;
if ($this->_class->hasField($fieldName)) {
return $this->$method($fieldName, $arguments[0], $hydrationMode);
} else if ($this->_class->hasRelation($by)) {
$relation = $this->_class->getRelation($by);
if ($relation['type'] === Doctrine_Relation::MANY) {
throw DoctrineException::updateMe('Cannot findBy many relationship.');
}
return $this->$method($relation['local'], $arguments[0], $hydrationMode);
} else {
throw DoctrineException::updateMe('Cannot find by: ' . $by . '. Invalid field or relationship alias.');
}
if ( ! isset($arguments[0])) {
throw DoctrineException::updateMe('You must specify the value to findBy.');
}
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
if ($this->_class->hasField($fieldName)) {
return $this->$method(array($fieldName => $arguments[0]));
} else {
throw \Doctrine\Common\DoctrineException::updateMe('Cannot find by: ' . $by . '. Invalid field.');
}
}
}
\ No newline at end of file
......@@ -207,11 +207,11 @@ class ObjectHydrator extends AbstractHydrator
);
$pColl->setOwner($entity, $assoc);
$reflField->setValue($entity, $pColl);
if ( ! $assoc->isLazilyFetched()) {
if ($assoc->isLazilyFetched()) {
$pColl->setInitialized(false);
} else {
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, $pColl, $this->_em);
} else {
$pColl->setInitialized(false);
}
}
}
......
......@@ -415,6 +415,28 @@ class StandardEntityPersister
return $this->_createEntity($result, $entity);
}
/**
* Loads all entities by a list of field criteria.
*
* @param array $criteria
* @return array
*/
public function loadAll(array $criteria = array())
{
$entities = array();
$stmt = $this->_conn->prepare($this->_getSelectEntitiesSql($criteria));
$stmt->execute(array_values($criteria));
$result = $stmt->fetchAll(Connection::FETCH_ASSOC);
$stmt->closeCursor();
foreach ($result as $row) {
$entities[] = $this->_createEntity($row);
}
return $entities;
}
/**
* Loads a collection of entities into a one-to-many association.
*
......@@ -570,7 +592,7 @@ class StandardEntityPersister
return 'SELECT ' . $columnList
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform)
. $joinSql
. ' WHERE ' . $conditionSql;
. ($conditionSql ? ' WHERE ' . $conditionSql : '');
}
/**
......
......@@ -21,8 +21,8 @@
namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata;
/**
* The ProxyClassGenerator is used to generate proxy objects for entities at runtime.
......@@ -42,7 +42,8 @@ class ProxyClassGenerator
* Generates and stores proxy class files in the given cache directory.
*
* @param EntityManager $em
* @param string $cacheDir
* @param string $cacheDir The directory where generated proxy classes will be saved.
* If not set, sys_get_temp_dir() is used.
*/
public function __construct(EntityManager $em, $cacheDir = null)
{
......@@ -84,7 +85,7 @@ class ProxyClassGenerator
return $this->_generateClass($originalClassName, $proxyClassName, self::$_assocProxyClassTemplate);
}
protected function _generateClass($originalClassName, $proxyClassName, $file)
private function _generateClass($originalClassName, $proxyClassName, $file)
{
$proxyFullyQualifiedClassName = self::$_ns . $proxyClassName;
......@@ -124,7 +125,7 @@ class ProxyClassGenerator
return $proxyFullyQualifiedClassName;
}
protected function _generateMethods(ClassMetadata $class)
private function _generateMethods(ClassMetadata $class)
{
$methods = '';
......@@ -165,7 +166,7 @@ class ProxyClassGenerator
return $methods;
}
public function _generateSleep(ClassMetadata $class)
private function _generateSleep(ClassMetadata $class)
{
$sleepImpl = '';
......
......@@ -21,8 +21,8 @@
namespace Doctrine\ORM;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\Parser,
Doctrine\ORM\Query\QueryException;
/**
* A Query object represents a DQL query.
......@@ -75,6 +75,7 @@ final class Query extends AbstractQuery
*/
const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
//const HINT_READ_ONLY = 'doctrine.readOnly';
/**
......@@ -169,6 +170,8 @@ final class Query extends AbstractQuery
// Check query cache
if ($queryCache = $this->getQueryCacheDriver()) {
// Calculate hash for dql query.
// TODO: Probably need to include query hints in hash calculation, because query hints
// can have influence on the SQL.
$hash = md5($this->getDql() . 'DOCTRINE_QUERY_CACHE_SALT');
$cached = ($this->_expireQueryCache) ? false : $queryCache->fetch($hash);
......
......@@ -42,11 +42,6 @@ class QuantifiedExpression extends Node
$this->subselect = $subselect;
}
public function getSubselect()
{
return $this->_subselect;
}
public function isAll()
{
return strtoupper($this->type) == 'ALL';
......
......@@ -116,11 +116,18 @@ class Parser
private $_nestingLevel = 0;
/**
* Tree walker chain
* Any additional custom tree walkers that modify the AST.
*
* @var array
*/
private $_customTreeWalkers = array();
/**
* The custom last tree walker, if any, that is responsible for producing the output.
*
* @var TreeWalker
*/
private $_treeWalker = 'Doctrine\ORM\Query\SqlWalker';
private $_customOutputWalker;
/**
* Creates a new query parser object.
......@@ -136,13 +143,24 @@ class Parser
}
/**
* Sets the custom tree walker.
* Sets a custom tree walker that produces output.
* This tree walker will be run last over the AST, after any other walkers.
*
* @param string $className
*/
public function setCustomOutputTreeWalker($className)
{
$this->_customOutputWalker = $className;
}
/**
* Adds a custom tree walker for modifying the AST.
*
* @param string $treeWalker
* @param string $className
*/
public function setTreeWalker($treeWalker)
public function addCustomTreeWalker($className)
{
$this->_treeWalker = $treeWalker;
$this->_customTreeWalkers[] = $className;
}
/**
......@@ -262,26 +280,38 @@ class Parser
if ($this->_lexer->lookahead !== null) {
$this->syntaxError('end of string');
}
if ($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) {
$this->_customTreeWalkers = $customWalkers;
}
// Create TreeWalker who creates the SQL from the AST
$treeWalker = new $this->_treeWalker(
$this->_query, $this->_parserResult, $this->_queryComponents
);
/*if ($this->_treeWalkers) {
// We got additional walkers, so build a chain.
$treeWalker = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents);
foreach ($this->_treeWalkers as $walker) {
$treeWalker->addTreeWalker(new $walker($this->_query, $this->_parserResult, $this->_queryComponents));
// Run any custom tree walkers over the AST
if ($this->_customTreeWalkers) {
$treeWalkerChain = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents);
foreach ($this->_customTreeWalkers as $walker) {
$treeWalkerChain->addTreeWalker($walker);
}
if ($AST instanceof AST\SelectStatement) {
$treeWalkerChain->walkSelectStatement($AST);
} else if ($AST instanceof AST\UpdateStatement) {
$treeWalkerChain->walkUpdateStatement($AST);
} else {
$treeWalkerChain->walkDeleteStatement($AST);
}
$treeWalker->setLastTreeWalker('Doctrine\ORM\Query\SqlWalker');
}
if ($this->_customOutputWalker) {
$outputWalker = new $this->_customOutputWalker(
$this->_query, $this->_parserResult, $this->_queryComponents
);
} else {
$treeWalker = new SqlWalker(
$outputWalker = new SqlWalker(
$this->_query, $this->_parserResult, $this->_queryComponents
);
}*/
}
// Assign an SQL executor to the parser result
$this->_parserResult->setSqlExecutor($treeWalker->getExecutor($AST));
$this->_parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
return $this->_parserResult;
}
......
......@@ -30,8 +30,6 @@ use Doctrine\ORM\Query,
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @todo Code review for schema usage with table names.
* (Prepend schema name to tables IF schema is defined AND platform supports them)
*/
class SqlWalker implements TreeWalker
{
......@@ -417,14 +415,7 @@ class SqlWalker implements TreeWalker
$sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.';
}
if (isset($class->associationMappings[$fieldName])) {
//FIXME: Inverse side support
//FIXME: Throw exception on composite key
$assoc = $class->associationMappings[$fieldName];
$sql .= $assoc->getQuotedJoinColumnName($assoc->joinColumns[0]['name'], $this->_platform);
} else {
$sql .= $class->getQuotedColumnName($fieldName, $this->_platform);
}
$sql .= $class->getQuotedColumnName($fieldName, $this->_platform);
break;
case AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION:
......@@ -604,7 +595,7 @@ class SqlWalker implements TreeWalker
*/
public function walkHavingClause($havingClause)
{
$condExpr = $havingClause->getConditionalExpression();
$condExpr = $havingClause->conditionalExpression;
return ' HAVING ' . implode(
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
......@@ -846,7 +837,7 @@ class SqlWalker implements TreeWalker
public function walkQuantifiedExpression($qExpr)
{
return ' ' . strtoupper($qExpr->type)
. '(' . $this->walkSubselect($qExpr->getSubselect()) . ')';
. '(' . $this->walkSubselect($qExpr->subselect) . ')';
}
/**
......@@ -921,7 +912,6 @@ class SqlWalker implements TreeWalker
if ($expr instanceof AST\PathExpression) {
$sql .= ' ' . $this->walkPathExpression($expr);
//...
} else if ($expr instanceof AST\AggregateExpression) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarAliasCounter++;
......@@ -932,8 +922,8 @@ class SqlWalker implements TreeWalker
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
} else {
// IdentificationVariable
// FIXME: Composite key support, or select all columns? Does that make
// in a subquery?
// FIXME: Composite key support, or select all columns? Does that make sense
// in a subquery?
$class = $this->_queryComponents[$expr]['metadata'];
$sql .= ' ' . $this->getSqlTableAlias($class->getTableName(), $expr) . '.'
. $class->getQuotedColumnName($class->identifier[0], $this->_platform);
......@@ -1420,7 +1410,7 @@ class SqlWalker implements TreeWalker
if ($leftExpr instanceof AST\Node) {
$sql .= $leftExpr->dispatch($this);
} else {
$sql .= $this->_conn->quote($leftExpr);
$sql .= is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr);
}
$sql .= ' ' . $compExpr->operator . ' ';
......@@ -1428,7 +1418,7 @@ class SqlWalker implements TreeWalker
if ($rightExpr instanceof AST\Node) {
$sql .= $rightExpr->dispatch($this);
} else {
$sql .= $this->_conn->quote($rightExpr);
$sql .= is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr);
}
return $sql;
......
......@@ -30,10 +30,24 @@ namespace Doctrine\ORM\Query;
*/
abstract class TreeWalkerAdapter implements TreeWalker
{
private $_query;
private $_parserResult;
private $_queryComponents;
/**
* @inheritdoc
*/
public function __construct($query, $parserResult, array $queryComponents) {}
public function __construct($query, $parserResult, array $queryComponents)
{
$this->_query = $query;
$this->_parserResult = $parserResult;
$this->_queryComponents = $queryComponents;
}
protected function _getQueryComponents()
{
return $this->_queryComponents;
}
/**
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
......@@ -366,4 +380,11 @@ abstract class TreeWalkerAdapter implements TreeWalker
* @return string The SQL.
*/
public function walkPathExpression($pathExpr) {}
/**
* Gets an executor that can be used to execute the result of this walker.
*
* @return AbstractExecutor
*/
public function getExecutor($AST) {}
}
\ No newline at end of file
This diff is collapsed.
......@@ -158,13 +158,6 @@ class UnitOfWork implements PropertyChangedListener
*/
private $_collectionDeletions = array();
/**
* All pending collection creations.
*
* @var array
*/
//private $_collectionCreations = array();
/**
* All pending collection updates.
*
......@@ -304,7 +297,6 @@ class UnitOfWork implements PropertyChangedListener
$this->getCollectionPersister($collectionToUpdate->getMapping())
->update($collectionToUpdate);
}
//TODO: collection recreations (insertions of complete collections)
// Entity deletions come last and need to be in reverse commit order
if ($this->_entityDeletions) {
......@@ -377,7 +369,7 @@ class UnitOfWork implements PropertyChangedListener
public function computeChangeSets()
{
// Compute changes for INSERTed entities first. This must always happen.
foreach ($this->_entityInsertions as $entity) {
foreach ($this->_entityInsertions as $oid => $entity) {
$class = $this->_em->getClassMetadata(get_class($entity));
$this->_computeEntityChanges($class, $entity);
// Look for changes in associations of the entity
......@@ -403,9 +395,9 @@ class UnitOfWork implements PropertyChangedListener
$this->_scheduledForDirtyCheck[$className] : $entities;
foreach ($entitiesToProcess as $entity) {
// Only MANAGED entities that are NOT INSERTED are processed here.
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
$oid = spl_object_hash($entity);
if (isset($this->_entityStates[$oid]) && ! isset($entityInsertions[$oid])) {
if ( ! isset($this->_entityInsertions[$oid]) && isset($this->_entityStates[$oid])) {
$this->_computeEntityChanges($class, $entity);
// Look for changes in associations of the entity
foreach ($class->associationMappings as $assoc) {
......@@ -581,7 +573,6 @@ class UnitOfWork implements PropertyChangedListener
$data[$name] = $refProp->getValue($entry);
$changeSet[$name] = array(null, $data[$name]);
if (isset($targetClass->associationMappings[$name])) {
//TODO: Prevent infinite recursion
$this->_computeAssociationChanges($targetClass->associationMappings[$name], $data[$name]);
}
}
......@@ -600,7 +591,7 @@ class UnitOfWork implements PropertyChangedListener
}
/**
* INTERNAL, EXPERIMENTAL:
* INTERNAL:
* Computes the changeset of an individual entity, independently of the
* computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
*
......@@ -810,7 +801,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $this->_commitOrderCalculator->hasClass($targetClass->name)) {
$this->_commitOrderCalculator->addClass($targetClass);
}
// add dependency
// add dependency ($targetClass before $class)
$this->_commitOrderCalculator->addDependency($targetClass, $class);
}
}
......@@ -823,7 +814,7 @@ class UnitOfWork implements PropertyChangedListener
* Schedules an entity for insertion into the database.
* If the entity already has an identifier, it will be added to the identity map.
*
* @param object $entity
* @param object $entity The entity to schedule for insertion.
*/
public function scheduleForInsert($entity)
{
......@@ -840,13 +831,14 @@ class UnitOfWork implements PropertyChangedListener
}
$this->_entityInsertions[$oid] = $entity;
if (isset($this->_entityIdentifiers[$oid])) {
$this->addToIdentityMap($entity);
}
}
/**
* Checks whether an entity is registered as new on this unit of work.
* Checks whether an entity is scheduled for insertion.
*
* @param object $entity
* @return boolean
......@@ -857,9 +849,9 @@ class UnitOfWork implements PropertyChangedListener
}
/**
* Registers a dirty entity.
* Schedules an entity for being updated.
*
* @param object $entity
* @param object $entity The entity to schedule for being updated.
*/
public function scheduleForUpdate($entity)
{
......@@ -880,7 +872,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* INTERNAL:
* Schedules an extra update that will be executed immediately after the
* regular entity updates.
* regular entity updates within the currently running commit cycle.
*
* @param $entity
* @param $changeset
......@@ -1588,7 +1580,6 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityUpdates =
$this->_entityDeletions =
$this->_collectionDeletions =
//$this->_collectionCreations =
$this->_collectionUpdates =
$this->_orphanRemovals = array();
$this->_commitOrderCalculator->clear();
......@@ -1778,7 +1769,7 @@ class UnitOfWork implements PropertyChangedListener
*/
public function tryGetById($id, $rootClassName)
{
$idHash = implode(' ', (array)$id);
$idHash = implode(' ', (array) $id);
if (isset($this->_identityMap[$rootClassName][$idHash])) {
return $this->_identityMap[$rootClassName][$idHash];
}
......@@ -1924,23 +1915,4 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityUpdates[$oid] = $entity;
}
}
public function dump()
{
var_dump($this->_identityMap);
var_dump($this->_entityIdentifiers);
var_dump($this->_originalEntityData);
var_dump($this->_entityChangeSets);
var_dump($this->_entityStates);
var_dump($this->_scheduledForDirtyCheck);
var_dump($this->_entityInsertions);
var_dump($this->_entityUpdates);
var_dump($this->_entityDeletions);
var_dump($this->_collectionDeletions);
//$this->_collectionCreations =
var_dump($this->_collectionUpdates);
var_dump($this->_orphanRemovals);
//var_dump($this->_commitOrderCalculator->clear();
}
}
......@@ -23,7 +23,6 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\EntityManagerTest');
$suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest');
$suite->addTestSuite('Doctrine\Tests\ORM\QueryBuilderTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Proxy\ProxyClassGeneratorTest');
$suite->addTest(Query\AllTests::suite());
$suite->addTest(Hydration\AllTests::suite());
$suite->addTest(Entity\AllTests::suite());
......@@ -32,6 +31,7 @@ class AllTests
$suite->addTest(Mapping\AllTests::suite());
$suite->addTest(Functional\AllTests::suite());
$suite->addTest(Id\AllTests::suite());
$suite->addTest(Proxy\AllTests::suite());
return $suite;
}
......
......@@ -123,7 +123,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
}
public function testBasicManyToMany()
{
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
......
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Query;
require_once __DIR__ . '/../../TestInit.php';
/**
* Test case for custom AST walking and modification.
*
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org
* @since 2.0
*/
class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
$this->useModelSet('cms');
parent::setUp();
}
public function testSupportsQueriesWithoutWhere()
{
$q = $this->_em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName');
$q->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker'));
$this->assertEquals("SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1", $q->getSql());
$q->setDql('select u from Doctrine\Tests\Models\CMS\CmsUser u');
$this->assertEquals("SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = 1", $q->getSql());
$q->setDql('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name');
$this->assertEquals("SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1", $q->getSql());
}
}
class CustomTreeWalker extends Query\TreeWalkerAdapter
{
public function walkSelectStatement(Query\AST\SelectStatement $selectStatement)
{
// Get the DQL aliases of all the classes we want to modify
$dqlAliases = array();
foreach ($this->_getQueryComponents() as $dqlAlias => $comp) {
// Hard-coded check just for demonstration: We want to modify the query if
// it involves the CmsUser class.
if ($comp['metadata']->name == 'Doctrine\Tests\Models\CMS\CmsUser') {
$dqlAliases[] = $dqlAlias;
}
}
// Create our conditions for all involved classes
$factors = array();
foreach ($dqlAliases as $alias) {
$pathExpr = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, $alias, array('id'));
$pathExpr->type = Query\AST\PathExpression::TYPE_STATE_FIELD;
$comparisonExpr = new Query\AST\ComparisonExpression($pathExpr, '=', 1);
$condPrimary = new Query\AST\ConditionalPrimary;
$condPrimary->simpleConditionalExpression = $comparisonExpr;
$factor = new Query\AST\ConditionalFactor($condPrimary);
$factors[] = $factor;
}
if ($selectStatement->whereClause !== null) {
// There is already a WHERE clause, so append the conditions
$existingTerms = $selectStatement->whereClause->conditionalExpression->conditionalTerms;
if (count($existingTerms) > 1) {
// More than one term, so we need to wrap all these terms in a single root term
// i.e: "WHERE u.name = :foo or u.other = :bar" => "WHERE (u.name = :foo or u.other = :bar) AND <our condition>"
$primary = new Query\AST\ConditionalPrimary;
$primary->conditionalExpression = new Query\AST\ConditionalExpression($existingTerms);
$existingFactor = new Query\AST\ConditionalFactor($primary);
$term = new Query\AST\ConditionalTerm(array_merge(array($existingFactor), $factors));
$selectStatement->whereClause->conditionalExpression->conditionalTerms = array($term);
} else {
// Just one term so we can simply append our factors to that term
$singleTerm = $selectStatement->whereClause->conditionalExpression->conditionalTerms[0];
$singleTerm->conditionalFactors = array_merge($singleTerm->conditionalFactors, $factors);
$selectStatement->whereClause->conditionalExpression->conditionalTerms = array($singleTerm);
}
} else {
// Create a new WHERE clause with our factors
$term = new Query\AST\ConditionalTerm($factors);
$condExpr = new Query\AST\ConditionalExpression(array($term));
$whereClause = new Query\AST\WhereClause($condExpr);
$selectStatement->whereClause = $whereClause;
}
}
}
......@@ -58,6 +58,10 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$this->assertLoadingOfAssociation($customer);
}
/**
* @group mine
* @return unknown_type
*/
public function testLazyLoadsAssociation()
{
$this->_createFixture();
......@@ -66,7 +70,7 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer');
$metadata->getAssociationMapping('mentor')->fetchMode = AssociationMapping::FETCH_LAZY;
$query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c');
$query = $this->_em->createQuery("select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c where c.name='Luke Skywalker'");
$result = $query->getResult();
$customer = $result[0];
$this->assertLoadingOfAssociation($customer);
......
......@@ -87,11 +87,11 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$affected = $query->execute();
$this->assertEquals(1, $affected);
$query = $this->_em->createQuery("delete Doctrine\Tests\ORM\Functional\ParentEntity e");
$affected = $query->execute();
$this->assertEquals(2, $affected);
$this->assertEquals(2, $affected);
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://schemas.doctrine-project.org/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.doctrine-project.org/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
/Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
<entity name="XmlMappingTest\User" table="cms_users">
......
<?php
namespace Doctrine\Tests\ORM\Proxy;
if (!defined('PHPUnit_MAIN_METHOD')) {
define('PHPUnit_MAIN_METHOD', 'Orm_Proxy_AllTests::main');
}
require_once __DIR__ . '/../../TestInit.php';
class AllTests
{
public static function main()
{
\PHPUnit_TextUI_TestRunner::run(self::suite());
}
public static function suite()
{
$suite = new \Doctrine\Tests\DoctrineTestSuite('Doctrine Orm Proxy');
$suite->addTestSuite('Doctrine\Tests\ORM\Proxy\ProxyClassGeneratorTest');
return $suite;
}
}
if (PHPUnit_MAIN_METHOD == 'Orm_Proxy_AllTests::main') {
AllTests::main();
}
\ No newline at end of file
......@@ -109,14 +109,16 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
$proxy->getDescription();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testReferenceProxyRespectsMethodsParametersTypeHinting()
{
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
$proxy = new $proxyClass($this->_getMockPersister(), null);
$proxy->setProduct(array('invalid parameter'));
$method = new \ReflectionMethod(get_class($proxy), 'setProduct');
$params = $method->getParameters();
$this->assertEquals(1, count($params));
$this->assertEquals('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $params[0]->getClass()->getName());
}
protected function _getMockPersister()
......
......@@ -47,8 +47,8 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
}
$parser = new \Doctrine\ORM\Query\Parser($query);
// We do NOT test SQL construction here. That only unnecessarily slows down the tests!
$parser->setTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker(null, null, array()));
// We do NOT test SQL output here. That only unnecessarily slows down the tests!
$parser->setCustomOutputTreeWalker('Doctrine\Tests\Mocks\MockTreeWalker');
return $parser->parse();
}
......@@ -340,6 +340,12 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS EMPTY');
}
public function testSingleValuedAssociationFieldInWhere()
{
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1');
$this->assertValidDql('SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1');
}
/**
* This checks for invalid attempt to hydrate a proxy. It should throw an exception
*
......
......@@ -382,4 +382,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (SELECT COUNT(c1_.user_id) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 0"
);
}
/* Not yet implemented, needs more thought
public function testSingleValuedAssociationFieldInWhere()
{
$this->assertSqlGeneration(
"SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1",
"SELECT c0_.phonenumber AS phonenumber0 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?"
);
$this->assertSqlGeneration(
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1",
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = (SELECT c1_.user_id FROM cms_addresses c1_ WHERE c1_.id = ?)"
);
}*/
}
......@@ -21,8 +21,8 @@
namespace Doctrine\Tests\ORM;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder,
Doctrine\ORM\Query\Expr;
require_once __DIR__ . '/../TestInit.php';
......@@ -36,8 +36,6 @@ require_once __DIR__ . '/../TestInit.php';
* @link http://www.phpdoctrine.org
* @since 2.0
* @version $Revision$
* @todo Remove QueryBuilder::create. Use constructor in tests instead. Users will use
* $em->createQueryBuilder().
*/
class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
{
......
......@@ -85,12 +85,14 @@ class OrmFunctionalTestCase extends OrmTestCase
$conn->executeUpdate('DELETE FROM ecommerce_features');
$conn->executeUpdate('DELETE FROM ecommerce_products');
$conn->executeUpdate('DELETE FROM ecommerce_shippings');
$conn->executeUpdate('UPDATE ecommerce_categories SET parent_id = NULL');
$conn->executeUpdate('DELETE FROM ecommerce_categories');
}
if (isset($this->_usedModelSets['company'])) {
$conn->executeUpdate('DELETE FROM company_persons_friends');
$conn->executeUpdate('DELETE FROM company_managers');
$conn->executeUpdate('DELETE FROM company_employees');
$conn->executeUpdate('UPDATE company_persons SET spouse_id = NULL');
$conn->executeUpdate('DELETE FROM company_persons');
}
if (isset($this->_usedModelSets['generic'])) {
......@@ -155,9 +157,8 @@ class OrmFunctionalTestCase extends OrmTestCase
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(self::$_metadataCacheImpl);
$config->setQueryCacheImpl(self::$_queryCacheImpl);
$eventManager = new \Doctrine\Common\EventManager();
$conn = $this->sharedFixture['conn'];
return \Doctrine\ORM\EntityManager::create($conn, $config, $eventManager);
return \Doctrine\ORM\EntityManager::create($conn, $config);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment