mirror of
https://github.com/doctrine/orm.git
synced 2026-04-24 15:08:12 +02:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0363a5548d | |||
| 3764e49e6c | |||
| 6ee20204a5 | |||
| d9b0c87ded | |||
| 8594e5c4da | |||
| 5f821f3b98 | |||
| b566525099 | |||
| 215c4a03e1 | |||
| b3ccd6466b | |||
| b596bbb29f | |||
| c204e6c6a1 | |||
| 0bc94589e1 | |||
| f37856829f | |||
| 157c793810 | |||
| 72d838a804 | |||
| 58f8dc5d4c | |||
| 7d3ecd9481 | |||
| 1bb55703a7 | |||
| 56cbcec13d | |||
| 837c19bfc0 | |||
| 7b8f09ee4a | |||
| 488a4dc78a | |||
| 1364b6acc6 | |||
| 3dbe181762 | |||
| a3acaab65c | |||
| f183d25a33 | |||
| 7c8350094e | |||
| c613410ba6 | |||
| 6bb7581dd7 | |||
| ab71dab7d1 | |||
| 2c114756bd | |||
| 45496f040d | |||
| b40866c624 | |||
| a89cc7abea | |||
| 5ac111e5f8 | |||
| c5f66e6e7f | |||
| b59f495875 | |||
| 3829b9c28b | |||
| 65bcdbf4c7 | |||
| 95d000e51b | |||
| 3657df3b01 | |||
| 1661ffae9a | |||
| b424a5cf14 | |||
| 2767a4eec4 | |||
| 9486867279 | |||
| 6f2bb08972 | |||
| da2d3b406e | |||
| c4b7d3fbea | |||
| 84373d05a4 | |||
| e82e7147fa | |||
| e23ed2250d | |||
| 192bb6fd21 | |||
| 0f3679f034 | |||
| 1d2cd82706 | |||
| b983d86612 | |||
| b11f01643c | |||
| b58fb8f5d4 | |||
| 925a22b71d | |||
| 0f0d8abd67 | |||
| 470c15ce05 | |||
| 3cc5fc0252 | |||
| fd0657089a | |||
| de3b237292 | |||
| 1221cc3a2a | |||
| 9efbc1fa71 |
@@ -44,6 +44,12 @@ Now parenthesis are considered, the previous DQL will generate:
|
||||
|
||||
# Upgrade to 2.3
|
||||
|
||||
## Auto Discriminator Map breaks userland implementations with Listener
|
||||
|
||||
The new feature to detect discriminator maps automatically when none
|
||||
are provided breaks userland implementations doing this with a
|
||||
listener in ``loadClassMetadata`` event.
|
||||
|
||||
## EntityManager#find() not calls EntityRepository#find() anymore
|
||||
|
||||
Previous to 2.3, calling ``EntityManager#find()`` would be delegated to
|
||||
|
||||
+1
-1
@@ -35,6 +35,6 @@
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor"]
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +350,7 @@
|
||||
<xs:element name="generator" type="orm:generator" minOccurs="0" />
|
||||
<xs:element name="sequence-generator" type="orm:sequence-generator" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="custom-id-generator" type="orm:custom-id-generator" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="options" type="orm:options" minOccurs="0" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
|
||||
@@ -190,17 +190,14 @@ class ArrayHydrator extends AbstractHydrator
|
||||
( ! isset($baseElement[$relationAlias]))
|
||||
) {
|
||||
$baseElement[$relationAlias] = null;
|
||||
} else if (
|
||||
( ! isset($baseElement[$relationAlias])) ||
|
||||
( ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]))
|
||||
) {
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
$coll =& $baseElement[$relationAlias];
|
||||
|
||||
if ($coll !== null) {
|
||||
if (is_array($coll)) {
|
||||
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1031,9 +1031,14 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function isIdentifier($fieldName)
|
||||
{
|
||||
if ( ! $this->identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->isIdentifierComposite) {
|
||||
return $fieldName === $this->identifier[0];
|
||||
}
|
||||
|
||||
return in_array($fieldName, $this->identifier);
|
||||
}
|
||||
|
||||
@@ -2483,6 +2488,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function addLifecycleCallback($callback, $event)
|
||||
{
|
||||
if(isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lifecycleCallbacks[$event][] = $callback;
|
||||
}
|
||||
|
||||
@@ -2790,8 +2799,12 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function setSequenceGeneratorDefinition(array $definition)
|
||||
{
|
||||
if (isset($definition['name']) && $definition['name'] == '`') {
|
||||
$definition['name'] = trim($definition['name'], '`');
|
||||
if ( ! isset($definition['sequenceName'])) {
|
||||
throw MappingException::missingSequenceName($this->name);
|
||||
}
|
||||
|
||||
if ($definition['sequenceName'][0] == '`') {
|
||||
$definition['sequenceName'] = trim($definition['sequenceName'], '`');
|
||||
$definition['quoted'] = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -450,12 +450,9 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
|
||||
/* @var $method \ReflectionMethod */
|
||||
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
// filter for the declaring class only, callbacks from parents will already be registered.
|
||||
if ($method->getDeclaringClass()->name !== $class->name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->getMethodCallbacks($method) as $value) {
|
||||
|
||||
$metadata->addLifecycleCallback($value[0], $value[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +278,10 @@ class XmlDriver extends FileDriver
|
||||
$mapping['columnDefinition'] = (string)$idElement['column-definition'];
|
||||
}
|
||||
|
||||
if (isset($idElement->options)) {
|
||||
$mapping['options'] = $this->_parseOptions($idElement->options->children());
|
||||
}
|
||||
|
||||
$metadata->mapField($mapping);
|
||||
|
||||
if (isset($idElement->generator)) {
|
||||
|
||||
@@ -264,6 +264,10 @@ class YamlDriver extends FileDriver
|
||||
$mapping['columnDefinition'] = $idElement['columnDefinition'];
|
||||
}
|
||||
|
||||
if (isset($idElement['options'])) {
|
||||
$mapping['options'] = $idElement['options'];
|
||||
}
|
||||
|
||||
$metadata->mapField($mapping);
|
||||
|
||||
if (isset($idElement['generator'])) {
|
||||
|
||||
@@ -757,4 +757,16 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
$cascades
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
*
|
||||
* @return MappingException
|
||||
*/
|
||||
public static function missingSequenceName($className)
|
||||
{
|
||||
return new self(
|
||||
sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ class OptimisticLockException extends ORMException
|
||||
*/
|
||||
public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion)
|
||||
{
|
||||
$expectedLockVersion = ($expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion;
|
||||
$actualLockVersion = ($actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion;
|
||||
return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
/**
|
||||
@@ -36,71 +38,60 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
class TrimFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
* @var boolean
|
||||
*/
|
||||
public $leading;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @var boolean
|
||||
*/
|
||||
public $trailing;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @var boolean
|
||||
*/
|
||||
public $both;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @var boolean
|
||||
*/
|
||||
public $trimChar = false;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\ORM\Query\AST\Node
|
||||
*/
|
||||
public $stringPrimary;
|
||||
|
||||
/**
|
||||
* @override
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
$pos = AbstractPlatform::TRIM_UNSPECIFIED;
|
||||
if ($this->leading) {
|
||||
$pos = AbstractPlatform::TRIM_LEADING;
|
||||
} else if ($this->trailing) {
|
||||
$pos = AbstractPlatform::TRIM_TRAILING;
|
||||
} else if ($this->both) {
|
||||
$pos = AbstractPlatform::TRIM_BOTH;
|
||||
}
|
||||
$stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary);
|
||||
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
|
||||
$trimMode = $this->getTrimMode();
|
||||
$trimChar = ($this->trimChar !== false)
|
||||
? $sqlWalker->getConnection()->quote($this->trimChar)
|
||||
: false;
|
||||
|
||||
return $sqlWalker->getConnection()->getDatabasePlatform()->getTrimExpression(
|
||||
$sqlWalker->walkStringPrimary($this->stringPrimary),
|
||||
$pos,
|
||||
($this->trimChar != false) ? $sqlWalker->getConnection()->quote($this->trimChar) : false
|
||||
);
|
||||
return $platform->getTrimExpression($stringPrimary, $trimMode, $trimChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$lexer = $parser->getLexer();
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
if (strcasecmp('leading', $lexer->lookahead['value']) === 0) {
|
||||
$parser->match(Lexer::T_LEADING);
|
||||
$this->leading = true;
|
||||
} else if (strcasecmp('trailing', $lexer->lookahead['value']) === 0) {
|
||||
$parser->match(Lexer::T_TRAILING);
|
||||
$this->trailing = true;
|
||||
} else if (strcasecmp('both', $lexer->lookahead['value']) === 0) {
|
||||
$parser->match(Lexer::T_BOTH);
|
||||
$this->both = true;
|
||||
}
|
||||
$this->parseTrimMode($parser);
|
||||
|
||||
if ($lexer->isNextToken(Lexer::T_STRING)) {
|
||||
$parser->match(Lexer::T_STRING);
|
||||
|
||||
$this->trimChar = $lexer->token['value'];
|
||||
}
|
||||
|
||||
@@ -112,4 +103,61 @@ class TrimFunction extends FunctionNode
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\Query\Parser $parser
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private function getTrimMode()
|
||||
{
|
||||
if ($this->leading) {
|
||||
return AbstractPlatform::TRIM_LEADING;
|
||||
}
|
||||
|
||||
if ($this->trailing) {
|
||||
return AbstractPlatform::TRIM_TRAILING;
|
||||
}
|
||||
|
||||
if ($this->both) {
|
||||
return AbstractPlatform::TRIM_BOTH;
|
||||
}
|
||||
|
||||
return AbstractPlatform::TRIM_UNSPECIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\Query\Parser $parser
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function parseTrimMode(Parser $parser)
|
||||
{
|
||||
$lexer = $parser->getLexer();
|
||||
$value = $lexer->lookahead['value'];
|
||||
|
||||
if (strcasecmp('leading', $value) === 0) {
|
||||
$parser->match(Lexer::T_LEADING);
|
||||
|
||||
$this->leading = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp('trailing', $value) === 0) {
|
||||
$parser->match(Lexer::T_TRAILING);
|
||||
|
||||
$this->trailing = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp('both', $value) === 0) {
|
||||
$parser->match(Lexer::T_BOTH);
|
||||
|
||||
$this->both = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,11 @@ class QueryExpressionVisitor extends ExpressionVisitor
|
||||
Comparison::LTE => Expr\Comparison::LTE
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $rootAlias;
|
||||
|
||||
/**
|
||||
* @var Expr
|
||||
*/
|
||||
@@ -58,10 +63,13 @@ class QueryExpressionVisitor extends ExpressionVisitor
|
||||
private $parameters = array();
|
||||
|
||||
/**
|
||||
* Constructor with internal initialization.
|
||||
* Constructor
|
||||
*
|
||||
* @param string $rootAlias
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct($rootAlias)
|
||||
{
|
||||
$this->rootAlias = $rootAlias;
|
||||
$this->expr = new Expr();
|
||||
}
|
||||
|
||||
@@ -133,33 +141,33 @@ class QueryExpressionVisitor extends ExpressionVisitor
|
||||
switch ($comparison->getOperator()) {
|
||||
case Comparison::IN:
|
||||
$this->parameters[] = $parameter;
|
||||
return $this->expr->in($comparison->getField(), $placeholder);
|
||||
return $this->expr->in($this->rootAlias . '.' . $comparison->getField(), $placeholder);
|
||||
|
||||
case Comparison::NIN:
|
||||
$this->parameters[] = $parameter;
|
||||
return $this->expr->notIn($comparison->getField(), $placeholder);
|
||||
return $this->expr->notIn($this->rootAlias . '.' . $comparison->getField(), $placeholder);
|
||||
|
||||
case Comparison::EQ:
|
||||
case Comparison::IS:
|
||||
if ($this->walkValue($comparison->getValue()) === null) {
|
||||
return $this->expr->isNull($comparison->getField());
|
||||
return $this->expr->isNull($this->rootAlias . '.' . $comparison->getField());
|
||||
}
|
||||
$this->parameters[] = $parameter;
|
||||
return $this->expr->eq($comparison->getField(), $placeholder);
|
||||
return $this->expr->eq($this->rootAlias . '.' . $comparison->getField(), $placeholder);
|
||||
|
||||
case Comparison::NEQ:
|
||||
if ($this->walkValue($comparison->getValue()) === null) {
|
||||
return $this->expr->isNotNull($comparison->getField());
|
||||
return $this->expr->isNotNull($this->rootAlias . '.' . $comparison->getField());
|
||||
}
|
||||
$this->parameters[] = $parameter;
|
||||
return $this->expr->neq($comparison->getField(), $placeholder);
|
||||
return $this->expr->neq($this->rootAlias . '.' . $comparison->getField(), $placeholder);
|
||||
|
||||
default:
|
||||
$operator = self::convertComparisonOperator($comparison->getOperator());
|
||||
if ($operator) {
|
||||
$this->parameters[] = $parameter;
|
||||
return new Expr\Comparison(
|
||||
$comparison->getField(),
|
||||
$this->rootAlias . '.' . $comparison->getField(),
|
||||
$operator,
|
||||
$placeholder
|
||||
);
|
||||
|
||||
@@ -168,7 +168,12 @@ class ResultSetMappingBuilder extends ResultSetMapping
|
||||
throw new \InvalidArgumentException("The column '$columnAlias' conflicts with another column in the mapper.");
|
||||
}
|
||||
|
||||
$this->addMetaResult($alias, $columnAlias, $columnName);
|
||||
$this->addMetaResult(
|
||||
$alias,
|
||||
$columnAlias,
|
||||
$columnName,
|
||||
(isset($associationMapping['id']) && $associationMapping['id'] === true)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,7 +1087,8 @@ class QueryBuilder
|
||||
*/
|
||||
public function addCriteria(Criteria $criteria)
|
||||
{
|
||||
$visitor = new QueryExpressionVisitor();
|
||||
$rootAlias = $this->getRootAlias();
|
||||
$visitor = new QueryExpressionVisitor($rootAlias);
|
||||
|
||||
if ($whereExpression = $criteria->getWhereExpression()) {
|
||||
$this->andWhere($visitor->dispatch($whereExpression));
|
||||
@@ -1098,7 +1099,7 @@ class QueryBuilder
|
||||
|
||||
if ($criteria->getOrderings()) {
|
||||
foreach ($criteria->getOrderings() as $sort => $order) {
|
||||
$this->addOrderBy($sort, $order);
|
||||
$this->addOrderBy($rootAlias . '.' . $sort, $order);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Helper;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Helper\Helper;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
|
||||
/**
|
||||
* Doctrine CLI Connection Helper.
|
||||
@@ -35,18 +36,18 @@ use Doctrine\ORM\EntityManager;
|
||||
class EntityManagerHelper extends Helper
|
||||
{
|
||||
/**
|
||||
* Doctrine ORM EntityManager.
|
||||
* Doctrine ORM EntityManagerInterface.
|
||||
*
|
||||
* @var EntityManager
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $_em;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Doctrine\ORM\EntityManager $em
|
||||
* @param EntityManagerInterface $em
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
@@ -54,7 +55,7 @@ class EntityManagerHelper extends Helper
|
||||
/**
|
||||
* Retrieves Doctrine ORM EntityManager.
|
||||
*
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
|
||||
use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Internal\CommitOrderCalculator;
|
||||
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
|
||||
@@ -47,7 +47,7 @@ use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
|
||||
class SchemaTool
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\EntityManager
|
||||
* @var \Doctrine\ORM\EntityManagerInterface
|
||||
*/
|
||||
private $em;
|
||||
|
||||
@@ -67,9 +67,9 @@ class SchemaTool
|
||||
* Initializes a new SchemaTool instance that uses the connection of the
|
||||
* provided EntityManager.
|
||||
*
|
||||
* @param \Doctrine\ORM\EntityManager $em
|
||||
* @param \Doctrine\ORM\EntityManagerInterface $em
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->platform = $em->getConnection()->getDatabasePlatform();
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace Doctrine\ORM\Tools;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
@@ -37,14 +37,14 @@ use Doctrine\DBAL\Types\Type;
|
||||
class SchemaValidator
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
* @param EntityManagerInterface $em
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$class = $this->em->getClassMetadata(get_class($entity));
|
||||
}
|
||||
|
||||
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush);
|
||||
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER;
|
||||
|
||||
if ($invoke !== ListenersInvoker::INVOKE_NONE) {
|
||||
$this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke);
|
||||
@@ -1736,6 +1736,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$associatedId = $this->getEntityIdentifier($idValue);
|
||||
|
||||
$flatId[$idField] = $associatedId[$targetClassMetadata->identifier[0]];
|
||||
} else {
|
||||
$flatId[$idField] = $idValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2231,6 +2233,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
function ($assoc) { return $assoc['isCascadeRemove']; }
|
||||
);
|
||||
|
||||
$entitiesToCascade = array();
|
||||
|
||||
foreach ($associationMappings as $assoc) {
|
||||
if ($entity instanceof Proxy && !$entity->__isInitialized__) {
|
||||
$entity->__load();
|
||||
@@ -2243,18 +2247,22 @@ class UnitOfWork implements PropertyChangedListener
|
||||
case (is_array($relatedEntities)):
|
||||
// If its a PersistentCollection initialization is intended! No unwrap!
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->doRemove($relatedEntity, $visited);
|
||||
$entitiesToCascade[] = $relatedEntity;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($relatedEntities !== null):
|
||||
$this->doRemove($relatedEntities, $visited);
|
||||
$entitiesToCascade[] = $relatedEntities;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($entitiesToCascade as $relatedEntity) {
|
||||
$this->doRemove($relatedEntity, $visited);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2490,14 +2498,14 @@ class UnitOfWork implements PropertyChangedListener
|
||||
&& isset($hints[Query::HINT_REFRESH_ENTITY])
|
||||
&& ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
|
||||
&& $unmanagedProxy instanceof Proxy
|
||||
&& (($unmanagedProxyClass = $this->em->getClassMetadata(get_class($unmanagedProxy))) === $class)
|
||||
&& $this->isIdentifierEquals($unmanagedProxy, $entity)
|
||||
) {
|
||||
// DDC-1238 - we have a managed instance, but it isn't the provided one.
|
||||
// Therefore we clear its identifier. Also, we must re-fetch metadata since the
|
||||
// refreshed object may be anything
|
||||
|
||||
foreach ($unmanagedProxyClass->identifier as $fieldName) {
|
||||
$unmanagedProxyClass->reflFields[$fieldName]->setValue($unmanagedProxy, null);
|
||||
foreach ($class->identifier as $fieldName) {
|
||||
$class->reflFields[$fieldName]->setValue($unmanagedProxy, null);
|
||||
}
|
||||
|
||||
return $unmanagedProxy;
|
||||
@@ -3206,4 +3214,37 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if two given entities actually are the same based on identifier comparison
|
||||
*
|
||||
* @param object $entity1
|
||||
* @param object $entity2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isIdentifierEquals($entity1, $entity2)
|
||||
{
|
||||
if ($entity1 === $entity2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$class = $this->em->getClassMetadata(get_class($entity1));
|
||||
|
||||
if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oid1 = spl_object_hash($entity1);
|
||||
$oid2 = spl_object_hash($entity2);
|
||||
|
||||
$id1 = isset($this->entityIdentifiers[$oid1])
|
||||
? $this->entityIdentifiers[$oid1]
|
||||
: $this->flattenIdentifier($class, $class->getIdentifierValues($entity1));
|
||||
$id2 = isset($this->entityIdentifiers[$oid2])
|
||||
? $this->entityIdentifiers[$oid2]
|
||||
: $this->flattenIdentifier($class, $class->getIdentifierValues($entity2));
|
||||
|
||||
return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class Version
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.4.0';
|
||||
const VERSION = '2.4.2';
|
||||
|
||||
/**
|
||||
* Compares a Doctrine version with the current one.
|
||||
|
||||
@@ -7,6 +7,8 @@ use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\Tests\TestUtil;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use DateTime;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
@@ -181,13 +183,44 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_conn->executeQuery('UPDATE optimistic_timestamp SET version = ? WHERE id = ?', array(date($format, strtotime($test->version->format($format)) + 3600), $test->id));
|
||||
|
||||
// Try and update the record and it should throw an exception
|
||||
$caughtException = null;
|
||||
$test->name = 'Testing again';
|
||||
try {
|
||||
$this->_em->flush();
|
||||
} catch (OptimisticLockException $e) {
|
||||
$this->assertSame($test, $e->getEntity());
|
||||
$caughtException = $e;
|
||||
}
|
||||
|
||||
$this->assertNotNull($caughtException, "No OptimisticLockingException was thrown");
|
||||
$this->assertSame($test, $caughtException->getEntity());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testOptimisticTimestampSetsDefaultValue
|
||||
*/
|
||||
public function testOptimisticTimestampLockFailureThrowsException(OptimisticTimestamp $entity)
|
||||
{
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id');
|
||||
$q->setParameter('id', $entity->id);
|
||||
$test = $q->getSingleResult();
|
||||
|
||||
$this->assertInstanceOf('DateTime', $test->version);
|
||||
|
||||
// Try to lock the record with an older timestamp and it should throw an exception
|
||||
$caughtException = null;
|
||||
try {
|
||||
$expectedVersionExpired = DateTime::createFromFormat('U', $test->version->getTimestamp()-3600);
|
||||
$this->_em->lock($test, LockMode::OPTIMISTIC, $expectedVersionExpired);
|
||||
} catch (OptimisticLockException $e) {
|
||||
$caughtException = $e;
|
||||
}
|
||||
|
||||
$this->assertNotNull($caughtException, "No OptimisticLockingException was thrown");
|
||||
$this->assertSame($test, $caughtException->getEntity());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-2645
|
||||
*/
|
||||
class DDC2645Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function testIssue()
|
||||
{
|
||||
$bar = new DDC2645Bar;
|
||||
$bar->id = 123;
|
||||
|
||||
$foo = new DDC2645Foo(1, $bar, 'Foo');
|
||||
$foo2 = new DDC2645Foo(1, $bar, 'Bar');
|
||||
|
||||
$this->_em->persist($bar);
|
||||
$this->_em->persist($foo);
|
||||
|
||||
$foo3 = $this->_em->merge($foo2);
|
||||
|
||||
$this->assertSame($foo, $foo3);
|
||||
$this->assertEquals('Bar', $foo->name);
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class DDC2645Foo
|
||||
{
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/** @Id @ManyToOne(targetEntity="DDC2645Bar") */
|
||||
private $bar;
|
||||
|
||||
/** @Column */
|
||||
public $name;
|
||||
|
||||
public function __construct($id, $bar, $name)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->bar = $bar;
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class DDC2645Bar
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue(strategy="NONE") */
|
||||
public $id;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @group
|
||||
*/
|
||||
class DDC2660Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function setup()
|
||||
{
|
||||
parent::setup();
|
||||
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Product'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Customer'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder')
|
||||
));
|
||||
} catch(\Exception $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$product = new DDC2660Product();
|
||||
$customer = new DDC2660Customer();
|
||||
$order = new DDC2660CustomerOrder($product, $customer, 'name' . $i);
|
||||
|
||||
$this->_em->persist($product);
|
||||
$this->_em->persist($customer);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->_em->persist($order);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
public function testIssueWithExtraColumn()
|
||||
{
|
||||
$sql = "SELECT o.product_id, o.customer_id, o.name FROM ddc_2660_customer_order o";
|
||||
|
||||
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
|
||||
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
|
||||
|
||||
$query = $this->_em->createNativeQuery($sql, $rsm);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(5, $result);
|
||||
|
||||
foreach ($result as $order) {
|
||||
$this->assertNotNull($order);
|
||||
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
|
||||
}
|
||||
}
|
||||
|
||||
public function testIssueWithoutExtraColumn()
|
||||
{
|
||||
$sql = "SELECT o.product_id, o.customer_id FROM ddc_2660_customer_order o";
|
||||
|
||||
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
|
||||
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
|
||||
|
||||
$query = $this->_em->createNativeQuery($sql, $rsm);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(5, $result);
|
||||
|
||||
foreach ($result as $order) {
|
||||
$this->assertNotNull($order);
|
||||
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @Entity @Table(name="ddc_2660_product")
|
||||
*/
|
||||
class DDC2660Product
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
}
|
||||
|
||||
/** @Entity @Table(name="ddc_2660_customer") */
|
||||
class DDC2660Customer
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
}
|
||||
|
||||
/** @Entity @Table(name="ddc_2660_customer_order") */
|
||||
class DDC2660CustomerOrder
|
||||
{
|
||||
/**
|
||||
* @Id @ManyToOne(targetEntity="DDC2660Product")
|
||||
*/
|
||||
public $product;
|
||||
|
||||
/**
|
||||
* @Id @ManyToOne(targetEntity="DDC2660Customer")
|
||||
*/
|
||||
public $customer;
|
||||
|
||||
/**
|
||||
* @Column(type="string")
|
||||
*/
|
||||
public $name;
|
||||
|
||||
public function __construct(DDC2660Product $product, DDC2660Customer $customer, $name)
|
||||
{
|
||||
$this->product = $product;
|
||||
$this->customer = $customer;
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\ORM\Event\PreFlushEventArgs;
|
||||
|
||||
/**
|
||||
* @group DDC-2692
|
||||
*/
|
||||
class DDC2692Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function setup()
|
||||
{
|
||||
parent::setup();
|
||||
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2692Foo'),
|
||||
));
|
||||
} catch(\Exception $e) {
|
||||
return;
|
||||
}
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
public function testIsListenerCalledOnlyOnceOnPreFlush()
|
||||
{
|
||||
$listener = $this->getMock('Doctrine\Tests\ORM\Functional\Ticket\DDC2692Listener', array('preFlush'));
|
||||
$listener->expects($this->once())->method('preFlush');
|
||||
|
||||
$this->_em->getEventManager()->addEventSubscriber($listener);
|
||||
|
||||
$this->_em->persist(new DDC2692Foo);
|
||||
$this->_em->persist(new DDC2692Foo);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @Entity @Table(name="ddc_2692_foo")
|
||||
*/
|
||||
class DDC2692Foo
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
}
|
||||
|
||||
class DDC2692Listener implements EventSubscriber {
|
||||
|
||||
public function getSubscribedEvents() {
|
||||
return array(\Doctrine\ORM\Events::preFlush);
|
||||
}
|
||||
|
||||
public function preFlush(PreFlushEventArgs $args) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
/**
|
||||
* @group DDC-2759
|
||||
*/
|
||||
class DDC2759Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function setup()
|
||||
{
|
||||
parent::setup();
|
||||
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Qualification'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Category'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759QualificationMetadata'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759MetadataCategory'),
|
||||
));
|
||||
} catch(\Exception $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$qualification = new DDC2759Qualification();
|
||||
$qualificationMetadata = new DDC2759QualificationMetadata($qualification);
|
||||
|
||||
$category1 = new DDC2759Category();
|
||||
$category2 = new DDC2759Category();
|
||||
|
||||
$metadataCategory1 = new DDC2759MetadataCategory($qualificationMetadata, $category1);
|
||||
$metadataCategory2 = new DDC2759MetadataCategory($qualificationMetadata, $category2);
|
||||
|
||||
$this->_em->persist($qualification);
|
||||
$this->_em->persist($qualificationMetadata);
|
||||
|
||||
$this->_em->persist($category1);
|
||||
$this->_em->persist($category2);
|
||||
|
||||
$this->_em->persist($metadataCategory1);
|
||||
$this->_em->persist($metadataCategory2);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
public function testCorrectNumberOfAssociationsIsReturned()
|
||||
{
|
||||
$repository = $this->_em->getRepository(__NAMESPACE__ . '\DDC2759Qualification');
|
||||
|
||||
$builder = $repository->createQueryBuilder('q')
|
||||
->select('q, qm, qmc')
|
||||
->innerJoin('q.metadata', 'qm')
|
||||
->innerJoin('qm.metadataCategories', 'qmc');
|
||||
|
||||
$result = $builder->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
$this->assertCount(2, $result[0]['metadata']['metadataCategories']);
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity @Table(name="ddc_2759_qualification") */
|
||||
class DDC2759Qualification
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @OneToOne(targetEntity="DDC2759QualificationMetadata", mappedBy="content") */
|
||||
public $metadata;
|
||||
}
|
||||
|
||||
/** @Entity @Table(name="ddc_2759_category") */
|
||||
class DDC2759Category
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="category") */
|
||||
public $metadataCategories;
|
||||
}
|
||||
|
||||
/** @Entity @Table(name="ddc_2759_qualification_metadata") */
|
||||
class DDC2759QualificationMetadata
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @OneToOne(targetEntity="DDC2759Qualification", inversedBy="metadata") */
|
||||
public $content;
|
||||
|
||||
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="metadata") */
|
||||
protected $metadataCategories;
|
||||
|
||||
public function __construct(DDC2759Qualification $content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity @Table(name="ddc_2759_metadata_category") */
|
||||
class DDC2759MetadataCategory
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @ManyToOne(targetEntity="DDC2759QualificationMetadata", inversedBy="metadataCategories") */
|
||||
public $metadata;
|
||||
|
||||
/** @ManyToOne(targetEntity="DDC2759Category", inversedBy="metadataCategories") */
|
||||
public $category;
|
||||
|
||||
public function __construct(DDC2759QualificationMetadata $metadata, DDC2759Category $category)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->category = $category;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
/**
|
||||
* Functional tests for cascade remove with class table inheritance.
|
||||
*
|
||||
* @author Matthieu Napoli <matthieu@mnapoli.fr>
|
||||
*/
|
||||
class DDC2775Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema(array(
|
||||
'Doctrine\Tests\ORM\Functional\Ticket\User',
|
||||
'Doctrine\Tests\ORM\Functional\Ticket\Role',
|
||||
'Doctrine\Tests\ORM\Functional\Ticket\AdminRole',
|
||||
'Doctrine\Tests\ORM\Functional\Ticket\Authorization',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2775
|
||||
*/
|
||||
public function testIssueCascadeRemove()
|
||||
{
|
||||
$user = new User();
|
||||
|
||||
$role = new AdminRole();
|
||||
$user->addRole($role);
|
||||
|
||||
$authorization = new Authorization();
|
||||
$user->addAuthorization($authorization);
|
||||
$role->addAuthorization($authorization);
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
// Need to clear so that associations are lazy-loaded
|
||||
$this->_em->clear();
|
||||
|
||||
$user = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\User', $user->id);
|
||||
|
||||
$this->_em->remove($user);
|
||||
$this->_em->flush();
|
||||
|
||||
// With the bug, the second flush throws an error because the cascade remove didn't work correctly
|
||||
$this->_em->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity @Table(name="ddc2775_role")
|
||||
* @InheritanceType("JOINED")
|
||||
* @DiscriminatorColumn(name="role_type", type="string")
|
||||
* @DiscriminatorMap({"admin"="AdminRole"})
|
||||
*/
|
||||
abstract class Role
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="User", inversedBy="roles")
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Authorization", mappedBy="role", cascade={"all"}, orphanRemoval=true)
|
||||
*/
|
||||
public $authorizations;
|
||||
|
||||
public function addAuthorization(Authorization $authorization)
|
||||
{
|
||||
$this->authorizations[] = $authorization;
|
||||
$authorization->role = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity @Table(name="ddc2775_admin_role") */
|
||||
class AdminRole extends Role
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity @Table(name="ddc2775_authorizations")
|
||||
*/
|
||||
class Authorization
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="User", inversedBy="authorizations")
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Role", inversedBy="authorizations")
|
||||
*/
|
||||
public $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity @Table(name="ddc2775_users")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Role", mappedBy="user", cascade={"all"}, orphanRemoval=true)
|
||||
*/
|
||||
public $roles;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Authorization", mappedBy="user", cascade={"all"}, orphanRemoval=true)
|
||||
*/
|
||||
public $authorizations;
|
||||
|
||||
public function addRole(Role $role)
|
||||
{
|
||||
$this->roles[] = $role;
|
||||
$role->user = $this;
|
||||
}
|
||||
|
||||
public function addAuthorization(Authorization $authorization)
|
||||
{
|
||||
$this->authorizations[] = $authorization;
|
||||
$authorization->user = $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
/**
|
||||
* Class DDC2895Test
|
||||
* @package Doctrine\Tests\ORM\Functional\Ticket
|
||||
* @author http://github.com/gwagner
|
||||
*/
|
||||
class DDC2895Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC2895'),
|
||||
));
|
||||
} catch(\Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function testPostLoadOneToManyInheritance()
|
||||
{
|
||||
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC2895');
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
"prePersist" => array("setLastModifiedPreUpdate"),
|
||||
"preUpdate" => array("setLastModifiedPreUpdate"),
|
||||
),
|
||||
$cm->lifecycleCallbacks
|
||||
);
|
||||
|
||||
$ddc2895 = new DDC2895();
|
||||
|
||||
$this->_em->persist($ddc2895);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
/** @var DDC2895 $ddc2895 */
|
||||
$ddc2895 = $this->_em->find(get_class($ddc2895), $ddc2895->id);
|
||||
|
||||
$this->assertNotNull($ddc2895->getLastModified());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @MappedSuperclass
|
||||
* @HasLifecycleCallbacks
|
||||
*/
|
||||
abstract class AbstractDDC2895
|
||||
{
|
||||
/**
|
||||
* @Column(name="last_modified", type="datetimetz", nullable=false)
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $lastModified;
|
||||
|
||||
/**
|
||||
* @PrePersist
|
||||
* @PreUpdate
|
||||
*/
|
||||
public function setLastModifiedPreUpdate()
|
||||
{
|
||||
$this->setLastModified(new \DateTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $lastModified
|
||||
*/
|
||||
public function setLastModified( $lastModified )
|
||||
{
|
||||
$this->lastModified = $lastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getLastModified()
|
||||
{
|
||||
return $this->lastModified;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @HasLifecycleCallbacks
|
||||
*/
|
||||
class DDC2895 extends AbstractDDC2895
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
*/
|
||||
public function setId( $id )
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-2931
|
||||
*/
|
||||
class DDC2931Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2931User'),
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
// no action needed - schema seems to be already in place
|
||||
}
|
||||
}
|
||||
|
||||
public function testIssue()
|
||||
{
|
||||
$first = new DDC2931User();
|
||||
$second = new DDC2931User();
|
||||
$third = new DDC2931User();
|
||||
|
||||
$second->parent = $first;
|
||||
$third->parent = $second;
|
||||
|
||||
$this->_em->persist($first);
|
||||
$this->_em->persist($second);
|
||||
$this->_em->persist($third);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$second = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC2931User', $second->id);
|
||||
|
||||
$this->assertSame(2, $second->getRank());
|
||||
}
|
||||
|
||||
public function testFetchJoinedEntitiesCanBeRefreshed()
|
||||
{
|
||||
$first = new DDC2931User();
|
||||
$second = new DDC2931User();
|
||||
$third = new DDC2931User();
|
||||
|
||||
$second->parent = $first;
|
||||
$third->parent = $second;
|
||||
|
||||
$first->value = 1;
|
||||
$second->value = 2;
|
||||
$third->value = 3;
|
||||
|
||||
$this->_em->persist($first);
|
||||
$this->_em->persist($second);
|
||||
$this->_em->persist($third);
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
$first->value = 4;
|
||||
$second->value = 5;
|
||||
$third->value = 6;
|
||||
|
||||
$refreshedSecond = $this
|
||||
->_em
|
||||
->createQuery(
|
||||
'SELECT e, p, c FROM '
|
||||
. __NAMESPACE__ . '\\DDC2931User e LEFT JOIN e.parent p LEFT JOIN e.child c WHERE e = :id'
|
||||
)
|
||||
->setParameter('id', $second)
|
||||
->setHint(Query::HINT_REFRESH, true)
|
||||
->getResult();
|
||||
|
||||
$this->assertCount(1, $refreshedSecond);
|
||||
$this->assertSame(1, $first->value);
|
||||
$this->assertSame(2, $second->value);
|
||||
$this->assertSame(3, $third->value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @Entity */
|
||||
class DDC2931User
|
||||
{
|
||||
|
||||
/** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */
|
||||
public $id;
|
||||
|
||||
/** @OneToOne(targetEntity="DDC2931User", inversedBy="child") */
|
||||
public $parent;
|
||||
|
||||
/** @OneToOne(targetEntity="DDC2931User", mappedBy="parent") */
|
||||
public $child;
|
||||
|
||||
/** @Column(type="integer") */
|
||||
public $value = 0;
|
||||
|
||||
/**
|
||||
* Return Rank recursively
|
||||
* My rank is 1 + rank of my parent
|
||||
* @return integer
|
||||
*/
|
||||
public function getRank()
|
||||
{
|
||||
return 1 + ($this->parent ? $this->parent->getRank() : 0);
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
echo 'foo';
|
||||
}
|
||||
}
|
||||
@@ -187,12 +187,32 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertTrue($class->fieldMappings['name']['nullable']);
|
||||
$this->assertTrue($class->fieldMappings['name']['unique']);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testEntityTableNameAndInheritance
|
||||
* @param ClassMetadata $class
|
||||
*/
|
||||
public function testFieldOptions($class)
|
||||
{
|
||||
$expected = array('foo' => 'bar', 'baz' => array('key' => 'val'));
|
||||
$this->assertEquals($expected, $class->fieldMappings['name']['options']);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testEntityTableNameAndInheritance
|
||||
* @param ClassMetadata $class
|
||||
*/
|
||||
public function testIdFieldOptions($class)
|
||||
{
|
||||
$this->assertEquals(array('foo' => 'bar'), $class->fieldMappings['id']['options']);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFieldMappings
|
||||
* @param ClassMetadata $class
|
||||
@@ -890,7 +910,7 @@ class User
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @Column(type="integer", options={"foo": "bar"})
|
||||
* @generatedValue(strategy="AUTO")
|
||||
* @SequenceGenerator(sequenceName="tablename_seq", initialValue=1, allocationSize=100)
|
||||
**/
|
||||
@@ -971,6 +991,7 @@ class User
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'columnName' => 'id',
|
||||
'options' => array('foo' => 'bar'),
|
||||
));
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'name',
|
||||
|
||||
@@ -1066,6 +1066,50 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertEquals(array('customtypeparent_source' => 'id'), $cm->associationMappings['friendsWithMe']['relationToSourceKeyColumns']);
|
||||
$this->assertEquals(array('customtypeparent_target' => 'id'), $cm->associationMappings['friendsWithMe']['relationToTargetKeyColumns']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2608
|
||||
*/
|
||||
public function testSetSequenceGeneratorThrowsExceptionWhenSequenceNameIsMissing()
|
||||
{
|
||||
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
|
||||
$cm->setSequenceGeneratorDefinition(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2662
|
||||
*/
|
||||
public function testQuotedSequenceName()
|
||||
{
|
||||
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
|
||||
|
||||
$cm->setSequenceGeneratorDefinition(array('sequenceName' => '`foo`'));
|
||||
|
||||
$this->assertEquals(array('sequenceName' => 'foo', 'quoted' => true), $cm->sequenceGeneratorDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2700
|
||||
*/
|
||||
public function testIsIdentifierMappedSuperClass()
|
||||
{
|
||||
$class = new ClassMetadata(__NAMESPACE__ . '\\DDC2700MappedSuperClass');
|
||||
|
||||
$this->assertFalse($class->isIdentifier('foo'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @MappedSuperclass
|
||||
*/
|
||||
class DDC2700MappedSuperClass
|
||||
{
|
||||
/** @Column */
|
||||
private $foo;
|
||||
}
|
||||
|
||||
class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy
|
||||
@@ -1092,4 +1136,4 @@ class MyPrefixNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy
|
||||
{
|
||||
return strtolower($this->classToTableName($className)) . '_' . $propertyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ $metadata->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'columnName' => 'id',
|
||||
'options' => array('foo' => 'bar'),
|
||||
));
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'name',
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
<sequence-generator sequence-name="tablename_seq" allocation-size="100" initial-value="1" />
|
||||
<options>
|
||||
<option name="foo">bar</option>
|
||||
</options>
|
||||
</id>
|
||||
|
||||
<field name="name" column="name" type="string" length="50" nullable="true" unique="true">
|
||||
|
||||
@@ -16,6 +16,8 @@ Doctrine\Tests\ORM\Mapping\User:
|
||||
sequenceName: tablename_seq
|
||||
allocationSize: 100
|
||||
initialValue: 1
|
||||
options:
|
||||
foo: bar
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
|
||||
@@ -47,7 +47,7 @@ class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
$this->visitor = new QueryExpressionVisitor();
|
||||
$this->visitor = new QueryExpressionVisitor('o');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,22 +71,22 @@ class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase
|
||||
$qb = new QueryBuilder();
|
||||
|
||||
return array(
|
||||
array($cb->eq('field', 'value'), $qb->eq('field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->neq('field', 'value'), $qb->neq('field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->eq('field', null), $qb->isNull('field')),
|
||||
array($cb->neq('field', null), $qb->isNotNull('field')),
|
||||
array($cb->isNull('field'), $qb->isNull('field')),
|
||||
array($cb->eq('field', 'value'), $qb->eq('o.field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->neq('field', 'value'), $qb->neq('o.field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->eq('field', null), $qb->isNull('o.field')),
|
||||
array($cb->neq('field', null), $qb->isNotNull('o.field')),
|
||||
array($cb->isNull('field'), $qb->isNull('o.field')),
|
||||
|
||||
array($cb->gt('field', 'value'), $qb->gt('field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->gte('field', 'value'), $qb->gte('field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->lt('field', 'value'), $qb->lt('field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->lte('field', 'value'), $qb->lte('field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->gt('field', 'value'), $qb->gt('o.field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->gte('field', 'value'), $qb->gte('o.field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->lt('field', 'value'), $qb->lt('o.field', ':field'), new Parameter('field', 'value')),
|
||||
array($cb->lte('field', 'value'), $qb->lte('o.field', ':field'), new Parameter('field', 'value')),
|
||||
|
||||
array($cb->in('field', array('value')), $qb->in('field', ':field'), new Parameter('field', array('value'))),
|
||||
array($cb->notIn('field', array('value')), $qb->notIn('field', ':field'), new Parameter('field', array('value'))),
|
||||
array($cb->in('field', array('value')), $qb->in('o.field', ':field'), new Parameter('field', array('value'))),
|
||||
array($cb->notIn('field', array('value')), $qb->notIn('o.field', ':field'), new Parameter('field', array('value'))),
|
||||
|
||||
// Test parameter conversion
|
||||
array($cb->eq('object.field', 'value'), $qb->eq('object.field', ':object_field'), new Parameter('object_field', 'value')),
|
||||
array($cb->eq('object.field', 'value'), $qb->eq('o.object.field', ':object_field'), new Parameter('object_field', 'value')),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +394,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2668
|
||||
*/
|
||||
public function testSupportsTrimLeadingZeroString()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(TRAILING '0' FROM u.name) != ''",
|
||||
"SELECT c0_.name AS name0 FROM cms_users c0_ WHERE TRIM(TRAILING '0' FROM c0_.name) <> ''"
|
||||
);
|
||||
}
|
||||
|
||||
// Ticket 894
|
||||
public function testSupportsBetweenClauseWithPositionalParameters()
|
||||
{
|
||||
|
||||
@@ -402,30 +402,39 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testAddCriteriaWhere()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->select('u')
|
||||
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
|
||||
|
||||
$criteria = new Criteria();
|
||||
$criteria->where($criteria->expr()->eq('field', 'value'));
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
$this->assertEquals('field = :field', (string) $qb->getDQLPart('where'));
|
||||
$this->assertEquals('u.field = :field', (string) $qb->getDQLPart('where'));
|
||||
$this->assertNotNull($qb->getParameter('field'));
|
||||
}
|
||||
|
||||
public function testAddCriteriaOrder()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->select('u')
|
||||
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
|
||||
|
||||
$criteria = new Criteria();
|
||||
$criteria->orderBy(array('field' => Criteria::DESC));
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
$this->assertCount(1, $orderBy = $qb->getDQLPart('orderBy'));
|
||||
$this->assertEquals('field DESC', (string) $orderBy[0]);
|
||||
$this->assertEquals('u.field DESC', (string) $orderBy[0]);
|
||||
}
|
||||
|
||||
public function testAddCriteriaLimit()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->select('u')
|
||||
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
|
||||
|
||||
$criteria = new Criteria();
|
||||
$criteria->setFirstResult(2);
|
||||
$criteria->setMaxResults(10);
|
||||
@@ -439,7 +448,11 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testAddCriteriaUndefinedLimit()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->setFirstResult(2)->setMaxResults(10);
|
||||
$qb->select('u')
|
||||
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
|
||||
->setFirstResult(2)
|
||||
->setMaxResults(10);
|
||||
|
||||
$criteria = new Criteria();
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
Reference in New Issue
Block a user