1
0
mirror of synced 2025-02-20 22:23:14 +03:00

Merge commit 'dc2master/master'

This commit is contained in:
Benjamin Eberlei 2010-04-27 19:28:34 +02:00
commit 0f7d71cac4
88 changed files with 1672 additions and 760 deletions

View File

@ -3,9 +3,9 @@
## Console migrated to Symfony Console
The Doctrine Cli has been replaced by Symfony Console Configuration
The Doctrine CLI has been replaced by Symfony Console Configuration
Instead of having to specifiy:
Instead of having to specify:
[php]
$cliConfig = new CliConfiguration();
@ -100,9 +100,22 @@ The "default" option for database column defaults has been removed. If desired,
be implemented by using the columnDefinition attribute of the @Column annotation (or the approriate XML and YAML equivalents).
Prefer PHP default values, if possible.
## Partial Objects
## Selecting Partial Objects
[TBD: New syntax, results, etc.]
Querying for partial objects now has a new syntax. The old syntax to query for partial objects
now has a different meaning. This is best illustrated by an example. If you previously
had a DQL query like this:
[sql]
SELECT u.id, u.name FROM User u
Since BETA1, simple state field path expressions in the select clause are used to select
object fields as plain scalar values (something that was not possible before).
To achieve the same result as previously (that is, a partial object with only id and name populated)
you need to use the following, explicit syntax:
[sql]
SELECT PARTIAL u.{id,name} FROM User u
## XML Mapping Driver
@ -133,3 +146,9 @@ by using the API on the `PreUpdateEventArgs` instance passed to the preUpdate li
to the state of the entitys properties won't affect the database UPDATE statement anymore. This gives drastic
performance benefits for the preUpdate event.
## Collection API
The Collection interface in the Common package has been updated with some missing methods
that were present only on the default implementation, ArrayCollection. Custom collection
implementations need to be updated to adhere to the updated interface.

View File

@ -27,7 +27,8 @@ namespace Doctrine\Common\Annotations;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
@ -52,14 +53,29 @@ class Annotation
$this->$key = $value;
}
}
/**
* Error handler for unknown property accessor in Annotation class.
*
* @param string $name Unknown property name
*/
public function __get($name)
{
throw new \BadMethodCallException("Unknown annotation property '$name' on annotation '".get_class($this)."'.");
throw new \BadMethodCallException(
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
);
}
/**
* Error handler for unknown property mutator in Annotation class.
*
* @param string $name Unkown property name
* @param mixed $value Property value
*/
public function __set($name, $value)
{
throw new \BadMethodCallException("Unknown annotation property '$name' on annotation '".get_class($this)."'.");
throw new \BadMethodCallException(
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
);
}
}

View File

@ -27,20 +27,32 @@ namespace Doctrine\Common\Annotations;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class AnnotationException extends \Doctrine\Common\CommonException
class AnnotationException extends \Exception
{
/**
* Creates a new AnnotationException describing a Syntax error.
*
* @param string $message Exception message
* @return AnnotationException
*/
public static function syntaxError($message)
{
return new self('[Syntax Error] ' . $message);
}
public static function semanticalError($message)
/**
* Creates a new AnnotationException describing a Semantical error.
*
* @param string $message Exception message
* @return AnnotationException
*/
public static function semanticalError($message)
{
return new self('[Semantical Error] ' . $message);
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -29,10 +27,7 @@ use \ReflectionClass,
/**
* A reader for docblock annotations.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
@ -46,7 +41,7 @@ class AnnotationReader
* @var string
* @static
*/
private static $CACHE_SALT = "@<Annot>";
private static $CACHE_SALT = '@<Annot>';
/**
* Annotations Parser
@ -56,15 +51,14 @@ class AnnotationReader
private $_parser;
/**
* Cache machanism to store processed Annotations
* Cache mechanism to store processed Annotations
*
* @var Doctrine\Common\Cache\Cache
*/
private $_cache;
/**
* Constructor. Initializes a new AnnotationReader that uses the given
* Cache provider.
* Constructor. Initializes a new AnnotationReader that uses the given Cache provider.
*
* @param Cache $cache The cache provider to use. If none is provided, ArrayCache is used.
*/
@ -112,7 +106,7 @@ class AnnotationReader
return $data;
}
$annotations = $this->_parser->parse($class->getDocComment(), "class ".$class->getName());
$annotations = $this->_parser->parse($class->getDocComment(), 'class ' . $class->getName());
$this->_cache->save($cacheKey, $annotations, null);
return $annotations;
@ -128,6 +122,7 @@ class AnnotationReader
public function getClassAnnotation(ReflectionClass $class, $annotation)
{
$annotations = $this->getClassAnnotations($class);
return isset($annotations[$annotation]) ? $annotations[$annotation] : null;
}
@ -148,7 +143,7 @@ class AnnotationReader
return $data;
}
$context = "property ".$property->getDeclaringClass()->getName()."::\$".$property->getName();
$context = 'property ' . $property->getDeclaringClass()->getName() . "::\$" . $property->getName();
$annotations = $this->_parser->parse($property->getDocComment(), $context);
$this->_cache->save($cacheKey, $annotations, null);
@ -165,6 +160,7 @@ class AnnotationReader
public function getPropertyAnnotation(ReflectionProperty $property, $annotation)
{
$annotations = $this->getPropertyAnnotations($property);
return isset($annotations[$annotation]) ? $annotations[$annotation] : null;
}
@ -185,7 +181,7 @@ class AnnotationReader
return $data;
}
$context = "method ".$method->getDeclaringClass()->getName()."::".$method->getName()."()";
$context = 'method ' . $method->getDeclaringClass()->getName() . '::' . $method->getName() . '()';
$annotations = $this->_parser->parse($method->getDocComment(), $context);
$this->_cache->save($cacheKey, $annotations, null);
@ -202,6 +198,7 @@ class AnnotationReader
public function getMethodAnnotation(ReflectionMethod $method, $annotation)
{
$annotations = $this->getMethodAnnotations($method);
return isset($annotations[$annotation]) ? $annotations[$annotation] : null;
}
}

View File

@ -27,7 +27,8 @@ namespace Doctrine\Common\Annotations;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
@ -80,7 +81,7 @@ class Lexer extends \Doctrine\Common\Lexer
$newVal = $this->_getNumeric($value);
// Checking numeric value
if ($newVal !== false){
if ($newVal !== false) {
$value = $newVal;
return (strpos($value, '.') !== false || stripos($value, 'e') !== false)
@ -93,16 +94,34 @@ class Lexer extends \Doctrine\Common\Lexer
return self::T_STRING;
} else {
switch (strtolower($value)) {
case '@': return self::T_AT;
case ',': return self::T_COMMA;
case '(': return self::T_OPEN_PARENTHESIS;
case ')': return self::T_CLOSE_PARENTHESIS;
case '{': return self::T_OPEN_CURLY_BRACES;
case '@':
return self::T_AT;
case ',':
return self::T_COMMA;
case '(':
return self::T_OPEN_PARENTHESIS;
case ')':
return self::T_CLOSE_PARENTHESIS;
case '{':
return self::T_OPEN_CURLY_BRACES;
case '}': return self::T_CLOSE_CURLY_BRACES;
case '=': return self::T_EQUALS;
case '\\': return self::T_NAMESPACE_SEPARATOR;
case 'true': return self::T_TRUE;
case 'false': return self::T_FALSE;
case '=':
return self::T_EQUALS;
case '\\':
return self::T_NAMESPACE_SEPARATOR;
case 'true':
return self::T_TRUE;
case 'false':
return self::T_FALSE;
default:
if (ctype_alpha($value[0]) || $value[0] === '_') {
return self::T_IDENTIFIER;
@ -126,6 +145,7 @@ class Lexer extends \Doctrine\Common\Lexer
if ( ! is_scalar($value)) {
return false;
}
// Checking for valid numeric numbers: 1.234, -1.234e-2
if (is_numeric($value)) {
return $value;

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -24,14 +22,11 @@ namespace Doctrine\Common\Annotations;
/**
* A simple parser for docblock annotations.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class Parser
{
@ -173,9 +168,10 @@ class Parser
$message .= "'{$token['value']}' at position {$token['position']}";
}
if(strlen($this->_context)) {
$message .= ' in '.$this->_context;
if (strlen($this->_context)) {
$message .= ' in ' . $this->_context;
}
$message .= '.';
throw AnnotationException::syntaxError($message);
@ -236,7 +232,7 @@ class Parser
$nameParts[] = $this->_lexer->token['value'];
}
// Effectively pick the name of class (append default NS if none, grab from NS alias, etc)
// Effectively pick the name of the class (append default NS if none, grab from NS alias, etc)
if (count($nameParts) == 1) {
if (strpos($nameParts[0], ':')) {
list ($alias, $simpleName) = explode(':', $nameParts[0]);
@ -250,7 +246,7 @@ class Parser
// Is it really an annotation class?
if (
(! $this->_isNestedAnnotation && $this->_lexer->lookahead != null &&
( ! $this->_isNestedAnnotation && $this->_lexer->lookahead != null &&
! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) &&
! $this->_lexer->isNextToken(Lexer::T_AT)) ||
! class_exists($name, false)
@ -411,6 +407,7 @@ class Parser
foreach ($values as $value) {
list ($key, $val) = $value;
if ($key !== null) {
$array[$key] = $val;
} else {

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -24,10 +22,7 @@ namespace Doctrine\Common\Cache;
/**
* Base class for cache driver implementations.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -21,16 +19,12 @@
namespace Doctrine\Common\Collections;
use \Closure, \ArrayIterator;
use Closure, ArrayIterator;
/**
* An ArrayCollection is a Collection implementation that uses a regular PHP array
* internally.
* An ArrayCollection is a Collection implementation that wraps a regular PHP array.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
@ -39,7 +33,6 @@ class ArrayCollection implements Collection
{
/**
* An array containing the entries of this collection.
* This is the internal php array.
*
* @var array
*/
@ -54,7 +47,7 @@ class ArrayCollection implements Collection
{
$this->_elements = $elements;
}
/**
* Gets the PHP array representation of this collection.
*
@ -121,7 +114,7 @@ class ArrayCollection implements Collection
* Removes an element with a specific key/index from the collection.
*
* @param mixed $key
* @return mixed
* @return mixed The removed element or NULL, if no element exists for the given key.
*/
public function remove($key)
{
@ -131,7 +124,7 @@ class ArrayCollection implements Collection
return $removed;
}
return null;
}
@ -413,6 +406,7 @@ class ArrayCollection implements Collection
/**
* Returns a string representation of this object.
*
* @return string
*/
public function __toString()
{
@ -421,7 +415,6 @@ class ArrayCollection implements Collection
/**
* Clears the collection.
*
*/
public function clear()
{

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -40,10 +38,7 @@ use Closure, Countable, IteratorAggregate, ArrayAccess;
* position unless you explicitly positioned it before. Prefer iteration with
* external iterators.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -22,15 +20,13 @@
namespace Doctrine\Common;
/**
* Simple generic lexical scanner.
* Base class for writing simple lexers, i.e. for creating small DSLs.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename: AbstractLexer
*/
abstract class Lexer
{
@ -50,7 +46,7 @@ abstract class Lexer
private $_peek = 0;
/**
* @var array The next token in the query string.
* @var array The next token in the input.
*/
public $lookahead;
@ -60,9 +56,12 @@ abstract class Lexer
public $token;
/**
* Inputs data to be tokenized
* Sets the input data to be tokenized.
*
* @param string $input input to be tokenized
* The Lexer is immediately reset and the new input tokenized.
* Any unprocessed tokens from any previous input are lost.
*
* @param string $input The input to be tokenized.
*/
public function setInput($input)
{
@ -72,20 +71,18 @@ abstract class Lexer
}
/**
* Resets the scanner
*
* Resets the lexer.
*/
public function reset()
{
$this->lookahead = null;
$this->token = null;
$this->_peek = 0;
$this->token = null;
$this->_peek = 0;
$this->_position = 0;
}
/**
* Resets the peek pointer to 0
*
* Resets the peek pointer to 0.
*/
public function resetPeek()
{
@ -93,7 +90,7 @@ abstract class Lexer
}
/**
* Resets the lexer position on the input to the given position
* Resets the lexer position on the input to the given position.
*
* @param integer $position Position to place the lexical scanner
*/
@ -235,14 +232,14 @@ abstract class Lexer
}
/**
* Lexical catchable patterns
* Lexical catchable patterns.
*
* @return array
*/
abstract protected function getCatchablePatterns();
/**
* Lexical non-catchable patterns
* Lexical non-catchable patterns.
*
* @return array
*/

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -21,15 +19,12 @@
namespace Doctrine\DBAL;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Logging\SQLLogger;
/**
* Configuration container for the Doctrine DBAL.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
@ -46,22 +41,12 @@ class Configuration
*/
protected $_attributes = array();
/**
* Creates a new DBAL configuration instance.
*/
public function __construct()
{
$this->_attributes = array(
'sqlLogger' => null
);
}
/**
* Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled.
*
* @param SQLLogger $logger
*/
public function setSQLLogger($logger)
public function setSQLLogger(SQLLogger $logger)
{
$this->_attributes['sqlLogger'] = $logger;
}
@ -73,6 +58,7 @@ class Configuration
*/
public function getSQLLogger()
{
return $this->_attributes['sqlLogger'];
return isset($this->_attributes['sqlLogger']) ?
$this->_attributes['sqlLogger'] : null;
}
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -78,7 +78,7 @@ EOT
if (preg_match('/^select/i', $sql)) {
$resultSet = $conn->fetchAll($sql);
} else {
$resultSet = $em->getConnection()->executeUpdate($sql);
$resultSet = $conn->executeUpdate($sql);
}
\Doctrine\Common\Util\Debug::dump($resultSet, (int) $depth);

View File

@ -27,8 +27,7 @@ use Doctrine\Common\Cache\Cache,
* It combines all configuration options from DBAL & ORM.
*
* @since 2.0
* @internal When adding a new configuration option just write a getter/setter
* pair and add the option to the _attributes array with a proper default value.
* @internal When adding a new configuration option just write a getter/setter pair.
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
@ -36,24 +35,6 @@ use Doctrine\Common\Cache\Cache,
*/
class Configuration extends \Doctrine\DBAL\Configuration
{
/**
* Creates a new configuration that can be used for Doctrine.
*/
public function __construct()
{
parent::__construct();
$this->_attributes = array_merge($this->_attributes, array(
'resultCacheImpl' => null,
'queryCacheImpl' => null,
'metadataCacheImpl' => null,
'metadataDriverImpl' => null,
'proxyDir' => null,
'useCExtension' => false,
'autoGenerateProxyClasses' => true,
'proxyNamespace' => null
));
}
/**
* Sets the directory where Doctrine generates any necessary proxy class files.
*
@ -71,7 +52,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getProxyDir()
{
return $this->_attributes['proxyDir'];
return isset($this->_attributes['proxyDir']) ?
$this->_attributes['proxyDir'] : null;
}
/**
@ -82,7 +64,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getAutoGenerateProxyClasses()
{
return $this->_attributes['autoGenerateProxyClasses'];
return isset($this->_attributes['autoGenerateProxyClasses']) ?
$this->_attributes['autoGenerateProxyClasses'] : true;
}
/**
@ -103,7 +86,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getProxyNamespace()
{
return $this->_attributes['proxyNamespace'];
return isset($this->_attributes['proxyNamespace']) ?
$this->_attributes['proxyNamespace'] : null;
}
/**
@ -188,7 +172,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getMetadataDriverImpl()
{
return $this->_attributes['metadataDriverImpl'];
return isset($this->_attributes['metadataDriverImpl']) ?
$this->_attributes['metadataDriverImpl'] : null;
}
/**
@ -198,7 +183,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getResultCacheImpl()
{
return $this->_attributes['resultCacheImpl'];
return isset($this->_attributes['resultCacheImpl']) ?
$this->_attributes['resultCacheImpl'] : null;
}
/**
@ -218,7 +204,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getQueryCacheImpl()
{
return $this->_attributes['queryCacheImpl'];
return isset($this->_attributes['queryCacheImpl']) ?
$this->_attributes['queryCacheImpl'] : null;
}
/**
@ -238,7 +225,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getMetadataCacheImpl()
{
return $this->_attributes['metadataCacheImpl'];
return isset($this->_attributes['metadataCacheImpl']) ?
$this->_attributes['metadataCacheImpl'] : null;
}
/**
@ -259,7 +247,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getUseCExtension()
{
return $this->_attributes['useCExtension'];
return isset($this->_attributes['useCExtension']) ?
$this->_attributes['useCExtension'] : false;
}
/**
@ -350,6 +339,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Such a function can then be used in any DQL statement in any place where string
* functions are allowed.
*
* DQL function names are case-insensitive.
*
* @param string $name
* @param string $className
*/
@ -366,15 +357,33 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getCustomStringFunction($name)
{
$name = strtolower($name);
return isset($this->_attributes['customStringFunctions'][$name]) ?
$this->_attributes['customStringFunctions'][$name] : null;
}
/**
* Sets a map of custom DQL string functions.
*
* Keys must be function names and values the FQCN of the implementing class.
* The function names will be case-insensitive in DQL.
*
* Any previously added string functions are discarded.
*
* @param array $functions The map of custom DQL string functions.
*/
public function setCustomStringFunctions(array $functions)
{
$this->_attributes['customStringFunctions'] = array_change_key_case($functions);
}
/**
* Registers a custom DQL function that produces a numeric value.
* Such a function can then be used in any DQL statement in any place where numeric
* functions are allowed.
*
* DQL function names are case-insensitive.
*
* @param string $name
* @param string $className
*/
@ -391,15 +400,33 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getCustomNumericFunction($name)
{
$name = strtolower($name);
return isset($this->_attributes['customNumericFunctions'][$name]) ?
$this->_attributes['customNumericFunctions'][$name] : null;
}
/**
* Sets a map of custom DQL numeric functions.
*
* Keys must be function names and values the FQCN of the implementing class.
* The function names will be case-insensitive in DQL.
*
* Any previously added numeric functions are discarded.
*
* @param array $functions The map of custom DQL numeric functions.
*/
public function setCustomNumericFunctions(array $functions)
{
$this->_attributes['customNumericFunctions'] = array_change_key_case($functions);
}
/**
* Registers a custom DQL function that produces a date/time value.
* Such a function can then be used in any DQL statement in any place where date/time
* functions are allowed.
*
* DQL function names are case-insensitive.
*
* @param string $name
* @param string $className
*/
@ -416,7 +443,23 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function getCustomDatetimeFunction($name)
{
$name = strtolower($name);
return isset($this->_attributes['customDatetimeFunctions'][$name]) ?
$this->_attributes['customDatetimeFunctions'][$name] : null;
}
/**
* Sets a map of custom DQL date/time functions.
*
* Keys must be function names and values the FQCN of the implementing class.
* The function names will be case-insensitive in DQL.
*
* Any previously added date/time functions are discarded.
*
* @param array $functions The map of custom DQL date/time functions.
*/
public function setCustomDatetimeFunctions(array $functions)
{
$this->_attributes['customDatetimeFunctions'] = array_change_key_case($functions);
}
}

View File

@ -440,7 +440,8 @@ class EntityManager
*
* @param object $entity The entity to copy.
* @return object The new entity.
* @todo Implementation or remove.
* @todo Implementation need. This is necessary since $e2 = clone $e1; throws an E_FATAL when access anything on $e:
* Fatal error: Maximum function nesting level of '100' reached, aborting!
*/
public function copy($entity, $deep = false)
{

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM;
@ -12,6 +29,6 @@ class EntityNotFoundException extends ORMException
{
public function __construct()
{
parent::__construct('Entity was found although one item was expected.');
parent::__construct('Entity was not found.');
}
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -28,10 +26,7 @@ namespace Doctrine\ORM;
* This class is designed for inheritance and users can subclass this class to
* 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 Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -25,12 +23,9 @@ namespace Doctrine\ORM\Event;
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
* of entities.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class LifecycleEventArgs extends \Doctrine\Common\EventArgs
{

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Id;

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -27,10 +25,7 @@ use Doctrine\ORM\ORMException;
/**
* Special generator for application-assigned identifiers (doesnt really generate anything).
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Id;

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -21,7 +19,7 @@
namespace Doctrine\ORM\Id;
use Doctrine\ORM\EntityManager;
use Serializable, Doctrine\ORM\EntityManager;
/**
* Represents an ID generator that uses a database sequence.
@ -29,7 +27,7 @@ use Doctrine\ORM\EntityManager;
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
*/
class SequenceGenerator extends AbstractIdGenerator implements \Serializable
class SequenceGenerator extends AbstractIdGenerator implements Serializable
{
private $_allocationSize;
private $_sequenceName;

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Id;

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Id;
@ -7,10 +24,7 @@ use Doctrine\ORM\EntityManager;
/**
* Id generator that uses a single-row database table and a hi/lo algorithm.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id: Hydrate.php 3192 2007-11-19 17:55:23Z romanb $
*
* 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
@ -30,10 +28,7 @@ use PDO,
* Base class for all hydrators. A hydrator is a class that provides some form
* of transformation of an SQL result set into another structure.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3192 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
*/

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id: UnitOfWork.php 4947 2008-09-12 13:16:05Z romanb $
*
* 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

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -242,8 +240,8 @@ class ObjectHydrator extends AbstractHydrator
* specified by the FROM clause in a DQL query.
*
* @param array $data The data of the row to process.
* @param array $cache
* @param array $result
* @param array $cache The cache to use.
* @param array $result The result array to fill.
*/
protected function _hydrateRow(array $data, array &$cache, array &$result)
{

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -43,6 +41,7 @@ class SingleScalarHydrator extends AbstractHydrator
} else if ($num > 1 || count($result[key($result)]) > 1) {
throw new \Doctrine\ORM\NonUniqueResultException;
}
$result = $this->_gatherScalarRowData($result[key($result)], $cache);
return array_shift($result);

View File

@ -32,6 +32,7 @@ namespace Doctrine\ORM\Mapping;
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
abstract class AssociationMapping
{
@ -58,7 +59,7 @@ abstract class AssociationMapping
public $isCascadeRemove;
/**
* READ-ONLY: Whether the association cascades save() operations from the source entity
* READ-ONLY: Whether the association cascades persist() operations from the source entity
* to the target entity/entities.
*
* @var boolean
@ -340,5 +341,58 @@ abstract class AssociationMapping
? $platform->quoteIdentifier($this->joinTable['name'])
: $this->joinTable['name'];
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = array(
'sourceEntityName',
'targetEntityName',
'sourceFieldName'
);
if ($this->isCascadeDetach) {
$serialized[] = 'isCascadeDetach';
}
if ($this->isCascadeMerge) {
$serialized[] = 'isCascadeMerge';
}
if ($this->isCascadePersist) {
$serialized[] = 'isCascadePersist';
}
if ($this->isCascadeRefresh) {
$serialized[] = 'isCascadeRefresh';
}
if ($this->isCascadeRemove) {
$serialized[] = 'isCascadeRemove';
}
if ( ! $this->isOwningSide) {
$serialized[] = 'isOwningSide';
}
if ($this->mappedBy) {
$serialized[] = 'mappedBy';
}
if ($this->inversedBy) {
$serialized[] = 'inversedBy';
}
if ($this->joinTable) {
$serialized[] = 'joinTable';
}
if ($this->inherited) {
$serialized[] = 'inherited';
}
if ($this->declared) {
$serialized[] = 'declared';
}
return $serialized;
}
}

View File

@ -264,8 +264,12 @@ class ClassMetadata extends ClassMetadataInfo
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* Parts that are NOT serialized because they can not be properly unserialized:
* Parts that are also NOT serialized because they can not be properly unserialized:
* - reflClass (ReflectionClass)
* - reflFields (ReflectionProperty array)
*
@ -273,31 +277,56 @@ class ClassMetadata extends ClassMetadataInfo
*/
public function __sleep()
{
return array(
'associationMappings', // unserialization "bottleneck" with many associations
'changeTrackingPolicy',
// This metadata is always serialized/cached.
$serialized = array(
'associationMappings',
'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
'customRepositoryClassName',
'discriminatorColumn',
'discriminatorValue',
'discriminatorMap',
'fieldMappings',//TODO: Not all of this stuff needs to be serialized. Only type, columnName and fieldName.
'fieldNames',
'generatorType',
'fieldMappings',
'fieldNames',
'identifier',
'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
'inheritanceType',
'isIdentifierComposite',
'isMappedSuperclass',
'isVersioned',
'lifecycleCallbacks',
'isIdentifierComposite', // TODO: REMOVE
'name',
'parentClasses',
'table',
'rootEntityName',
'subClasses',
'versionField'
'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
);
// The rest of the metadata is only serialized if necessary.
if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
$serialized[] = 'changeTrackingPolicy';
}
if ($this->customRepositoryClassName) {
$serialized[] = 'customRepositoryClassName';
}
if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
$serialized[] = 'inheritanceType';
$serialized[] = 'discriminatorColumn';
$serialized[] = 'discriminatorValue';
$serialized[] = 'discriminatorMap';
$serialized[] = 'parentClasses';
$serialized[] = 'subClasses';
}
if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
$serialized[] = 'generatorType';
}
if ($this->isMappedSuperclass) {
$serialized[] = 'isMappedSuperclass';
}
if ($this->isVersioned) {
$serialized[] = 'isVersioned';
$serialized[] = 'versionField';
}
if ($this->lifecycleCallbacks) {
$serialized[] = 'lifecycleCallbacks';
}
return $serialized;
}
/**

View File

@ -26,12 +26,9 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Contract for metadata drivers.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @version $Revision: 1393 $
* @author Jonathan H. Wage <jonwage@gmail.com>
* @todo Rename: MetadataDriver
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
* @todo Rename: MetadataDriver or MappingDriver
*/
interface Driver
{

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -29,14 +27,12 @@ use Doctrine\ORM\Mapping\Driver\Driver,
* The DriverChain allows you to add multiple other mapping drivers for
* certain namespaces
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename: MappingDriverChain or MetadataDriverChain
*/
class DriverChain implements Driver
{

View File

@ -30,7 +30,7 @@ use Doctrine\Common\Cache\ArrayCache,
Doctrine\ORM\Mapping\Driver\AbstractFileDriver;
/**
* The PhpDriver includes php files which just populate ClassMetadataInfo
* The PHPDriver includes php files which just populate ClassMetadataInfo
* instances with plain php code
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
@ -43,7 +43,7 @@ use Doctrine\Common\Cache\ArrayCache,
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename: PHPDriver
*/
class PhpDriver extends AbstractFileDriver
class PHPDriver extends AbstractFileDriver
{
/**
* {@inheritdoc}

View File

@ -0,0 +1,122 @@
<?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\Mapping\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\MappingException;
/**
* The StaticPHPDriver calls a static loadMetadata() method on your entity
* classes where you can manually populate the ClassMetadata instance.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class StaticPHPDriver implements Driver
{
private $_paths = array();
public function __construct($paths)
{
$this->addPaths((array) $paths);
}
public function addPaths(array $paths)
{
$this->_paths = array_unique(array_merge($this->_paths, $paths));
}
/**
* {@inheritdoc}
*/
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
{
call_user_func_array(array($className, 'loadMetadata'), array($metadata));
}
/**
* {@inheritDoc}
* @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it?
*/
public function getAllClassNames()
{
if ($this->_classNames !== null) {
return $this->_classNames;
}
if (!$this->_paths) {
throw MappingException::pathRequired();
}
$classes = array();
$includedFiles = array();
foreach ($this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) {
continue;
}
$sourceFile = realpath($file->getPathName());
require_once $sourceFile;
$includedFiles[] = $sourceFile;
}
}
$declared = get_declared_classes();
foreach ($declared as $className) {
$rc = new \ReflectionClass($className);
$sourceFile = $rc->getFileName();
if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) {
$classes[] = $className;
}
}
$this->_classNames = $classes;
return $classes;
}
/**
* {@inheritdoc}
*/
public function isTransient($className)
{
return method_exists($className, 'loadMetadata') ? false : true;
}
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -39,6 +37,7 @@ namespace Doctrine\ORM\Mapping;
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
class ManyToManyMapping extends AssociationMapping
{
@ -142,39 +141,11 @@ class ManyToManyMapping extends AssociationMapping
* @param object The owner of the collection.
* @param object The collection to populate.
* @param array
* @todo Remove
*/
public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array())
{
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
$joinTableConditions = array();
if ($this->isOwningSide) {
foreach ($this->relationToSourceKeyColumns as $relationKeyColumn => $sourceKeyColumn) {
// getting id
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
} else {
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->associationMappings[$this->mappedBy];
// TRICKY: since the association is inverted source and target are flipped
foreach ($owningAssoc->relationToTargetKeyColumns as $relationKeyColumn => $sourceKeyColumn) {
// getting id
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
}
$persister = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName);
$persister->loadManyToManyCollection($this, $joinTableConditions, $targetCollection);
$em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadManyToManyCollection($this, $sourceEntity, $targetCollection);
}
/** {@inheritdoc} */
@ -182,4 +153,25 @@ class ManyToManyMapping extends AssociationMapping
{
return true;
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = parent::__sleep();
$serialized[] = 'joinTableColumns';
$serialized[] = 'relationToSourceKeyColumns';
$serialized[] = 'relationToTargetKeyColumns';
if ($this->orderBy) {
$serialized[] = 'orderBy';
}
return $serialized;
}
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -43,6 +41,7 @@ namespace Doctrine\ORM\Mapping;
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @since 2.0
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
class OneToManyMapping extends AssociationMapping
{
@ -101,8 +100,6 @@ class OneToManyMapping extends AssociationMapping
/**
* {@inheritdoc}
*
* @override
*/
public function isOneToMany()
{
@ -117,23 +114,31 @@ class OneToManyMapping extends AssociationMapping
* @param $em The EntityManager to use.
* @param $joinColumnValues
* @return void
* @todo Remove
*/
public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array())
{
$persister = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName);
// a one-to-many is always inverse (does not have foreign key)
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->associationMappings[$this->mappedBy];
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) {
// getting id
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$conditions[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
$conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn];
}
}
$em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadOneToManyCollection($this, $sourceEntity, $targetCollection);
}
$persister->loadOneToManyCollection($this, $conditions, $targetCollection);
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = parent::__sleep();
if ($this->orderBy) {
$serialized[] = 'orderBy';
}
if ($this->orphanRemoval) {
$serialized[] = 'orphanRemoval';
}
return $serialized;
}
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -39,6 +37,7 @@ namespace Doctrine\ORM\Mapping;
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
class OneToOneMapping extends AssociationMapping
{
@ -135,56 +134,32 @@ class OneToOneMapping extends AssociationMapping
* @param object $targetEntity the entity to load data in
* @param EntityManager $em
* @param array $joinColumnValues Values of the join columns of $sourceEntity.
* @todo Remove
*/
public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues = array())
{
$targetClass = $em->getClassMetadata($this->targetEntityName);
return $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadOneToOneEntity($this, $sourceEntity, $targetEntity, $joinColumnValues);
}
if ($this->isOwningSide) {
// Mark inverse side as fetched in the hints, otherwise the UoW would
// try to load it in a separate query (remember: to-one inverse sides can not be lazy).
$hints = array();
if ($this->inversedBy) {
$hints['fetched'][$targetClass->name][$this->inversedBy] = true;
if ($targetClass->subClasses) {
foreach ($targetClass->subClasses as $targetSubclassName) {
$hints['fetched'][$targetSubclassName][$this->inversedBy] = true;
}
}
}
/* cascade read-only status
if ($em->getUnitOfWork()->isReadOnly($sourceEntity)) {
$hints[Query::HINT_READ_ONLY] = true;
}
*/
$targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($joinColumnValues, $targetEntity, $this, $hints);
if ($targetEntity !== null && $this->inversedBy && ! $targetClass->isCollectionValuedAssociation($this->inversedBy)) {
$targetClass->reflFields[$this->inversedBy]->setValue($targetEntity, $sourceEntity);
}
} else {
$conditions = array();
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
$owningAssoc = $targetClass->getAssociationMapping($this->mappedBy);
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$conditions[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
$targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity, $this);
if ($targetEntity !== null) {
$targetClass->setFieldValue($targetEntity, $this->mappedBy, $sourceEntity);
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = parent::__sleep();
$serialized[] = 'joinColumns';
$serialized[] = 'joinColumnFieldNames';
$serialized[] = 'sourceToTargetKeyColumns';
$serialized[] = 'targetToSourceKeyColumns';
if ($this->orphanRemoval) {
$serialized[] = 'orphanRemoval';
}
return $targetEntity;
return $serialized;
}
}

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM;

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM;

View File

@ -1,4 +1,21 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM;

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

View File

@ -30,7 +30,7 @@ use Doctrine\ORM\Mapping\ClassMetadata,
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
abstract class AbstractEntityInheritancePersister extends StandardEntityPersister
abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
{
/**
* Map from column names to class names that declare the field the column is mapped to.
@ -91,7 +91,7 @@ abstract class AbstractEntityInheritancePersister extends StandardEntityPersiste
protected function _getSelectColumnSQL($field, ClassMetadata $class)
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
$this->_resultColumnNames[$columnAlias] = $columnName;

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -28,19 +26,52 @@ use PDO,
Doctrine\ORM\EntityManager,
Doctrine\ORM\Query,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Mapping\ClassMetadata;
Doctrine\ORM\Mapping\MappingException,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\OneToOneMapping,
Doctrine\ORM\Mapping\OneToManyMapping,
Doctrine\ORM\Mapping\ManyToManyMapping;
/**
* A basic entity persister that maps an entity with no (mapped) inheritance to a single table
* in the relational database.
* A BasicEntityPersiter maps an entity to a single table in a relational database.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @since 2.0
* @todo Rename: BasicEntityPersister
* A persister is always responsible for a single entity type.
*
* EntityPersisters are used during a UnitOfWork to apply any changes to the persistent
* state of entities onto a relational database when the UnitOfWork is committed,
* as well as for basic querying of entities and their associations (not DQL).
*
* The persisting operations that are invoked during a commit of a UnitOfWork to
* persist the persistent entity state are:
*
* - {@link addInsert} : To schedule an entity for insertion.
* - {@link executeInserts} : To execute all scheduled insertions.
* - {@link update} : To update the persistent state of an entity.
* - {@link delete} : To delete the persistent state of an entity.
*
* As can be seen from the above list, insertions are batched and executed all at once
* for increased efficiency.
*
* The querying operations invoked during a UnitOfWork, either through direct find
* requests or lazy-loading, are the following:
*
* - {@link load} : Loads (the state of) a single, managed entity.
* - {@link loadAll} : Loads multiple, managed entities.
* - {@link loadOneToOneEntity} : Loads a one/many-to-one association (lazy-loading).
* - {@link loadOneToManyCollection} : Loads a one-to-many association (lazy-loading).
* - {@link loadManyToManyCollection} : Loads a many-to-many association (lazy-loading).
*
* The BasicEntityPersister implementation provides the default behavior for
* persisting and querying entities that are mapped to a single database table.
*
* Subclasses can be created to provide custom persisting and querying strategies,
* i.e. spanning multiple tables.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @since 2.0
*/
class StandardEntityPersister
class BasicEntityPersister
{
/**
* Metadata object that describes the mapping of the mapped entity class.
@ -50,16 +81,16 @@ class StandardEntityPersister
protected $_class;
/**
* The underlying Connection of the used EntityManager.
* The underlying DBAL Connection of the used EntityManager.
*
* @var Doctrine\DBAL\Connection $conn
*/
protected $_conn;
/**
* The database platform.
*
* @var AbstractPlatform
* @var Doctrine\DBAL\Platforms\AbstractPlatform
*/
protected $_platform;
@ -87,8 +118,8 @@ class StandardEntityPersister
protected $_resultColumnNames = array();
/**
* The map of column names to DBAL mapping types of all prepared columns used when INSERTing
* or UPDATEing an entity.
* The map of column names to DBAL mapping types of all prepared columns used
* when INSERTing or UPDATEing an entity.
*
* @var array
* @see _prepareInsertData($entity)
@ -127,7 +158,7 @@ class StandardEntityPersister
protected $_sqlTableAliases = array();
/**
* Initializes a new <tt>StandardEntityPersister</tt> that uses the given EntityManager
* Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager
* and persists instances of the class described by the given ClassMetadata descriptor.
*
* @param Doctrine\ORM\EntityManager $em
@ -143,9 +174,9 @@ class StandardEntityPersister
/**
* Adds an entity to the queued insertions.
* The entity remains queued until {@link executeInserts()} is invoked.
* The entity remains queued until {@link executeInserts} is invoked.
*
* @param object $entity The entitiy to queue for insertion.
* @param object $entity The entity to queue for insertion.
*/
public function addInsert($entity)
{
@ -171,7 +202,7 @@ class StandardEntityPersister
$idGen = $this->_class->idGenerator;
$isPostInsertId = $idGen->isPostInsertGenerator();
$stmt = $this->_conn->prepare($this->getInsertSQL());
$stmt = $this->_conn->prepare($this->_getInsertSQL());
$tableName = $this->_class->table['name'];
foreach ($this->_queuedInserts as $entity) {
@ -209,9 +240,9 @@ class StandardEntityPersister
* by the preceding INSERT statement and assigns it back in to the
* entities version field.
*
* @param $class
* @param $entity
* @param $id
* @param Doctrine\ORM\Mapping\ClassMetadata $class
* @param object $entity
* @param mixed $id
*/
protected function _assignDefaultVersionValue($class, $entity, $id)
{
@ -226,7 +257,16 @@ class StandardEntityPersister
}
/**
* Updates an entity.
* Updates a managed entity. The entity is updated according to its current changeset
* in the running UnitOfWork. If there is no changeset, nothing is updated.
*
* The data to update is retrieved through {@link _prepareUpdateData}.
* Subclasses that override this method are supposed to obtain the update data
* in the same way, through {@link _prepareUpdateData}.
*
* Subclasses are also supposed to take care of versioning when overriding this method,
* if necessary. The {@link _updateTable} method can be used to apply the data retrieved
* from {@_prepareUpdateData} on the target tables, thereby optionally applying versioning.
*
* @param object $entity The entity to update.
*/
@ -241,14 +281,14 @@ class StandardEntityPersister
/**
* Performs an UPDATE statement for an entity on a specific table.
* The UPDATE can be optionally versioned, which requires the entity to have a version field.
* The UPDATE can optionally be versioned, which requires the entity to have a version field.
*
* @param object $entity The entity object being updated.
* @param string $tableName The name of the table to apply the UPDATE on.
* @param array $updateData The map of columns to update (column => value).
* @param boolean $versioned Whether the UPDATE should be versioned.
*/
protected function _updateTable($entity, $tableName, $updateData, $versioned = false)
protected final function _updateTable($entity, $tableName, array $updateData, $versioned = false)
{
$set = $params = $types = array();
@ -261,7 +301,7 @@ class StandardEntityPersister
$params[] = $value;
$types[] = $this->_columnTypes[$columnName];
}
$where = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
foreach ($this->_class->identifier as $idField) {
@ -284,7 +324,7 @@ class StandardEntityPersister
$types[] = $this->_class->fieldMappings[$versionField]['type'];
}
$sql = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set)
$sql = "UPDATE $tableName SET " . implode(', ', $set)
. ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';
$result = $this->_conn->executeUpdate($sql, $params, $types);
@ -295,7 +335,12 @@ class StandardEntityPersister
}
/**
* Deletes an entity.
* Deletes a managed entity.
*
* The entity to delete must be managed and have a persistent identifier.
* The deletion happens instantaneously.
*
* Subclasses may override this method to customize the semantics of entity deletion.
*
* @param object $entity The entity to delete.
*/
@ -319,7 +364,9 @@ class StandardEntityPersister
}
/**
* Prepares the data changeset of an entity for database insertion.
* Prepares the changeset of an entity for database insertion (UPDATE).
*
* The changeset is obtained from the currently running UnitOfWork.
*
* During this preparation the array that is passed as the second parameter is filled with
* <columnName> => <value> pairs, grouped by table name.
@ -333,8 +380,6 @@ class StandardEntityPersister
* )
* </code>
*
* Notes to inheritors: Be sure to call <code>parent::_prepareData($entity, $result, $isInsert);</code>
*
* @param object $entity The entity for which to prepare the data.
* @return array The prepared data.
*/
@ -348,7 +393,7 @@ class StandardEntityPersister
}
foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
if ($versioned && $versionField == $field) {
if ($versioned && $versionField == $field) { //TODO: Needed?
continue;
}
@ -356,9 +401,9 @@ class StandardEntityPersister
$newVal = $change[1];
if (isset($this->_class->associationMappings[$field])) {
$assocMapping = $this->_class->associationMappings[$field];
$assoc = $this->_class->associationMappings[$field];
// Only owning side of x-1 associations can have a FK column.
if ( ! $assocMapping->isOwningSide || ! $assocMapping->isOneToOne()) {
if ( ! $assoc->isOwningSide || ! $assoc->isOneToOne()) {
continue;
}
@ -379,10 +424,10 @@ class StandardEntityPersister
$newValId = $uow->getEntityIdentifier($newVal);
}
$targetClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
$targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
$owningTable = $this->getOwningTable($field);
foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
foreach ($assoc->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
if ($newVal === null) {
$result[$owningTable][$sourceColumn] = null;
} else {
@ -399,6 +444,16 @@ class StandardEntityPersister
return $result;
}
/**
* Prepares the data changeset of a managed entity for database insertion (initial INSERT).
* The changeset of the entity is obtained from the currently running UnitOfWork.
*
* The default insert data preparation is the same as for updates.
*
* @param object $entity The entity for which to prepare the data.
* @return array The prepared data for the tables to update.
* @see _prepareUpdateData
*/
protected function _prepareInsertData($entity)
{
return $this->_prepareUpdateData($entity);
@ -407,8 +462,12 @@ class StandardEntityPersister
/**
* Gets the name of the table that owns the column the given field is mapped to.
*
* @param string $fieldName
* @return string
* The default implementation in BasicEntityPersister always returns the name
* of the table the entity type of this persister is mapped to, since an entity
* is always persisted to a single table with a BasicEntityPersister.
*
* @param string $fieldName The field name.
* @return string The table name.
*/
public function getOwningTable($fieldName)
{
@ -423,13 +482,13 @@ class StandardEntityPersister
* a new entity is created.
* @param $assoc The association that connects the entity to load to another entity, if any.
* @param array $hints Hints for entity creation.
* @return The loaded entity instance or NULL if the entity/the data can not be found.
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
* @todo Check identity map? loadById method? Try to guess whether $criteria is the id?
*/
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array())
{
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
$params = array_values($criteria);
$stmt = $this->_conn->executeQuery($sql, $params);
$stmt = $this->_conn->executeQuery($sql, array_values($criteria));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
@ -437,17 +496,80 @@ class StandardEntityPersister
}
/**
* Refreshes an entity.
* Loads an entity of this persister's mapped class as part of a single-valued
* association from another entity.
*
* @param OneToOneMapping $assoc The association to load.
* @param object $sourceEntity The entity that owns the association (not necessarily the "owning side").
* @param object $targetEntity The existing ghost entity (proxy) to load, if any.
* @param array $identifier The identifier of the entity to load. Must be provided if
* the association to load represents the owning side, otherwise
* the identifier is derived from the $sourceEntity.
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
*/
public function loadOneToOneEntity(OneToOneMapping $assoc, $sourceEntity, $targetEntity, array $identifier = array())
{
$targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
if ($assoc->isOwningSide) {
// Mark inverse side as fetched in the hints, otherwise the UoW would
// try to load it in a separate query (remember: to-one inverse sides can not be lazy).
$hints = array();
if ($assoc->inversedBy) {
$hints['fetched'][$targetClass->name][$assoc->inversedBy] = true;
if ($targetClass->subClasses) {
foreach ($targetClass->subClasses as $targetSubclassName) {
$hints['fetched'][$targetSubclassName][$assoc->inversedBy] = true;
}
}
}
/* cascade read-only status
if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) {
$hints[Query::HINT_READ_ONLY] = true;
}
*/
$targetEntity = $this->load($identifier, $targetEntity, $assoc, $hints);
// Complete bidirectional association, if necessary
if ($targetEntity !== null && $assoc->inversedBy && ! $targetClass->isCollectionValuedAssociation($assoc->inversedBy)) {
$targetClass->reflFields[$assoc->inversedBy]->setValue($targetEntity, $sourceEntity);
}
} else {
$sourceClass = $this->_em->getClassMetadata($assoc->sourceEntityName);
$owningAssoc = $targetClass->getAssociationMapping($assoc->mappedBy);
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$identifier[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
$targetEntity = $this->load($identifier, $targetEntity, $assoc);
if ($targetEntity !== null) {
$targetClass->setFieldValue($targetEntity, $assoc->mappedBy, $sourceEntity);
}
}
return $targetEntity;
}
/**
* Refreshes a managed entity.
*
* @param array $id The identifier of the entity as an associative array from column names to values.
* @param array $id The identifier of the entity as an associative array from
* column or field names to values.
* @param object $entity The entity to refresh.
*/
public function refresh(array $id, $entity)
{
$sql = $this->_getSelectEntitiesSQL($id);
$params = array_values($id);
$stmt = $this->_conn->executeQuery($sql, $params);
$stmt = $this->_conn->executeQuery($sql, array_values($id));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
@ -527,10 +649,8 @@ class StandardEntityPersister
public function loadAll(array $criteria = array())
{
$entities = array();
$sql = $this->_getSelectEntitiesSQL($criteria);
$params = array_values($criteria);
$stmt = $this->_conn->executeQuery($sql, $params);
$stmt = $this->_conn->executeQuery($sql, array_values($criteria));
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
@ -541,37 +661,44 @@ class StandardEntityPersister
return $entities;
}
/**
* Loads a collection of entities in a one-to-many association.
*
* @param OneToManyMapping $assoc
* @param array $criteria The criteria by which to select the entities.
* @param PersistentCollection The collection to fill.
*/
public function loadOneToManyCollection($assoc, array $criteria, PersistentCollection $coll)
{
$owningAssoc = $this->_class->associationMappings[$coll->getMapping()->mappedBy];
$sql = $this->_getSelectEntitiesSQL($criteria, $owningAssoc, $assoc->orderBy);
$params = array_values($criteria);
$stmt = $this->_conn->executeQuery($sql, $params);
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$coll->hydrateAdd($this->_createEntity($result));
}
$stmt->closeCursor();
}
/**
* Loads a collection of entities of a many-to-many association.
*
* @param ManyToManyMapping $assoc
* @param array $criteria
* @param ManyToManyMapping $assoc The association mapping of the association being loaded.
* @param object $sourceEntity The entity that owns the collection.
* @param PersistentCollection $coll The collection to fill.
*/
public function loadManyToManyCollection($assoc, array $criteria, PersistentCollection $coll)
public function loadManyToManyCollection(ManyToManyMapping $assoc, $sourceEntity, PersistentCollection $coll)
{
$sql = $this->_getSelectManyToManyEntityCollectionSQL($assoc, $criteria);
$params = array_values($criteria);
$stmt = $this->_conn->executeQuery($sql, $params);
$criteria = array();
$sourceClass = $this->_em->getClassMetadata($assoc->sourceEntityName);
$joinTableConditions = array();
if ($assoc->isOwningSide) {
foreach ($assoc->relationToSourceKeyColumns as $relationKeyColumn => $sourceKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
} else {
$owningAssoc = $this->_em->getClassMetadata($assoc->targetEntityName)->associationMappings[$assoc->mappedBy];
// TRICKY: since the association is inverted source and target are flipped
foreach ($owningAssoc->relationToTargetKeyColumns as $relationKeyColumn => $sourceKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
}
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
$stmt = $this->_conn->executeQuery($sql, array_values($criteria));
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$coll->hydrateAdd($this->_createEntity($result));
}
@ -613,10 +740,15 @@ class StandardEntityPersister
/**
* Processes an SQL result set row that contains data for an entity of the type
* this persister is responsible for.
*
* Subclasses are supposed to override this method if they need to change the
* hydration procedure for entities loaded through basic find operations or
* lazy-loading (not DQL).
*
* @param array $sqlResult The SQL result set row to process.
* @return array A tuple where the first value is the actual type of the entity and
* the second value the prepared data of the entity.
* the second value the prepared data of the entity (a map from field
* names to values).
*/
protected function _processSQLResult(array $sqlResult)
{
@ -640,51 +772,37 @@ class StandardEntityPersister
*
* @param array $criteria
* @param AssociationMapping $assoc
* @param string $orderBy
* @return string
* @todo Refactor: _getSelectSQL(...)
*/
protected function _getSelectEntitiesSQL(array &$criteria, $assoc = null, $orderBy = null)
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null)
{
// Construct WHERE conditions
$conditionSql = '';
foreach ($criteria as $field => $value) {
if ($conditionSql != '') {
$conditionSql .= ' AND ';
}
if (isset($this->_class->columnNames[$field])) {
$conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
} else if (isset($this->_class->fieldNames[$field])) {
$conditionSql .= $this->_class->getQuotedColumnName($this->_class->fieldNames[$field], $this->_platform);
} else if ($assoc !== null) {
$conditionSql .= $field;
} else {
throw ORMException::unrecognizedField($field);
}
$conditionSql .= ' = ?';
}
$joinSql = $assoc != null && $assoc->isManyToMany() ?
$this->_getSelectManyToManyJoinSQL($assoc) : '';
$orderBySql = '';
if ($orderBy !== null) {
$orderBySql = $this->_getCollectionOrderBySQL(
$orderBy, $this->_getSQLTableAlias($this->_class)
);
}
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBySql = $assoc !== null && isset($assoc->orderBy) ?
$this->_getCollectionOrderBySQL($assoc->orderBy, $this->_getSQLTableAlias($this->_class->name))
: '';
return 'SELECT ' . $this->_getSelectColumnListSQL()
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($this->_class)
. ($conditionSql ? ' WHERE ' . $conditionSql : '') . $orderBySql;
. $this->_getSQLTableAlias($this->_class->name)
. $joinSql
. ($conditionSql ? ' WHERE ' . $conditionSql : '')
. $orderBySql;
}
/**
* Generate ORDER BY SQL snippet for ordered collections.
* Gets the ORDER BY SQL snippet for ordered collections.
*
* @param array $orderBy
* @param string $baseTableAlias
* @return string
* @todo Rename: _getOrderBySQL
*/
protected function _getCollectionOrderBySQL(array $orderBy, $baseTableAlias)
protected final function _getCollectionOrderBySQL(array $orderBy, $baseTableAlias)
{
$orderBySql = '';
foreach ($orderBy as $fieldName => $orientation) {
@ -693,27 +811,28 @@ class StandardEntityPersister
}
$tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ?
$this->_getSQLTableAlias($this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']))
$this->_getSQLTableAlias($this->_class->fieldMappings[$fieldName]['inherited'])
: $baseTableAlias;
$columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform);
if ($orderBySql != '') {
$orderBySql .= ', ';
} else {
$orderBySql = ' ORDER BY ';
}
$orderBySql .= $orderBySql ? ', ' : 'ORDER BY ';
$orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation;
}
return $orderBySql;
}
/**
* Gets the SQL fragment with the list of columns to select when querying for
* an entity within this persister.
* an entity in this persister.
*
* Subclasses should override this method to alter or change the select column
* list SQL fragment. Note that in the implementation of BasicEntityPersister
* the resulting SQL fragment is generated only once and cached in {@link _selectColumnListSql}.
* Subclasses may or may not do the same.
*
* @return string The SQL fragment.
* @todo Rename: _getSelectColumnListSQL()
* @todo Rename: _getSelectColumnsSQL()
*/
protected function _getSelectColumnListSQL()
{
@ -725,7 +844,7 @@ class StandardEntityPersister
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) {
if ($columnList != '') $columnList .= ', ';
if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $this->_class);
}
@ -733,15 +852,15 @@ class StandardEntityPersister
return $this->_selectColumnListSql;
}
/**
* Gets the SQL to select a collection of entities in a many-many association.
* Gets the SQL join fragment used when selecting entities from a
* many-to-many association.
*
* @param ManyToManyMapping $manyToMany
* @param array $criteria
* @return string
*/
protected function _getSelectManyToManyEntityCollectionSQL($manyToMany, array &$criteria)
protected function _getSelectManyToManyJoinSQL(ManyToManyMapping $manyToMany)
{
if ($manyToMany->isOwningSide) {
$owningAssoc = $manyToMany;
@ -756,32 +875,12 @@ class StandardEntityPersister
$joinSql = '';
foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
if ($joinSql != '') $joinSql .= ' AND ';
$joinSql .= $this->_getSQLTableAlias($this->_class) .
$joinSql .= $this->_getSQLTableAlias($this->_class->name) .
'.' . $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform) . ' = '
. $joinTableName . '.' . $joinTableColumn;
}
$joinSql = ' INNER JOIN ' . $joinTableName . ' ON ' . $joinSql;
$conditionSql = '';
foreach ($criteria as $joinColumn => $value) {
if ($conditionSql != '') $conditionSql .= ' AND ';
$columnName = $joinTableName . '.' . $joinColumn;
$conditionSql .= $columnName . ' = ?';
}
$orderBySql = '';
if ($manyToMany->orderBy !== null) {
$orderBySql = $this->_getCollectionOrderBySQL(
$manyToMany->orderBy, $this->_getSQLTableAlias($this->_class)
);
}
return 'SELECT ' . $this->_getSelectColumnListSQL()
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($this->_class)
. $joinSql
. ' WHERE ' . $conditionSql . $orderBySql;
return " INNER JOIN $joinTableName ON $joinSql";
}
/**
@ -789,19 +888,36 @@ class StandardEntityPersister
*
* @return string
*/
public function getInsertSQL()
protected function _getInsertSQL()
{
if ($this->_insertSql === null) {
$this->_insertSql = $this->_generateInsertSQL();
$insertSql = '';
$columns = $this->_getInsertColumnList();
if (empty($columns)) {
$insertSql = $this->_platform->getEmptyIdentityInsertSQL(
$this->_class->getQuotedTableName($this->_platform),
$this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform)
);
} else {
$columns = array_unique($columns);
$values = array_fill(0, count($columns), '?');
$insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform)
. ' (' . implode(', ', $columns) . ') '
. 'VALUES (' . implode(', ', $values) . ')';
}
$this->_insertSql = $insertSql;
}
return $this->_insertSql;
}
/**
* Gets the list of columns to put in the INSERT SQL statement.
*
*
* Subclasses should override this method to alter or change the list of
* columns placed in the INSERT statements used by the persister.
*
* @return array The list of columns.
* @internal INSERT SQL is cached by getInsertSQL() per request.
*/
protected function _getInsertColumnList()
{
@ -826,33 +942,6 @@ class StandardEntityPersister
return $columns;
}
/**
* Generates the INSERT SQL used by the persister to persist entities.
*
* @return string
* @internal Result is cached by getInsertSQL() per request.
*/
protected function _generateInsertSQL()
{
$insertSql = '';
$columns = $this->_getInsertColumnList();
if (empty($columns)) {
$insertSql = $this->_platform->getEmptyIdentityInsertSQL(
$this->_class->getQuotedTableName($this->_platform),
$this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform)
);
} else {
$columns = array_unique($columns);
$values = array_fill(0, count($columns), '?');
$insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform)
. ' (' . implode(', ', $columns) . ') '
. 'VALUES (' . implode(', ', $values) . ')';
}
return $insertSql;
}
/**
* Gets the SQL snippet of a qualified column name for the given field name.
*
@ -863,7 +952,7 @@ class StandardEntityPersister
protected function _getSelectColumnSQL($field, ClassMetadata $class)
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
$this->_resultColumnNames[$columnAlias] = $columnName;
@ -875,17 +964,19 @@ class StandardEntityPersister
/**
* Gets the SQL snippet for all join columns of the given class that are to be
* placed in an SQL SELECT statement.
*
*
* @param $class
* @return string
* @todo Not reused... inline?
*/
protected function _getSelectJoinColumnsSQL(ClassMetadata $class)
private function _getSelectJoinColumnsSQL(ClassMetadata $class)
{
$sql = '';
foreach ($class->associationMappings as $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$sql .= ', ' . $this->_getSQLTableAlias($this->_class) . ".$srcColumn AS $columnAlias";
$sql .= ', ' . $this->_getSQLTableAlias($this->_class->name) . ".$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
@ -898,19 +989,92 @@ class StandardEntityPersister
}
/**
* Gets the SQL table alias for the given class.
* Gets the SQL table alias for the given class name.
*
* @param ClassMetadata $class
* @param string $className
* @return string The SQL table alias.
* @todo Remove. Binding table aliases to class names is not such a good idea.
*/
protected function _getSQLTableAlias(ClassMetadata $class)
protected function _getSQLTableAlias($className)
{
if (isset($this->_sqlTableAliases[$class->name])) {
return $this->_sqlTableAliases[$class->name];
if (isset($this->_sqlTableAliases[$className])) {
return $this->_sqlTableAliases[$className];
}
$tableAlias = $class->table['name'][0] . $this->_sqlAliasCounter++;
$this->_sqlTableAliases[$class->name] = $tableAlias;
$tableAlias = 't' . $this->_sqlAliasCounter++;
$this->_sqlTableAliases[$className] = $tableAlias;
return $tableAlias;
}
/**
* Gets the conditional SQL fragment used in the WHERE clause when selecting
* entities in this persister.
*
* Subclasses are supposed to override this method if they intend to change
* or alter the criteria by which entities are selected.
*
* @param array $criteria
* @param AssociationMapping $assoc
* @return string
*/
protected function _getSelectConditionSQL(array $criteria, $assoc = null)
{
$conditionSql = '';
foreach ($criteria as $field => $value) {
$conditionSql .= $conditionSql ? ' AND ' : '';
if (isset($this->_class->columnNames[$field])) {
if (isset($this->_class->fieldMappings[$field]['inherited'])) {
$conditionSql .= $this->_getSQLTableAlias($this->_class->fieldMappings[$field]['inherited']) . '.';
} else {
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
}
$conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
} else if ($assoc !== null) {
if ($assoc->isManyToMany()) {
$owningAssoc = $assoc->isOwningSide ? $assoc : $this->_em->getClassMetadata($assoc->targetEntityName)
->associationMappings[$assoc->mappedBy];
$conditionSql .= $owningAssoc->getQuotedJoinTableName($this->_platform) . '.' . $field;
} else {
$conditionSql .= $field;
}
} else {
throw ORMException::unrecognizedField($field);
}
$conditionSql .= ' = ?';
}
return $conditionSql;
}
/**
* Loads a collection of entities in a one-to-many association.
*
* @param OneToManyMapping $assoc
* @param array $criteria The criteria by which to select the entities.
* @param PersistentCollection The collection to load/fill.
*/
public function loadOneToManyCollection(OneToManyMapping $assoc, $sourceEntity, PersistentCollection $coll)
{
$criteria = array();
$owningAssoc = $this->_class->associationMappings[$assoc->mappedBy];
$sourceClass = $this->_em->getClassMetadata($assoc->sourceEntityName);
foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) {
$criteria[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
}
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
$params = array_values($criteria);
$stmt = $this->_conn->executeQuery($sql, $params);
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$coll->hydrateAdd($this->_createEntity($result));
}
$stmt->closeCursor();
}
//TODO
/*protected function _getOneToOneEagerFetchSQL()
{
}*/
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -21,15 +19,14 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\ORMException,
Doctrine\ORM\Mapping\ManyToManyMapping;
/**
* The joined subclass persister maps a single entity instance to several tables in the
* database as it is defined by the <tt>Class Table Inheritance</tt> strategy.
*
* @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
* @see http://martinfowler.com/eaaCatalog/classTableInheritance.html
*/
@ -116,19 +113,19 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$this->_class : $this->_em->getClassMetadata($this->_class->rootEntityName);
$rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name);
$rootTableName = $rootClass->table['name'];
$rootTableStmt = $this->_conn->prepare($rootPersister->getInsertSQL());
$rootTableStmt = $this->_conn->prepare($rootPersister->_getInsertSQL());
// Prepare statements for sub tables.
$subTableStmts = array();
if ($rootClass !== $this->_class) {
$subTableStmts[$this->_class->table['name']] = $this->_conn->prepare($this->getInsertSQL());
$subTableStmts[$this->_class->table['name']] = $this->_conn->prepare($this->_getInsertSQL());
}
foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$parentTableName = $parentClass->table['name'];
if ($parentClass !== $rootClass) {
$parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName);
$subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->getInsertSQL());
$subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL());
}
}
@ -231,27 +228,30 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
/**
* {@inheritdoc}
*/
protected function _getSelectEntitiesSQL(array &$criteria, $assoc = null, $orderBy = null)
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null)
{
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = $this->_getSQLTableAlias($this->_class);
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
// Create the column list fragment only once
if ($this->_selectColumnListSql === null) {
// Add regular columns
$columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($fieldName,
isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class);
isset($mapping['inherited']) ?
$this->_em->getClassMetadata($mapping['inherited']) :
$this->_class);
}
// Add foreign key columns
foreach ($this->_class->associationMappings as $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
$tableAlias = $assoc->inherited ?
$this->_getSQLTableAlias($this->_em->getClassMetadata($assoc->inherited))
foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne()) {
$tableAlias = $assoc2->inherited ?
$this->_getSQLTableAlias($assoc2->inherited)
: $baseTableAlias;
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ", $tableAlias.$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
@ -262,12 +262,12 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
}
}
// Add discriminator column (DO NOT ALIAS THIS COLUMN, see StandardEntityPersister#_processSQLResultInheritanceAware).
// Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#_processSQLResult).
$discrColumn = $this->_class->discriminatorColumn['name'];
if ($this->_class->rootEntityName == $this->_class->name) {
$columnList .= ", $baseTableAlias.$discrColumn";
} else {
$columnList .= ', ' . $this->_getSQLTableAlias($this->_em->getClassMetadata($this->_class->rootEntityName))
$columnList .= ', ' . $this->_getSQLTableAlias($this->_class->rootEntityName)
. ".$discrColumn";
}
@ -279,7 +279,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClass);
$tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($idColumns as $idColumn) {
@ -291,7 +291,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// OUTER JOIN sub tables
foreach ($this->_class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$tableAlias = $this->_getSQLTableAlias($subClass);
$tableAlias = $this->_getSQLTableAlias($subClassName);
if ($this->_selectColumnListSql === null) {
// Add subclass columns
@ -326,27 +326,14 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
}
}
$conditionSql = '';
foreach ($criteria as $field => $value) {
if ($conditionSql != '') $conditionSql .= ' AND ';
if (isset($this->_class->fieldMappings[$field]['inherited'])) {
$conditionSql .= $this->_getSQLTableAlias($this->_em->getClassMetadata($this->_class->fieldMappings[$field]['inherited'])) . '.';
} else {
$conditionSql .= $baseTableAlias . '.';
}
if (isset($this->_class->columnNames[$field])) {
$conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
} else if ($assoc !== null) {
$conditionSql .= $field;
} else {
throw ORMException::unrecognizedField($field);
}
$conditionSql .= ' = ?';
}
$joinSql .= $assoc != null && $assoc->isManyToMany() ?
$this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBySql = '';
if ($orderBy !== null) {
$orderBySql = $this->_getCollectionOrderBySQL($orderBy, $baseTableAlias);
if ($assoc != null && isset($assoc->orderBy)) {
$orderBySql = $this->_getCollectionOrderBySQL($assoc->orderBy, $baseTableAlias);
}
if ($this->_selectColumnListSql === null) {
@ -359,10 +346,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql;
}
/** Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */
/* Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */
protected function _getSelectColumnListSQL()
{
throw new \BadMethodCallException("Illegal invocation of ".__METHOD__." on JoinedSubclassPersister.");
throw new \BadMethodCallException("Illegal invocation of ".__METHOD__.".");
}
/** {@inheritdoc} */
@ -398,17 +385,4 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
return $columns;
}
/**
* Gets the SQL to select a collection of entities in a many-many association.
*
* @param ManyToManyMapping $manyToMany
* @param array $criteria
* @return string
*/
protected function _getSelectManyToManyEntityCollectionSQL($manyToMany, array &$criteria)
{
// @todo
throw new \BadMethodCallException("Not yet implemented, see http://www.doctrine-project.org/jira/browse/DDC-342");
}
}

View File

@ -26,7 +26,6 @@ use Doctrine\ORM\Mapping\ClassMetadata;
* SINGLE_TABLE strategy.
*
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @since 2.0
* @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html
*/
@ -42,24 +41,25 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
protected function _getSelectColumnListSQL()
{
$columnList = parent::_getSelectColumnListSQL();
// Append discriminator column
$discrColumn = $this->_class->discriminatorColumn['name'];
$columnList .= ", $discrColumn";
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$tableAlias = $this->_getSQLTableAlias($rootClass);
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
// Append subclass columns
foreach ($this->_class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
// Append subclass columns
// Regular columns
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) {
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
}
}
// Append subclass foreign keys
// Foreign key columns
foreach ($subClass->associationMappings as $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne() && ! $assoc->inherited) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
@ -88,14 +88,27 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
}
/** {@inheritdoc} */
protected function _getSQLTableAlias(ClassMetadata $class)
protected function _getSQLTableAlias($className)
{
if (isset($this->_sqlTableAliases[$class->rootEntityName])) {
return $this->_sqlTableAliases[$class->rootEntityName];
}
$tableAlias = $this->_em->getClassMetadata($class->rootEntityName)->table['name'][0] . $this->_sqlAliasCounter++;
$this->_sqlTableAliases[$class->rootEntityName] = $tableAlias;
return parent::_getSQLTableAlias($this->_class->rootEntityName);
}
return $tableAlias;
/** {@inheritdoc} */
protected function _getSelectConditionSQL(array $criteria, $assoc = null)
{
$conditionSql = parent::_getSelectConditionSQL($criteria, $assoc);
// Append discriminator condition
if ($conditionSql) $conditionSql .= ' AND ';
$values = array($this->_conn->quote($this->_class->discriminatorValue));
$discrValues = array_flip($this->_class->discriminatorMap);
foreach ($this->_class->subClasses as $subclassName) {
$values[] = $this->_conn->quote($discrValues[$subclassName]);
}
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.'
. $this->_class->discriminatorColumn['name']
. ' IN (' . implode(', ', $values) . ')';
return $conditionSql;
}
}

View File

@ -2,7 +2,7 @@
namespace Doctrine\ORM\Persisters;
class UnionSubclassPersister extends StandardEntityPersister
class UnionSubclassPersister extends BasicEntityPersister
{
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -44,13 +42,13 @@ class ProxyFactory
private $_proxyDir;
/**
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
* connected to the given <tt>EntityManager</tt>.
*
* @param EntityManager $em The EntityManager the new factory works for.
* @param string $proxyDir The directory to use for the proxy classes. It must exist.
* @param string $proxyNs The namespace to use for the proxy classes.
* @param boolean $autoGenerate Whether to automatically generate proxy classes.
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
* connected to the given <tt>EntityManager</tt>.
*
* @param EntityManager $em The EntityManager the new factory works for.
* @param string $proxyDir The directory to use for the proxy classes. It must exist.
* @param string $proxyNs The namespace to use for the proxy classes.
* @param boolean $autoGenerate Whether to automatically generate proxy classes.
*/
public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false)
{
@ -240,7 +238,7 @@ class ProxyFactory
return $sleepImpl;
}
/** Reference Proxy class code template */
/** Proxy class code template */
private static $_proxyClassTemplate =
'<?php

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -31,10 +29,7 @@ namespace Doctrine\ORM\Query\AST;
* SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField
* SimpleStateFieldAssociationPathExpression ::= SingleValuedAssociationPathExpression "." StateField
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -27,10 +25,7 @@ use Doctrine\ORM\Query;
* An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
* Parses a DQL query, reports any errors in it, and generates an AST.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
@ -208,12 +203,15 @@ class Parser
// Process any deferred validations of some nodes in the AST.
// This also allows post-processing of the AST for modification purposes.
$this->_processDeferredIdentificationVariables();
if ($this->_deferredPartialObjectExpressions) {
$this->_processDeferredPartialObjectExpressions();
}
if ($this->_deferredPathExpressions) {
$this->_processDeferredPathExpressions($AST);
}
if ($this->_deferredResultVariables) {
$this->_processDeferredResultVariables();
}
@ -461,7 +459,7 @@ class Parser
// Check if IdentificationVariable exists in queryComponents
if ( ! isset($this->_queryComponents[$identVariable])) {
$this->semanticalError(
"'$identVariable' is not defined.", $deferredItem['token']
"'$identVariable' is not defined.", $deferredItem['token']
);
}
@ -470,14 +468,14 @@ class Parser
// Check if queryComponent points to an AbstractSchemaName or a ResultVariable
if ( ! isset($qComp['metadata'])) {
$this->semanticalError(
"'$identVariable' does not point to a Class.", $deferredItem['token']
"'$identVariable' does not point to a Class.", $deferredItem['token']
);
}
// Validate if identification variable nesting level is lower or equal than the current one
if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
$this->semanticalError(
"'$identVariable' is used outside the scope of its declaration.", $deferredItem['token']
"'$identVariable' is used outside the scope of its declaration.", $deferredItem['token']
);
}
}
@ -491,15 +489,15 @@ class Parser
foreach ($expr->partialFieldSet as $field) {
if ( ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
"There is no mapped field named '$field' on class " . $class->name . ".",
$deferredItem['token']
"There is no mapped field named '$field' on class " . $class->name . ".",
$deferredItem['token']
);
}
}
if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) {
$this->semanticalError(
"The partial field selection of class " . $class->name . " must contain the identifier.",
$deferredItem['token']
"The partial field selection of class " . $class->name . " must contain the identifier.",
$deferredItem['token']
);
}
}
@ -519,7 +517,7 @@ class Parser
// Check if ResultVariable exists in queryComponents
if ( ! isset($this->_queryComponents[$resultVariable])) {
$this->semanticalError(
"'$resultVariable' is not defined.", $deferredItem['token']
"'$resultVariable' is not defined.", $deferredItem['token']
);
}
@ -528,14 +526,14 @@ class Parser
// Check if queryComponent points to an AbstractSchemaName or a ResultVariable
if ( ! isset($qComp['resultVariable'])) {
$this->semanticalError(
"'$identVariable' does not point to a ResultVariable.", $deferredItem['token']
"'$identVariable' does not point to a ResultVariable.", $deferredItem['token']
);
}
// Validate if identification variable nesting level is lower or equal than the current one
if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
$this->semanticalError(
"'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token']
"'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token']
);
}
}
@ -557,11 +555,16 @@ class Parser
{
foreach ($this->_deferredPathExpressions as $deferredItem) {
$pathExpression = $deferredItem['expression'];
$parts = $pathExpression->parts;
$numParts = count($parts);
$qComp = $this->_queryComponents[$pathExpression->identificationVariable];
$numParts = count($pathExpression->parts);
if ($numParts == 0) {
$pathExpression->parts = array($qComp['metadata']->identifier[0]);
$numParts++;
}
$parts = $pathExpression->parts;
$aliasIdentificationVariable = $pathExpression->identificationVariable;
$parentField = $pathExpression->identificationVariable;
$class = $qComp['metadata'];
@ -572,24 +575,24 @@ class Parser
// Check if it is not in a state field
if ($fieldType & AST\PathExpression::TYPE_STATE_FIELD) {
$this->semanticalError(
'Cannot navigate through state field named ' . $field . ' on ' . $parentField,
$deferredItem['token']
'Cannot navigate through state field named ' . $field . ' on ' . $parentField,
$deferredItem['token']
);
}
// Check if it is not a collection field
if ($fieldType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
$this->semanticalError(
'Cannot navigate through collection field named ' . $field . ' on ' . $parentField,
$deferredItem['token']
'Cannot navigate through collection field named ' . $field . ' on ' . $parentField,
$deferredItem['token']
);
}
// Check if field or association exists
if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
'Class ' . $class->name . ' has no field or association named ' . $field,
$deferredItem['token']
'Class ' . $class->name . ' has no field or association named ' . $field,
$deferredItem['token']
);
}
@ -602,8 +605,8 @@ class Parser
$class = $this->_em->getClassMetadata($assoc->targetEntityName);
if (
($curIndex != $numParts - 1) &&
! isset($this->_queryComponents[$aliasIdentificationVariable . '.' . $field])
($curIndex != $numParts - 1) &&
! isset($this->_queryComponents[$aliasIdentificationVariable . '.' . $field])
) {
// Building queryComponent
$joinQueryComponent = array(
@ -617,14 +620,15 @@ class Parser
// Create AST node
$joinVariableDeclaration = new AST\JoinVariableDeclaration(
new AST\Join(
AST\Join::JOIN_TYPE_INNER,
new AST\JoinAssociationPathExpression($aliasIdentificationVariable, $field),
$aliasIdentificationVariable . '.' . $field,
false
),
null
new AST\Join(
AST\Join::JOIN_TYPE_INNER,
new AST\JoinAssociationPathExpression($aliasIdentificationVariable, $field),
$aliasIdentificationVariable . '.' . $field,
false
),
null
);
$AST->fromClause->identificationVariableDeclarations[0]->joinVariableDeclarations[] = $joinVariableDeclaration;
$this->_queryComponents[$aliasIdentificationVariable . '.' . $field] = $joinQueryComponent;
@ -915,12 +919,12 @@ class Parser
$identVariable = $this->IdentificationVariable();
$parts = array();
do {
while ($this->_lexer->isNextToken(Lexer::T_DOT)) {
$this->match(Lexer::T_DOT);
$this->match(Lexer::T_IDENTIFIER);
$parts[] = $this->_lexer->token['value'];
} while ($this->_lexer->isNextToken(Lexer::T_DOT));
}
// Creating AST node
$pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $parts);
@ -2168,7 +2172,7 @@ class Parser
return $this->SingleValuedPathExpression();
}
return $this->IdentificationVariable();
return $this->SimpleStateFieldPathExpression();
case Lexer::T_INPUT_PARAMETER:
return $this->InputParameter();
@ -2619,9 +2623,10 @@ class Parser
public function CustomFunctionsReturningNumerics()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcNameLower);
$function = new $funcClass($funcNameLower);
$funcName = strtolower($this->_lexer->lookahead['value']);
// getCustomNumericFunction is case-insensitive
$funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcName);
$function = new $funcClass($funcName);
$function->parse($this);
return $function;
@ -2642,9 +2647,10 @@ class Parser
public function CustomFunctionsReturningDatetime()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcNameLower);
$function = new $funcClass($funcNameLower);
$funcName = $this->_lexer->lookahead['value'];
// getCustomDatetimeFunction is case-insensitive
$funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcName);
$function = new $funcClass($funcName);
$function->parse($this);
return $function;
@ -2670,9 +2676,10 @@ class Parser
public function CustomFunctionsReturningStrings()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcNameLower);
$function = new $funcClass($funcNameLower);
$funcName = $this->_lexer->lookahead['value'];
// getCustomStringFunction is case-insensitive
$funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcName);
$function = new $funcClass($funcName);
$function->parse($this);
return $function;

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -240,16 +238,17 @@ class SqlWalker implements TreeWalker
{
$sql = '';
$baseTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
$baseTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
// INNER JOIN parent class tables
foreach ($class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->getSqlTableAlias($parentClass->table['name'], $dqlAlias);
$sql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform)
$tableAlias = $this->getSQLTableAlias($parentClass->table['name'], $dqlAlias);
// If this is a joined association we must use left joins to preserve the correct result.
$sql .= isset($this->_queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform)
. ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
if ($first) $first = false; else $sql .= ' AND ';
@ -260,14 +259,13 @@ class SqlWalker implements TreeWalker
}
}
// LEFT JOIN subclass tables, if partial objects disallowed
// LEFT JOIN subclass tables, if partial objects disallowed.
if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$tableAlias = $this->getSqlTableAlias($subClass->table['name'], $dqlAlias);
$tableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias);
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform)
. ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
if ($first) $first = false; else $sql .= ' AND ';
@ -288,9 +286,7 @@ class SqlWalker implements TreeWalker
$sql = '';
foreach ($this->_selectedClasses AS $dqlAlias => $class) {
$qComp = $this->_queryComponents[$dqlAlias];
if (isset($qComp['relation']) && ($qComp['relation']->isManyToMany() || $qComp['relation']->isOneToMany())
&& $qComp['relation']->orderBy != null) {
if (isset($qComp['relation']->orderBy)) {
foreach ($qComp['relation']->orderBy AS $fieldName => $orientation) {
if ($qComp['metadata']->isInheritanceTypeJoined()) {
$tableName = $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName);
@ -301,8 +297,8 @@ class SqlWalker implements TreeWalker
if ($sql != '') {
$sql .= ', ';
}
$sql .= $this->getSqlTableAlias($tableName, $dqlAlias) . "." .
$qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . " ".$orientation;
$sql .= $this->getSqlTableAlias($tableName, $dqlAlias) . '.' .
$qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . " $orientation";
}
}
}
@ -315,7 +311,7 @@ class SqlWalker implements TreeWalker
* @param string $dqlAlias
* @return string
*/
private function _generateDiscriminatorColumnConditionSql($dqlAlias)
private function _generateDiscriminatorColumnConditionSQL($dqlAlias)
{
$sql = '';
@ -340,7 +336,6 @@ class SqlWalker implements TreeWalker
return $sql;
}
/**
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
*
@ -353,7 +348,7 @@ class SqlWalker implements TreeWalker
if (($whereClause = $AST->whereClause) !== null) {
$sql .= $this->walkWhereClause($whereClause);
} else if (($discSql = $this->_generateDiscriminatorColumnConditionSql($this->_currentRootAlias)) !== '') {
} else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias)) !== '') {
$sql .= ' WHERE ' . $discSql;
}
@ -387,7 +382,7 @@ class SqlWalker implements TreeWalker
if (($whereClause = $AST->whereClause) !== null) {
$sql .= $this->walkWhereClause($whereClause);
} else if (($discSql = $this->_generateDiscriminatorColumnConditionSql($this->_currentRootAlias)) !== '') {
} else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias)) !== '') {
$sql .= ' WHERE ' . $discSql;
}
@ -407,7 +402,7 @@ class SqlWalker implements TreeWalker
if (($whereClause = $AST->whereClause) !== null) {
$sql .= $this->walkWhereClause($whereClause);
} else if (($discSql = $this->_generateDiscriminatorColumnConditionSql($this->_currentRootAlias)) !== '') {
} else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias)) !== '') {
$sql .= ' WHERE ' . $discSql;
}
@ -458,6 +453,7 @@ class SqlWalker implements TreeWalker
$sql .= $class->getQuotedColumnName($fieldName, $this->_platform);
break;
case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:
// 1- the owning side:
// Just use the foreign key, i.e. u.group_id
@ -477,13 +473,18 @@ class SqlWalker implements TreeWalker
if (count($assoc->sourceToTargetKeyColumns) > 1) {
throw QueryException::associationPathCompositeKeyNotSupported();
}
$sql .= $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.'
. reset($assoc->targetToSourceKeyColumns);
if ($this->_useSqlTableAliases) {
$sql .= $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.';
}
$sql .= reset($assoc->targetToSourceKeyColumns);
} else {
// 2- Inverse side: NOT (YET?) SUPPORTED
throw QueryException::associationPathInverseSideNotSupported();
}
break;
default:
throw QueryException::invalidPathExpression($pathExpr);
}
@ -780,7 +781,7 @@ class SqlWalker implements TreeWalker
). ')';
}
$discrSql = $this->_generateDiscriminatorColumnConditionSql($joinedDqlAlias);
$discrSql = $this->_generateDiscriminatorColumnConditionSQL($joinedDqlAlias);
if ($discrSql) {
$sql .= ' AND ' . $discrSql;
@ -1217,7 +1218,7 @@ class SqlWalker implements TreeWalker
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
);
$discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_currentRootAlias);
$discrSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias);
if ($discrSql) {
$sql .= ' AND ' . $discrSql;

View File

@ -134,16 +134,16 @@ EOT
}
$converter = new ConvertDoctrine1Schema($fromPaths);
$metadatas = $converter->getMetadatas();
$metadata = $converter->getMetadata();
if ($metadatas) {
$output->write(PHP_EOL);
foreach ($metadatas as $metadata) {
$output->write(sprintf('Processing entity "<info>%s</info>"', $metadata->name) . PHP_EOL);
foreach ($metadata as $class) {
$output->write(sprintf('Processing entity "<info>%s</info>"', $class->name) . PHP_EOL);
}
$exporter->setMetadatas($metadatas);
$exporter->setMetadata($metadata);
$exporter->export();
$output->write(PHP_EOL . sprintf(

View File

@ -97,8 +97,8 @@ EOT
}
$cmf = new DisconnectedClassMetadataFactory($em);
$metadatas = $cmf->getAllMetadata();
$metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));
$metadata = $cmf->getAllMetadata();
$metadata = MetadataFilter::filter($metadata, $input->getOption('filter'));
// Process destination directory
if ( ! is_dir($destPath = $input->getArgument('dest-path'))) {
@ -132,12 +132,12 @@ EOT
}
}
if (count($metadatas)) {
foreach ($metadatas as $metadata) {
$output->write(sprintf('Processing entity "<info>%s</info>"', $metadata->name) . PHP_EOL);
if (count($metadata)) {
foreach ($metadata as $class) {
$output->write(sprintf('Processing entity "<info>%s</info>"', $class->name) . PHP_EOL);
}
$exporter->setMetadatas($metadatas);
$exporter->setMetadata($metadata);
$exporter->export();
$output->write(PHP_EOL . sprintf(

View File

@ -113,7 +113,7 @@ EOT
);
}
if ( count($metadatas)) {
if (count($metadatas)) {
// Create EntityGenerator
$entityGenerator = new EntityGenerator();

View File

@ -24,7 +24,8 @@ namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console,
Doctrine\ORM\Tools\Console\MetadataFilter;
Doctrine\ORM\Tools\Console\MetadataFilter,
Doctrine\ORM\Tools\EntityRepositoryGenerator;
/**
* Command to generate repository classes for mapping information.
@ -40,23 +41,6 @@ use Symfony\Components\Console\Input\InputArgument,
*/
class GenerateRepositoriesCommand extends Console\Command\Command
{
private static $_template =
'<?php
namespace <namespace>;
use \Doctrine\ORM\EntityRepository;
/**
* <className>
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class <className> extends EntityRepository
{
}';
/**
* @see Console\Command\Command
*/
@ -103,8 +87,9 @@ EOT
);
}
if ( count($metadatas)) {
if (count($metadatas)) {
$numRepositories = 0;
$generator = new EntityRepositoryGenerator();
foreach ($metadatas as $metadata) {
if ($metadata->customRepositoryClassName) {
@ -112,7 +97,7 @@ EOT
sprintf('Processing repository "<info>%s</info>"', $metadata->customRepositoryClassName) . PHP_EOL
);
$this->_generateRepositoryClass($metadata, $destPath);
$generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath);
$numRepositories++;
}
@ -128,19 +113,4 @@ EOT
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
private function _generateRepositoryClass($metadata, $destPath)
{
$code = $this->_generateRepositoryClass($metadata->customRepositoryClassName);
$path = $destPath . DIRECTORY_SEPARATOR
. str_replace('\\', \DIRECTORY_SEPARATOR, $metadata->customRepositoryClassName) . '.php';
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($path, $code);
}
}

View File

@ -41,7 +41,8 @@ class ConvertDoctrine1Schema
private $_legacyTypeMap = array(
// TODO: This list may need to be updated
'clob' => 'text',
'timestamp' => 'datetime'
'timestamp' => 'datetime',
'enum' => 'string'
);
/**
@ -238,6 +239,7 @@ class ConvertDoctrine1Schema
if (isset($relation['refClass'])) {
$type = 'many';
$foreignType = 'many';
$joinColumns = array();
} else {
$type = isset($relation['type']) ? $relation['type'] : 'one';
$foreignType = isset($relation['foreignType']) ? $relation['foreignType'] : 'many';

View File

@ -98,7 +98,7 @@ class EntityGenerator
*/
public function <methodName>()
{
return $this-><fieldName>;
<spaces>return $this-><fieldName>;
}';
private static $_setMethodTemplate =
@ -109,7 +109,7 @@ public function <methodName>()
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
$this-><fieldName> = $<variableName>;
<spaces>$this-><fieldName> = $<variableName>;
}';
private static $_addMethodTemplate =
@ -120,7 +120,7 @@ public function <methodName>(<methodTypeHint>$<variableName>)
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
$this-><fieldName>[] = $<variableName>;
<spaces>$this-><fieldName>[] = $<variableName>;
}';
private static $_lifecycleCallbackMethodTemplate =
@ -129,7 +129,7 @@ public function <methodName>(<methodTypeHint>$<variableName>)
*/
public function <methodName>()
{
// Add your code here
<spaces>// Add your code here
}';
/**
@ -203,7 +203,8 @@ public function <methodName>()
$this->_generateEntityBody($metadata)
);
return str_replace($placeHolders, $replacements, self::$_classTemplate);
$code = str_replace($placeHolders, $replacements, self::$_classTemplate);
return str_replace('<spaces>', $this->_spaces, $code);
}
/**
@ -218,9 +219,10 @@ public function <methodName>()
$currentCode = file_get_contents($path);
$body = $this->_generateEntityBody($metadata);
$body = str_replace('<spaces>', $this->_spaces, $body);
$last = strrpos($currentCode, '}');
return substr($currentCode, 0, $last) . $body . '}';
return substr($currentCode, 0, $last) . $body . "\n}";
}
/**

View File

@ -0,0 +1,83 @@
<?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\Tools;
/**
* Class to generate entity repository classes
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityRepositoryGenerator
{
protected static $_template =
'<?php
namespace <namespace>;
use Doctrine\ORM\EntityRepository;
/**
* <className>
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class <className> extends EntityRepository
{
}';
public function generateEntityRepositoryClass($fullClassName)
{
$namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\'));
$className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName));
$variables = array(
'<namespace>' => $namespace,
'<className>' => $className
);
return str_replace(array_keys($variables), array_values($variables), self::$_template);
}
public function writeEntityRepositoryClass($fullClassName, $outputDirectory)
{
$code = $this->generateEntityRepositoryClass($fullClassName);
$path = $outputDirectory . DIRECTORY_SEPARATOR
. str_replace('\\', \DIRECTORY_SEPARATOR, $fullClassName) . '.php';
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
}
if ( ! file_exists($path)) {
file_put_contents($path, $code);
}
}
}

View File

@ -63,7 +63,7 @@ class ClassMetadataExporter
* @param string $source The directory where the exporter will export to
* @return AbstractExporter $exporter
*/
public function getExporter($type, $dest)
public function getExporter($type, $dest = null)
{
if ( ! isset(self::$_exporterDrivers[$type])) {
throw ExportException::invalidExporterDriverType($type);

View File

@ -49,6 +49,9 @@ class AnnotationExporter extends AbstractExporter
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
{
if ( ! $this->_entityGenerator) {
throw new \RuntimeException('For the AnnotationExporter you must set an EntityGenerator instance with the setEntityGenerator() method.');
}
$this->_entityGenerator->setGenerateAnnotations(true);
$this->_entityGenerator->setGenerateStubMethods(false);
$this->_entityGenerator->setRegenerateEntityIfExists(false);

View File

@ -2,8 +2,12 @@
namespace Doctrine\ORM\Tools;
class ToolsException extends ORMException {
public static function couldNotMapDoctrine1Type($type) {
use Doctrine\ORM\ORMException;
class ToolsException extends ORMException
{
public static function couldNotMapDoctrine1Type($type)
{
return new self("Could not map doctrine 1 type '$type'!");
}
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id: UnitOfWork.php 4947 2008-09-12 13:16:05Z romanb $
*
* 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
@ -33,15 +31,12 @@ use Doctrine\Common\Collections\ArrayCollection,
* "object-level" transaction and for writing out changes to the database
* in the correct order.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @internal This class contains performance-critical code.
* @internal This class contains highly performance-sensitive code.
*/
class UnitOfWork implements PropertyChangedListener
{
@ -1971,7 +1966,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! isset($this->_persisters[$entityName])) {
$class = $this->_em->getClassMetadata($entityName);
if ($class->isInheritanceTypeNone()) {
$persister = new Persisters\StandardEntityPersister($this->_em, $class);
$persister = new Persisters\BasicEntityPersister($this->_em, $class);
} else if ($class->isInheritanceTypeSingleTable()) {
$persister = new Persisters\SingleTablePersister($this->_em, $class);
} else if ($class->isInheritanceTypeJoined()) {

View File

@ -488,7 +488,7 @@ class Application
{
// namespace
$namespace = '';
if (false !== $pos = strpos($name, ':'))
if (false !== $pos = strrpos($name, ':'))
{
$namespace = $this->findNamespace(substr($name, 0, $pos));
$name = substr($name, $pos + 1);

View File

@ -276,7 +276,7 @@ class Command
*/
public function setName($name)
{
if (false !== $pos = strpos($name, ':'))
if (false !== $pos = strrpos($name, ':'))
{
$namespace = substr($name, 0, $pos);
$name = substr($name, $pos + 1);
@ -375,6 +375,28 @@ class Command
return $this->help;
}
/**
* Returns the processed help for the command replacing the %command.name% and
* %command.full_name% patterns with the real values dynamically.
*
* @return string The processed help for the command
*/
public function getProcessedHelp()
{
$name = $this->namespace.':'.$this->name;
$placeholders = array(
'%command.name%',
'%command.full_name%'
);
$replacements = array(
$name,
$_SERVER['PHP_SELF'].' '.$name
);
return str_replace($placeholders, $replacements, $this->getHelp());
}
/**
* Sets the aliases for the command.
*
@ -457,7 +479,7 @@ class Command
$messages[] = $this->definition->asText();
if ($help = $this->getHelp())
if ($help = $this->getProcessedHelp())
{
$messages[] = '<comment>Help:</comment>';
$messages[] = ' '.implode("\n ", explode("\n", $help))."\n";

View File

@ -70,7 +70,7 @@ class ArgvInput extends Input
protected function parse()
{
$this->parsed = $this->tokens;
while ($token = array_shift($this->parsed))
while (null !== ($token = array_shift($this->parsed)))
{
if ('--' === substr($token, 0, 2))
{

View File

@ -5,7 +5,7 @@ namespace Doctrine\Tests\Mocks;
/**
* EntityPersister implementation used for mocking during tests.
*/
class EntityPersisterMock extends \Doctrine\ORM\Persisters\StandardEntityPersister
class EntityPersisterMock extends \Doctrine\ORM\Persisters\BasicEntityPersister
{
private $_inserts = array();
private $_updates = array();

View File

@ -6,9 +6,9 @@ namespace Doctrine\Tests\Models\Company;
* @Entity @Table(name="company_events")
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="event_type", type="string")
* @DiscriminatorMap({"auction" = "CompanyAuction", "raffle" = "CompanyRaffle"})
* @DiscriminatorMap({"auction"="CompanyAuction", "raffle"="CompanyRaffle"})
*/
class CompanyEvent {
abstract class CompanyEvent {
/**
* @Id @Column(type="integer")
* @GeneratedValue

View File

@ -294,5 +294,4 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
->getResult()) > 0);
}
}

View File

@ -17,9 +17,10 @@ class ClassTableInheritanceTest2 extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\CTIParent'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\CTIChild'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\CTIRelated')
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\CTIRelated'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\CTIRelated2')
));
} catch (\Exception $e) {
} catch (\Exception $ignored) {
// Swallow all exceptions. We do not test the schema tool here.
}
}
@ -49,6 +50,27 @@ class ClassTableInheritanceTest2 extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertSame($related2, $related2->getCTIParent()->getRelated());
}
public function testManyToManyToCTIHierarchy()
{
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
$mmrel = new CTIRelated2;
$child = new CTIChild;
$child->setData('child');
$mmrel->addCTIChild($child);
$this->_em->persist($mmrel);
$this->_em->persist($child);
$this->_em->flush();
$this->_em->clear();
$mmrel2 = $this->_em->find(get_class($mmrel), $mmrel->getId());
$this->assertFalse($mmrel2->getCTIChildren()->isInitialized());
$this->assertEquals(1, count($mmrel2->getCTIChildren()));
$this->assertTrue($mmrel2->getCTIChildren()->isInitialized());
$this->assertTrue($mmrel2->getCTIChildren()->get(0) instanceof CTIChild);
}
}
/**
@ -126,3 +148,29 @@ class CTIRelated {
$this->ctiParent = $ctiParent;
}
}
/** @Entity */
class CTIRelated2
{
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @ManyToMany(targetEntity="CTIChild") */
private $ctiChildren;
public function __construct() {
$this->ctiChildren = new \Doctrine\Common\Collections\ArrayCollection;
}
public function getId() {
return $this->id;
}
public function addCTIChild(CTIChild $child) {
$this->ctiChildren->add($child);
}
public function getCTIChildren() {
return $this->ctiChildren;
}
}

View File

@ -66,6 +66,7 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
// READ by DQL on subtype
$query = $this->_em->createQuery("select e from Doctrine\Tests\ORM\Functional\ChildEntity e");
$entities = $query->getResult();
$this->assertEquals(1, count($entities));
@ -77,6 +78,18 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
// READ by findAll() on subtype
$entities = $this->_em->getRepository('Doctrine\Tests\ORM\Functional\ChildEntity')->findAll();
$this->assertEquals(1, count($entities));
$this->assertTrue($entities[0] instanceof ChildEntity);
$this->assertTrue(is_numeric($entities[0]->getId()));
$this->assertEquals('thedata', $entities[0]->getData());
$this->assertEquals(1234, $entities[0]->getNumber());
$this->assertNull($entities[0]->getParentRelated());
$this->_em->clear();
// READ by joining into an STI hierarchy from outwards
$query = $this->_em->createQuery("select r,o from Doctrine\Tests\ORM\Functional\RelatedEntity r join r.owner o");
$entities = $query->getResult();
@ -194,7 +207,7 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
class ParentEntity {
/**
* @Id
* @Column(type="integer")
* @Column(name="parent_id", type="integer")
* @GeneratedValue(strategy="AUTO")
*/
private $id;
@ -317,7 +330,7 @@ class ParentRelatedEntity {
public function setData($data) {$this->data = $data;}
/**
* @OneToOne(targetEntity="ParentEntity")
* @JoinColumn(name="parent_id", referencedColumnName="id")
* @JoinColumn(name="parent_id", referencedColumnName="parent_id")
*/
private $parent;
public function getParent() {return $this->parent;}

View File

@ -0,0 +1,94 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
require_once __DIR__ . '/../../../TestInit.php';
class DDC512Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC512Customer'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC512OfferItem'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC512Item'),
));
}
public function testIssue()
{
$customer1 = new DDC512Customer();
$item = new DDC512OfferItem();
$customer1->item = $item;
$this->_em->persist($customer1);
$customer2 = new DDC512Customer();
$this->_em->persist($customer2);
$this->_em->flush();
$this->_em->clear();
$q = $this->_em->createQuery("select u,i from ".__NAMESPACE__."\\DDC512Customer u left join u.item i");
$result = $q->getResult();
$this->assertEquals(2, count($result));
$this->assertTrue($result[0] instanceof DDC512Customer);
$this->assertTrue($result[1] instanceof DDC512Customer);
if ($result[0]->id == $customer1->id) {
$this->assertTrue($result[0]->item instanceof DDC512OfferItem);
$this->assertEquals($item->id, $result[0]->item->id);
$this->assertNull($result[1]->item);
} else {
$this->assertTrue($result[1]->item instanceof DDC512OfferItem);
$this->assertNull($result[0]->item);
}
}
}
/**
* @Entity
*/
class DDC512Customer {
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* NOTE that we can currently not name the join column the same as the field
* (item = item), this currently confuses Doctrine.
*
* @OneToOne(targetEntity="DDC512OfferItem", cascade={"remove","persist"})
* @JoinColumn(name="item_id", referencedColumnName="id")
*/
public $item;
}
/**
* @Entity
*/
class DDC512OfferItem extends DDC512Item
{
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"item" = "DDC512Item", "offerItem" = "DDC512OfferItem"})
*/
class DDC512Item
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
}

View File

@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\Driver\XmlDriver,
Doctrine\ORM\Mapping\Driver\YamlDriver;
@ -264,4 +265,109 @@ class User
{
}
public static function loadMetadata(ClassMetadataInfo $metadata)
{
$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_NONE);
$metadata->setPrimaryTable(array(
'name' => 'cms_users',
));
$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT);
$metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist');
$metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', 'prePersist');
$metadata->addLifecycleCallback('doStuffOnPostPersist', 'postPersist');
$metadata->mapField(array(
'id' => true,
'fieldName' => 'id',
'type' => 'integer',
'columnName' => 'id',
));
$metadata->mapField(array(
'fieldName' => 'name',
'type' => 'string',
'length' => 50,
'unique' => true,
'nullable' => true,
'columnName' => 'name',
));
$metadata->mapField(array(
'fieldName' => 'email',
'type' => 'string',
'columnName' => 'user_email',
'columnDefinition' => 'CHAR(32) NOT NULL',
));
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
$metadata->mapOneToOne(array(
'fieldName' => 'address',
'targetEntity' => 'Doctrine\\Tests\\ORM\\Mapping\\Address',
'cascade' =>
array(
0 => 'remove',
),
'mappedBy' => NULL,
'inversedBy' => 'user',
'joinColumns' =>
array(
0 =>
array(
'name' => 'address_id',
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE',
'onUpdate' => 'CASCADE'
),
),
'orphanRemoval' => false,
));
$metadata->mapOneToMany(array(
'fieldName' => 'phonenumbers',
'targetEntity' => 'Doctrine\\Tests\\ORM\\Mapping\\Phonenumber',
'cascade' =>
array(
1 => 'persist',
),
'mappedBy' => 'user',
'orphanRemoval' => false,
'orderBy' =>
array(
'number' => 'ASC',
),
));
$metadata->mapManyToMany(array(
'fieldName' => 'groups',
'targetEntity' => 'Doctrine\\Tests\\ORM\\Mapping\\Group',
'cascade' =>
array(
0 => 'remove',
1 => 'persist',
2 => 'refresh',
3 => 'merge',
4 => 'detach',
),
'mappedBy' => NULL,
'joinTable' =>
array(
'name' => 'cms_users_groups',
'joinColumns' =>
array(
0 =>
array(
'name' => 'user_id',
'referencedColumnName' => 'id',
'unique' => false,
'nullable' => false,
),
),
'inverseJoinColumns' =>
array(
0 =>
array(
'name' => 'group_id',
'referencedColumnName' => 'id',
'columnDefinition' => 'INT NULL',
),
),
),
'orderBy' => NULL,
));
}
}

View File

@ -23,7 +23,8 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\XmlMappingDriverTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\YamlMappingDriverTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\AnnotationDriverTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\PhpMappingDriverTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\PHPMappingDriverTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\StaticPHPMappingDriverTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\ClassMetadataFactoryTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\ClassMetadataLoadEventTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\BasicInheritanceMappingTest');

View File

@ -20,8 +20,10 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->rootEntityName);
$this->assertEquals(array(), $cm->subClasses);
$this->assertEquals(array(), $cm->parentClasses);
$this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm->inheritanceType);
// Customize state
$cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
$cm->setSubclasses(array("One", "Two", "Three"));
$cm->setParentClasses(array("UserParent"));
$cm->setCustomRepositoryClass("UserRepository");

View File

@ -3,12 +3,12 @@
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\Driver\PhpDriver,
Doctrine\ORM\Mapping\Driver\PHPDriver,
Doctrine\ORM\Tools\Export\ClassMetadataExporter;
require_once __DIR__ . '/../../TestInit.php';
class PhpMappingDriverTest extends AbstractMappingDriverTest
class PHPMappingDriverTest extends AbstractMappingDriverTest
{
protected function _loadDriver()
{
@ -26,6 +26,6 @@ class PhpMappingDriverTest extends AbstractMappingDriverTest
$exporter->export();
*/
return new PhpDriver($path);
return new PHPDriver($path);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\Driver\StaticPHPDriver,
Doctrine\ORM\Tools\Export\ClassMetadataExporter;
require_once __DIR__ . '/../../TestInit.php';
class StaticPHPMappingDriverTest extends AbstractMappingDriverTest
{
protected function _loadDriver()
{
return new StaticPHPDriver(__DIR__ . DIRECTORY_SEPARATOR . 'php');
}
}

View File

@ -156,7 +156,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
protected function _getMockPersister()
{
$persister = $this->getMock('Doctrine\ORM\Persisters\StandardEntityPersister', array('load'), array(), '', false);
$persister = $this->getMock('Doctrine\ORM\Persisters\BasicEntityPersister', array('load'), array(), '', false);
return $persister;
}
}

View File

@ -21,6 +21,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$query = $this->_em->createQuery($dqlToBeTested);
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)
->useQueryCache(false);
parent::assertEquals($sqlToBeConfirmed, $query->getSql());
$query->free();
} catch (\Exception $e) {
@ -385,7 +386,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('SELECT d0_.id AS id0 FROM date_time_model d0_ WHERE d0_.col_datetime > CURRENT_TIMESTAMP', $q->getSql());
}
/*public function testExistsExpressionInWhereCorrelatedSubqueryAssocCondition()
public function testExistsExpressionInWhereCorrelatedSubqueryAssocCondition()
{
$this->assertSqlGeneration(
// DQL
@ -402,7 +403,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
. ')'
);
}*/
}
public function testLimitFromQueryClass()
{
@ -584,4 +585,62 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"SELECT c0_.name AS name0, (SELECT COUNT(c1_.phonenumber) AS dctrn__1 FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234) AS sclr1 FROM cms_users c0_ WHERE c0_.name = 'jon'"
);
}
/**
* DDC-430
*/
public function testSupportSelectWithMoreThan10InputParameters()
{
$this->assertSqlGeneration(
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1 OR u.id = ?2 OR u.id = ?3 OR u.id = ?4 OR u.id = ?5 OR u.id = ?6 OR u.id = ?7 OR u.id = ?8 OR u.id = ?9 OR u.id = ?10 OR u.id = ?11",
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ?"
);
}
/**
* DDC-431
*/
public function testSupportToCustomDQLFunctions()
{
$config = $this->_em->getConfiguration();
$config->addCustomNumericFunction('MYABS', 'Doctrine\Tests\ORM\Query\MyAbsFunction');
$this->assertSqlGeneration(
'SELECT MYABS(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p',
'SELECT ABS(c0_.phonenumber) AS sclr0 FROM cms_phonenumbers c0_'
);
$config->setCustomNumericFunctions(array());
}
}
class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
{
public $simpleArithmeticExpression;
/**
* @override
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression(
$this->simpleArithmeticExpression
) . ')';
}
/**
* @override
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(\Doctrine\ORM\Query\Lexer::T_IDENTIFIER);
$parser->match(\Doctrine\ORM\Query\Lexer::T_OPEN_PARENTHESIS);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(\Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS);
}
}

View File

@ -159,4 +159,12 @@ class UpdateSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
'UPDATE cms_users SET status = ? WHERE id BETWEEN ? AND ?'
);
}
public function testSingleValuedAssociationFieldInWhere()
{
$this->assertSqlGeneration(
"UPDATE Doctrine\Tests\Models\CMS\CmsPhonenumber p SET p.phonenumber = 1234 WHERE p.user = ?1",
"UPDATE cms_phonenumbers SET phonenumber = 1234 WHERE user_id = ?"
);
}
}

View File

@ -15,7 +15,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
private static $_queryCacheImpl = null;
/* Shared connection when a TestCase is run alone (outside of it's functional suite) */
private static $_sharedConn;
protected static $_sharedConn;
/**
* @var \Doctrine\ORM\EntityManager
@ -33,13 +33,13 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
protected $_sqlLoggerStack;
/** The names of the model sets used in this testcase. */
private $_usedModelSets = array();
protected $_usedModelSets = array();
/** Whether the database schema has already been created. */
private static $_tablesCreated = array();
protected static $_tablesCreated = array();
/** List of model sets and their classes. */
private static $_modelSets = array(
protected static $_modelSets = array(
'cms' => array(
'Doctrine\Tests\Models\CMS\CmsUser',
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
@ -170,11 +170,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$forceCreateTables = false;
if ( ! isset($this->sharedFixture['conn'])) {
if ( ! isset(self::$_sharedConn)) {
self::$_sharedConn = TestUtil::getConnection();
if ( ! isset(static::$_sharedConn)) {
static::$_sharedConn = TestUtil::getConnection();
}
$this->sharedFixture['conn'] = self::$_sharedConn;
$this->sharedFixture['conn'] = static::$_sharedConn;
if ($this->sharedFixture['conn']->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) {
$forceCreateTables = true;
@ -189,12 +189,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$classes = array();
foreach ($this->_usedModelSets as $setName => $bool) {
if ( ! isset(self::$_tablesCreated[$setName])/* || $forceCreateTables*/) {
foreach (self::$_modelSets[$setName] as $className) {
if ( ! isset(static::$_tablesCreated[$setName])/* || $forceCreateTables*/) {
foreach (static::$_modelSets[$setName] as $className) {
$classes[] = $this->_em->getClassMetadata($className);
}
self::$_tablesCreated[$setName] = true;
static::$_tablesCreated[$setName] = true;
}
}

View File

@ -23,7 +23,7 @@ $connectionOptions = array(
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
$helperSet = new \Symfony\Components\Console\Helper\HelperSet(array(
$helpers = array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
);

View File

@ -13,7 +13,10 @@ require __DIR__ . '/cli-config.php';
$cli = new \Symfony\Components\Console\Application('Doctrine Command Line Interface', Doctrine\Common\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
$helperSet = $cli->getHelperSet();
foreach ($helpers as $name => $helper) {
$helperSet->set($helper, $name);
}
$cli->addCommands(array(
// DBAL Commands
new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),