1
0
mirror of synced 2025-01-31 04:21:44 +03:00

Merged Master into IBM DB2 branch

This commit is contained in:
Benjamin Eberlei 2010-05-01 04:07:32 +02:00
commit 604ed962e5
785 changed files with 100517 additions and 6230 deletions

View File

@ -1,6 +1,75 @@
# Upgrade from 2.0-ALPHA4 to 2.0-BETA1
## EntityRepository deprecates access to protected variables
Instead of accessing protected variables for the EntityManager in
a custom EntityRepository it is now required to use the getter methods
for all the three instance variables:
* `$this->_em` now accessible through `$this->getEntityManager()`
* `$this->_class` now accessible through `$this->getClassMetadata()`
* `$this->_entityName` now accessible through `$this->getEntityName()`
Important: For Beta 2 the protected visibility of these three properties will be
changed to private!
## Console migrated to Symfony Console
The Doctrine CLI has been replaced by Symfony Console Configuration
Instead of having to specify:
[php]
$cliConfig = new CliConfiguration();
$cliConfig->setAttribute('em', $entityManager);
You now have to configure the script like:
[php]
$helperSet = new \Symfony\Components\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
## Console: No need for Mapping Paths anymore
In previous versions you had to specify the --from and --from-path options
to show where your mapping paths are from the console. However this information
is already known from the Mapping Driver configuration, so the requirement
for this options were dropped.
Instead for each console command all the entities are loaded and to
restrict the operation to one or more sub-groups you can use the --filter flag.
## AnnotationDriver is not a default mapping driver anymore
In conjunction with the recent changes to Console we realized that the
annotations driver being a default metadata driver lead to lots of glue
code in the console components to detect where entities lie and how to load
them for batch updates like SchemaTool and other commands. However the
annotations driver being a default driver does not really help that much
anyways.
Therefore we decided to break backwards compability in this issue and drop
the support for Annotations as Default Driver and require our users to
specify the driver explicitly (which allows us to ask for the path to all
entities).
If you are using the annotations metadata driver as default driver, you
have to add the following lines to your bootstrap code:
$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities"));
$config->setMetadataDriverImpl($driverImpl);
You have to specify the path to your entities as either string of a single
path or array of multiple paths
to your entities. This information will be used by all console commands to
access all entities.
Xml and Yaml Drivers work as before!
## New inversedBy attribute
It is now *mandatory* that the owning side of a bidirectional association specifies the
@ -44,9 +113,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
xxx
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
@ -77,3 +159,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

@ -2,12 +2,15 @@
require_once 'Doctrine/Common/ClassLoader.php';
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine');
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine', __DIR__ . '/../lib');
$classLoader->register();
$classLoader = new \Doctrine\Common\ClassLoader('Symfony', __DIR__ . '/../lib/vendor');
$classLoader->register();
$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php';
$configuration = null;
$helperSet = null;
if (file_exists($configFile)) {
if ( ! is_readable($configFile)) {
trigger_error(
@ -17,15 +20,38 @@ if (file_exists($configFile)) {
require $configFile;
foreach ($GLOBALS as $configCandidate) {
if ($configCandidate instanceof \Doctrine\Common\CLI\Configuration) {
$configuration = $configCandidate;
foreach ($GLOBALS as $helperSetCandidate) {
if ($helperSetCandidate instanceof \Symfony\Components\Console\Helper\HelperSet) {
$helperSet = $helperSetCandidate;
break;
}
}
}
$configuration = ($configuration) ?: new \Doctrine\Common\CLI\Configuration();
$helperSet = ($helperSet) ?: new \Symfony\Components\Console\Helper\HelperSet();
$cli = new \Doctrine\Common\CLI\CLIController($configuration);
$cli->run($_SERVER['argv']);
$cli = new \Symfony\Components\Console\Application('Doctrine Command Line Interface', Doctrine\Common\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
$cli->addCommands(array(
// DBAL Commands
new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),
new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(),
// ORM Commands
new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(),
new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
));
$cli->run();

View File

@ -142,16 +142,16 @@
<nativephpunit testfile="./tests/Doctrine/Tests/ORM/Performance/AllTests.php" testdirectory="./tests" haltonfailure="false" haltonerror="false" />
<tstamp/>
<svnlastrevision svnpath="${svn.path}" workingcopy="." propertyname="svn.lastrevision"/>
<copy file="${build.dir}/logs/testsuites.xml" tofile="${log.archive.dir}/${svn.lastrevision}/log.xml" overwrite="true"/>
<!--<svnlastrevision svnpath="${svn.path}" workingcopy="." propertyname="svn.lastrevision"/>-->
<copy file="${build.dir}/logs/testsuites.xml" tofile="${log.archive.dir}/latest/log.xml" overwrite="true"/>
<if><equals arg1="${test.pmd_reports}" arg2="1" />
<then>
<exec command="${test.pdepend_exec} --jdepend-xml=${build.dir}/logs/jdepend.xml ./lib/Doctrine" />
<exec command="${test.phpmd_exec} ./lib/Doctrine xml codesize --reportfile ${build.dir}/logs/phpmd.xml" />
<copy file="${build.dir}/logs/jdepend.xml" tofile="${log.archive.dir}/${svn.lastrevision}/jdepend.xml" overwrite="true"/>
<copy file="${build.dir}/logs/phpmd.xml" tofile="${log.archive.dir}/${svn.lastrevision}/phpmd.xml" overwrite="true"/>
<copy file="${build.dir}/logs/jdepend.xml" tofile="${log.archive.dir}/latest/jdepend.xml" overwrite="true"/>
<copy file="${build.dir}/logs/phpmd.xml" tofile="${log.archive.dir}/latest/phpmd.xml" overwrite="true"/>
</then>
</if>
</target>
@ -163,7 +163,7 @@
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/common/DoctrineCommon-${version}">
<name>DoctrineCommon</name>
<summary>Common Doctrine code</summary>
<channel>pear.phpdoctrine.org</channel>
<channel>pear.doctrine-project.org</channel>
<description>The Doctrine Common package contains shared code between the other packages.</description>
<lead user="jwage" name="Jonathan H. Wage" email="jonwage@gmail.com" />
<lead user="guilhermeblanco" name="Guilherme Blanco" email="guilhermeblanco@gmail.com" />
@ -182,7 +182,7 @@
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/dbal/DoctrineDBAL-${version}">
<name>DoctrineDBAL</name>
<summary>Doctrine Database Abstraction Layer</summary>
<channel>pear.phpdoctrine.org</channel>
<channel>pear.doctrine-project.org</channel>
<description>The Doctrine DBAL package is the database abstraction layer used to power the ORM package.</description>
<lead user="jwage" name="Jonathan H. Wage" email="jonwage@gmail.com" />
<lead user="guilhermeblanco" name="Guilherme Blanco" email="guilhermeblanco@gmail.com" />
@ -201,7 +201,7 @@
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/orm/DoctrineORM-${version}">
<name>DoctrineORM</name>
<summary>Doctrine Object Relationl Mapper</summary>
<channel>pear.phpdoctrine.org</channel>
<channel>pear.doctrine-project.org</channel>
<description>The Doctrine ORM package is the primary package containing the object relational mapper.</description>
<lead user="jwage" name="Jonathan H. Wage" email="jonwage@gmail.com" />
<lead user="guilhermeblanco" name="Guilherme Blanco" email="guilhermeblanco@gmail.com" />

View File

@ -173,12 +173,19 @@
<xs:complexType name="id">
<xs:sequence>
<xs:element name="generator" type="orm:generator" minOccurs="0" />
<xs:element name="sequence-generator" type="orm:sequence-generator" minOccurs="0" maxOccurs="1" />
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" use="required" />
<xs:attribute name="column" type="xs:NMTOKEN" />
</xs:complexType>
<xs:complexType name="sequence-generator">
<xs:attribute name="sequence-name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="allocation-size" type="xs:integer" use="optional" default="1" />
<xs:attribute name="initial-value" type="xs:integer" use="optional" default="1" />
</xs:complexType>
<xs:complexType name="inverse-join-columns">
<xs:sequence>
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
@ -217,7 +224,7 @@
</xs:complexType>
<xs:complexType name="order-by-field">
<xs:attribute name="field" type="XS:NMTOKEN" use="required" />
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="direction" type="orm:order-by-direction" default="ASC" />
</xs:complexType>
@ -237,6 +244,7 @@
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
</xs:complexType>
@ -246,7 +254,7 @@
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
</xs:sequence>
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
@ -264,6 +272,7 @@
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
</xs:complexType>
<xs:complexType name="one-to-one">
@ -274,9 +283,10 @@
<xs:element name="join-columns" type="orm:join-columns"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
</xs:complexType>

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,234 +0,0 @@
<?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\Common\CLI;
use Doctrine\Common\Util\Inflector;
/**
* Abstract CLI Namespace class
*
* @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>
*/
abstract class AbstractNamespace
{
/**
* @var Configuration CLI Configuration instance
*/
private $_configuration = null;
/**
* @var AbstractPrinter CLI Printer instance
*/
private $_printer = null;
/**
* @var AbstractNamespace CLI Namespace instance
*/
private $_parentNamespace = null;
/**
* @var array Available namespaces
*/
private $_namespaces = array();
/**
* Add a single namespace to CLI.
* Example of inclusion support to a single namespace:
*
* [php]
* $cliOrmNamespace->addNamespace('my-custom-namespace');
*
* @param string $name CLI Namespace name
*
* @return CliController This object instance
*/
public function addNamespace($name)
{
$name = self::formatName($name);
if ($this->hasNamespace($name)) {
throw CLIException::cannotOverrideNamespace($name);
}
return $this->overrideNamespace($name);
}
/**
* Overrides a namespace to CLI.
* Example of inclusion support to a single namespace:
*
* [php]
* $cli->overrideNamespace('orm');
*
* @param string $name CLI Namespace name
*
* @return AbstractNamespace Newly created CLI Namespace
*/
public function overrideNamespace($name)
{
$taskNamespace = new TaskNamespace($name);
$taskNamespace->setParentNamespace($this);
$taskNamespace->setPrinter($this->_printer);
$taskNamespace->setConfiguration($this->_configuration);
$this->_namespaces[$taskNamespace->getName()] = $taskNamespace;
return $taskNamespace;
}
/**
* Retrieve CLI Namespace.
* Example of usage:
*
* [php]
* $cliOrmNamespace = $cli->getNamespace('ORM');
*
* @param string $name CLI Namespace name
*
* @return TaskNamespace CLI Namespace
*/
public function getNamespace($name)
{
$name = self::formatName($name);
return isset($this->_namespaces[$name])
? $this->_namespaces[$name] : null;
}
/**
* Check existance of a CLI Namespace
*
* @param string CLI Namespace name
*
* @return boolean TRUE if namespace if defined, false otherwise
*/
public function hasNamespace($name)
{
return ($this->getNamespace($name) !== null);
}
/**
* Defines the parent CLI Namespace
*
* @return AbstractNamespace
*/
public function setParentNamespace(AbstractNamespace $namespace)
{
$this->_parentNamespace = $namespace;
return $this;
}
/**
* Retrieves currently parent CLI Namespace
*
* @return AbstractNamespace
*/
public function getParentNamespace()
{
return $this->_parentNamespace;
}
/**
* Retrieve all defined CLI Tasks
*
* @return array
*/
public function getAvailableTasks()
{
$tasks = array();
foreach ($this->_namespaces as $namespace) {
$tasks = array_merge($tasks, $namespace->getAvailableTasks());
}
return $tasks;
}
/**
* Defines the CLI Output Printer
*
* @param AbstractPrinter $printer CLI Output Printer
*
* @return AbstractNamespace
*/
public function setPrinter(Printers\AbstractPrinter $printer = null)
{
$this->_printer = $printer ?: new Printers\AnsiColorPrinter;
return $this;
}
/**
* Retrieves currently used CLI Output Printer
*
* @return AbstractPrinter
*/
public function getPrinter()
{
return $this->_printer;
}
/**
* Defines the CLI Configuration
*
* #param Configuration $configuration CLI Configuration
*
* @return AbstractNamespace
*/
public function setConfiguration(Configuration $config)
{
$this->_configuration = $config;
return $this;
}
/**
* Retrieves currently used CLI Configuration
*
* @return Configuration
*/
public function getConfiguration()
{
return $this->_configuration;
}
/**
* Formats the CLI Namespace name into a camel-cased name
*
* @param string $name CLI Namespace name
*
* @return string Formatted CLI Namespace name
*/
public static function formatName($name)
{
return Inflector::classify($name);
}
}

View File

@ -1,306 +0,0 @@
<?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\Common\CLI;
/**
* Generic CLI Controller of Tasks execution
*
* To include a new Task support, create a task:
*
* [php]
* class MyProject\Tools\CLI\Tasks\MyTask extends Doctrine\ORM\Tools\CLI\Tasks\AbstractTask
* {
* public function run();
* public function basicHelp();
* public function extendedHelp();
* public function validate();
* }
*
* And then, load the namespace assoaicated an include the support to it in your command-line script:
*
* [php]
* $cli = new Doctrine\Common\CLI\CLIController();
* $cliNS = $cli->getNamespace('custom');
* $cliNS->addTask('myTask', 'MyProject\Tools\CLI\Tasks\MyTask');
*
* To execute, just type any classify-able name:
*
* $ cli.php custom:my-task
*
* @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 CLIController extends AbstractNamespace
{
/**
* The CLI processor of tasks
*
* @param Configuration $config
* @param AbstractPrinter $printer CLI Output printer
*/
public function __construct(Configuration $config, Printers\AbstractPrinter $printer = null)
{
$this->setPrinter($printer);
$this->setConfiguration($config);
// Include core namespaces of tasks
$ns = 'Doctrine\Common\CLI\Tasks';
$this->addNamespace('Core')
->addTask('help', $ns . '\HelpTask')
->addTask('version', $ns . '\VersionTask');
$ns = 'Doctrine\ORM\Tools\CLI\Tasks';
$this->addNamespace('Orm')
->addTask('clear-cache', $ns . '\ClearCacheTask')
->addTask('convert-mapping', $ns . '\ConvertMappingTask')
->addTask('ensure-production-settings', $ns . '\EnsureProductionSettingsTask')
->addTask('generate-proxies', $ns . '\GenerateProxiesTask')
->addTask('run-dql', $ns . '\RunDqlTask')
->addTask('schema-tool', $ns . '\SchemaToolTask')
->addTask('version', $ns . '\VersionTask')
->addTask('convert-d1-schema', $ns . '\ConvertDoctrine1SchemaTask')
->addTask('generate-entities', $ns . '\GenerateEntitiesTask')
->addTask('generate-repositories', $ns . '\GenerateRepositoriesTask');
$ns = 'Doctrine\DBAL\Tools\CLI\Tasks';
$this->addNamespace('Dbal')
->addTask('run-sql', $ns . '\RunSqlTask')
->addTask('version', $ns . '\VersionTask');
}
/**
* Add a single task to CLI Core Namespace. This method acts as a delegate.
* Example of inclusion support to a single task:
*
* [php]
* $cli->addTask('my-custom-task', 'MyProject\CLI\Tasks\MyCustomTask');
*
* @param string $name CLI Task name
* @param string $class CLI Task class (FQCN - Fully Qualified Class Name)
*
* @return CLIController This object instance
*/
public function addTask($name, $class)
{
$this->getNamespace('Core')->addTask($name, $class);
return $this;
}
/**
* Processor of CLI Tasks. Handles multiple task calls, instantiate
* respective classes and run them.
*
* @param array $args CLI Arguments
*/
public function run($args = array())
{
// Remove script file argument
$scriptFile = array_shift($args);
// If not arguments are defined, include "help"
if (empty($args)) {
array_unshift($args, 'Core:Help');
}
// Process all sent arguments
$args = $this->_processArguments($args);
try {
$this->getPrinter()->writeln('Doctrine Command Line Interface' . PHP_EOL, 'HEADER');
// Handle possible multiple tasks on a single command
foreach($args as $taskData) {
$taskName = $taskData['name'];
$taskArguments = $taskData['args'];
$this->runTask($taskName, $taskArguments);
}
return true;
} catch (\Exception $e) {
$message = $taskName . ' => ' . $e->getMessage();
if (isset($taskArguments['trace']) && $taskArguments['trace']) {
$message .= PHP_EOL . PHP_EOL . $e->getTraceAsString();
}
$this->getPrinter()->writeln($message, 'ERROR');
return false;
}
}
/**
* Executes a given CLI Task
*
* @param atring $name CLI Task name
* @param array $args CLI Arguments
*/
public function runTask($name, $args = array())
{
// Retrieve namespace name, task name and arguments
$taskPath = explode(':', $name);
// Find the correct namespace where the task is defined
$taskName = array_pop($taskPath);
$taskNamespace = $this->_retrieveTaskNamespace($taskPath);
$taskNamespace->runTask($taskName, $args);
}
/**
* Processes arguments and returns a structured hierachy.
* Example:
*
* cli.php foo -abc --option=value bar --option -a=value --optArr=value1,value2
*
* Returns:
*
* array(
* 0 => array(
* 'name' => 'foo',
* 'args' => array(
* 'a' => true,
* 'b' => true,
* 'c' => true,
* 'option' => 'value',
* ),
* ),
* 1 => array(
* 'name' => 'bar',
* 'args' => array(
* 'option' => true,
* 'a' => 'value',
* 'optArr' => array(
* 'value1', 'value2'
* ),
* ),
* ),
* )
*
* Based on implementation of Patrick Fisher <patrick@pwfisher.com> available at:
* http://pwfisher.com/nucleus/index.php?itemid=45
*
* @param array $args
*
* @return array
*/
private function _processArguments($args = array())
{
$flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE;
$regex = '/\s*[,]?\s*"([^"]*)"|\s*[,]?\s*([^,]*)/i';
$preparedArgs = array();
$out = & $preparedArgs;
foreach ($args as $arg){
// --foo --bar=baz
if (substr($arg, 0, 2) == '--'){
$eqPos = strpos($arg, '=');
// --foo
if ($eqPos === false){
$key = substr($arg, 2);
$out[$key] = isset($out[$key]) ? $out[$key] : true;
// --bar=baz
} else {
$key = substr($arg, 2, $eqPos - 2);
$value = substr($arg, $eqPos + 1);
$value = (strpos($value, ' ') !== false) ? $value : array_values(array_filter(
explode(',', $value), function ($v) { return trim($v) != ''; }
));
$out[$key] = ( ! is_array($value) || empty($value) || (is_array($value) && count($value) > 1))
? $value : $value[0];
}
// -k=value -abc
} else if (substr($arg, 0, 1) == '-'){
// -k=value
if (substr($arg, 2, 1) == '='){
$key = substr($arg, 1, 1);
$value = substr($arg, 3);
$value = (strpos($value, ' ') !== false) ? $value : array_values(array_filter(
explode(',', $value), function ($v) { return trim($v) != ''; }
));
$out[$key] = ( ! is_array($value) || (is_array($value) && count($value) > 1))
? $value : $value[0];
// -abc
} else {
$chars = str_split(substr($arg, 1));
foreach ($chars as $char){
$key = $char;
$out[$key] = isset($out[$key]) ? $out[$key] : true;
}
}
// plain-arg
} else {
$key = count($preparedArgs);
$preparedArgs[$key] = array(
'name' => $arg,
'args' => array()
);
$out = & $preparedArgs[$key]['args'];
}
}
return $preparedArgs;
}
/**
* Retrieve the correct namespace given a namespace path
*
* @param array $namespacePath CLI Namespace path
*
* @return AbstractNamespace
*/
private function _retrieveTaskNamespace($namespacePath)
{
$taskNamespace = $this;
$currentNamespacePath = '';
// Consider possible missing namespace (ie. "help") and forward to "core"
if (count($namespacePath) == 0) {
$namespacePath = array('Core');
}
// Loop through each namespace
foreach ($namespacePath as $namespaceName) {
$taskNamespace = $taskNamespace->getNamespace($namespaceName);
// If the given namespace returned "null", throw exception
if ($taskNamespace === null) {
throw CLIException::namespaceDoesNotExist($namespaceName, $currentNamespacePath);
}
$currentNamespacePath = (( ! empty($currentNamespacePath)) ? ':' : '')
. $taskNamespace->getName();
}
return $taskNamespace;
}
}

View File

@ -1,86 +0,0 @@
<?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\Common\CLI;
/**
* CLI Configuration class
*
* @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 Configuration
{
/**
* @var array Configuration attributes
*/
private $_attributes = array();
/**
* Defines a new configuration attribute
*
* @param string $name Attribute name
* @param mixed $value Attribute value
*
* @return Configuration This object instance
*/
public function setAttribute($name, $value = null)
{
$this->_attributes[$name] = $value;
if ($value === null) {
unset($this->_attributes[$name]);
}
return $this;
}
/**
* Retrieves a configuration attribute
*
* @param string $name Attribute name
*
* @return mixed Attribute value
*/
public function getAttribute($name)
{
return isset($this->_attributes[$name])
? $this->_attributes[$name] : null;
}
/**
* Checks if configuration attribute is defined
*
* @param string $name Attribute name
*
* @return boolean TRUE if attribute exists, FALSE otherwise
*/
public function hasAttribute($name)
{
return isset($this->_attributes[$name]);
}
}

View File

@ -1,103 +0,0 @@
<?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\Common\CLI;
/**
* CLI Option definition
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class Option
{
/** @var string Option name */
private $_name;
/** @var string Option default value */
private $_defaultValue;
/** @var string Option description */
private $description;
/**
* Constructs a CLI Option
*
* @param string Option name
* @param integer Option type
* @param string Option description
*/
public function __construct($name, $defaultValue, $description)
{
$this->_name = $name;
$this->_defaultValue = $defaultValue;
$this->_description = $description;
}
/**
* Retrieves the CLI Option name
*
* @return string Option name
*/
public function getName()
{
return $this->_name;
}
/**
* Retrieves the CLI Option default value
*
* @return mixed Option default value
*/
public function getDefaultValue()
{
return $this->_defaultValue;
}
/**
* Retrieves the CLI Option description
*
* @return string Option description
*/
public function getDescription()
{
return $this->_description;
}
/**
* Converts the Option instance into a string representation
*
* @return string CLI Option representation in string
*/
public function __toString()
{
$defaultValue = ( ! is_null($this->_defaultValue))
? '=' . (is_array($this->_defaultValue) ? implode(',', $this->_defaultValue) : $this->_defaultValue)
: '';
return '--' . $this->_name . $defaultValue;
}
}

View File

@ -1,482 +0,0 @@
<?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\Common\CLI;
use Doctrine\Common\CLI\Printers\AbstractPrinter;
/**
* CLI Option Group definition
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class OptionGroup
{
/* CLI Option Group CARDINALITY */
/**
* Defines the cardinality 0..N to CLI Option Group.
* This means options in this group are optional and you can
* define more than one CLI Option on a single command.
*/
const CARDINALITY_0_N = 0; // [...] [...] [...]
/**
* Defines the cardinality 0..1 to CLI Option Group.
* This means all options in this group are optional and you can
* define only one CLI Option on a single command.
*/
const CARDINALITY_0_1 = 1; // [...|...|...]
/**
* Defines the cardinality 1..1 to CLI Option Group.
* This means all options in this group are required and you must
* define only one CLI Option on a single command.
*/
const CARDINALITY_1_1 = 2; // (...|...|...)
/**
* Defines the cardinality 1..N to CLI Option Group.
* This means all options in this group are required and you must
* define at least one CLI Option on a single command.
*/
const CARDINALITY_1_N = 3; // (... ... ...)
/**
* Defines the cardinality N..N to CLI Option Group.
* This means all options in this group are required and you must
* define all CLI Options on a single command.
*/
const CARDINALITY_N_N = 4; // ... ... ...
/**
* Defines the cardinality M..N to CLI Option Group.
* This means all options in this group are either required or
* optional and you can CLI Options on a single command.
* This is the option to skip CLI Option validation.
*/
const CARDINALITY_M_N = 5; // ... ... ...
/** @var integer Option Group cardinality */
private $_cadinality;
/** @var array Option Group list of CLI Options */
private $_options;
/**
* Constructs a new CLI Option Group
*
* @param integer Option Group cardinality
* @param array CLI Option Group options
*/
public function __construct($cardinality, $options = array())
{
$this->_cardinality = $cardinality;
$this->_options = $options;
}
/**
* Retrieves the CLI Option Group cardinality
*
* @return integer Option Group cardinality
*/
public function getCardinality()
{
return $this->_cardinality;
}
/**
* Retrieves the CLI Option Group options
*
* @return array Option Group options
*/
public function getOptions()
{
return $this->_options;
}
/**
* Cleans the CLI Options inside this CLI Option Group
*
*/
public function clear()
{
$this->_options = array();
}
/**
* Includes a new CLI Option to the Option Group
*
* @param Option|OptionGroup CLI Option or CLI Option Group
* @return OptionGroup This object instance
*/
public function addOption($option)
{
if ($option instanceof Option || $option instanceof OptionGroup) {
$this->_options[] = $option;
}
return $this;
}
/**
* Formats the CLI Option Group into a single line representation
*
* @param AbstractPrinter CLI Printer
* @return string Single line string representation of CLI Option Group
*/
public function formatPlain(AbstractPrinter $printer)
{
$numOptions = count($this->_options);
if ($numOptions == 0) {
return '';
}
$style = $this->_getGroupOptionStyle();
$shouldDisplayExtras = (
$numOptions > 1 ||
$this->_cardinality == self::CARDINALITY_0_1 ||
$this->_cardinality == self::CARDINALITY_0_N
);
$str = ($shouldDisplayExtras) ? $printer->format($this->_startGroupDeclaration(), $style) : '';
// Loop through all CLI Options defined in OptionGroup
for ($i = 0; $i < $numOptions; $i++) {
$option = $this->_options[$i];
// Check for possible recursive OptionGroup
if ($option instanceof OptionGroup) {
// Simple increase nesting level by calling format recursively
$str .= $option->formatPlain($printer);
} else {
// Expose the option formatted
$str .= $printer->format((string) $option, $style);
}
// Possibly append content if needed
if ($i < $numOptions - 1) {
$str .= $printer->format($this->_separatorGroupDeclaration(), $style);
}
}
$str .= ($shouldDisplayExtras) ? $printer->format($this->_endGroupDeclaration(), $style) : '';
return $str;
}
/**
* INTERNAL:
* Defines the start Option Group declaration string
*
* @return string Start Option Group declaration string
*/
private function _startGroupDeclaration()
{
$str = '';
// Inspect cardinality of OptionGroup
switch ($this->_cardinality) {
case self::CARDINALITY_0_1:
case self::CARDINALITY_0_N:
$str .= '[';
break;
case self::CARDINALITY_1_1:
case self::CARDINALITY_1_N:
$str .= '(';
break;
case self::CARDINALITY_N_N:
case self::CARDINALITY_M_N:
default:
// Does nothing
break;
}
return $str;
}
/**
* INTERNAL:
* Defines the separator Option Group declaration string
*
* @return string Separator Option Group declaration string
*/
private function _separatorGroupDeclaration()
{
$str = '';
// Inspect cardinality of OptionGroup
switch ($this->_cardinality) {
case self::CARDINALITY_0_1:
case self::CARDINALITY_1_1:
$str .= ' | ';
break;
case self::CARDINALITY_1_N:
case self::CARDINALITY_N_N:
case self::CARDINALITY_M_N:
$str .= ' ';
break;
case self::CARDINALITY_0_N:
$str .= '] [';
break;
default:
// Does nothing
break;
}
return $str;
}
/**
* INTERNAL:
* Defines the end Option Group declaration string
*
* @return string End Option Group declaration string
*/
private function _endGroupDeclaration()
{
$str = '';
// Inspect cardinality of OptionGroup
switch ($this->_cardinality) {
case self::CARDINALITY_0_1:
case self::CARDINALITY_0_N:
$str .= ']';
break;
case self::CARDINALITY_1_1:
case self::CARDINALITY_1_N:
$str .= ')';
break;
case self::CARDINALITY_N_N:
case self::CARDINALITY_M_N:
default:
// Does nothing
break;
}
return $str;
}
/**
* INTERNAL:
* Retrieve the Option Group style based on defined cardinality
*
* @return string CLI Style string representation
*/
private function _getGroupOptionStyle()
{
$style = 'NONE';
// Inspect cardinality of OptionGroup
switch ($this->_cardinality) {
case self::CARDINALITY_0_1:
case self::CARDINALITY_0_N:
$style = 'OPT_ARG';
break;
case self::CARDINALITY_1_1:
case self::CARDINALITY_1_N:
case self::CARDINALITY_N_N:
case self::CARDINALITY_M_N:
$style = 'REQ_ARG';
break;
default:
// Does nothing
break;
}
return $style;
}
/**
* Formats the CLI Option Group into a multi-line list with respective description
*
* @param AbstractPrinter CLI Printer
* @return string Multi-line string representation of CLI Option Group
*/
public function formatWithDescription(AbstractPrinter $printer)
{
$numOptions = count($this->_options);
if ($numOptions == 0) {
return 'No available options' . PHP_EOL . PHP_EOL;
}
$str = '';
// Get list of required and optional and max length options
list(
$requiredOptions, $optionalOptions, $maxOptionLength
) = $this->_getOrganizedOptions(
$this->_options, $this->_cardinality, 0
);
// Array-unique options
$requiredOptions = array_unique($requiredOptions);
$optionalOptions = array_unique($optionalOptions);
// TODO Sort options alphabetically
// Displaying required options
for ($i = 0, $l = count($requiredOptions); $i < $l; $i++) {
$str .= $this->_displayOptionWithDescription(
$printer, $requiredOptions[$i], 'REQ_ARG', $maxOptionLength
);
// Include extra line breaks between options
$str .= PHP_EOL . PHP_EOL;
}
// Displaying optional options
for ($i = 0, $l = count($optionalOptions); $i < $l; $i++) {
$str .= $this->_displayOptionWithDescription(
$printer, $optionalOptions[$i], 'OPT_ARG', $maxOptionLength
);
// Include extra line breaks between options
$str .= PHP_EOL . PHP_EOL;
}
return $str;
}
/**
* Organize the Options into arrays of required and optional options.
* Also define the maximum length of CLI Options.
*
* @param array Array of CLI Option or CLI Option Group
* @param integer Current CLI OptionGroup cardinality
* @param integer Maximum length of CLI Options
* @return array Array containing 3 indexes: required options, optional
* options and maximum length of CLI Options
*/
private function _getOrganizedOptions($options, $cardinality, $maxColumn)
{
// Calculate maximum length and also organize the
// options into required and optional ones
$numOptions = count($options);
$requiredOptions = array();
$optionalOptions = array();
for ($i = 0; $i < $numOptions; $i++) {
$option = $options[$i];
// Check for possible recursive OptionGroup
if ($option instanceof OptionGroup) {
// Initialize OptionGroup options
$groupRequiredOptions = array();
$groupOptionalOptions = array();
// Get nested information
list(
$groupRequiredOptions, $groupOptionalOptions, $maxGroupColumn
) = $this->_getOrganizedOptions(
$option->getOptions(), $option->getCardinality(), $maxColumn
);
// Merge nested required and optional options
$requiredOptions = array_merge($requiredOptions, $groupRequiredOptions);
$optionalOptions = array_merge($optionalOptions, $groupOptionalOptions);
// If OptionGroup length is bigger than the current maximum, update
if ($maxColumn < $maxGroupColumn) {
$maxColumn = $maxGroupColumn;
}
} else {
// Cardinality defines between optional or required options
switch ($cardinality) {
case self::CARDINALITY_0_1:
case self::CARDINALITY_0_N:
$optionalOptions[] = $option;
break;
case self::CARDINALITY_1_1:
case self::CARDINALITY_1_N:
case self::CARDINALITY_N_N:
case self::CARDINALITY_M_N:
$requiredOptions[] = $option;
break;
default:
// Does nothing
break;
}
// Build Option string
$optionStr = (string) $option;
// + 2 = aditional spaces after option
$length = strlen($optionStr) + 2;
if ($maxColumn < $length) {
$maxColumn = $length;
}
}
}
return array($requiredOptions, $optionalOptions, $maxColumn);
}
/**
* INTERNAL:
* Formats the CLI Option and also include the description
*
* @param AbstractPrinter CLI Printer
* @param Option CLI Option to be formatted
* @param string CLI Style string representation
* @param integer Maximum CLI Option length
* @return string Formats the current CLI Option line(s)
*/
private function _displayOptionWithDescription($printer, $option, $style, $maxOptionLength)
{
// Expose the option formatted
$optionStr = (string) $option;
// Format Option string
$str = $printer->format($optionStr, $style);
// Include missing spaces
$str .= str_repeat(' ', $maxOptionLength - strlen($optionStr));
// Calculate and display description
$str .= str_replace(
PHP_EOL, PHP_EOL . str_repeat(' ', $maxOptionLength), $option->getDescription()
);
return $str;
}
}

View File

@ -1,190 +0,0 @@
<?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\Common\CLI\Printers;
use Doctrine\Common\CLI\Style;
/**
* CLI Output Printer.
* Abstract class responsable to provide basic methods to support output
* styling and excerpt limited by output margin.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
abstract class AbstractPrinter
{
/**
* @var resource Output Stream
*/
protected $_stream;
/**
* @var integer Maximum column size
*/
protected $_maxColumnSize;
/**
* @var array Array of Styles
*/
protected $_styles;
/**
* Creates an instance of Printer
*
* @param resource $stream Output Stream
*/
public function __construct($stream = STDOUT)
{
$this->_stream = $stream;
$this->_maxColumnSize = 80;
$this->_initStyles();
}
/**
* Initializes Printer Styles
*
*/
protected function _initStyles()
{
// Defines base styles
$this->addStyles(array(
'ERROR' => new Style(),
'INFO' => new Style(),
'COMMENT' => new Style(),
'HEADER' => new Style(),
'NONE' => new Style(),
));
}
/**
* Add a collection of styles to the Printer.
* To include them, just call the method with the following structure:
*
* [php]
* $printer->addStyles(array(
* 'ERROR' => new Style('BLACK', 'DEFAULT', array('BOLD' => true)),
* ...
* ));
*
* @param array $tasks CLI Tasks to be included
*/
public function addStyles($styles)
{
foreach ($styles as $name => $style) {
$this->addStyle($name, $style);
}
}
/**
* Add a single Style to Printer.
* Example of inclusion to support a new Style:
*
* [php]
* $printer->addStyle('ERROR', new Style('BLACK', 'DEFAULT', array('BOLD' => true)));
*
* @param string $name Style name
* @param Style $style Style instance
*/
public function addStyle($name, Style $style)
{
$this->_styles[strtoupper($name)] = $style;
}
/**
* Retrieves a defined Style.
*
* @return Style
*/
public function getStyle($name)
{
if (is_string($name)) {
$name = strtoupper($name);
return isset($this->_styles[$name]) ? $this->_styles[$name] : null;
}
return $name;
}
/**
* Sets the maximum column size (defines the CLI margin).
*
* @param integer $maxColumnSize The maximum column size for a message
*/
public function setMaxColumnSize($maxColumnSize)
{
$this->_maxColumnSize = $maxColumnSize;
}
/**
* Writes to the output stream.
*
* @param string $message Message to be outputted
*/
public function output($message)
{
fwrite($this->_stream, $message);
return $this;
}
/**
* Formats message applying the defined style and writes to the output stream.
*
* @param string $message Message to be outputted
* @param mixed $style Optional style to be applied in message
*/
public function write($message, $style = 'NONE')
{
$this->output($this->format($message, $style));
return $this;
}
/**
* Writes a line to the output stream, formatting it by applying the defined style.
*
* @param string $message Message to be outputted
* @param mixed $style Optional style to be applied in message
*/
public function writeln($message, $style = 'NONE')
{
$this->output($this->format($message, $style) . PHP_EOL);
return $this;
}
/**
* Formats the given message with the defined style.
*
* @param string $message Message to be formatted
* @param mixed $style Style to be applied in message
* @return string Formatted message
*/
abstract public function format($message, $style);
}

View File

@ -1,214 +0,0 @@
<?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\Common\CLI\Printers;
use Doctrine\Common\CLI\Style;
/**
* CLI Output Printer for ANSI Color terminal
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class AnsiColorPrinter extends AbstractPrinter
{
/**
* @inheritdoc
*/
protected function _initStyles()
{
$this->addStyles(array(
'HEADER' => new Style('DEFAULT', 'DEFAULT', array('BOLD' => true)),
'ERROR' => new Style('WHITE', 'RED', array('BOLD' => true)),
'WARNING' => new Style('DEFAULT', 'YELLOW'),
'KEYWORD' => new Style('BLUE', 'DEFAULT', array('BOLD' => true)),
'REQ_ARG' => new Style('MAGENTA', 'DEFAULT', array('BOLD' => true)),
'OPT_ARG' => new Style('CYAN', 'DEFAULT', array('BOLD' => true)),
'INFO' => new Style('GREEN', 'DEFAULT', array('BOLD' => true)),
'COMMENT' => new Style('DEFAULT', 'MAGENTA'),
'NONE' => new Style(),
));
}
/**
* @inheritdoc
*/
public function format($message, $style = 'NONE')
{
if ( ! $this->_supportsColor()) {
return $message;
}
$style = $this->getStyle($style);
$str = $this->_getForegroundString($style)
. $this->_getBackgroundString($style);
$styleSet = ($str != '');
return $str . $message . ($styleSet ? chr(27) . '[0m' : '');
}
/**
* Retrieves the ANSI string representation of requested color name
*
* @param Style $style Style
* @return string
*/
protected function _getBackgroundString(Style $style)
{
$background = $style->getBackground();
if (empty($background)) {
return '';
}
$esc = chr(27);
switch (strtoupper($background)) {
case 'BLACK':
return $esc . '[40m';
case 'RED':
return $esc . '[41m';
case 'GREEN':
return $esc . '[42m';
case 'YELLOW':
return $esc . '[43m';
case 'BLUE':
return $esc . '[44m';
case 'MAGENTA':
return $esc . '[45m';
case 'CYAN':
return $esc . '[46m';
case 'WHITE':
return $esc . '[47m';
case 'DEFAULT':
default:
return $esc . '[48m';
}
}
/**
* Retrieves the ANSI string representation of requested color name
*
* @param Style $style Style
* @return string
*/
protected function _getForegroundString(Style $style)
{
$foreground = $style->getForeground();
if (empty($foreground)) {
return '';
}
$str = chr(27) . '[' . $this->_getOptionsString($style);
switch (strtoupper($foreground)) {
case 'BLACK':
return $str . '30m';
case 'RED':
return $str . '31m';
case 'GREEN':
return $str . '32m';
case 'YELLOW':
return $str . '33m';
case 'BLUE':
return $str . '34m';
case 'MAGENTA':
return $str . '35m';
case 'CYAN':
return $str . '36m';
case 'WHITE':
return $str . '37m';
case 'DEFAULT_FGU':
return $str . '38m';
case 'DEFAULT':
default:
return $str . '39m';
}
}
/**
* Retrieves the ANSI string representation of requested options
*
* @param Style $style Style
* @return string
*/
protected function _getOptionsString(Style $style)
{
$options = $style->getOptions();
if (empty($options)) {
return '';
}
$str = '';
foreach ($options as $name => $value) {
if ($value) {
$name = strtoupper($name);
switch ($name) {
case 'BOLD':
$str .= '1;';
break;
case 'HALF':
$str .= '2;';
break;
case 'UNDERLINE':
$str .= '4;';
break;
case 'BLINK':
$str .= '5;';
break;
case 'REVERSE':
$str .= '7;';
break;
case 'CONCEAL':
$str .= '8;';
break;
default:
// Ignore unknown option
break;
}
}
}
return $str;
}
/**
* Checks if the current Output Stream supports ANSI Colors
*
* @return boolean
*/
private function _supportsColor()
{
return DIRECTORY_SEPARATOR != '\\' &&
function_exists('posix_isatty') &&
@posix_isatty($this->_stream);
}
}

View File

@ -1,46 +0,0 @@
<?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\Common\CLI\Printers;
use Doctrine\Common\CLI\Style;
/**
* CLI Output Printer for Normal terminal
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class NormalPrinter extends AbstractPrinter
{
/**
* @inheritdoc
*/
public function format($message, $style)
{
return $message;
}
}

View File

@ -1,93 +0,0 @@
<?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\Common\CLI;
/**
* CLI Output Style
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class Style
{
/**
* @var string Background color
*/
private $_background;
/**
* @var string Foreground color
*/
private $_foreground;
/**
* @var array Formatting options
*/
private $_options = array();
/**
* @param string $foreground Foreground color name
* @param string $background Background color name
* @param array $options Formatting options
*/
public function __construct($foreground = null, $background = null, $options = array())
{
$this->_foreground = strtoupper($foreground);
$this->_background = strtoupper($background);
$this->_options = $options;
}
/**
* Retrieves the foreground color name
*
* @return string
*/
public function getForeground()
{
return $this->_foreground;
}
/**
* Retrieves the background color name
*
* @return string
*/
public function getBackground()
{
return $this->_background;
}
/**
* Retrieves the formatting options
*
* @return string
*/
public function getOptions()
{
return $this->_options;
}
}

View File

@ -1,192 +0,0 @@
<?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\Common\CLI;
use Doctrine\Common\CLI\Printers\AbstractPrinter,
Doctrine\Common\CLI\OptionGroup,
Doctrine\Common\CLI\Option;
/**
* CLI Task documentation
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class TaskDocumentation
{
/** @var AbstractPrinter CLI Printer */
private $_printer;
/** @var AbstractNamespace CLI Namespace */
private $_namespace;
/** @var string CLI Task name */
private $_name;
/** @var string CLI Task description */
private $_description;
/** @var array CLI Task Option Group */
private $_optionGroup;
/**
* Constructs a new CLI Task Documentation
*
* @param AbstractNamespace CLI Namespace
*/
public function __construct(AbstractNamespace $namespace)
{
$this->_namespace = $namespace;
$this->_printer = $namespace->getPrinter();
$this->_optionGroup = new OptionGroup(OptionGroup::CARDINALITY_M_N);
}
/**
* Retrieves the CLI Namespace
*
* @return AbstractNamespace
*/
public function getNamespace()
{
return $this->_namespace;
}
/**
* Defines the CLI Task name
*
* @param string Task name
* @return TaskDocumentation This object instance
*/
public function setName($name)
{
$this->_name = $name;
return $this;
}
/**
* Retrieves the CLI Task name
*
* @return string Task name
*/
public function getName()
{
return $this->_name;
}
/**
* Retrieves the full CLI Task name
*
* @return string Task full name
*/
public function getFullName()
{
return $this->getNamespace()->getFullName() . ':' . $this->_name;
}
/**
* Defines the CLI Task description
*
* @param string Task description
* @return TaskDocumentation This object instance
*/
public function setDescription($description)
{
$this->_description = $description;
return $this;
}
/**
* Retrieves the CLI Task description
*
* @var string Task description
*/
public function getDescription()
{
return $this->_description;
}
/**
* Retrieves the CLI Task Option Group
*
* @return OptionGroup CLI Task Option Group
*/
public function getOptionGroup()
{
return $this->_optionGroup;
}
/**
* Includes a new CLI Option Group to the CLI Task documentation
*
* @param OptionGroup CLI Option Group
* @return TaskDocumentation This object instance
*/
public function addOption($option)
{
if ($option instanceof OptionGroup) {
$this->_optionGroup->addOption($option);
}
return $this;
}
/**
* Retrieves the synopsis of associated CLI Task
*
* @return string CLI Task synopsis
*/
public function getSynopsis()
{
return $this->_printer->format($this->getFullName(), 'KEYWORD') . ' '
. trim($this->_optionGroup->formatPlain($this->_printer));
}
/**
* Retrieve the complete documentation of associated CLI Task
*
* @return string CLI Task complete documentation
*/
public function getCompleteDocumentation()
{
$printer = $this->_printer;
return $printer->format('Task: ')
. $printer->format($this->getFullName(), 'KEYWORD')
. $printer->format(PHP_EOL)
. $printer->format('Synopsis: ')
. $this->getSynopsis()
. $printer->format(PHP_EOL)
. $printer->format('Description: ')
. $printer->format($this->_description)
. $printer->format(PHP_EOL)
. $printer->format('Options: ')
. $printer->format(PHP_EOL)
. $this->_optionGroup->formatWithDescription($printer);
}
}

View File

@ -1,251 +0,0 @@
<?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\Common\CLI;
/**
* CLI Namespace class
*
* @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 TaskNamespace extends AbstractNamespace
{
/**
* @var boolean CLI Tasks flag to check if they are already initialized
*/
private $_initialized = false;
/**
* @var string CLI Namespace full name
*/
private $_fullName = null;
/**
* @var string CLI Namespace name
*/
private $_name = null;
/**
* @var array Available tasks
*/
private $_tasks = array();
/**
* The CLI namespace
*
* @param string $name CLI Namespace name
*/
public function __construct($name)
{
$this->_name = self::formatName($name);
}
/**
* Retrieve an instantiated CLI Task by given its name.
*
* @param string $name CLI Task name
*
* @return AbstractTask
*/
public function getTask($name)
{
// Check if task exists in namespace
if ($this->hasTask($name)) {
$taskClass = $this->_tasks[self::formatName($name)];
return new $taskClass($this);
}
throw CLIException::taskDoesNotExist($name, $this->getFullName());
}
/**
* Retrieve all CLI Task in this Namespace.
*
* @return array
*/
public function getTasks()
{
return $this->_tasks;
}
/**
* Retrieve all defined CLI Tasks
*
* @return array
*/
public function getAvailableTasks()
{
$tasks = parent::getAvailableTasks();
foreach ($this->_tasks as $taskName => $taskClass) {
$fullName = $this->getFullName() . ':' . $taskName;
$tasks[$fullName] = $taskClass;
}
return $tasks;
}
/**
* Add a single task to CLI Namespace.
* Example of inclusion support to a single task:
*
* [php]
* $cliOrmNamespace->addTask('my-custom-task', 'MyProject\Cli\Tasks\MyCustomTask');
*
* @param string $name CLI Task name
* @param string $class CLI Task class (FQCN - Fully Qualified Class Name)
*
* @return TaskNamespace This object instance
*/
public function addTask($name, $class)
{
$name = self::formatName($name);
if ($this->hasTask($name)) {
throw CLIException::cannotOverrideTask($name);
}
return $this->overrideTask($name, $class);
}
/**
* Overrides task on CLI Namespace.
* Example of inclusion support to a single task:
*
* [php]
* $cliOrmNamespace->overrideTask('schema-tool', 'MyProject\Cli\Tasks\MyCustomTask');
*
* @param string $name CLI Task name
* @param string $class CLI Task class (FQCN - Fully Qualified Class Name)
*
* @return TaskNamespace This object instance
*/
public function overrideTask($name, $class)
{
$name = self::formatName($name);
$this->_tasks[$name] = $class;
return $this;
}
/**
* Check existance of a CLI Task
*
* @param string CLI Task name
*
* @return boolean TRUE if CLI Task if defined, false otherwise
*/
public function hasTask($name)
{
$name = self::formatName($name);
return isset($this->_tasks[$name]);
}
/**
* Retrieves the CLI Namespace name
*
* @return string CLI Namespace name
*/
public function getName()
{
return $this->_name;
}
/**
* Retrieves the full CLI Namespace name
*
* @return string CLI Namespace full name
*/
public function getFullName()
{
if ($this->_fullName === null) {
$str = $this->_name;
while (
($parentNamespace = $this->getParentNamespace()) !== null &&
! ($parentNamespace instanceof CliController)
) {
$str = $parentNamespace->getFullName() . ':' . $str;
}
$this->_fullName = $str;
}
return $this->_fullName;
}
/**
* Effectively instantiate and execute a given CLI Task
*
* @param string $name CLI Task name
* @param array $arguments CLI Task arguments
*/
public function runTask($name, $arguments = array())
{
try {
$task = $this->getTask($name);
// Merge global configuration if it exists
if (($globalArgs = $this->getConfiguration()->getAttribute('globalArguments')) !== null) {
$arguments = array_merge($globalArgs, $arguments);
}
$task->setArguments($arguments);
if ((isset($arguments['help']) && $arguments['help']) || (isset($arguments['h']) && $arguments['h'])) {
$task->extendedHelp(); // User explicitly asked for help option
} else if (isset($arguments['basic-help']) && $arguments['basic-help']) {
$task->basicHelp(); // User explicitly asked for basic help option
} else if ($task->validate()) {
$task->run();
}
} catch (CLIException $e) {
$message = $this->getFullName() . ':' . $name . ' => ' . $e->getMessage();
$printer = $this->getPrinter();
// If we want the trace of calls, append to error message
if (isset($arguments['trace']) && $arguments['trace']) {
$message .= PHP_EOL . PHP_EOL . $e->getTraceAsString();
}
$printer->writeln($message, 'ERROR');
// Unable instantiate task or task is not valid
if (isset($task) && $task !== null) {
$printer->write(PHP_EOL);
$task->basicHelp(); // Fallback of not-valid task arguments
}
$printer->write(PHP_EOL);
}
}
}

View File

@ -1,200 +0,0 @@
<?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\Common\CLI\Tasks;
use Doctrine\Common\CLI\AbstractNamespace,
Doctrine\Common\CLI\TaskDocumentation;
/**
* Base class for CLI Tasks.
* Provides basic methods and requires implementation of methods that
* each task should implement in order to correctly work.
*
* @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>
*/
abstract class AbstractTask
{
/**
* @var AbstractNamespace CLI Namespace
*/
protected $_printer;
/**
* @var TaskDocumentation CLI Task Documentation
*/
protected $_documentation;
/**
* @var array CLI Task arguments
*/
protected $_arguments = array();
/**
* Constructor of CLI Task
*
* @param AbstractNamespace CLI Namespace
*/
public function __construct(AbstractNamespace $namespace)
{
$this->_namespace = $namespace;
$this->_documentation = new TaskDocumentation($namespace);
// Complete the CLI Task Documentation creation
$this->buildDocumentation();
}
/**
* Retrieves the CLI Namespace
*
* @return AbstractNamespace
*/
public function getNamespace()
{
return $this->_namespace;
}
/**
* Retrieves the CLI Task Documentation
*
* @return TaskDocumentation
*/
public function getDocumentation()
{
return $this->_documentation;
}
/**
* Defines the CLI Task arguments
*
* @param array $arguments CLI Task arguments
*
* @return AbstractTask
*/
public function setArguments(array $arguments = array())
{
$this->_arguments = $arguments;
return $this;
}
/**
* Retrieves the CLI Task arguments
*
* @return array
*/
public function getArguments()
{
return $this->_arguments;
}
/**
* Retrieves currently used CLI Output Printer
*
* @return AbstractPrinter
*/
public function getPrinter()
{
return $this->_namespace->getPrinter();
}
/**
* Retrieves current used CLI Configuration
*
* @return Configuration
*/
public function getConfiguration()
{
return $this->_namespace->getConfiguration();
}
/**
* Expose to CLI Output Printer the extended help of the given task.
* This means it should detail all parameters, options and the meaning
* of each one.
* This method is executed when user types in CLI the following command:
*
* [bash]
* ./doctrine task --help
*
*/
public function extendedHelp()
{
$this->getPrinter()->output($this->_documentation->getCompleteDocumentation());
}
/**
* Expose to CLI Output Printer the basic help of the given task.
* This means it should only expose the basic task call. It is also
* executed when user calls the global help; so this means it should
* not pollute the Printer.
* Basic help exposure is displayed when task does not pass the validate
* (which means when user does not type the required options or when given
* options are invalid, ie: invalid option), or when user requests to have
* description of all available tasks.
* This method is executed when user uses the following commands:
*
* [bash]
* ./doctrine task --invalid-option
* ./doctrine --help
*
*/
public function basicHelp()
{
$this->getPrinter()
->output($this->_documentation->getSynopsis())
->output(PHP_EOL)
->output(' ' . $this->_documentation->getDescription())
->output(PHP_EOL . PHP_EOL);
}
/**
* Assures the given arguments matches with required/optional ones.
* This method should be used to introspect arguments to check for
* missing required arguments and also for invalid defined options.
*
* @return boolean
*/
public function validate()
{
// TODO implement DAG here!
return true;
}
/**
* Safely execution of task.
* Each CLI task should implement this as normal flow execution of
* what is supposed to do.
*/
abstract public function run();
/**
* Generate the CLI Task Documentation
*/
abstract public function buildDocumentation();
}

View File

@ -1,79 +0,0 @@
<?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\Common\CLI\Tasks;
use Doctrine\Common\CLI\CLIException;
/**
* CLI Task to display available commands help
*
* @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 HelpTask extends AbstractTask
{
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$doc = $this->getDocumentation();
$doc->setName('help')
->setDescription('Exposes helpful information about all available tasks.');
}
/**
* @inheritdoc
*/
public function extendedHelp()
{
$this->run();
}
/**
* Exposes the available tasks
*
*/
public function run()
{
$this->getPrinter()->writeln('Available Tasks:', 'HEADER')->write(PHP_EOL);
// Find the CLI Controller
$cliController = $this->getNamespace()->getParentNamespace();
// Switch between ALL available tasks and display the basic Help of each one
$availableTasks = $cliController->getAvailableTasks();
//unset($availableTasks['Core:Help']);
ksort($availableTasks);
foreach (array_keys($availableTasks) as $taskName) {
$cliController->runTask($taskName, array('basic-help' => 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
@ -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

@ -26,7 +26,7 @@ namespace Doctrine\Common;
*
* This class contains no event data. It is used by events that do not pass state
* information to an event handler when an event is raised. The single empty EventArgs
* instance can be obtained through {@link getEmptyInstance()}.
* instance can be obtained through {@link getEmptyInstance}.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org

View File

@ -16,7 +16,7 @@
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common;
@ -24,13 +24,12 @@ namespace Doctrine\Common;
/**
* An EventSubscriber knows himself what events he is interested in.
* If an EventSubscriber is added to an EventManager, the manager invokes
* getSubscribedEvents() and registers the subscriber as a listener for all
* {@link getSubscribedEvents} and registers the subscriber as a listener for all
* returned events.
*
* @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>
@ -38,7 +37,7 @@ namespace Doctrine\Common;
interface EventSubscriber
{
/**
* Returns an array of events that this subscriber listens
* Returns an array of events this subscriber wants to listen to.
*
* @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
@ -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
@ -38,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.0 ALPHA 4';
const VERSION = '2.0-DEV';
/**
* Compares a Doctrine version with the current one.

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

@ -60,6 +60,7 @@ class OCI8Statement implements \Doctrine\DBAL\Driver\Statement
* placeholders and converted to a named parameter.
*
* @param string $statement The SQL statement to convert.
* @todo review and test for lost spaces. we experienced missing spaces with oci8 in some sql statements.
*/
private function _convertPositionalToNamedPlaceholders($statement)
{

View File

@ -510,7 +510,24 @@ class Table extends AbstractAsset
*/
public function getColumns()
{
return $this->_columns;
$columns = $this->_columns;
$pkCols = array();
$fkCols = array();
if ($this->hasIndex($this->_primaryKeyName)) {
$pkCols = $this->getPrimaryKey()->getColumns();
}
foreach ($this->getForeignKeys() AS $fk) {
/* @var $fk ForeignKeyConstraint */
$fkCols = array_merge($fkCols, $fk->getColumns());
}
$colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
uksort($columns, function($a, $b) use($colNames) {
return (array_search($a, $colNames) >= array_search($b, $colNames));
});
return $columns;
}

View File

@ -1,171 +0,0 @@
<?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\DBAL\Tools\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\Common\CLI\CLIException,
Doctrine\Common\Util\Debug,
Doctrine\Common\CLI\Option,
Doctrine\Common\CLI\OptionGroup;
/**
* Task for executing arbitrary SQL that can come from a file or directly from
* the command line.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class RunSqlTask extends AbstractTask
{
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$dqlAndFile = new OptionGroup(OptionGroup::CARDINALITY_1_1, array(
new Option(
'sql', '<SQL>', 'The SQL to execute.' . PHP_EOL .
'If defined, --file can not be requested on same task.'
),
new Option(
'file', '<PATH>', 'The path to the file with the SQL to execute.' . PHP_EOL .
'If defined, --sql can not be requested on same task.'
)
));
$depth = new OptionGroup(OptionGroup::CARDINALITY_0_1, array(
new Option('depth', '<DEPTH>', 'Dumping depth of Entities graph.')
));
$doc = $this->getDocumentation();
$doc->setName('run-sql')
->setDescription('Executes arbitrary SQL from a file or directly from the command line.')
->getOptionGroup()
->addOption($dqlAndFile)
->addOption($depth);
}
/**
* @inheritdoc
*/
public function validate()
{
$arguments = $this->getArguments();
$em = $this->getConfiguration()->getAttribute('em');
if ($em === null) {
throw new CLIException(
"Attribute 'em' of CLI Configuration is not defined or it is not a valid EntityManager."
);
}
if ( ! (isset($arguments['sql']) ^ isset($arguments['file']))) {
throw new CLIException('One of --sql or --file required, and only one.');
}
return true;
}
/**
* Executes the task.
*/
public function run()
{
$arguments = $this->getArguments();
if (isset($arguments['file'])) {
$em = $this->getConfiguration()->getAttribute('em');
$conn = $em->getConnection();
$printer = $this->getPrinter();
$fileNames = (array) $arguments['file'];
foreach ($fileNames as $fileName) {
if ( ! file_exists($fileName)) {
throw new CLIException(sprintf('The SQL file [%s] does not exist.', $fileName));
} else if ( ! is_readable($fileName)) {
throw new CLIException(sprintf('The SQL file [%s] does not have read permissions.', $fileName));
}
$printer->write('Processing file [' . $fileName . ']... ');
$sql = file_get_contents($fileName);
if ($conn instanceof \Doctrine\DBAL\Driver\PDOConnection) {
// PDO Drivers
try {
$lines = 0;
$stmt = $conn->prepare($sql);
$stmt->execute();
do {
// Required due to "MySQL has gone away!" issue
$stmt->fetch();
$stmt->closeCursor();
$lines++;
} while ($stmt->nextRowset());
$printer->writeln(sprintf('%d statements executed!', $lines));
} catch (\PDOException $e) {
$printer->writeln('error!')
->writeln($e->getMessage());
}
} else {
// Non-PDO Drivers (ie. OCI8 driver)
$stmt = $conn->prepare($sql);
$rs = $stmt->execute();
if ($rs) {
$printer->writeln('OK!');
} else {
$error = $stmt->errorInfo();
$printer->writeln('error!')
->writeln($error['message']);
}
$stmt->closeCursor();
}
}
} else if (isset($arguments['sql'])) {
$em = $this->getConfiguration()->getAttribute('em');
if (preg_match('/^select/i', $arguments['sql'])) {
$stmt = $em->getConnection()->executeQuery($arguments['sql']);
$resultSet = $stmt->fetchAll(\Doctrine\DBAL\Connection::FETCH_ASSOC);
} else {
$resultSet = $em->getConnection()->executeUpdate($arguments['sql']);
}
$maxDepth = isset($arguments['depth']) ? $arguments['depth'] : 7;
Debug::dump($resultSet, $maxDepth);
}
}
}

View File

@ -0,0 +1,127 @@
<?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\DBAL\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console;
/**
* Task for executing arbitrary SQL that can come from a file or directly from
* the command line.
*
* @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 ImportCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('dbal:import')
->setDescription('Import SQL file(s) directly to Database.')
->setDefinition(array(
new InputArgument(
'file', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'File path(s) of SQL to be executed.'
)
))
->setHelp(<<<EOT
Import SQL file(s) directly to Database.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$conn = $this->getHelper('db')->getConnection();
if (($fileNames = $input->getArgument('file')) !== null) {
foreach ((array) $fileNames as $fileName) {
$fileName = realpath($fileName);
if ( ! file_exists($fileName)) {
throw new \InvalidArgumentException(
sprintf("SQL file '<info>%s</info>' does not exist.", $fileName)
);
} else if ( ! is_readable($fileName)) {
throw new \InvalidArgumentException(
sprintf("SQL file '<info>%s</info>' does not have read permissions.", $fileName)
);
}
$output->write(sprintf("Processing file '<info>%s</info>'... ", $fileName));
$sql = file_get_contents($fileName);
if ($conn instanceof \Doctrine\DBAL\Driver\PDOConnection) {
// PDO Drivers
try {
$lines = 0;
$stmt = $conn->prepare($sql);
$stmt->execute();
do {
// Required due to "MySQL has gone away!" issue
$stmt->fetch();
$stmt->closeCursor();
$lines++;
} while ($stmt->nextRowset());
$output->write(sprintf('%d statements executed!', $lines) . PHP_EOL);
} catch (\PDOException $e) {
$output->write('error!' . PHP_EOL);
throw new \RuntimeException($e->getMessage(), $e->getCode(), $e);
}
} else {
// Non-PDO Drivers (ie. OCI8 driver)
$stmt = $conn->prepare($sql);
$rs = $stmt->execute();
if ($rs) {
$printer->writeln('OK!');
} else {
$error = $stmt->errorInfo();
$output->write('error!' . PHP_EOL);
throw new \RuntimeException($error[2], $error[0]);
}
$stmt->closeCursor();
}
}
}
}
}

View File

@ -0,0 +1,86 @@
<?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\DBAL\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console;
/**
* Task for executing arbitrary SQL that can come from a file or directly from
* the command line.
*
* @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 RunSqlCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('dbal:run-sql')
->setDescription('Executes arbitrary SQL directly from the command line.')
->setDefinition(array(
new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'),
new InputOption('depth', null, InputOption::PARAMETER_REQUIRED, 'Dumping depth of result set.', 7)
))
->setHelp(<<<EOT
Executes arbitrary SQL directly from the command line.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$conn = $this->getHelper('db')->getConnection();
if (($sql = $input->getArgument('sql')) === null) {
throw new \RuntimeException("Argument 'SQL' is required in order to execute this command correctly.");
}
$depth = $input->getOption('depth');
if ( ! is_numeric($depth)) {
throw new \LogicException("Option 'depth' must contains an integer value");
}
if (preg_match('/^select/i', $sql)) {
$resultSet = $conn->fetchAll($sql);
} else {
$resultSet = $conn->executeUpdate($sql);
}
\Doctrine\Common\Util\Debug::dump($resultSet, (int) $depth);
}
}

View File

@ -18,14 +18,14 @@
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Tools\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\Common\Version;
namespace Doctrine\DBAL\Tools\Console\Helper;
use Symfony\Components\Console\Helper\Helper,
Doctrine\DBAL\Connection;
/**
* CLI Task to display the doctrine version
* Doctrine CLI Connection Helper.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
@ -36,27 +36,39 @@ use Doctrine\Common\CLI\Tasks\AbstractTask,
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class VersionTask extends AbstractTask
class ConnectionHelper extends Helper
{
/**
* @inheritdoc
* Doctrine Database Connection
* @var Connection
*/
public function buildDocumentation()
protected $_connection;
/**
* Constructor
*
* @param Connection $connection Doctrine Database Connection
*/
public function __construct(Connection $connection)
{
// There're no options on this task
$this->getDocumentation()->getOptionGroup()->clear();
$doc = $this->getDocumentation();
$doc->setName('version')
->setDescription('Displays the current installed Doctrine version.');
$this->_connection = $connection;
}
/**
* Displays the current version of Doctrine
* Retrieves Doctrine Database Connection
*
* @return Connection
*/
public function run()
public function getConnection()
{
$this->getPrinter()->writeln('You are currently running Doctrine ' . Version::VERSION, 'INFO');
return $this->_connection;
}
/**
* @see Helper
*/
public function getName()
{
return 'connection';
}
}

View File

@ -410,11 +410,6 @@ abstract class AbstractQuery
throw new NonUniqueResultException;
}
return array_shift($result);
} else if (is_object($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return $result->first();
}
return $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
@ -21,13 +19,15 @@
namespace Doctrine\ORM;
use Doctrine\Common\Cache\Cache,
Doctrine\ORM\Mapping\Driver\Driver;
/**
* Configuration container for all configuration options of Doctrine.
* 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>
@ -35,24 +35,6 @@ namespace Doctrine\ORM;
*/
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.
*
@ -70,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;
}
/**
@ -81,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;
}
/**
@ -102,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;
}
/**
@ -118,15 +103,29 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets the cache driver implementation that is used for metadata caching.
*
* @param object $driverImpl
* @param Driver $driverImpl
* @todo Force parameter to be a Closure to ensure lazy evaluation
* (as soon as a metadata cache is in effect, the driver never needs to initialize).
*/
public function setMetadataDriverImpl($driverImpl)
public function setMetadataDriverImpl(Driver $driverImpl)
{
$this->_attributes['metadataDriverImpl'] = $driverImpl;
}
/**
* Add a new default annotation driver with a correctly configured annotation reader.
*
* @param array $paths
* @return Mapping\Driver\AnnotationDriver
*/
public function newDefaultAnnotationDriver($paths = array())
{
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, (array)$paths);
}
/**
* Adds a namespace under a certain alias.
*
@ -168,35 +167,32 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the cache driver implementation that is used for the mapping metadata.
*
* @return object
* @throws ORMException
* @return Mapping\Driver\Driver
*/
public function getMetadataDriverImpl()
{
if ($this->_attributes['metadataDriverImpl'] == null) {
$reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache);
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$this->_attributes['metadataDriverImpl'] = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
}
return $this->_attributes['metadataDriverImpl'];
return isset($this->_attributes['metadataDriverImpl']) ?
$this->_attributes['metadataDriverImpl'] : null;
}
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return object
* @return \Doctrine\Common\Cache\Cache
*/
public function getResultCacheImpl()
{
return $this->_attributes['resultCacheImpl'];
return isset($this->_attributes['resultCacheImpl']) ?
$this->_attributes['resultCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @param object $cacheImpl
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setResultCacheImpl($cacheImpl)
public function setResultCacheImpl(Cache $cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
@ -204,19 +200,20 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the cache driver implementation that is used for the query cache (SQL cache).
*
* @return object
* @return \Doctrine\Common\Cache\Cache
*/
public function getQueryCacheImpl()
{
return $this->_attributes['queryCacheImpl'];
return isset($this->_attributes['queryCacheImpl']) ?
$this->_attributes['queryCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for the query cache (SQL cache).
*
* @param object $cacheImpl
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setQueryCacheImpl($cacheImpl)
public function setQueryCacheImpl(Cache $cacheImpl)
{
$this->_attributes['queryCacheImpl'] = $cacheImpl;
}
@ -224,19 +221,20 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the cache driver implementation that is used for metadata caching.
*
* @return object
* @return \Doctrine\Common\Cache\Cache
*/
public function getMetadataCacheImpl()
{
return $this->_attributes['metadataCacheImpl'];
return isset($this->_attributes['metadataCacheImpl']) ?
$this->_attributes['metadataCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for metadata caching.
*
* @param object $cacheImpl
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setMetadataCacheImpl($cacheImpl)
public function setMetadataCacheImpl(Cache $cacheImpl)
{
$this->_attributes['metadataCacheImpl'] = $cacheImpl;
}
@ -249,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;
}
/**
@ -340,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
*/
@ -356,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
*/
@ -381,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
*/
@ -406,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)
{
@ -593,9 +594,11 @@ class EntityManager
* @param EventManager $eventManager The EventManager instance to use.
* @return EntityManager The created EntityManager.
*/
public static function create($conn, Configuration $config = null, EventManager $eventManager = null)
public static function create($conn, Configuration $config, EventManager $eventManager = null)
{
$config = $config ?: new Configuration();
if (!$config->getMetadataDriverImpl()) {
throw ORMException::missingMappingDriverImpl();
}
if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager()));

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>
@ -181,4 +176,28 @@ class EntityRepository
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
}
}
/**
* @return string
*/
protected function getEntityName()
{
return $this->_entityName;
}
/**
* @return EntityManager
*/
protected function getEntityManager()
{
return $this->_em;
}
/**
* @return Mapping\ClassMetadata
*/
protected function getClassMetadata()
{
return $this->_class;
}
}

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
@ -87,8 +85,8 @@ class ObjectHydrator extends AbstractHydrator
if ($assoc->mappedBy) {
$this->_hints['fetched'][$className][$assoc->mappedBy] = true;
} else {
if (isset($class->inverseMappings[$sourceClassName][$assoc->sourceFieldName])) {
$inverseAssoc = $class->inverseMappings[$sourceClassName][$assoc->sourceFieldName];
if ($assoc->inversedBy) {
$inverseAssoc = $class->associationMappings[$assoc->inversedBy];
if ($inverseAssoc->isOneToOne()) {
$this->_hints['fetched'][$className][$inverseAssoc->sourceFieldName] = true;
if ($class->subClasses) {
@ -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)
{
@ -346,9 +344,10 @@ class ObjectHydrator extends AbstractHydrator
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
$targetClass = $this->_ce[$relation->targetEntityName];
if ($relation->isOwningSide) {
//TODO: Just check hints['fetched'] here?
// If there is an inverse mapping on the target class its bidirectional
if (isset($targetClass->inverseMappings[$relation->sourceEntityName][$relationField])) {
$inverseAssoc = $targetClass->inverseMappings[$relation->sourceEntityName][$relationField];
if ($relation->inversedBy) {
$inverseAssoc = $targetClass->associationMappings[$relation->inversedBy];
if ($inverseAssoc->isOneToOne()) {
$targetClass->reflFields[$inverseAssoc->sourceFieldName]->setValue($element, $parentObject);
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc->sourceFieldName, $parentObject);

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
@ -36,9 +34,14 @@ class SingleScalarHydrator extends AbstractHydrator
{
$cache = array();
$result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
if (count($result) > 1 || count($result[key($result)]) > 1) {
$num = count($result);
if ($num == 0) {
throw new \Doctrine\ORM\NoResultException;
} 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

@ -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
@ -34,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
{
@ -60,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
@ -152,7 +151,23 @@ abstract class AssociationMapping
*
* @var array
*/
public $joinTable = array();
public $joinTable;
/**
* READ-ONLY: The name of the entity class from which the association was
* inherited in an inheritance hierarchy.
*
* @var string
*/
public $inherited;
/**
* READ-ONLY: The name of the entity or mapped superclass that declares
* the association field in an inheritance hierarchy.
*
* @var string
*/
public $declared;
/**
* Initializes a new instance of a class derived from AssociationMapping.
@ -161,9 +176,7 @@ abstract class AssociationMapping
*/
public function __construct(array $mapping)
{
if ($mapping) {
$this->_validateAndCompleteMapping($mapping);
}
$this->_validateAndCompleteMapping($mapping);
}
/**
@ -317,8 +330,9 @@ abstract class AssociationMapping
abstract public function load($sourceEntity, $target, $em, array $joinColumnValues = array());
/**
*
* @param $platform
* Gets the (possibly quoted) name of the join table.
*
* @param AbstractPlatform $platform
* @return string
*/
public function getQuotedJoinTableName($platform)
@ -327,5 +341,59 @@ 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',
'fetchMode'
);
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

@ -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,13 +41,6 @@ use ReflectionClass, ReflectionProperty;
*/
class ClassMetadata extends ClassMetadataInfo
{
/**
* The ReflectionClass instance of the mapped class.
*
* @var ReflectionClass
*/
public $reflClass;
/**
* The ReflectionProperty instances of the mapped class.
*
@ -72,21 +63,10 @@ class ClassMetadata extends ClassMetadataInfo
*/
public function __construct($entityName)
{
$this->name = $entityName;
$this->reflClass = new \ReflectionClass($entityName);
parent::__construct($entityName);
$this->reflClass = new ReflectionClass($entityName);
$this->namespace = $this->reflClass->getNamespaceName();
$this->table['name'] = $this->reflClass->getShortName();
$this->rootEntityName = $entityName;
}
/**
* Gets the ReflectionClass instance of the mapped class.
*
* @return ReflectionClass
*/
public function getReflectionClass()
{
return $this->reflClass;
}
/**
@ -99,18 +79,6 @@ class ClassMetadata extends ClassMetadataInfo
return $this->reflFields;
}
/**
* INTERNAL:
* Adds a reflection property. Usually only used by the ClassMetadataFactory
* while processing inheritance mappings.
*
* @param array $props
*/
public function addReflectionProperty($propName, \ReflectionProperty $property)
{
$this->reflFields[$propName] = $property;
}
/**
* Gets a ReflectionProperty for a specific field of the mapped class.
*
@ -189,7 +157,7 @@ class ClassMetadata extends ClassMetadataInfo
public function setIdentifierValues($entity, $id)
{
if ($this->isIdentifierComposite) {
foreach ((array)$id as $idField => $idValue) {
foreach ($id as $idField => $idValue) {
$this->reflFields[$idField]->setValue($entity, $idValue);
}
} else {
@ -220,18 +188,6 @@ class ClassMetadata extends ClassMetadataInfo
return $this->reflFields[$field]->getValue($entity);
}
/**
* Sets the field mapped to the specified column to the specified value on the given entity.
*
* @param object $entity
* @param string $field
* @param mixed $value
*/
public function setColumnValue($entity, $column, $value)
{
$this->reflFields[$this->fieldNames[$column]]->setValue($entity, $value);
}
/**
* Stores the association mapping.
*
@ -243,10 +199,10 @@ class ClassMetadata extends ClassMetadataInfo
// Store ReflectionProperty of mapped field
$sourceFieldName = $assocMapping->sourceFieldName;
$refProp = $this->reflClass->getProperty($sourceFieldName);
$refProp->setAccessible(true);
$this->reflFields[$sourceFieldName] = $refProp;
$refProp = $this->reflClass->getProperty($sourceFieldName);
$refProp->setAccessible(true);
$this->reflFields[$sourceFieldName] = $refProp;
}
/**
@ -291,8 +247,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)
*
@ -300,33 +260,57 @@ 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',
'inheritedAssociationFields',
'inverseMappings', //TODO: Remove! DDC-193
'isIdentifierComposite',
'isMappedSuperclass',
'isVersioned',
'lifecycleCallbacks',
'isIdentifierComposite', // TODO: REMOVE
'name',
'parentClasses',
'namespace', // TODO: REMOVE
'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;
}
/**
@ -338,20 +322,20 @@ class ClassMetadata extends ClassMetadataInfo
{
// Restore ReflectionClass and properties
$this->reflClass = new ReflectionClass($this->name);
foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['inherited'])) {
$reflField = new ReflectionProperty($mapping['inherited'], $field);
} else {
$reflField = $this->reflClass->getProperty($field);
}
if (isset($mapping['declared'])) {
$reflField = new ReflectionProperty($mapping['declared'], $field);
} else {
$reflField = $this->reflClass->getProperty($field);
}
$reflField->setAccessible(true);
$this->reflFields[$field] = $reflField;
}
foreach ($this->associationMappings as $field => $mapping) {
if (isset($this->inheritedAssociationFields[$field])) {
$reflField = new ReflectionProperty($this->inheritedAssociationFields[$field], $field);
if ($mapping->declared) {
$reflField = new ReflectionProperty($mapping->declared, $field);
} else {
$reflField = $this->reflClass->getProperty($field);
}

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
@ -30,10 +28,7 @@ use Doctrine\ORM\ORMException,
* metadata mapping informations of a class which describes how a class should be mapped
* to a relational database.
*
* @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>
@ -190,7 +185,25 @@ class ClassMetadataFactory
{
$this->_loadedMetadata[$className] = $class;
}
/**
* Get array of parent classes for the given entity class
*
* @param string $name
* @return array $parentClasses
*/
protected function _getParentClasses($name)
{
// Collect parent classes, ignoring transient (not-mapped) classes.
$parentClasses = array();
foreach (array_reverse(class_parents($name)) as $parentClass) {
if ( ! $this->_driver->isTransient($parentClass)) {
$parentClasses[] = $parentClass;
}
}
return $parentClasses;
}
/**
* Loads the metadata of the class in question and all it's ancestors whose metadata
* is still not loaded.
@ -203,19 +216,10 @@ class ClassMetadataFactory
if ( ! $this->_initialized) {
$this->_initialize();
}
$loaded = array();
// Collect parent classes, ignoring transient (not-mapped) classes.
//TODO: Evaluate whether we can use class_parents() here.
$parentClass = $name;
$parentClasses = array();
while ($parentClass = get_parent_class($parentClass)) {
if ( ! $this->_driver->isTransient($parentClass)) {
$parentClasses[] = $parentClass;
}
}
$parentClasses = array_reverse($parentClasses);
$parentClasses = $this->_getParentClasses($name);
$parentClasses[] = $name;
// Move down the hierarchy of parent classes, starting from the topmost class
@ -231,7 +235,7 @@ class ClassMetadataFactory
}
$class = $this->_newClassMetadataInstance($className);
if ($parent) {
$class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn);
@ -262,8 +266,8 @@ class ClassMetadataFactory
} else if ($parent->isIdGeneratorTable()) {
$class->getTableGeneratorDefinition($parent->tableGeneratorDefinition);
}
if ($generatorType = $parent->generatorType) {
$class->setIdGeneratorType($generatorType);
if ($parent->generatorType) {
$class->setIdGeneratorType($parent->generatorType);
}
if ($parent->idGenerator) {
$class->setIdGenerator($parent->idGenerator);
@ -282,18 +286,18 @@ class ClassMetadataFactory
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class);
$this->_evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
$this->_loadedMetadata[$className] = $class;
$parent = $class;
if ( ! $class->isMappedSuperclass) {
array_unshift($visited, $className);
}
$loaded[] = $className;
}
return $loaded;
}
@ -320,31 +324,33 @@ class ClassMetadataFactory
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
$mapping['inherited'] = $parentClass->name;
}
$subClass->addFieldMapping($mapping);
if ( ! isset($mapping['declared'])) {
$mapping['declared'] = $parentClass->name;
}
$subClass->addInheritedFieldMapping($mapping);
}
foreach ($parentClass->reflFields as $name => $field) {
$subClass->reflFields[$name] = $field;
}
}
/**
* Adds inherited associations to the subclass mapping.
* Adds inherited association mappings to the subclass mapping.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function _addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
{
foreach ($parentClass->associationMappings as $mapping) {
if (isset($parentClass->inheritedAssociationFields[$mapping->sourceFieldName])) {
// parent class also inherited that one
$subClass->addAssociationMapping($mapping, $parentClass->inheritedAssociationFields[$mapping->sourceFieldName]);
} else if ( ! $parentClass->isMappedSuperclass) {
// parent class defined that one
$subClass->addAssociationMapping($mapping, $parentClass->name);
} else {
$subClass->addAssociationMapping($mapping);
foreach ($parentClass->associationMappings as $field => $mapping) {
$subclassMapping = clone $mapping;
if ( ! isset($mapping->inherited) && ! $parentClass->isMappedSuperclass) {
$subclassMapping->inherited = $parentClass->name;
}
if ( ! isset($mapping->declared)) {
$subclassMapping->declared = $parentClass->name;
}
$subClass->addInheritedAssociationMapping($subclassMapping);
}
}
@ -354,7 +360,7 @@ class ClassMetadataFactory
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function _completeIdGeneratorMapping(ClassMetadata $class)
private function _completeIdGeneratorMapping(ClassMetadataInfo $class)
{
$idGenType = $class->generatorType;
if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {

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,6 +19,8 @@
namespace Doctrine\ORM\Mapping;
use ReflectionClass;
/**
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
* of an entity and it's associations.
@ -127,9 +127,9 @@ class ClassMetadataInfo
public $namespace;
/**
* READ-ONLY: The name of the entity class that is at the root of the entity inheritance
* hierarchy. If the entity is not part of an inheritance hierarchy this is the same
* as $_entityName.
* READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance
* hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
* as {@link $entityName}.
*
* @var string
*/
@ -158,7 +158,7 @@ class ClassMetadataInfo
public $parentClasses = array();
/**
* READ-ONLY: The names of all subclasses.
* READ-ONLY: The names of all subclasses (descendants).
*
* @var array
*/
@ -195,9 +195,9 @@ class ClassMetadataInfo
* - <b>fieldName</b> (string)
* The name of the field in the Entity.
*
* - <b>type</b> (object Doctrine\DBAL\Types\* or custom type)
* The type of the column. Can be one of Doctrine's portable types
* or a custom type.
* - <b>type</b> (string)
* The type name of the mapped field. Can be one of Doctrine's mapping types
* or a custom mapping type.
*
* - <b>columnName</b> (string, optional)
* The column name. Optional. Defaults to the field name.
@ -207,15 +207,9 @@ class ClassMetadataInfo
* the type.
*
* - <b>id</b> (boolean, optional)
* Marks the field as the primary key of the Entity. Multiple fields of an
* Marks the field as the primary key of the entity. Multiple fields of an
* entity can have the id attribute, forming a composite key.
*
* - <b>idGenerator</b> (string, optional)
* Either: idGenerator => 'nameOfGenerator', usually only for TABLE/SEQUENCE generators
* Or: idGenerator => 'identity' or 'auto' or 'table' or 'sequence'
* Note that 'auto', 'table', 'sequence' and 'identity' are reserved names and
* therefore cant be used as a generator name!
*
* - <b>nullable</b> (boolean, optional)
* Whether the column is nullable. Defaults to FALSE.
*
@ -306,20 +300,12 @@ class ClassMetadataInfo
public $lifecycleCallbacks = array();
/**
* READ-ONLY: The association mappings. All mappings, inverse and owning side.
* READ-ONLY: The association mappings of this class.
*
* @var array
*/
public $associationMappings = array();
/**
* READ-ONLY: List of inverse association mappings, indexed by mappedBy field name.
*
* @var array
* @todo Remove! See http://www.doctrine-project.org/jira/browse/DDC-193
*/
public $inverseMappings = array();
/**
* READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
*
@ -331,6 +317,7 @@ class ClassMetadataInfo
* READ-ONLY: The ID generator used for generating IDs for this class.
*
* @var AbstractIdGenerator
* @todo Remove
*/
public $idGenerator;
@ -366,15 +353,6 @@ class ClassMetadataInfo
*/
public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
/**
* READ-ONLY: A map of field names to class names, where the field names are association
* fields that have been inherited from another class and values are the names
* of the classes that define the association.
*
* @var array
*/
public $inheritedAssociationFields = array();
/**
* READ-ONLY: A flag for whether or not instances of this class are to be versioned
* with optimistic locking.
@ -390,6 +368,13 @@ class ClassMetadataInfo
*/
public $versionField;
/**
* The ReflectionClass instance of the mapped class.
*
* @var ReflectionClass
*/
public $reflClass;
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
@ -402,6 +387,19 @@ class ClassMetadataInfo
$this->rootEntityName = $entityName;
}
/**
* Gets the ReflectionClass instance of the mapped class.
*
* @return ReflectionClass
*/
public function getReflectionClass()
{
if ( ! $this->reflClass) {
$this->reflClass = new ReflectionClass($entityName);
}
return $this->reflClass;
}
/**
* Sets the change tracking policy used by this class.
*
@ -531,34 +529,6 @@ class ClassMetadataInfo
return $this->associationMappings[$fieldName];
}
/**
* Gets the inverse association mapping for the given target class name and
* owning fieldname.
*
* @param string $mappedByFieldName The field on the
* @return Doctrine\ORM\Mapping\AssociationMapping The mapping or NULL if there is no such
* inverse association mapping.
*/
public function getInverseAssociationMapping($targetClassName, $mappedByFieldName)
{
return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]) ?
$this->inverseMappings[$targetClassName][$mappedByFieldName] : null;
}
/**
* Checks whether the class has an inverse association mapping that points to the
* specified class and ha the specified mappedBy field.
*
* @param string $targetClassName The name of the target class.
* @param string $mappedByFieldName The name of the mappedBy field that points to the field on
* the target class that owns the association.
* @return boolean
*/
public function hasInverseAssociationMapping($targetClassName, $mappedByFieldName)
{
return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]);
}
/**
* Gets all association mappings of the class.
*
@ -628,16 +598,6 @@ class ClassMetadataInfo
}
}
/**
* Maps an embedded value object.
*
* @todo Implementation.
*/
/*public function mapEmbeddedValue()
{
//...
}*/
/**
* Gets the identifier (primary key) field names of the class.
*
@ -744,7 +704,7 @@ class ClassMetadataInfo
/**
* Checks whether the mapped class uses an Id generator.
*
* @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
* @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
*/
public function usesIdGenerator()
{
@ -752,7 +712,6 @@ class ClassMetadataInfo
}
/**
*
* @return boolean
*/
public function isInheritanceTypeNone()
@ -892,16 +851,6 @@ class ClassMetadataInfo
}
}
/**
* Checks whether the class has any persistent subclasses.
*
* @return boolean TRUE if the class has one or more persistent subclasses, FALSE otherwise.
*/
public function hasSubclasses()
{
return ! $this->subClasses;
}
/**
* Sets the parent class names.
* Assumes that the class names in the passed array are in the order:
@ -915,16 +864,6 @@ class ClassMetadataInfo
}
}
/**
* Checks whether the class has any persistent parent classes.
*
* @return boolean TRUE if the class has one or more persistent parent classes, FALSE otherwise.
*/
public function hasParentClasses()
{
return ! $this->parentClasses;
}
/**
* Sets the inheritance type used by the class and it's subclasses.
*
@ -939,7 +878,7 @@ class ClassMetadataInfo
}
/**
* Checks whether a mapped field is inherited from a superclass.
* Checks whether a mapped field is inherited from an entity superclass.
*
* @return boolean TRUE if the field is inherited, FALSE otherwise.
*/
@ -956,7 +895,7 @@ class ClassMetadataInfo
*/
public function isInheritedAssociation($fieldName)
{
return isset($this->inheritedAssociationFields[$fieldName]);
return isset($this->associationMappings[$fieldName]->inherited);
}
/**
@ -999,21 +938,6 @@ class ClassMetadataInfo
$type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
}
/**
* Checks whether the given type identifies an id generator type.
*
* @param string $type
* @return boolean
*/
private function _isIdGeneratorType($type)
{
return $type == self::GENERATOR_TYPE_AUTO ||
$type == self::GENERATOR_TYPE_IDENTITY ||
$type == self::GENERATOR_TYPE_SEQUENCE ||
$type == self::GENERATOR_TYPE_TABLE ||
$type == self::GENERATOR_TYPE_NONE;
}
/**
* Makes some automatic additions to the association mapping to make the life
* easier for the user, and store join columns in the metadata.
@ -1031,7 +955,7 @@ class ClassMetadataInfo
}
/**
* Adds a field mapping.
* Adds a mapped field to the class.
*
* @param array $mapping The field mapping.
*/
@ -1051,19 +975,13 @@ class ClassMetadataInfo
*
* @param AssociationMapping $mapping
* @param string $owningClassName The name of the class that defined this mapping.
* @todo Rename: addInheritedAssociationMapping
*/
public function addAssociationMapping(AssociationMapping $mapping, $owningClassName = null)
public function addInheritedAssociationMapping(AssociationMapping $mapping/*, $owningClassName = null*/)
{
$sourceFieldName = $mapping->sourceFieldName;
if (isset($this->associationMappings[$sourceFieldName])) {
throw MappingException::duplicateAssociationMapping($this->name, $sourceFieldName);
if (isset($this->associationMappings[$mapping->sourceFieldName])) {
throw MappingException::duplicateAssociationMapping($this->name, $mapping->sourceFieldName);
}
$this->associationMappings[$sourceFieldName] = $mapping;
if ($owningClassName !== null) {
$this->inheritedAssociationFields[$sourceFieldName] = $owningClassName;
}
$this->_registerMappingIfInverse($mapping);
$this->associationMappings[$mapping->sourceFieldName] = $mapping;
}
/**
@ -1074,7 +992,7 @@ class ClassMetadataInfo
* @param array $mapping
* @todo Rename: addInheritedFieldMapping
*/
public function addFieldMapping(array $fieldMapping)
public function addInheritedFieldMapping(array $fieldMapping)
{
$this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
$this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
@ -1093,20 +1011,6 @@ class ClassMetadataInfo
$this->_storeAssociationMapping($oneToOneMapping);
}
/**
* Registers the mapping as an inverse mapping, if it is a mapping on the
* inverse side of an association mapping.
*
* @param AssociationMapping The mapping to register as inverse if it is a mapping
* for the inverse side of an association.
*/
private function _registerMappingIfInverse(AssociationMapping $assoc)
{
if ( ! $assoc->isOwningSide) {
$this->inverseMappings[$assoc->targetEntityName][$assoc->mappedBy] = $assoc;
}
}
/**
* Adds a one-to-many mapping.
*
@ -1154,7 +1058,6 @@ class ClassMetadataInfo
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
}
$this->associationMappings[$sourceFieldName] = $assocMapping;
$this->_registerMappingIfInverse($assocMapping);
}
/**
@ -1171,8 +1074,8 @@ class ClassMetadataInfo
* Dispatches the lifecycle event of the given entity to the registered
* lifecycle callbacks and lifecycle listeners.
*
* @param string $event The lifecycle event.
* @param Entity $entity The Entity on which the event occured.
* @param string $event The lifecycle event.
* @param Entity $entity The Entity on which the event occured.
*/
public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
{
@ -1277,17 +1180,6 @@ class ClassMetadataInfo
}
}
/**
* Checks whether the given column name is the discriminator column.
*
* @param string $columnName
* @return boolean
*/
public function isDiscriminatorColumn($columnName)
{
return $columnName === $this->discriminatorColumn['name'];
}
/**
* Checks whether the class has a mapped association with the given field name.
*
@ -1356,7 +1248,7 @@ class ClassMetadataInfo
/**
* Sets the version field mapping used for versioning. Sets the default
* value to use depending on the column type
* value to use depending on the column type.
*
* @param array $mapping The version field mapping array
*/

View File

@ -55,7 +55,7 @@ abstract class AbstractFileDriver implements Driver
* @var string
*/
protected $_fileExtension;
/**
* Initializes a new FileDriver that looks in the given path(s) for mapping
* documents and operates in the specified operating mode.
@ -76,7 +76,7 @@ abstract class AbstractFileDriver implements Driver
{
$this->_paths = array_unique(array_merge($this->_paths, $paths));
}
/**
* Retrieve the defined metadata lookup paths.
*
@ -107,7 +107,7 @@ abstract class AbstractFileDriver implements Driver
{
$this->_fileExtension = $fileExtension;
}
/**
* Get the element of schema meta data for the class from the mapping file.
* This will lazily load the mapping file if it is not loaded yet
@ -132,7 +132,7 @@ abstract class AbstractFileDriver implements Driver
public function isTransient($className)
{
$fileName = str_replace('\\', '.', $className) . $this->_fileExtension;
// Check whether file exists
foreach ((array) $this->_paths as $path) {
if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) {
@ -142,7 +142,7 @@ abstract class AbstractFileDriver implements Driver
return true;
}
/**
* Gets the names of all mapped classes known to this driver.
*
@ -151,11 +151,11 @@ abstract class AbstractFileDriver implements Driver
public function getAllClassNames()
{
$classes = array();
if ($this->_paths) {
foreach ((array) $this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::driverRequiresConfiguredDirectoryPath();
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
}
$iterator = new \RecursiveIteratorIterator(

View File

@ -143,7 +143,7 @@ class AnnotationDriver implements Driver
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
}
// Evaluate DoctrineTable annotation
// Evaluate Table annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) {
$tableAnnot = $classAnnotations['Doctrine\ORM\Mapping\Table'];
$primaryTable = array(
@ -173,7 +173,7 @@ class AnnotationDriver implements Driver
// Evaluate InheritanceType annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) {
$inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType'];
$metadata->setInheritanceType(constant('\Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value));
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value));
}
// Evaluate DiscriminatorColumn annotation
@ -195,7 +195,7 @@ class AnnotationDriver implements Driver
// Evaluate DoctrineChangeTrackingPolicy annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) {
$changeTrackingAnnot = $classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'];
$metadata->setChangeTrackingPolicy(constant('\Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value));
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value));
}
// Evaluate annotations on properties/fields
@ -428,40 +428,41 @@ class AnnotationDriver implements Driver
return $this->_classNames;
}
if (!$this->_paths) {
throw MappingException::pathRequired();
}
$classes = array();
$includedFiles = array();
if ($this->_paths) {
$includedFiles = array();
foreach ((array) $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;
}
foreach ($this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
}
$declared = get_declared_classes();
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($declared as $className) {
$rc = new \ReflectionClass($className);
$sourceFile = $rc->getFileName();
if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) {
$classes[] = $className;
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;
}
}
@ -470,4 +471,19 @@ class AnnotationDriver implements Driver
return $classes;
}
/**
* Factory method for the Annotation Driver
*
* @param array|string $paths
* @param AnnotationReader $reader
* @return AnnotationDriver
*/
static public function create($paths = array(), AnnotationReader $reader = null)
{
if ($reader == null) {
$reader = new AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
}
return new self($reader, $paths);
}
}

View File

@ -29,7 +29,7 @@ use Doctrine\Common\Cache\ArrayCache,
Doctrine\Common\Util\Inflector;
/**
* The DatabaseDriver reverse engineers the mapping metadata from a database
* The DatabaseDriver reverse engineers the mapping metadata from a database.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
@ -67,7 +67,7 @@ class DatabaseDriver implements Driver
$columns = $this->_sm->listTableColumns($tableName);
if($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $this->_sm->listTableForeignKeys($tableName);
} else {
$foreignKeys = array();

View File

@ -37,7 +37,6 @@ final class DiscriminatorColumn extends Annotation {
public $length;
}
final class DiscriminatorMap extends Annotation {}
/*final class SubClasses extends Annotation {}*/
final class Id extends Annotation {}
final class GeneratedValue extends Annotation {
public $strategy = 'AUTO';
@ -124,7 +123,6 @@ final class SequenceGenerator extends Annotation {
public $initialValue = 1;
}
final class ChangeTrackingPolicy extends Annotation {}
final class OrderBy extends Annotation {}
/* Annotations for lifecycle callbacks */

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
{
@ -46,7 +42,7 @@ class DriverChain implements Driver
private $_drivers = array();
/**
* Add a nested driver
* Add a nested driver.
*
* @param Driver $nestedDriver
* @param string $namespace
@ -57,7 +53,7 @@ class DriverChain implements Driver
}
/**
* Get the array of nested drivers
* Get the array of nested drivers.
*
* @return array $drivers
*/
@ -74,7 +70,7 @@ class DriverChain implements Driver
*/
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
{
foreach ($this->_drivers AS $namespace => $driver) {
foreach ($this->_drivers as $namespace => $driver) {
if (strpos($className, $namespace) === 0) {
$driver->loadMetadataForClass($className, $metadata);
return;

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,13 +43,12 @@ use Doctrine\Common\Cache\ArrayCache,
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename: PHPDriver
*/
class PhpDriver extends AbstractFileDriver
class PHPDriver extends AbstractFileDriver
{
/**
* {@inheritdoc}
*/
protected $_fileExtension = '.php';
protected $_metadata;
/**

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

@ -21,7 +21,8 @@
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
use SimpleXMLElement,
Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\MappingException;
/**
@ -117,15 +118,17 @@ class XmlDriver extends AbstractFileDriver
// Evaluate <unique-constraints..>
if (isset($xmlRoot->{'unique-constraints'})) {
foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $unique) {
if (is_string($unique['columns'])) {
$columns = explode(',', $unique['columns']);
$columns = explode(',', (string)$unique['columns']);
if (isset($unique['name'])) {
$metadata->table['uniqueConstraints'][(string)$unique['name']] = array(
'columns' => $columns
);
} else {
$columns = $unique['columns'];
$metadata->table['uniqueConstraints'][] = array(
'columns' => $columns
);
}
$metadata->table['uniqueConstraints'][$unique['name']] = array(
'columns' => $columns
);
}
}
@ -202,9 +205,9 @@ class XmlDriver extends AbstractFileDriver
if (isset($idElement->{'sequence-generator'})) {
$seqGenerator = $idElement->{'sequence-generator'};
$metadata->setSequenceGeneratorDefinition(array(
'sequenceName' => $seqGenerator->{'sequence-name'},
'allocationSize' => $seqGenerator->{'allocation-size'},
'initialValue' => $seqGeneratorAnnot->{'initial-value'}
'sequenceName' => (string)$seqGenerator['sequence-name'],
'allocationSize' => (string)$seqGenerator['allocation-size'],
'initialValue' => (string)$seqGenerator['initial-value']
));
} else if (isset($idElement->{'table-generator'})) {
throw MappingException::tableIdGeneratorNotImplemented($className);
@ -226,6 +229,9 @@ class XmlDriver extends AbstractFileDriver
if (isset($oneToOneElement['mapped-by'])) {
$mapping['mappedBy'] = (string)$oneToOneElement['mapped-by'];
} else {
if (isset($oneToOneElement['inversed-by'])) {
$mapping['inversedBy'] = (string)$oneToOneElement['inversed-by'];
}
$joinColumns = array();
if (isset($oneToOneElement->{'join-column'})) {
@ -295,9 +301,13 @@ class XmlDriver extends AbstractFileDriver
if (isset($manyToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . (string)$manyToOneElement['fetch']);
}
if (isset($manyToOneElement['inversed-by'])) {
$mapping['inversedBy'] = (string)$manyToOneElement['inversed-by'];
}
$joinColumns = array();
if (isset($manyToOneElement->{'join-column'})) {
$joinColumns[] = $this->_getJoinColumnMapping($manyToOneElement->{'join-column'});
} else if (isset($manyToOneElement->{'join-columns'})) {
@ -305,13 +315,12 @@ class XmlDriver extends AbstractFileDriver
if (!isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $name;
}
$joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement);
}
}
$mapping['joinColumns'] = $joinColumns;
if (isset($manyToOneElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
}
@ -339,11 +348,15 @@ class XmlDriver extends AbstractFileDriver
if (isset($manyToManyElement['mapped-by'])) {
$mapping['mappedBy'] = (string)$manyToManyElement['mapped-by'];
} else if (isset($manyToManyElement->{'join-table'})) {
if (isset($manyToManyElement['inversed-by'])) {
$mapping['inversedBy'] = (string)$manyToManyElement['inversed-by'];
}
$joinTableElement = $manyToManyElement->{'join-table'};
$joinTable = array(
'name' => (string)$joinTableElement['name']
);
if (isset($joinTableElement['schema'])) {
$joinTable['schema'] = (string)$joinTableElement['schema'];
}
@ -382,7 +395,7 @@ class XmlDriver extends AbstractFileDriver
// Evaluate <lifecycle-callbacks...>
if (isset($xmlRoot->{'lifecycle-callbacks'})) {
foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
$metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('\Doctrine\ORM\Events::' . (string)$lifecycleCallback['type']));
$metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string)$lifecycleCallback['type']));
}
}
}
@ -394,7 +407,7 @@ class XmlDriver extends AbstractFileDriver
* @param $joinColumnElement The XML element.
* @return array The mapping array.
*/
private function _getJoinColumnMapping(\SimpleXMLElement $joinColumnElement)
private function _getJoinColumnMapping(SimpleXMLElement $joinColumnElement)
{
$joinColumn = array(
'name' => (string)$joinColumnElement['name'],

View File

@ -135,6 +135,10 @@ class YamlDriver extends AbstractFileDriver
if (isset($element['id'])) {
// Evaluate identifier settings
foreach ($element['id'] as $name => $idElement) {
if (!isset($idElement['type'])) {
throw MappingException::propertyTypeIsRequired($className, $name);
}
$mapping = array(
'id' => true,
'fieldName' => $name,
@ -151,6 +155,12 @@ class YamlDriver extends AbstractFileDriver
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
. strtoupper($idElement['generator']['strategy'])));
}
// Check for SequenceGenerator/TableGenerator definition
if (isset($idElement['sequenceGenerator'])) {
$metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']);
} else if (isset($idElement['tableGenerator'])) {
throw MappingException::tableIdGeneratorNotImplemented($className);
}
}
}
@ -177,12 +187,6 @@ class YamlDriver extends AbstractFileDriver
. strtoupper($fieldMapping['generator']['strategy'])));
}
}
// Check for SequenceGenerator/TableGenerator definition
if (isset($fieldMapping['sequenceGenerator'])) {
$metadata->setSequenceGeneratorDefinition($fieldMapping['sequenceGenerator']);
} else if (isset($fieldMapping['tableGenerator'])) {
throw MappingException::tableIdGeneratorNotImplemented($className);
}
if (isset($fieldMapping['column'])) {
$mapping['columnName'] = $fieldMapping['column'];
}
@ -230,6 +234,10 @@ class YamlDriver extends AbstractFileDriver
if (isset($oneToOneElement['mappedBy'])) {
$mapping['mappedBy'] = $oneToOneElement['mappedBy'];
} else {
if (isset($oneToOneElement['inversedBy'])) {
$mapping['inversedBy'] = $oneToOneElement['inversedBy'];
}
$joinColumns = array();
if (isset($oneToOneElement['joinColumn'])) {
@ -292,6 +300,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToOneElement['fetch']);
}
if (isset($manyToOneElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToOneElement['inversedBy'];
}
$joinColumns = array();
if (isset($manyToOneElement['joinColumn'])) {
@ -331,6 +343,10 @@ class YamlDriver extends AbstractFileDriver
if (isset($manyToManyElement['mappedBy'])) {
$mapping['mappedBy'] = $manyToManyElement['mappedBy'];
} else if (isset($manyToManyElement['joinTable'])) {
if (isset($manyToManyElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
}
$joinTableElement = $manyToManyElement['joinTable'];
$joinTable = array(
'name' => $joinTableElement['name']
@ -375,7 +391,7 @@ class YamlDriver extends AbstractFileDriver
if (isset($element['lifecycleCallbacks'])) {
foreach ($element['lifecycleCallbacks'] as $type => $methods) {
foreach ($methods as $method) {
$metadata->addLifecycleCallback($method, constant('\Doctrine\ORM\Events::' . $type));
$metadata->addLifecycleCallback($method, constant('Doctrine\ORM\Events::' . $type));
}
}
}

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
{
@ -70,10 +69,7 @@ class ManyToManyMapping extends AssociationMapping
public $orderBy;
/**
* Validates and completes the mapping.
*
* @param array $mapping
* @override
* {@inheritdoc}
*/
protected function _validateAndCompleteMapping(array $mapping)
{
@ -89,13 +85,15 @@ class ManyToManyMapping extends AssociationMapping
'joinColumns' => array(
array(
'name' => $sourceShortName . '_id',
'referencedColumnName' => 'id'
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE'
)
),
'inverseJoinColumns' => array(
array(
'name' => $targetShortName . '_id',
'referencedColumnName' => 'id'
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE'
)
)
);
@ -143,43 +141,37 @@ 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} */
public function isManyToMany()
{
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
@ -28,6 +26,12 @@ namespace Doctrine\ORM\Mapping;
*/
class MappingException extends \Doctrine\ORM\ORMException
{
public static function pathRequired()
{
return new self("Specifying the paths to your entities is required ".
"in the AnnotationDriver to retrieve all class names.");
}
public static function identifierRequired($entityName)
{
return new self("No identifier/primary key specified for Entity '$entityName'."

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,60 +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) {
$inverseField = isset($targetClass->inverseMappings[$this->sourceEntityName][$this->sourceFieldName]) ?
$targetClass->inverseMappings[$this->sourceEntityName][$this->sourceFieldName]->sourceFieldName
: false;
// 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 ($inverseField) {
$hints['fetched'][$targetClass->name][$inverseField] = true;
if ($targetClass->subClasses) {
foreach ($targetClass->subClasses as $targetSubclassName) {
$hints['fetched'][$targetSubclassName][$inverseField] = 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 && $inverseField && ! $targetClass->isCollectionValuedAssociation($inverseField)) {
$targetClass->reflFields[$inverseField]->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,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,8 +19,6 @@
namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type;
/**
* Represents a native SQL query.
*

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;
@ -10,6 +27,12 @@ namespace Doctrine\ORM;
*/
class ORMException extends \Exception
{
public static function missingMappingDriverImpl()
{
return new self("It's a requirement to specify a Metadata Driver and pass it ".
"to Doctrine\ORM\Configuration::setMetadataDriverImpl().");
}
public static function entityMissingAssignedId($entity)
{
return new self("Entity of type " . get_class($entity) . " is missing an assigned 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
@ -24,11 +22,8 @@ namespace Doctrine\ORM;
/**
* OptimisticLockException
*
* @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
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class OptimisticLockException extends ORMException
{

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
@ -134,18 +132,7 @@ final class PersistentCollection implements Collection
{
$this->_owner = $entity;
$this->_association = $assoc;
// Check for bidirectionality
//$this->_backRefFieldName = $assoc->inversedBy ?: $assoc->mappedBy;
if ( ! $assoc->isOwningSide) {
// For sure bi-directional
$this->_backRefFieldName = $assoc->mappedBy;
} else {
if (isset($this->_typeClass->inverseMappings[$assoc->sourceEntityName][$assoc->sourceFieldName])) {
// Bi-directional
$this->_backRefFieldName = $this->_typeClass->inverseMappings[$assoc->sourceEntityName][$assoc->sourceFieldName]->sourceFieldName;
}
}
$this->_backRefFieldName = $assoc->inversedBy ?: $assoc->mappedBy;
}
/**
@ -174,8 +161,8 @@ final class PersistentCollection implements Collection
public function hydrateAdd($element)
{
$this->_coll->add($element);
// If _backRefFieldName is set, then the association is bidirectional
// and we need to set the back reference.
// If _backRefFieldName is set and its a one-to-many association,
// we need to set the back reference.
if ($this->_backRefFieldName && $this->_association->isOneToMany()) {
// Set back reference to owner
$this->_typeClass->reflFields[$this->_backRefFieldName]
@ -372,7 +359,7 @@ final class PersistentCollection implements Collection
$this->_em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
}
return $removed;
}

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
@ -33,24 +31,22 @@ use Doctrine\ORM\EntityManager,
abstract class AbstractCollectionPersister
{
/**
*
* @var EntityManager
*/
protected $_em;
/**
* @var \Doctrine\DBAL\Connection
* @var Doctrine\DBAL\Connection
*/
protected $_conn;
/**
*
* @var \Doctrine\ORM\UnitOfWork
* @var Doctrine\ORM\UnitOfWork
*/
protected $_uow;
/**
* Initializes a new instance of a class derived from {@link AbstractCollectionPersister}.
* Initializes a new instance of a class derived from AbstractCollectionPersister.
*
* @param Doctrine\ORM\EntityManager $em
*/
@ -71,8 +67,8 @@ abstract class AbstractCollectionPersister
if ( ! $coll->getMapping()->isOwningSide) {
return; // ignore inverse side
}
$sql = $this->_getDeleteSql($coll);
$this->_conn->executeUpdate($sql, $this->_getDeleteSqlParameters($coll));
$sql = $this->_getDeleteSQL($coll);
$this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll));
}
/**
@ -80,7 +76,7 @@ abstract class AbstractCollectionPersister
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteSql(PersistentCollection $coll);
abstract protected function _getDeleteSQL(PersistentCollection $coll);
/**
* Gets the SQL parameters for the corresponding SQL statement to delete
@ -88,7 +84,7 @@ abstract class AbstractCollectionPersister
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteSqlParameters(PersistentCollection $coll);
abstract protected function _getDeleteSQLParameters(PersistentCollection $coll);
/**
* Updates the given collection, synchronizing it's state with the database
@ -109,9 +105,9 @@ abstract class AbstractCollectionPersister
public function deleteRows(PersistentCollection $coll)
{
$deleteDiff = $coll->getDeleteDiff();
$sql = $this->_getDeleteRowSql($coll);
$sql = $this->_getDeleteRowSQL($coll);
foreach ($deleteDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSqlParameters($coll, $element));
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
}
}
@ -121,9 +117,9 @@ abstract class AbstractCollectionPersister
public function insertRows(PersistentCollection $coll)
{
$insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSql($coll);
$sql = $this->_getInsertRowSQL($coll);
foreach ($insertDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getInsertRowSqlParameters($coll, $element));
$this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element));
}
}
@ -132,7 +128,7 @@ abstract class AbstractCollectionPersister
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteRowSql(PersistentCollection $coll);
abstract protected function _getDeleteRowSQL(PersistentCollection $coll);
/**
* Gets the SQL parameters for the corresponding SQL statement to delete the given
@ -141,21 +137,21 @@ abstract class AbstractCollectionPersister
* @param PersistentCollection $coll
* @param mixed $element
*/
abstract protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element);
abstract protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element);
/**
* Gets the SQL statement used for updating a row in the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getUpdateRowSql(PersistentCollection $coll);
abstract protected function _getUpdateRowSQL(PersistentCollection $coll);
/**
* Gets the SQL statement used for inserting a row in the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getInsertRowSql(PersistentCollection $coll);
abstract protected function _getInsertRowSQL(PersistentCollection $coll);
/**
* Gets the SQL parameters for the corresponding SQL statement to insert the given
@ -164,5 +160,5 @@ abstract class AbstractCollectionPersister
* @param PersistentCollection $coll
* @param mixed $element
*/
abstract protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element);
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
}

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
@ -32,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.
@ -93,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();
@ -492,8 +614,8 @@ class StandardEntityPersister
if ($found = $this->_em->getUnitOfWork()->tryGetById($joinColumnValues, $targetClass->rootEntityName)) {
$this->_class->reflFields[$field]->setValue($entity, $found);
// Complete inverse side, if necessary.
if (isset($targetClass->inverseMappings[$this->_class->name][$field])) {
$inverseAssoc = $targetClass->inverseMappings[$this->_class->name][$field];
if ($assoc->inversedBy) {
$inverseAssoc = $targetClass->associationMappings[$assoc->inversedBy];
$targetClass->reflFields[$inverseAssoc->sourceFieldName]->setValue($found, $entity);
}
$newData[$field] = $found;
@ -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,26 +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()
{
@ -724,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);
}
@ -732,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;
@ -755,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";
}
/**
@ -788,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()
{
@ -825,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.
*
@ -862,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;
@ -874,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;
@ -897,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
*/
@ -78,14 +75,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
public function getOwningTable($fieldName)
{
if ( ! isset($this->_owningTableMap[$fieldName])) {
if (isset($this->_class->associationMappings[$fieldName])) {
if (isset($this->_class->inheritedAssociationFields[$fieldName])) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->inheritedAssociationFields[$fieldName]
)->table['name'];
} else {
$this->_owningTableMap[$fieldName] = $this->_class->table['name'];
}
if (isset($this->_class->associationMappings[$fieldName]->inherited)) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->associationMappings[$fieldName]->inherited
)->table['name'];
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->fieldMappings[$fieldName]['inherited']
@ -120,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());
}
}
@ -235,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 = isset($this->_class->inheritedAssociationFields[$assoc->sourceFieldName]) ?
$this->_getSQLTableAlias($this->_em->getClassMetadata($this->_class->inheritedAssociationFields[$assoc->sourceFieldName]))
: $baseTableAlias;
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne()) {
$tableAlias = $assoc2->inherited ?
$this->_getSQLTableAlias($assoc2->inherited)
: $baseTableAlias;
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ", $tableAlias.$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
@ -266,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";
}
@ -283,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) {
@ -295,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
@ -308,7 +304,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Add join columns (foreign keys)
foreach ($subClass->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne() && ! isset($subClass->inheritedAssociationFields[$assoc2->sourceFieldName])) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne() && ! $assoc2->inherited) {
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias";
@ -330,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) {
@ -363,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} */
@ -377,7 +360,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
foreach ($this->_class->reflFields as $name => $field) {
if (isset($this->_class->fieldMappings[$name]['inherited']) && ! isset($this->_class->fieldMappings[$name]['id'])
|| isset($this->_class->inheritedAssociationFields[$name])
|| isset($this->_class->associationMappings[$name]->inherited)
|| ($this->_class->isVersioned && $this->_class->versionField == $name)) {
continue;
}
@ -402,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

@ -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,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
*/
@ -44,26 +41,27 @@ 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() && ! isset($subClass->inheritedAssociationFields[$assoc->sourceFieldName])) {
if ($assoc->isOwningSide && $assoc->isOneToOne() && ! $assoc->inherited) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias";
@ -90,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
@ -27,13 +25,10 @@ use Doctrine\ORM\Query\Parser,
/**
* A Query object represents a DQL query.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 1.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @since 1.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
*/
final class Query extends AbstractQuery
{
@ -62,6 +57,7 @@ final class Query extends AbstractQuery
* partial objects.
*
* @var string
* @todo Rename: HINT_OPTIMIZE
*/
const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
/**
@ -149,10 +145,10 @@ final class Query extends AbstractQuery
*
* @param Doctrine\ORM\EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
/*public function __construct(EntityManager $entityManager)
{
parent::__construct($entityManager);
}
}*/
/**
* Gets the SQL query/queries that correspond to this DQL query.
@ -162,7 +158,7 @@ final class Query extends AbstractQuery
*/
public function getSQL()
{
return $this->_parse()->getSqlExecutor()->getSqlStatements();
return $this->_parse()->getSQLExecutor()->getSQLStatements();
}
/**
@ -366,7 +362,7 @@ final class Query extends AbstractQuery
* @param string $dqlQuery DQL Query
* @return Doctrine\ORM\AbstractQuery
*/
public function setDql($dqlQuery)
public function setDQL($dqlQuery)
{
if ($dqlQuery !== null) {
$this->_dql = $dqlQuery;
@ -380,7 +376,7 @@ final class Query extends AbstractQuery
*
* @return string DQL query
*/
public function getDql()
public function getDQL()
{
return $this->_dql;
}
@ -408,7 +404,7 @@ final class Query extends AbstractQuery
*/
public function contains($dql)
{
return stripos($this->getDql(), $dql) === false ? false : true;
return stripos($this->getDQL(), $dql) === false ? 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
@ -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
@ -29,11 +27,8 @@ use Doctrine\DBAL\Connection,
* Executes the SQL statements for bulk DQL UPDATE statements on classes in
* Class Table Inheritance (JOINED).
*
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class MultiTableUpdateExecutor extends AbstractSqlExecutor
{

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;
}
@ -432,7 +427,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
}
return $this->getSqlTableAlias($class->table['name'], $identificationVariable);
return $this->getSQLTableAlias($class->table['name'], $identificationVariable);
}
/**
@ -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
@ -465,6 +461,11 @@ class SqlWalker implements TreeWalker
$fieldName = array_pop($parts);
$dqlAlias = $pathExpr->identificationVariable;
$class = $this->_queryComponents[$dqlAlias]['metadata'];
if (isset($class->associationMappings[$fieldName]->inherited)) {
$class = $this->_em->getClassMetadata($class->associationMappings[$fieldName]->inherited);
}
$assoc = $class->associationMappings[$fieldName];
if ($assoc->isOwningSide) {
@ -472,13 +473,18 @@ class SqlWalker implements TreeWalker
if (count($assoc->sourceToTargetKeyColumns) > 1) {
throw QueryException::associationPathCompositeKeyNotSupported();
}
$sql .= $this->walkIdentificationVariable($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);
}
@ -533,8 +539,8 @@ class SqlWalker implements TreeWalker
//FIXME: Include foreign key columns of child classes also!!??
foreach ($class->associationMappings as $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
if (isset($class->inheritedAssociationFields[$assoc->sourceFieldName])) {
$owningClass = $this->_em->getClassMetadata($class->inheritedAssociationFields[$assoc->sourceFieldName]);
if ($assoc->inherited) {
$owningClass = $this->_em->getClassMetadata($assoc->inherited);
$sqlTableAlias = $this->getSqlTableAlias($owningClass->table['name'], $dqlAlias);
} else {
$sqlTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
@ -683,23 +689,15 @@ class SqlWalker implements TreeWalker
$joinAssocPathExpr = $join->joinAssociationPathExpression;
$joinedDqlAlias = $join->aliasIdentificationVariable;
$targetQComp = $this->_queryComponents[$joinedDqlAlias];
$targetClass = $targetQComp['metadata'];
$relation = $targetQComp['relation'];
$sourceClass = $this->_queryComponents[$joinAssocPathExpr->identificationVariable]['metadata'];
$relation = $this->_queryComponents[$joinedDqlAlias]['relation'];
$targetClass = $this->_em->getClassMetadata($relation->targetEntityName);
$sourceClass = $this->_em->getClassMetadata($relation->sourceEntityName);
$targetTableName = $targetClass->getQuotedTableName($this->_platform);
$targetTableAlias = $this->getSqlTableAlias($targetClass->getTableName(), $joinedDqlAlias);
$sourceTableAlias = $this->getSqlTableAlias(
$sourceClass->getTableName(), $joinAssocPathExpr->identificationVariable
);
$targetTableAlias = $this->getSqlTableAlias($targetClass->table['name'], $joinedDqlAlias);
$sourceTableAlias = $this->getSqlTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable);
// Ensure we got the owning side, since it has all mapping info
if ( ! $relation->isOwningSide) {
$assoc = $targetClass->associationMappings[$relation->mappedBy];
} else {
$assoc = $relation;
}
$assoc = ( ! $relation->isOwningSide) ? $targetClass->associationMappings[$relation->mappedBy] : $relation;
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true) {
if ($relation->isOneToMany() || $relation->isManyToMany()) {
@ -713,7 +711,7 @@ class SqlWalker implements TreeWalker
foreach ($assoc->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
if ( ! $first) $sql .= ' AND '; else $first = false;
if ($relation->isOwningSide) {
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
$sql .= $sourceTableAlias . '.' . $sourceColumn
@ -783,12 +781,13 @@ class SqlWalker implements TreeWalker
). ')';
}
$discrSql = $this->_generateDiscriminatorColumnConditionSql($joinedDqlAlias);
$discrSql = $this->_generateDiscriminatorColumnConditionSQL($joinedDqlAlias);
if ($discrSql) {
$sql .= ' AND ' . $discrSql;
}
//FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
if ($targetClass->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
}
@ -960,7 +959,7 @@ class SqlWalker implements TreeWalker
// Add join columns (foreign keys) of the subclass
//TODO: Probably better do this in walkSelectClause to honor the INCLUDE_META_COLUMNS hint
foreach ($subClass->associationMappings as $fieldName => $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne() && ! isset($subClass->inheritedAssociationFields[$fieldName])) {
if ($assoc->isOwningSide && $assoc->isOneToOne() && ! $assoc->inherited) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
if ($beginning) $beginning = false; else $sql .= ', ';
$columnAlias = $this->getSqlColumnAlias($srcColumn);
@ -1219,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

@ -168,7 +168,7 @@ class QueryBuilder
*
* @return string The DQL string
*/
public function getDql()
public function getDQL()
{
if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) {
return $this->_dql;
@ -178,16 +178,16 @@ class QueryBuilder
switch ($this->_type) {
case self::DELETE:
$dql = $this->_getDqlForDelete();
$dql = $this->_getDQLForDelete();
break;
case self::UPDATE:
$dql = $this->_getDqlForUpdate();
$dql = $this->_getDQLForUpdate();
break;
case self::SELECT:
default:
$dql = $this->_getDqlForSelect();
$dql = $this->_getDQLForSelect();
break;
}
@ -211,7 +211,7 @@ class QueryBuilder
*/
public function getQuery()
{
return $this->_em->createQuery($this->getDql())
return $this->_em->createQuery($this->getDQL())
->setParameters($this->_params)
->setFirstResult($this->_firstResult)
->setMaxResults($this->_maxResults);
@ -613,7 +613,7 @@ class QueryBuilder
*/
public function andWhere($where)
{
$where = $this->getDqlPart('where');
$where = $this->getDQLPart('where');
$args = func_get_args();
if ($where instanceof Expr\Andx) {
@ -744,7 +744,7 @@ class QueryBuilder
array_unshift($args, $having);
$having = new Expr\Orx($args);
}
return $this->add('having', $having);
}
@ -779,7 +779,7 @@ class QueryBuilder
* @param string $queryPartName
* @return mixed $queryPart
*/
public function getDqlPart($queryPartName)
public function getDQLPart($queryPartName)
{
return $this->_dqlParts[$queryPartName];
}
@ -789,43 +789,43 @@ class QueryBuilder
*
* @return array $dqlParts
*/
public function getDqlParts()
public function getDQLParts()
{
return $this->_dqlParts;
}
private function _getDqlForDelete()
private function _getDQLForDelete()
{
return 'DELETE'
. $this->_getReducedDqlQueryPart('from', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE '))
. $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
. $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
. $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
}
private function _getDqlForUpdate()
private function _getDQLForUpdate()
{
return 'UPDATE'
. $this->_getReducedDqlQueryPart('from', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('set', array('pre' => ' SET ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE '))
. $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
. $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('set', array('pre' => ' SET ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
. $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
}
private function _getDqlForSelect()
private function _getDQLForSelect()
{
return 'SELECT'
. $this->_getReducedDqlQueryPart('select', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('join', array('pre' => ' ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE '))
. $this->_getReducedDqlQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('having', array('pre' => ' HAVING '))
. $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
. $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('from', array('pre' => ' FROM ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('join', array('pre' => ' ', 'separator' => ' '))
. $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
. $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING '))
. $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
}
private function _getReducedDqlQueryPart($queryPartName, $options = array())
private function _getReducedDQLQueryPart($queryPartName, $options = array())
{
$queryPart = $this->getDqlPart($queryPartName);
$queryPart = $this->getDQLPart($queryPartName);
if (empty($queryPart)) {
return (isset($options['empty']) ? $options['empty'] : '');
@ -838,11 +838,6 @@ class QueryBuilder
public function __toString()
{
return $this->getDql();
return $this->getDQL();
}
/*public function __clone()
{
$this->_q = clone $this->_q;
}*/
}

View File

@ -1,205 +0,0 @@
<?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\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\Common\CLI\CliException,
Doctrine\Common\CLI\Option,
Doctrine\Common\CLI\OptionGroup,
Doctrine\Common\Cache\AbstractDriver;
/**
* CLI Task to clear the cache of the various cache drivers
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ClearCacheTask extends AbstractTask
{
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$cacheOptions = new OptionGroup(OptionGroup::CARDINALITY_1_1, array(
new Option('query', null, 'Clear the query cache.'),
new Option('metadata', null, 'Clear the metadata cache.'),
new OptionGroup(OptionGroup::CARDINALITY_M_N, array(
new OptionGroup(OptionGroup::CARDINALITY_1_1, array(
new Option('result', null, 'Clear the result cache.')
)),
new OptionGroup(OptionGroup::CARDINALITY_0_N, array(
new Option('id', '<ID>', 'The id of the cache entry to delete (accepts * wildcards).'),
new Option('regex', '<REGEX>', 'Delete cache entries that match the given regular expression.'),
new Option('prefix', '<PREFIX>', 'Delete cache entries that have the given prefix.'),
new Option('suffix', '<SUFFIX>', 'Delete cache entries that have the given suffix.')
))
))
));
$doc = $this->getDocumentation();
$doc->setName('clear-cache')
->setDescription('Clear cache from configured query, result and metadata drivers.')
->getOptionGroup()
->addOption($cacheOptions);
}
/**
* @inheritdoc
*/
public function validate()
{
$arguments = $this->getArguments();
// Check if we have an active EntityManager
$em = $this->getConfiguration()->getAttribute('em');
if ($em === null) {
throw new CLIException(
"Attribute 'em' of CLI Configuration is not defined or it is not a valid EntityManager."
);
}
// When clearing the query cache no need to specify
// id, regex, prefix or suffix.
if (
(isset($arguments['query']) || isset($arguments['metadata'])) && (isset($arguments['id']) ||
isset($arguments['regex']) || isset($arguments['prefix']) || isset($arguments['suffix']))
) {
throw new CLIException(
'When clearing the query or metadata cache do not ' .
'specify any --id, --regex, --prefix or --suffix.'
);
}
return true;
}
/**
* @inheritdoc
*/
public function run()
{
$arguments = $this->getArguments();
$printer = $this->getPrinter();
$query = isset($arguments['query']);
$result = isset($arguments['result']);
$metadata = isset($arguments['metadata']);
$id = isset($arguments['id']) ? $arguments['id'] : null;
$regex = isset($arguments['regex']) ? $arguments['regex'] : null;
$prefix = isset($arguments['prefix']) ? $arguments['prefix'] : null;
$suffix = isset($arguments['suffix']) ? $arguments['suffix'] : null;
$all = false;
if ( ! $query && ! $result && ! $metadata) {
$all = true;
}
$em = $this->getConfiguration()->getAttribute('em');
$configuration = $em->getConfiguration();
if ($query || $all) {
$this->_doDelete(
'query', $configuration->getQueryCacheImpl(), $id, $regex, $prefix, $suffix
);
}
if ($result || $all) {
$this->_doDelete(
'result', $configuration->getResultCacheImpl(), $id, $regex, $prefix, $suffix
);
}
if ($metadata || $all) {
$this->_doDelete(
'metadata', $configuration->getMetadataCacheImpl(), $id, $regex, $prefix, $suffix
);
}
}
private function _doDelete($type, $cacheDriver, $id, $regex, $prefix, $suffix)
{
$printer = $this->getPrinter();
if ( ! $cacheDriver) {
throw new CLIException('No driver has been configured for the ' . $type . ' cache.');
}
if ($id) {
$printer->writeln('Clearing ' . $type . ' cache entries that match the id "' . $id . '".', 'INFO');
$deleted = $cacheDriver->delete($id);
if (is_array($deleted)) {
$this->_printDeleted($type, $deleted);
} else if (is_bool($deleted) && $deleted) {
$this->_printDeleted($type, array($id));
}
}
if ($regex) {
$printer->writeln('Clearing ' . $type . ' cache entries that match the regular expression ".' . $regex . '"', 'INFO');
$this->_printDeleted($type, $cacheDriver->deleteByRegex('/' . $regex. '/'));
}
if ($prefix) {
$printer->writeln('Clearing ' . $type . ' cache entries that have the prefix "' . $prefix . '".', 'INFO');
$this->_printDeleted($type, $cacheDriver->deleteByPrefix($prefix));
}
if ($suffix) {
$printer->writeln('Clearing ' . $type . ' cache entries that have the suffix "' . $suffix . '".', 'INFO');
$this->_printDeleted($type, $cacheDriver->deleteBySuffix($suffix));
}
if ( ! $id && ! $regex && ! $prefix && ! $suffix) {
$printer->writeln('Clearing all ' . $type . ' cache entries.', 'INFO');
$this->_printDeleted($type, $cacheDriver->deleteAll());
}
}
private function _printDeleted($type, array $ids)
{
$printer = $this->getPrinter();
if ( ! empty($ids)) {
foreach ($ids as $id) {
$printer->writeln(' - ' . $id);
}
} else {
throw new CLIException('No ' . $type . ' cache entries found.');
}
$printer->write(PHP_EOL);
}
}

View File

@ -1,117 +0,0 @@
<?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\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\ORM\Tools\Export\ClassMetadataExporter,
Doctrine\Common\CLI\CliException,
Doctrine\Common\CLI\Option,
Doctrine\Common\CLI\OptionGroup,
Doctrine\ORM\Tools\ConvertDoctrine1Schema,
Doctrine\ORM\Tools\EntityGenerator;
/**
* CLI Task to convert a Doctrine 1 schema to a Doctrine 2 mapping file
*
* @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 ConvertDoctrine1SchemaTask extends AbstractTask
{
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$options = new OptionGroup(OptionGroup::CARDINALITY_N_N, array(
new Option('from', '<FROM>', 'The path to the Doctrine 1 schema.'),
new Option('to', '<TO>', 'The Doctrine 2 mapping format to convert to.'),
new Option('dest', '<DEST>', 'The path to export the converted schema.')
));
$doc = $this->getDocumentation();
$doc->setName('convert-10-schema')
->setDescription('Converts a Doctrine 1.X schema into a Doctrine 2.X schema.')
->getOptionGroup()
->addOption($options);
}
/**
* @inheritdoc
*/
public function validate()
{
$arguments = $this->getArguments();
$em = $this->getConfiguration()->getAttribute('em');
if ( ! isset($arguments['from']) || ! isset($arguments['to']) || ! isset($arguments['dest'])) {
throw new CLIException('You must specify a value for --from, --to and --dest');
}
return true;
}
public function run()
{
$arguments = $this->getArguments();
$printer = $this->getPrinter();
$printer->writeln(sprintf(
'Converting Doctrine 1 schema at "%s" to the "%s" format',
$printer->format($arguments['from'], 'KEYWORD'),
$printer->format($arguments['to'], 'KEYWORD')
)
);
$cme = new ClassMetadataExporter();
$exporter = $cme->getExporter($arguments['to'], $arguments['dest']);
if ($arguments['to'] === 'annotation') {
$entityGenerator = new EntityGenerator();
$exporter->setEntityGenerator($entityGenerator);
}
$converter = new ConvertDoctrine1Schema($arguments['from']);
$metadatas = $converter->getMetadatas();
foreach ($metadatas as $metadata) {
$printer->writeln(
sprintf('Processing entity "%s"', $printer->format($metadata->name, 'KEYWORD'))
);
}
$exporter->setMetadatas($metadatas);
$exporter->export();
$printer->writeln(sprintf(
'Writing Doctrine 2 mapping files to "%s"',
$printer->format($arguments['dest'], 'KEYWORD')
)
);
}
}

View File

@ -1,163 +0,0 @@
<?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\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\Common\CLI\CliException,
Doctrine\Common\CLI\Option,
Doctrine\Common\CLI\OptionGroup,
Doctrine\ORM\Tools\EntityGenerator,
Doctrine\ORM\Tools\Export\ClassMetadataExporter,
Doctrine\ORM\Mapping\Driver\DriverChain,
Doctrine\ORM\Mapping\Driver\AnnotationDriver,
Doctrine\ORM\Mapping\Driver\Driver;
/**
* CLI Task to convert your mapping information between the various formats
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ConvertMappingTask extends AbstractTask
{
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$convertOptions = new OptionGroup(OptionGroup::CARDINALITY_N_N, array(
new OptionGroup(OptionGroup::CARDINALITY_1_1, array(
new Option('from', '<SOURCE>', 'The path to the mapping information to convert from (yml, xml, php, annotation).'),
new Option('from-database', null, 'Use this option if you wish to reverse engineer your database to a set of Doctrine mapping files.')
)),
new Option('to', '<TYPE>', 'The format to convert to (yml, xml, php, annotation).'),
new Option('dest', '<PATH>', 'The path to write the converted mapping information.')
));
$doc = $this->getDocumentation();
$doc->setName('convert-mapping')
->setDescription('Convert mapping information between supported formats.')
->getOptionGroup()
->addOption($convertOptions);
}
/**
* @inheritdoc
*/
public function validate()
{
$arguments = $this->getArguments();
if (isset($arguments['from-database']) && $arguments['from-database']) {
$arguments['from'] = 'database';
$this->setArguments($arguments);
}
if (!(isset($arguments['from']) && isset($arguments['to']) && isset($arguments['dest']))) {
throw new CLIException(
'You must include a value for all three options: --from, --to and --dest.'
);
}
if (strtolower($arguments['to']) != 'annotation' && isset($arguments['extend'])) {
throw new CLIException(
'You can only use the --extend argument when converting to annotations.'
);
}
if (strtolower($arguments['from']) == 'database') {
// Check if we have an active EntityManager
$em = $this->getConfiguration()->getAttribute('em');
if ($em === null) {
throw new CLIException(
"Attribute 'em' of CLI Configuration is not defined or it is not a valid EntityManager."
);
}
$config = $em->getConfiguration();
$config->setMetadataDriverImpl(
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
)
);
}
return true;
}
public function run()
{
$arguments = $this->getArguments();
$cme = new ClassMetadataExporter();
$cme->setEntityManager($this->getConfiguration()->getAttribute('em'));
$printer = $this->getPrinter();
// Get exporter and configure it
$exporter = $cme->getExporter($arguments['to'], $arguments['dest']);
if ($arguments['to'] === 'annotation') {
$entityGenerator = new EntityGenerator();
$exporter->setEntityGenerator($entityGenerator);
if (isset($arguments['extend']) && $arguments['extend']) {
$entityGenerator->setClassToExtend($arguments['extend']);
}
if (isset($arguments['num-spaces']) && $arguments['extend']) {
$entityGenerator->setNumSpaces($arguments['num-spaces']);
}
}
$from = (array) $arguments['from'];
foreach ($from as $source) {
$cme->addMappingSource($source);
}
$metadatas = $cme->getMetadatas();
foreach ($metadatas as $metadata) {
$printer->writeln(
sprintf('Processing entity "%s"', $printer->format($metadata->name, 'KEYWORD'))
);
}
$printer->writeln('');
$printer->writeln(
sprintf(
'Exporting "%s" mapping information to "%s"',
$printer->format($arguments['to'], 'KEYWORD'),
$printer->format($arguments['dest'], 'KEYWORD')
)
);
$exporter->setMetadatas($metadatas);
$exporter->export();
}
}

View File

@ -1,120 +0,0 @@
<?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\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\Common\CLI\Option,
Doctrine\Common\CLI\OptionGroup,
Doctrine\Common\CLI\CLIException,
Doctrine\ORM\Tools\EntityGenerator,
Doctrine\ORM\Tools\ClassMetadataReader;
/**
* CLI Task to generate entity classes and method stubs from your mapping information.
*
* @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 GenerateEntitiesTask extends AbstractTask
{
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$options = new OptionGroup(OptionGroup::CARDINALITY_N_N, array(
new Option('from', '<FROM>', 'The path to mapping information.'),
new Option('dest', '<DEST>', 'The path to generate your entity classes.')
));
$doc = $this->getDocumentation();
$doc->setName('generate-entities')
->setDescription('Generate entity classes and method stubs from your mapping information.')
->getOptionGroup()
->addOption($options);
}
/**
* @inheritdoc
*/
public function validate()
{
$arguments = $this->getArguments();
if ( ! isset($arguments['from']) || ! isset($arguments['dest'])) {
throw new CLIException('You must specify a value for --from and --dest');
}
return true;
}
/**
* @inheritdoc
*/
public function run()
{
$printer = $this->getPrinter();
$arguments = $this->getArguments();
$from = $arguments['from'];
$dest = realpath($arguments['dest']);
$entityGenerator = new EntityGenerator();
$entityGenerator->setGenerateAnnotations(false);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(false);
$entityGenerator->setUpdateEntityIfExists(true);
if (isset($arguments['extend']) && $arguments['extend']) {
$entityGenerator->setClassToExtend($arguments['extend']);
}
if (isset($arguments['num-spaces']) && $arguments['extend']) {
$entityGenerator->setNumSpaces($arguments['num-spaces']);
}
$reader = new ClassMetadataReader();
$reader->setEntityManager($this->getConfiguration()->getAttribute('em'));
$reader->addMappingSource($from);
$metadatas = $reader->getMetadatas();
foreach ($metadatas as $metadata) {
$printer->writeln(
sprintf('Processing entity "%s"', $printer->format($metadata->name, 'KEYWORD'))
);
}
$entityGenerator->generate($metadatas, $dest);
$printer->write(PHP_EOL);
$printer->writeln(
sprintf('Entity classes generated to "%s"',
$printer->format($dest, 'KEYWORD')
)
);
}
}

View File

@ -1,108 +0,0 @@
<?php
namespace Doctrine\ORM\Tools\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\Common\CLI\CLIException,
Doctrine\Common\CLI\Option,
Doctrine\Common\CLI\OptionGroup;
/**
* Task to (re)generate the proxy classes used by doctrine.
*
* @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>
*/
class GenerateProxiesTask extends AbstractTask
{
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$classDir = new OptionGroup(OptionGroup::CARDINALITY_1_1, array(
new Option('class-dir', '<PATH>', 'Specified directory where mapping classes are located.')
));
$toDir = new OptionGroup(OptionGroup::CARDINALITY_0_1, array(
new Option('to-dir', '<PATH>', 'Generates the classes in the specified directory.')
));
$doc = $this->getDocumentation();
$doc->setName('generate-proxies')
->setDescription('Generates proxy classes for entity classes.')
->getOptionGroup()
->addOption($classDir)
->addOption($toDir);
}
/**
* @inheritdoc
*/
public function validate()
{
$arguments = $this->getArguments();
$em = $this->getConfiguration()->getAttribute('em');
if ($em === null) {
throw new CLIException(
"Attribute 'em' of CLI Configuration is not defined or it is not a valid EntityManager."
);
}
$metadataDriver = $em->getConfiguration()->getMetadataDriverImpl();
if ($metadataDriver instanceof \Doctrine\ORM\Mapping\Driver\AnnotationDriver) {
if (isset($arguments['class-dir'])) {
$metadataDriver->addPaths((array) $arguments['class-dir']);
} else {
throw new CLIException(
'The supplied configuration uses the annotation metadata driver. ' .
"The 'class-dir' argument is required for this driver."
);
}
}
return true;
}
/**
* @inheritdoc
*/
public function run()
{
$arguments = $this->getArguments();
$printer = $this->getPrinter();
$em = $this->getConfiguration()->getAttribute('em');
$cmf = $em->getMetadataFactory();
$classes = $cmf->getAllMetadata();
$factory = $em->getProxyFactory();
if (empty($classes)) {
$printer->writeln('No classes to process.', 'INFO');
} else {
foreach ($classes as $class) {
$printer->writeln(
sprintf('Processing entity "%s"', $printer->format($class->name, 'KEYWORD'))
);
}
$factory->generateProxyClasses(
$classes, isset($arguments['to-dir']) ? $arguments['to-dir'] : null
);
$printer->writeln('');
$printer->writeln(
sprintf('Proxy classes generated to "%s"',
$printer->format(isset($arguments['to-dir']) ? $arguments['to-dir'] : $em->getConfiguration()->getProxyDir(), 'KEYWORD'))
);
}
}
}

View File

@ -1,146 +0,0 @@
<?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\CLI\Tasks;
use Doctrine\Common\CLI\Tasks\AbstractTask,
Doctrine\Common\CLI\Option,
Doctrine\Common\CLI\OptionGroup,
Doctrine\Common\CLI\CLIException,
Doctrine\ORM\Tools\ClassMetadataReader;
/**
* CLI Task to generate repository classes for some mapping information
*
* @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 GenerateRepositoriesTask extends ConvertMappingTask
{
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
{
}';
/**
* @inheritdoc
*/
public function buildDocumentation()
{
$options = new OptionGroup(OptionGroup::CARDINALITY_N_N, array(
new Option('from', '<FROM>', 'The path to mapping information.'),
new Option('dest', '<DEST>', 'The path to generate your repository classes.')
));
$doc = $this->getDocumentation();
$doc->setName('generate-repositories')
->setDescription('Generate repository classes for some mapping information.')
->getOptionGroup()
->addOption($options);
}
/**
* @inheritdoc
*/
public function validate()
{
$arguments = $this->getArguments();
$em = $this->getConfiguration()->getAttribute('em');
if ( ! isset($arguments['from']) || ! isset($arguments['dest'])) {
throw new CLIException('You must specify a value for --from and --dest');
}
return true;
}
public function run()
{
$printer = $this->getPrinter();
$arguments = $this->getArguments();
$dest = realpath($arguments['dest']);
$reader = new ClassMetadataReader();
$reader->setEntityManager($this->getConfiguration()->getAttribute('em'));
$reader->addMappingSource($arguments['from']);
$metadatas = $reader->getMetadatas();
foreach ($metadatas as $metadata) {
if ($metadata->customRepositoryClassName) {
$code = $this->_generateRepositoryClass($metadata->customRepositoryClassName);
$path = $dest . '/' . str_replace('\\', '/', $metadata->customRepositoryClassName) . '.php';
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
}
$printer->writeln(
sprintf('Processing entity "%s"',
$printer->format($metadata->customRepositoryClassName, 'KEYWORD')
)
);
file_put_contents($path, $code);
}
}
$printer->writeln('');
$printer->writeln(
sprintf('Entity repository classes generated to "%s"',
$printer->format($dest, 'KEYWORD')
)
);
}
private function _generateRepositoryClass($fullyQualifiedClassName)
{
$namespace = substr($fullyQualifiedClassName, 0, strrpos($fullyQualifiedClassName, '\\'));
$pos = strrpos($fullyQualifiedClassName, '\\');
$className = substr($fullyQualifiedClassName, $pos + 1, strlen($fullyQualifiedClassName));
$placeHolders = array(
'<namespace>',
'<className>'
);
$replacements = array(
$namespace,
$className
);
$code = str_replace($placeHolders, $replacements, self::$_template);
return $code;
}
}

Some files were not shown because too many files have changed in this diff Show More