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 @@ ...@@ -97,7 +97,7 @@
</fileset> </fileset>
</batchtest> </batchtest>
</phpunit> </phpunit>
<!-- <phpunitreport infile="build/logs/testsuites.xml" format="frames" todir="reports/tests" />--> <phpunitreport infile="build/logs/testsuites.xml" format="frames" todir="reports/tests" />
</target> </target>
<!-- <!--
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.doctrine-project.org/orm/doctrine-mapping" targetNamespace="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:orm="http://schemas.doctrine-project.org/orm/doctrine-mapping" xmlns:orm="http://doctrine-project.org/schemas/orm/doctrine-mapping"
elementFormDefault="qualified"> elementFormDefault="qualified">
<xs:annotation> <xs:annotation>
......
<?php
/*
* $Id: Inflector.php 3189 2007-11-18 20:37:44Z meus $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Util;
/**
* Doctrine inflector has static methods for inflecting text
*
* The methods in these classes are from several different sources collected
* across several different php projects and several different authors. The
* original author names and emails are not known
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 1.0
* @version $Revision: 3189 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class Inflector
{
/**
* Convert word in to the format for a Doctrine table name. Converts 'ModelName' to 'model_name'
*
* @param string $word Word to tableize
* @return string $word Tableized word
*/
public static function tableize($word)
{
return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
}
/**
* Convert a word in to the format for a Doctrine class name. Converts 'table_name' to 'TableName'
*
* @param string $word Word to classify
* @return string $word Classified word
*/
public static function classify($word)
{
$word = preg_replace('/[$]/', '', $word);
return preg_replace_callback('~(_?)(_)([\w])~', array(__CLASS__, "classifyCallback"), ucfirst(strtolower($word)));
}
/**
* Callback function to classify a classname properly.
*
* @param array $matches An array of matches from a pcre_replace call
* @return string $string A string with matches 1 and mathces 3 in upper case.
*/
public static function classifyCallback($matches)
{
return $matches[1] . strtoupper($matches[3]);
}
/**
* Check if a string has utf7 characters in it
*
* By bmorel at ssi dot fr
*
* @param string $string
* @return boolean $bool
*/
public static function seemsUtf8($string)
{
for ($i = 0; $i < strlen($string); $i++) {
if (ord($string[$i]) < 0x80) continue; # 0bbbbbbb
elseif ((ord($string[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif ((ord($string[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif ((ord($string[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif ((ord($string[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
elseif ((ord($string[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == strlen($string)) || ((ord($string[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
/**
* Remove any illegal characters, accents, etc.
*
* @param string $string String to unaccent
* @return string $string Unaccented string
*/
public static function unaccent($string)
{
if ( ! preg_match('/[\x80-\xff]/', $string) ) {
return $string;
}
if (self::seemsUtf8($string)) {
$chars = array(
// Decompositions for Latin-1 Supplement
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
chr(195).chr(191) => 'y',
// Decompositions for Latin Extended-A
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
chr(197).chr(148) => 'R', chr(197).chr(149) => 'r',
chr(197).chr(150) => 'R', chr(197).chr(151) => 'r',
chr(197).chr(152) => 'R', chr(197).chr(153) => 'r',
chr(197).chr(154) => 'S', chr(197).chr(155) => 's',
chr(197).chr(156) => 'S', chr(197).chr(157) => 's',
chr(197).chr(158) => 'S', chr(197).chr(159) => 's',
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
// Euro Sign
chr(226).chr(130).chr(172) => 'E',
// GBP (Pound) Sign
chr(194).chr(163) => '',
'Ä' => 'Ae', 'ä' => 'ae', 'Ü' => 'Ue', 'ü' => 'ue',
'Ö' => 'Oe', 'ö' => 'oe', 'ß' => 'ss');
$string = strtr($string, $chars);
} else {
// Assume ISO-8859-1 if not UTF-8
$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
.chr(252).chr(253).chr(255);
$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
$string = strtr($string, $chars['in'], $chars['out']);
$doubleChars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
$doubleChars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
$string = str_replace($doubleChars['in'], $doubleChars['out'], $string);
}
return $string;
}
/**
* Convert any passed string to a url friendly string. Converts 'My first blog post' to 'my-first-blog-post'
*
* @param string $text Text to urlize
* @return string $text Urlized text
*/
public static function urlize($text)
{
// Remove all non url friendly characters with the unaccent function
$text = self::unaccent($text);
if (function_exists('mb_strtolower'))
{
$text = mb_strtolower($text);
} else {
$text = strtolower($text);
}
// Remove all none word characters
$text = preg_replace('/\W/', ' ', $text);
// More stripping. Replace spaces with dashes
$text = strtolower(preg_replace('/[^A-Z^a-z^0-9^\/]+/', '-',
preg_replace('/([a-z\d])([A-Z])/', '\1_\2',
preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1_\2',
preg_replace('/::/', '/', $text)))));
return trim($text, '-');
}
}
\ No newline at end of file
...@@ -87,7 +87,7 @@ class Configuration ...@@ -87,7 +87,7 @@ class Configuration
public function setCustomTypes(array $types, $override = false) public function setCustomTypes(array $types, $override = false)
{ {
foreach ($types as $name => $typeClassName) { foreach ($types as $name => $typeClassName) {
$method = (Type::hasType($name) ? 'override' : 'add') . 'Type'; $method = (Type::hasType($name) && $override ? 'override' : 'add') . 'Type';
Type::$method($name, $typeClassName); Type::$method($name, $typeClassName);
} }
......
...@@ -47,7 +47,6 @@ class Driver implements \Doctrine\DBAL\Driver ...@@ -47,7 +47,6 @@ class Driver implements \Doctrine\DBAL\Driver
$password, $password,
$driverOptions $driverOptions
); );
$conn->setAttribute(Connection::ATTR_AUTOCOMMIT, false);
return $conn; return $conn;
} }
......
...@@ -141,7 +141,7 @@ interface Statement ...@@ -141,7 +141,7 @@ interface Statement
* bound parameters in the SQL statement being executed. * bound parameters in the SQL statement being executed.
* @return boolean Returns TRUE on success or FALSE on failure. * @return boolean Returns TRUE on success or FALSE on failure.
*/ */
function execute($params = null); function execute($params = array());
/** /**
* fetch * fetch
......
...@@ -585,19 +585,18 @@ class EntityManager ...@@ -585,19 +585,18 @@ class EntityManager
*/ */
public static function create($conn, Configuration $config = null, EventManager $eventManager = null) public static function create($conn, Configuration $config = null, EventManager $eventManager = null)
{ {
if (is_array($conn)) { $config = $config ?: new Configuration();
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager);
} else if ( ! $conn instanceof Connection) {
throw DoctrineException::updateMe("Invalid parameter '$conn'.");
}
if ($config === null) { if (is_array($conn)) {
$config = new Configuration(); $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.");
} }
if ($eventManager === null) { } else {
$eventManager = new EventManager(); throw DoctrineException::updateMe("Invalid parameter '$conn'.");
} }
return new EntityManager($conn, $config, $eventManager); return new EntityManager($conn, $config, $conn->getEventManager());
} }
} }
...@@ -26,12 +26,11 @@ namespace Doctrine\ORM; ...@@ -26,12 +26,11 @@ namespace Doctrine\ORM;
* business specific methods for retrieving entities. * business specific methods for retrieving entities.
* *
* This class is designed for inheritance and users can subclass this class to * 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 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
*/ */
class EntityRepository class EntityRepository
...@@ -53,21 +52,6 @@ class EntityRepository ...@@ -53,21 +52,6 @@ class EntityRepository
$this->_class = $class; $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. * Clears the repository, causing all managed entities to become detached.
*/ */
...@@ -81,9 +65,9 @@ class EntityRepository ...@@ -81,9 +65,9 @@ class EntityRepository
* *
* @param $id The identifier. * @param $id The identifier.
* @param int $hydrationMode The hydration mode to use. * @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 // Check identity map first
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) { if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
...@@ -102,48 +86,41 @@ class EntityRepository ...@@ -102,48 +86,41 @@ class EntityRepository
* Finds all entities in the repository. * Finds all entities in the repository.
* *
* @param int $hydrationMode * @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 $column
* @param string $value * @param string $value
* @param string $hydrationMode * @return array
* @return void
*/ */
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 $column
* @param string $value * @param string $value
* @param string $hydrationMode * @return object
* @return void
*/ */
protected function findOneBy($fieldName, $value, $hydrationMode = null) public function findOneBy(array $criteria)
{ {
$results = $this->_createQuery()->where($fieldName . ' = ?') return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria);
->setMaxResults(1)
->execute(array($value), $hydrationMode);
return $hydrationMode === Query::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst();
} }
/** /**
* Adds support for magic finders. * 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 * @throws BadMethodCallException If the method called is an invalid find* method
* or no find* method at all and therefore an invalid * or no find* method at all and therefore an invalid
* method call. * method call.
...@@ -160,25 +137,16 @@ class EntityRepository ...@@ -160,25 +137,16 @@ class EntityRepository
throw new BadMethodCallException("Undefined method '$method'."); throw new BadMethodCallException("Undefined method '$method'.");
} }
if (isset($by)) {
if ( ! isset($arguments[0])) { if ( ! isset($arguments[0])) {
throw DoctrineException::updateMe('You must specify the value to findBy.'); throw DoctrineException::updateMe('You must specify the value to findBy.');
} }
$fieldName = Doctrine::tableize($by); $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
$hydrationMode = isset($arguments[1]) ? $arguments[1]:null;
if ($this->_class->hasField($fieldName)) { if ($this->_class->hasField($fieldName)) {
return $this->$method($fieldName, $arguments[0], $hydrationMode); return $this->$method(array($fieldName => $arguments[0]));
} 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 { } else {
throw DoctrineException::updateMe('Cannot find by: ' . $by . '. Invalid field or relationship alias.'); 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 ...@@ -207,11 +207,11 @@ class ObjectHydrator extends AbstractHydrator
); );
$pColl->setOwner($entity, $assoc); $pColl->setOwner($entity, $assoc);
$reflField->setValue($entity, $pColl); $reflField->setValue($entity, $pColl);
if ( ! $assoc->isLazilyFetched()) { if ($assoc->isLazilyFetched()) {
$pColl->setInitialized(false);
} else {
//TODO: Allow more efficient and configurable batching of these loads //TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, $pColl, $this->_em); $assoc->load($entity, $pColl, $this->_em);
} else {
$pColl->setInitialized(false);
} }
} }
} }
......
...@@ -415,6 +415,28 @@ class StandardEntityPersister ...@@ -415,6 +415,28 @@ class StandardEntityPersister
return $this->_createEntity($result, $entity); 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. * Loads a collection of entities into a one-to-many association.
* *
...@@ -570,7 +592,7 @@ class StandardEntityPersister ...@@ -570,7 +592,7 @@ class StandardEntityPersister
return 'SELECT ' . $columnList return 'SELECT ' . $columnList
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' FROM ' . $this->_class->getQuotedTableName($this->_platform)
. $joinSql . $joinSql
. ' WHERE ' . $conditionSql; . ($conditionSql ? ' WHERE ' . $conditionSql : '');
} }
/** /**
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
namespace Doctrine\ORM\Proxy; namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager,
use Doctrine\ORM\Mapping\ClassMetadata; Doctrine\ORM\Mapping\ClassMetadata;
/** /**
* The ProxyClassGenerator is used to generate proxy objects for entities at runtime. * The ProxyClassGenerator is used to generate proxy objects for entities at runtime.
...@@ -42,7 +42,8 @@ class ProxyClassGenerator ...@@ -42,7 +42,8 @@ class ProxyClassGenerator
* Generates and stores proxy class files in the given cache directory. * Generates and stores proxy class files in the given cache directory.
* *
* @param EntityManager $em * @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) public function __construct(EntityManager $em, $cacheDir = null)
{ {
...@@ -84,7 +85,7 @@ class ProxyClassGenerator ...@@ -84,7 +85,7 @@ class ProxyClassGenerator
return $this->_generateClass($originalClassName, $proxyClassName, self::$_assocProxyClassTemplate); return $this->_generateClass($originalClassName, $proxyClassName, self::$_assocProxyClassTemplate);
} }
protected function _generateClass($originalClassName, $proxyClassName, $file) private function _generateClass($originalClassName, $proxyClassName, $file)
{ {
$proxyFullyQualifiedClassName = self::$_ns . $proxyClassName; $proxyFullyQualifiedClassName = self::$_ns . $proxyClassName;
...@@ -124,7 +125,7 @@ class ProxyClassGenerator ...@@ -124,7 +125,7 @@ class ProxyClassGenerator
return $proxyFullyQualifiedClassName; return $proxyFullyQualifiedClassName;
} }
protected function _generateMethods(ClassMetadata $class) private function _generateMethods(ClassMetadata $class)
{ {
$methods = ''; $methods = '';
...@@ -165,7 +166,7 @@ class ProxyClassGenerator ...@@ -165,7 +166,7 @@ class ProxyClassGenerator
return $methods; return $methods;
} }
public function _generateSleep(ClassMetadata $class) private function _generateSleep(ClassMetadata $class)
{ {
$sleepImpl = ''; $sleepImpl = '';
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\Parser,
use Doctrine\ORM\Query\QueryException; Doctrine\ORM\Query\QueryException;
/** /**
* A Query object represents a DQL query. * A Query object represents a DQL query.
...@@ -75,6 +75,7 @@ final class Query extends AbstractQuery ...@@ -75,6 +75,7 @@ final class Query extends AbstractQuery
*/ */
const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns'; const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
//const HINT_READ_ONLY = 'doctrine.readOnly'; //const HINT_READ_ONLY = 'doctrine.readOnly';
/** /**
...@@ -169,6 +170,8 @@ final class Query extends AbstractQuery ...@@ -169,6 +170,8 @@ final class Query extends AbstractQuery
// Check query cache // Check query cache
if ($queryCache = $this->getQueryCacheDriver()) { if ($queryCache = $this->getQueryCacheDriver()) {
// Calculate hash for dql query. // 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'); $hash = md5($this->getDql() . 'DOCTRINE_QUERY_CACHE_SALT');
$cached = ($this->_expireQueryCache) ? false : $queryCache->fetch($hash); $cached = ($this->_expireQueryCache) ? false : $queryCache->fetch($hash);
......
...@@ -42,11 +42,6 @@ class QuantifiedExpression extends Node ...@@ -42,11 +42,6 @@ class QuantifiedExpression extends Node
$this->subselect = $subselect; $this->subselect = $subselect;
} }
public function getSubselect()
{
return $this->_subselect;
}
public function isAll() public function isAll()
{ {
return strtoupper($this->type) == 'ALL'; return strtoupper($this->type) == 'ALL';
......
...@@ -116,11 +116,18 @@ class Parser ...@@ -116,11 +116,18 @@ class Parser
private $_nestingLevel = 0; 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 * @var TreeWalker
*/ */
private $_treeWalker = 'Doctrine\ORM\Query\SqlWalker'; private $_customOutputWalker;
/** /**
* Creates a new query parser object. * Creates a new query parser object.
...@@ -136,13 +143,24 @@ class Parser ...@@ -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;
} }
/** /**
...@@ -263,25 +281,37 @@ class Parser ...@@ -263,25 +281,37 @@ class Parser
$this->syntaxError('end of string'); $this->syntaxError('end of string');
} }
// Create TreeWalker who creates the SQL from the AST if ($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) {
$treeWalker = new $this->_treeWalker( $this->_customTreeWalkers = $customWalkers;
}
// 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);
}
}
if ($this->_customOutputWalker) {
$outputWalker = new $this->_customOutputWalker(
$this->_query, $this->_parserResult, $this->_queryComponents $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));
}
$treeWalker->setLastTreeWalker('Doctrine\ORM\Query\SqlWalker');
} else { } else {
$treeWalker = new SqlWalker( $outputWalker = new SqlWalker(
$this->_query, $this->_parserResult, $this->_queryComponents $this->_query, $this->_parserResult, $this->_queryComponents
); );
}*/ }
// Assign an SQL executor to the parser result // Assign an SQL executor to the parser result
$this->_parserResult->setSqlExecutor($treeWalker->getExecutor($AST)); $this->_parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
return $this->_parserResult; return $this->_parserResult;
} }
......
...@@ -30,8 +30,6 @@ use Doctrine\ORM\Query, ...@@ -30,8 +30,6 @@ use Doctrine\ORM\Query,
* *
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @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 class SqlWalker implements TreeWalker
{ {
...@@ -417,14 +415,7 @@ class SqlWalker implements TreeWalker ...@@ -417,14 +415,7 @@ class SqlWalker implements TreeWalker
$sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.'; $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; break;
case AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION: case AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION:
...@@ -604,7 +595,7 @@ class SqlWalker implements TreeWalker ...@@ -604,7 +595,7 @@ class SqlWalker implements TreeWalker
*/ */
public function walkHavingClause($havingClause) public function walkHavingClause($havingClause)
{ {
$condExpr = $havingClause->getConditionalExpression(); $condExpr = $havingClause->conditionalExpression;
return ' HAVING ' . implode( return ' HAVING ' . implode(
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms) ' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
...@@ -846,7 +837,7 @@ class SqlWalker implements TreeWalker ...@@ -846,7 +837,7 @@ class SqlWalker implements TreeWalker
public function walkQuantifiedExpression($qExpr) public function walkQuantifiedExpression($qExpr)
{ {
return ' ' . strtoupper($qExpr->type) return ' ' . strtoupper($qExpr->type)
. '(' . $this->walkSubselect($qExpr->getSubselect()) . ')'; . '(' . $this->walkSubselect($qExpr->subselect) . ')';
} }
/** /**
...@@ -921,7 +912,6 @@ class SqlWalker implements TreeWalker ...@@ -921,7 +912,6 @@ class SqlWalker implements TreeWalker
if ($expr instanceof AST\PathExpression) { if ($expr instanceof AST\PathExpression) {
$sql .= ' ' . $this->walkPathExpression($expr); $sql .= ' ' . $this->walkPathExpression($expr);
//...
} else if ($expr instanceof AST\AggregateExpression) { } else if ($expr instanceof AST\AggregateExpression) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) { if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarAliasCounter++; $alias = $this->_scalarAliasCounter++;
...@@ -932,7 +922,7 @@ class SqlWalker implements TreeWalker ...@@ -932,7 +922,7 @@ class SqlWalker implements TreeWalker
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias; $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
} else { } else {
// IdentificationVariable // IdentificationVariable
// FIXME: Composite key support, or select all columns? Does that make // FIXME: Composite key support, or select all columns? Does that make sense
// in a subquery? // in a subquery?
$class = $this->_queryComponents[$expr]['metadata']; $class = $this->_queryComponents[$expr]['metadata'];
$sql .= ' ' . $this->getSqlTableAlias($class->getTableName(), $expr) . '.' $sql .= ' ' . $this->getSqlTableAlias($class->getTableName(), $expr) . '.'
...@@ -1420,7 +1410,7 @@ class SqlWalker implements TreeWalker ...@@ -1420,7 +1410,7 @@ class SqlWalker implements TreeWalker
if ($leftExpr instanceof AST\Node) { if ($leftExpr instanceof AST\Node) {
$sql .= $leftExpr->dispatch($this); $sql .= $leftExpr->dispatch($this);
} else { } else {
$sql .= $this->_conn->quote($leftExpr); $sql .= is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr);
} }
$sql .= ' ' . $compExpr->operator . ' '; $sql .= ' ' . $compExpr->operator . ' ';
...@@ -1428,7 +1418,7 @@ class SqlWalker implements TreeWalker ...@@ -1428,7 +1418,7 @@ class SqlWalker implements TreeWalker
if ($rightExpr instanceof AST\Node) { if ($rightExpr instanceof AST\Node) {
$sql .= $rightExpr->dispatch($this); $sql .= $rightExpr->dispatch($this);
} else { } else {
$sql .= $this->_conn->quote($rightExpr); $sql .= is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr);
} }
return $sql; return $sql;
......
...@@ -30,10 +30,24 @@ namespace Doctrine\ORM\Query; ...@@ -30,10 +30,24 @@ namespace Doctrine\ORM\Query;
*/ */
abstract class TreeWalkerAdapter implements TreeWalker abstract class TreeWalkerAdapter implements TreeWalker
{ {
private $_query;
private $_parserResult;
private $_queryComponents;
/** /**
* @inheritdoc * @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. * Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
...@@ -366,4 +380,11 @@ abstract class TreeWalkerAdapter implements TreeWalker ...@@ -366,4 +380,11 @@ abstract class TreeWalkerAdapter implements TreeWalker
* @return string The SQL. * @return string The SQL.
*/ */
public function walkPathExpression($pathExpr) {} 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
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query;
/**
* Represents a chain of tree walkers that modify an AST and finally emit output.
* Only the last walker in the chain can emit output. Any previous walkers can modify
* the AST to influence the final output produced by the last walker.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class TreeWalkerChain implements TreeWalker
{
/** The tree walkers. */
private $_walkers = array();
/** The original Query. */
private $_query;
/** The ParserResult of the original query that was produced by the Parser. */
private $_parserResult;
/** The query components of the original query (the "symbol table") that was produced by the Parser. */
private $_queryComponents;
/**
* @inheritdoc
*/
public function __construct($query, $parserResult, array $queryComponents)
{
$this->_query = $query;
$this->_parserResult = $parserResult;
$this->_queryComponents = $queryComponents;
}
/**
* Adds a tree walker to the chain.
*
* @param string $walkerClass The class of the walker to instantiate.
*/
public function addTreeWalker($walkerClass)
{
$this->_walkers[] = new $walkerClass($this->_query, $this->_parserResult, $this->_queryComponents);
}
/**
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkSelectStatement(AST\SelectStatement $AST)
{
foreach ($this->_walkers as $walker) {
$walker->walkSelectStatement($AST);
}
}
/**
* Walks down a SelectClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkSelectClause($selectClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkSelectClause($selectClause);
}
}
/**
* Walks down a FromClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFromClause($fromClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkFromClause($fromClause);
}
}
/**
* Walks down a FunctionNode AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFunction($function)
{
foreach ($this->_walkers as $walker) {
$walker->walkFunction($function);
}
}
/**
* Walks down an OrderByClause AST node, thereby generating the appropriate SQL.
*
* @param OrderByClause
* @return string The SQL.
*/
public function walkOrderByClause($orderByClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkOrderByClause($orderByClause);
}
}
/**
* Walks down an OrderByItem AST node, thereby generating the appropriate SQL.
*
* @param OrderByItem
* @return string The SQL.
*/
public function walkOrderByItem($orderByItem)
{
foreach ($this->_walkers as $walker) {
$walker->walkOrderByItem($orderByItem);
}
}
/**
* Walks down a HavingClause AST node, thereby generating the appropriate SQL.
*
* @param HavingClause
* @return string The SQL.
*/
public function walkHavingClause($havingClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkHavingClause($havingClause);
}
}
/**
* Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL.
*
* @param JoinVariableDeclaration $joinVarDecl
* @return string The SQL.
*/
public function walkJoinVariableDeclaration($joinVarDecl)
{
foreach ($this->_walkers as $walker) {
$walker->walkJoinVariableDeclaration($joinVarDecl);
}
}
/**
* Walks down a SelectExpression AST node and generates the corresponding SQL.
*
* @param SelectExpression $selectExpression
* @return string The SQL.
*/
public function walkSelectExpression($selectExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkSelectExpression($selectExpression);
}
}
/**
* Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL.
*
* @param QuantifiedExpression
* @return string The SQL.
*/
public function walkQuantifiedExpression($qExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkQuantifiedExpression($qExpr);
}
}
/**
* Walks down a Subselect AST node, thereby generating the appropriate SQL.
*
* @param Subselect
* @return string The SQL.
*/
public function walkSubselect($subselect)
{
foreach ($this->_walkers as $walker) {
$walker->walkSubselect($subselect);
}
}
/**
* Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL.
*
* @param SubselectFromClause
* @return string The SQL.
*/
public function walkSubselectFromClause($subselectFromClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkSubselectFromClause($subselectFromClause);
}
}
/**
* Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectClause
* @return string The SQL.
*/
public function walkSimpleSelectClause($simpleSelectClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkSimpleSelectClause($simpleSelectClause);
}
}
/**
* Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectExpression
* @return string The SQL.
*/
public function walkSimpleSelectExpression($simpleSelectExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkSimpleSelectExpression($simpleSelectExpression);
}
}
/**
* Walks down an AggregateExpression AST node, thereby generating the appropriate SQL.
*
* @param AggregateExpression
* @return string The SQL.
*/
public function walkAggregateExpression($aggExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkAggregateExpression($aggExpression);
}
}
/**
* Walks down a GroupByClause AST node, thereby generating the appropriate SQL.
*
* @param GroupByClause
* @return string The SQL.
*/
public function walkGroupByClause($groupByClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkGroupByClause($groupByClause);
}
}
/**
* Walks down a GroupByItem AST node, thereby generating the appropriate SQL.
*
* @param GroupByItem
* @return string The SQL.
*/
public function walkGroupByItem(AST\PathExpression $pathExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkGroupByItem($pathExpr);
}
}
/**
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
*
* @param UpdateStatement
* @return string The SQL.
*/
public function walkUpdateStatement(AST\UpdateStatement $AST)
{
foreach ($this->_walkers as $walker) {
$walker->walkUpdateStatement($AST);
}
}
/**
* Walks down a DeleteStatement AST node, thereby generating the appropriate SQL.
*
* @param DeleteStatement
* @return string The SQL.
*/
public function walkDeleteStatement(AST\DeleteStatement $AST)
{
foreach ($this->_walkers as $walker) {
$walker->walkDeleteStatement($AST);
}
}
/**
* Walks down a DeleteClause AST node, thereby generating the appropriate SQL.
*
* @param DeleteClause
* @return string The SQL.
*/
public function walkDeleteClause(AST\DeleteClause $deleteClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkDeleteClause($deleteClause);
}
}
/**
* Walks down an UpdateClause AST node, thereby generating the appropriate SQL.
*
* @param UpdateClause
* @return string The SQL.
*/
public function walkUpdateClause($updateClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkUpdateClause($updateClause);
}
}
/**
* Walks down an UpdateItem AST node, thereby generating the appropriate SQL.
*
* @param UpdateItem
* @return string The SQL.
*/
public function walkUpdateItem($updateItem)
{
foreach ($this->_walkers as $walker) {
$walker->walkUpdateItem($updateItem);
}
}
/**
* Walks down a WhereClause AST node, thereby generating the appropriate SQL.
*
* @param WhereClause
* @return string The SQL.
*/
public function walkWhereClause($whereClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkWhereClause($whereClause);
}
}
/**
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
*
* @param ConditionalTerm
* @return string The SQL.
*/
public function walkConditionalTerm($condTerm)
{
foreach ($this->_walkers as $walker) {
$walker->walkConditionalTerm($condTerm);
}
}
/**
* Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.
*
* @param ConditionalFactor
* @return string The SQL.
*/
public function walkConditionalFactor($factor)
{
foreach ($this->_walkers as $walker) {
$walker->walkConditionalFactor($factor);
}
}
/**
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
*
* @param ExistsExpression
* @return string The SQL.
*/
public function walkExistsExpression($existsExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkExistsExpression($existsExpr);
}
}
/**
* Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.
*
* @param CollectionMemberExpression
* @return string The SQL.
*/
public function walkCollectionMemberExpression($collMemberExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkCollectionMemberExpression($collMemberExpr);
}
}
/**
* Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param EmptyCollectionComparisonExpression
* @return string The SQL.
*/
public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr);
}
}
/**
* Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param NullComparisonExpression
* @return string The SQL.
*/
public function walkNullComparisonExpression($nullCompExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkNullComparisonExpression($nullCompExpr);
}
}
/**
* Walks down an InExpression AST node, thereby generating the appropriate SQL.
*
* @param InExpression
* @return string The SQL.
*/
public function walkInExpression($inExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkInExpression($inExpr);
}
}
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkLiteral($literal)
{
foreach ($this->_walkers as $walker) {
$walker->walkLiteral($literal);
}
}
/**
* Walks down a BetweenExpression AST node, thereby generating the appropriate SQL.
*
* @param BetweenExpression
* @return string The SQL.
*/
public function walkBetweenExpression($betweenExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkBetweenExpression($betweenExpr);
}
}
/**
* Walks down a LikeExpression AST node, thereby generating the appropriate SQL.
*
* @param LikeExpression
* @return string The SQL.
*/
public function walkLikeExpression($likeExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkLikeExpression($likeExpr);
}
}
/**
* Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL.
*
* @param StateFieldPathExpression
* @return string The SQL.
*/
public function walkStateFieldPathExpression($stateFieldPathExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkStateFieldPathExpression($stateFieldPathExpression);
}
}
/**
* Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param ComparisonExpression
* @return string The SQL.
*/
public function walkComparisonExpression($compExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkComparisonExpression($compExpr);
}
}
/**
* Walks down an InputParameter AST node, thereby generating the appropriate SQL.
*
* @param InputParameter
* @return string The SQL.
*/
public function walkInputParameter($inputParam)
{
foreach ($this->_walkers as $walker) {
$walker->walkInputParameter($inputParam);
}
}
/**
* Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param ArithmeticExpression
* @return string The SQL.
*/
public function walkArithmeticExpression($arithmeticExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkArithmeticExpression($arithmeticExpr);
}
}
/**
* Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticTerm($term)
{
foreach ($this->_walkers as $walker) {
$walker->walkArithmeticTerm($term);
}
}
/**
* Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkStringPrimary($stringPrimary)
{
foreach ($this->_walkers as $walker) {
$walker->walkStringPrimary($stringPrimary);
}
}
/**
* Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticFactor($factor)
{
foreach ($this->_walkers as $walker) {
$walker->walkArithmeticFactor($factor);
}
}
/**
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleArithmeticExpression
* @return string The SQL.
*/
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkSimpleArithmeticExpression($simpleArithmeticExpr);
}
}
/**
* Walks down an PathExpression AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkPathExpression($pathExpr)
{
foreach ($this->_walkers as $walker) {
$walker->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
...@@ -158,13 +158,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -158,13 +158,6 @@ class UnitOfWork implements PropertyChangedListener
*/ */
private $_collectionDeletions = array(); private $_collectionDeletions = array();
/**
* All pending collection creations.
*
* @var array
*/
//private $_collectionCreations = array();
/** /**
* All pending collection updates. * All pending collection updates.
* *
...@@ -304,7 +297,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -304,7 +297,6 @@ class UnitOfWork implements PropertyChangedListener
$this->getCollectionPersister($collectionToUpdate->getMapping()) $this->getCollectionPersister($collectionToUpdate->getMapping())
->update($collectionToUpdate); ->update($collectionToUpdate);
} }
//TODO: collection recreations (insertions of complete collections)
// Entity deletions come last and need to be in reverse commit order // Entity deletions come last and need to be in reverse commit order
if ($this->_entityDeletions) { if ($this->_entityDeletions) {
...@@ -377,7 +369,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -377,7 +369,7 @@ class UnitOfWork implements PropertyChangedListener
public function computeChangeSets() public function computeChangeSets()
{ {
// Compute changes for INSERTed entities first. This must always happen. // 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)); $class = $this->_em->getClassMetadata(get_class($entity));
$this->_computeEntityChanges($class, $entity); $this->_computeEntityChanges($class, $entity);
// Look for changes in associations of the entity // Look for changes in associations of the entity
...@@ -403,9 +395,9 @@ class UnitOfWork implements PropertyChangedListener ...@@ -403,9 +395,9 @@ class UnitOfWork implements PropertyChangedListener
$this->_scheduledForDirtyCheck[$className] : $entities; $this->_scheduledForDirtyCheck[$className] : $entities;
foreach ($entitiesToProcess as $entity) { 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); $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); $this->_computeEntityChanges($class, $entity);
// Look for changes in associations of the entity // Look for changes in associations of the entity
foreach ($class->associationMappings as $assoc) { foreach ($class->associationMappings as $assoc) {
...@@ -581,7 +573,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -581,7 +573,6 @@ class UnitOfWork implements PropertyChangedListener
$data[$name] = $refProp->getValue($entry); $data[$name] = $refProp->getValue($entry);
$changeSet[$name] = array(null, $data[$name]); $changeSet[$name] = array(null, $data[$name]);
if (isset($targetClass->associationMappings[$name])) { if (isset($targetClass->associationMappings[$name])) {
//TODO: Prevent infinite recursion
$this->_computeAssociationChanges($targetClass->associationMappings[$name], $data[$name]); $this->_computeAssociationChanges($targetClass->associationMappings[$name], $data[$name]);
} }
} }
...@@ -600,7 +591,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -600,7 +591,7 @@ class UnitOfWork implements PropertyChangedListener
} }
/** /**
* INTERNAL, EXPERIMENTAL: * INTERNAL:
* Computes the changeset of an individual entity, independently of the * Computes the changeset of an individual entity, independently of the
* computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
* *
...@@ -810,7 +801,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -810,7 +801,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $this->_commitOrderCalculator->hasClass($targetClass->name)) { if ( ! $this->_commitOrderCalculator->hasClass($targetClass->name)) {
$this->_commitOrderCalculator->addClass($targetClass); $this->_commitOrderCalculator->addClass($targetClass);
} }
// add dependency // add dependency ($targetClass before $class)
$this->_commitOrderCalculator->addDependency($targetClass, $class); $this->_commitOrderCalculator->addDependency($targetClass, $class);
} }
} }
...@@ -823,7 +814,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -823,7 +814,7 @@ class UnitOfWork implements PropertyChangedListener
* Schedules an entity for insertion into the database. * Schedules an entity for insertion into the database.
* If the entity already has an identifier, it will be added to the identity map. * 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) public function scheduleForInsert($entity)
{ {
...@@ -840,13 +831,14 @@ class UnitOfWork implements PropertyChangedListener ...@@ -840,13 +831,14 @@ class UnitOfWork implements PropertyChangedListener
} }
$this->_entityInsertions[$oid] = $entity; $this->_entityInsertions[$oid] = $entity;
if (isset($this->_entityIdentifiers[$oid])) { if (isset($this->_entityIdentifiers[$oid])) {
$this->addToIdentityMap($entity); $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 * @param object $entity
* @return boolean * @return boolean
...@@ -857,9 +849,9 @@ class UnitOfWork implements PropertyChangedListener ...@@ -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) public function scheduleForUpdate($entity)
{ {
...@@ -880,7 +872,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -880,7 +872,7 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* INTERNAL: * INTERNAL:
* Schedules an extra update that will be executed immediately after the * 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 $entity
* @param $changeset * @param $changeset
...@@ -1588,7 +1580,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1588,7 +1580,6 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityUpdates = $this->_entityUpdates =
$this->_entityDeletions = $this->_entityDeletions =
$this->_collectionDeletions = $this->_collectionDeletions =
//$this->_collectionCreations =
$this->_collectionUpdates = $this->_collectionUpdates =
$this->_orphanRemovals = array(); $this->_orphanRemovals = array();
$this->_commitOrderCalculator->clear(); $this->_commitOrderCalculator->clear();
...@@ -1778,7 +1769,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1778,7 +1769,7 @@ class UnitOfWork implements PropertyChangedListener
*/ */
public function tryGetById($id, $rootClassName) public function tryGetById($id, $rootClassName)
{ {
$idHash = implode(' ', (array)$id); $idHash = implode(' ', (array) $id);
if (isset($this->_identityMap[$rootClassName][$idHash])) { if (isset($this->_identityMap[$rootClassName][$idHash])) {
return $this->_identityMap[$rootClassName][$idHash]; return $this->_identityMap[$rootClassName][$idHash];
} }
...@@ -1924,23 +1915,4 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1924,23 +1915,4 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityUpdates[$oid] = $entity; $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 ...@@ -23,7 +23,6 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\EntityManagerTest'); $suite->addTestSuite('Doctrine\Tests\ORM\EntityManagerTest');
$suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest'); $suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest');
$suite->addTestSuite('Doctrine\Tests\ORM\QueryBuilderTest'); $suite->addTestSuite('Doctrine\Tests\ORM\QueryBuilderTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Proxy\ProxyClassGeneratorTest');
$suite->addTest(Query\AllTests::suite()); $suite->addTest(Query\AllTests::suite());
$suite->addTest(Hydration\AllTests::suite()); $suite->addTest(Hydration\AllTests::suite());
$suite->addTest(Entity\AllTests::suite()); $suite->addTest(Entity\AllTests::suite());
...@@ -32,6 +31,7 @@ class AllTests ...@@ -32,6 +31,7 @@ class AllTests
$suite->addTest(Mapping\AllTests::suite()); $suite->addTest(Mapping\AllTests::suite());
$suite->addTest(Functional\AllTests::suite()); $suite->addTest(Functional\AllTests::suite());
$suite->addTest(Id\AllTests::suite()); $suite->addTest(Id\AllTests::suite());
$suite->addTest(Proxy\AllTests::suite());
return $suite; return $suite;
} }
......
<?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 ...@@ -58,6 +58,10 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$this->assertLoadingOfAssociation($customer); $this->assertLoadingOfAssociation($customer);
} }
/**
* @group mine
* @return unknown_type
*/
public function testLazyLoadsAssociation() public function testLazyLoadsAssociation()
{ {
$this->_createFixture(); $this->_createFixture();
...@@ -66,7 +70,7 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction ...@@ -66,7 +70,7 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer'); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer');
$metadata->getAssociationMapping('mentor')->fetchMode = AssociationMapping::FETCH_LAZY; $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(); $result = $query->getResult();
$customer = $result[0]; $customer = $result[0];
$this->assertLoadingOfAssociation($customer); $this->assertLoadingOfAssociation($customer);
......
<?xml version="1.0" encoding="UTF-8"?> <?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" 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"> /Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
<entity name="XmlMappingTest\User" table="cms_users"> <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 ...@@ -109,14 +109,16 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
$proxy->getDescription(); $proxy->getDescription();
} }
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testReferenceProxyRespectsMethodsParametersTypeHinting() public function testReferenceProxyRespectsMethodsParametersTypeHinting()
{ {
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
$proxy = new $proxyClass($this->_getMockPersister(), null); $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() protected function _getMockPersister()
......
...@@ -47,8 +47,8 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase ...@@ -47,8 +47,8 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
} }
$parser = new \Doctrine\ORM\Query\Parser($query); $parser = new \Doctrine\ORM\Query\Parser($query);
// We do NOT test SQL construction here. That only unnecessarily slows down the tests! // We do NOT test SQL output here. That only unnecessarily slows down the tests!
$parser->setTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker(null, null, array())); $parser->setCustomOutputTreeWalker('Doctrine\Tests\Mocks\MockTreeWalker');
return $parser->parse(); return $parser->parse();
} }
...@@ -340,6 +340,12 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase ...@@ -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'); $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 * This checks for invalid attempt to hydrate a proxy. It should throw an exception
* *
......
...@@ -382,4 +382,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ...@@ -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" "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 @@ ...@@ -21,8 +21,8 @@
namespace Doctrine\Tests\ORM; namespace Doctrine\Tests\ORM;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder,
use Doctrine\ORM\Query\Expr; Doctrine\ORM\Query\Expr;
require_once __DIR__ . '/../TestInit.php'; require_once __DIR__ . '/../TestInit.php';
...@@ -36,8 +36,6 @@ require_once __DIR__ . '/../TestInit.php'; ...@@ -36,8 +36,6 @@ require_once __DIR__ . '/../TestInit.php';
* @link http://www.phpdoctrine.org * @link http://www.phpdoctrine.org
* @since 2.0 * @since 2.0
* @version $Revision$ * @version $Revision$
* @todo Remove QueryBuilder::create. Use constructor in tests instead. Users will use
* $em->createQueryBuilder().
*/ */
class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
{ {
......
...@@ -85,12 +85,14 @@ class OrmFunctionalTestCase extends OrmTestCase ...@@ -85,12 +85,14 @@ class OrmFunctionalTestCase extends OrmTestCase
$conn->executeUpdate('DELETE FROM ecommerce_features'); $conn->executeUpdate('DELETE FROM ecommerce_features');
$conn->executeUpdate('DELETE FROM ecommerce_products'); $conn->executeUpdate('DELETE FROM ecommerce_products');
$conn->executeUpdate('DELETE FROM ecommerce_shippings'); $conn->executeUpdate('DELETE FROM ecommerce_shippings');
$conn->executeUpdate('UPDATE ecommerce_categories SET parent_id = NULL');
$conn->executeUpdate('DELETE FROM ecommerce_categories'); $conn->executeUpdate('DELETE FROM ecommerce_categories');
} }
if (isset($this->_usedModelSets['company'])) { if (isset($this->_usedModelSets['company'])) {
$conn->executeUpdate('DELETE FROM company_persons_friends'); $conn->executeUpdate('DELETE FROM company_persons_friends');
$conn->executeUpdate('DELETE FROM company_managers'); $conn->executeUpdate('DELETE FROM company_managers');
$conn->executeUpdate('DELETE FROM company_employees'); $conn->executeUpdate('DELETE FROM company_employees');
$conn->executeUpdate('UPDATE company_persons SET spouse_id = NULL');
$conn->executeUpdate('DELETE FROM company_persons'); $conn->executeUpdate('DELETE FROM company_persons');
} }
if (isset($this->_usedModelSets['generic'])) { if (isset($this->_usedModelSets['generic'])) {
...@@ -155,9 +157,8 @@ class OrmFunctionalTestCase extends OrmTestCase ...@@ -155,9 +157,8 @@ class OrmFunctionalTestCase extends OrmTestCase
$config = new \Doctrine\ORM\Configuration(); $config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(self::$_metadataCacheImpl); $config->setMetadataCacheImpl(self::$_metadataCacheImpl);
$config->setQueryCacheImpl(self::$_queryCacheImpl); $config->setQueryCacheImpl(self::$_queryCacheImpl);
$eventManager = new \Doctrine\Common\EventManager();
$conn = $this->sharedFixture['conn']; $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