Skip to content

Commit

Permalink
Merge pull request #7405 from Majkl578/2.x-into-master
Browse files Browse the repository at this point in the history
Cherry-pick stuff from 2.x into master
  • Loading branch information
Ocramius authored Sep 24, 2018
2 parents bfa6520 + ce76688 commit 36aeed4
Show file tree
Hide file tree
Showing 19 changed files with 587 additions and 20 deletions.
2 changes: 1 addition & 1 deletion docs/en/cookbook/aggregate-fields.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ The aggregate field ``Account::$balance`` is now -200, however the
SUM over all entries amounts yields -400. A violation of our max
credit rule.

You can use both optimistic or pessimistic locking to save-guard
You can use both optimistic or pessimistic locking to safe-guard
your aggregate fields against this kind of race-conditions. Reading
Eric Evans DDD carefully he mentions that the "Aggregate Root"
(Account in our example) needs a locking mechanism.
Expand Down
4 changes: 2 additions & 2 deletions docs/en/reference/inheritance-mapping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -436,15 +436,15 @@ Could be used by an entity that extends a mapped superclass to override a field
* column=@Column(
* name = "guest_id",
* type = "integer",
length = 140
* length = 140
* )
* ),
* @AttributeOverride(name="name",
* column=@Column(
* name = "guest_name",
* nullable = false,
* unique = true,
length = 240
* length = 240
* )
* )
* })
Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ private function checkLockRequirements(int $lockMode, ClassMetadata $class) : vo
if (! $class->isVersioned()) {
throw OptimisticLockException::notVersioned($class->getClassName());
}
// Intentional fallthrough
break;
case LockMode::PESSIMISTIC_READ:
case LockMode::PESSIMISTIC_WRITE:
if (! $this->getConnection()->isTransactionActive()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/EntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Selectable;
use Doctrine\Common\Inflector\Inflector;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\Common\Util\Inflector;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
Expand Down
10 changes: 10 additions & 0 deletions lib/Doctrine/ORM/Internal/CommitOrderCalculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ private function visit($vertex)
case self::IN_PROGRESS:
if (isset($adjacentVertex->dependencyList[$vertex->hash]) &&
$adjacentVertex->dependencyList[$vertex->hash]->weight < $edge->weight) {
// If we have some non-visited dependencies in the in-progress dependency, we
// need to visit them before adding the node.
foreach ($adjacentVertex->dependencyList as $adjacentEdge) {
$adjacentEdgeVertex = $this->nodeList[$adjacentEdge->to];

if ($adjacentEdgeVertex->state === self::NOT_VISITED) {
$this->visit($adjacentEdgeVertex);
}
}

$adjacentVertex->state = self::VISITED;

$this->sortedNodeList[] = $adjacentVertex->value;
Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Doctrine\ORM\Mapping\Driver;

use Doctrine\Common\Util\Inflector;
use Doctrine\Common\Inflector\Inflector;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Query/Expr.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class Expr
* // (u.type = ?1) AND (u.role = ?2)
* $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2'));
*
* @param Expr\Comparison|Expr\Func|Expr\Orx $x Optional clause. Defaults to null, but requires at least one
* defined when converting to string.
* @param Expr\Comparison|Expr\Func|Expr\Orx|string $x Optional clause. Defaults to null, but requires at least one
* defined when converting to string.
*
* @return Expr\Andx
*/
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Query/Expr/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ abstract class Base
protected $parts = [];

/**
* @param mixed[] $args
* @param mixed $args
*/
public function __construct($args = [])
{
$this->addMultiple($args);
}

/**
* @param mixed[] $args
* @param mixed $args
*
* @return Base
*/
Expand Down
3 changes: 2 additions & 1 deletion lib/Doctrine/ORM/Query/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\ORM\Query;

use Doctrine\Common\Lexer\AbstractLexer;
use function constant;
use function ctype_alpha;
use function defined;
Expand All @@ -18,7 +19,7 @@
/**
* Scans a DQL query for tokens.
*/
class Lexer extends \Doctrine\Common\Lexer
class Lexer extends AbstractLexer
{
// All tokens that are not valid identifiers must be < 100
public const T_NONE = 1;
Expand Down
4 changes: 4 additions & 0 deletions lib/Doctrine/ORM/Query/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2907,6 +2907,10 @@ public function StringPrimary()
case Lexer::T_COALESCE:
case Lexer::T_NULLIF:
return $this->CaseExpression();
default:
if ($this->isAggregateFunction($lookaheadType)) {
return $this->AggregateExpression();
}
}

$this->syntaxError(
Expand Down
16 changes: 11 additions & 5 deletions lib/Doctrine/ORM/Tools/Pagination/Paginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public function getIterator()
$subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
} else {
$this->appendTreeWalker($subQuery, LimitSubqueryWalker::class);
$this->unbindUnusedQueryParams($subQuery);
}

$subQuery->setFirstResult($offset)->setMaxResults($length);
Expand Down Expand Up @@ -236,14 +237,21 @@ private function getCountQuery()
$countQuery->setResultSetMapping($rsm);
} else {
$this->appendTreeWalker($countQuery, CountWalker::class);
$this->unbindUnusedQueryParams($countQuery);
}

$countQuery->setFirstResult(null)->setMaxResults(null);

$parser = new Parser($countQuery);
return $countQuery;
}

private function unbindUnusedQueryParams(Query $query) : void
{
$parser = new Parser($query);
$parameterMappings = $parser->parse()->getParameterMappings();

/** @var Collection|Parameter[] $parameters */
$parameters = $countQuery->getParameters();
$parameters = $query->getParameters();

foreach ($parameters as $key => $parameter) {
$parameterName = $parameter->getName();
Expand All @@ -253,8 +261,6 @@ private function getCountQuery()
}
}

$countQuery->setParameters($parameters);

return $countQuery;
$query->setParameters($parameters);
}
}
33 changes: 33 additions & 0 deletions tests/Doctrine/Tests/ORM/CommitOrderCalculatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,39 @@ public function testCommitOrdering2() : void

self::assertSame($correctOrder, $sorted);
}

public function testCommitOrdering3()
{
// this test corresponds to the GH7259Test::testPersistFileBeforeVersion functional test
$class1 = new ClassMetadata(NodeClass1::class, $this->metadataBuildingContext);
$class2 = new ClassMetadata(NodeClass2::class, $this->metadataBuildingContext);
$class3 = new ClassMetadata(NodeClass3::class, $this->metadataBuildingContext);
$class4 = new ClassMetadata(NodeClass4::class, $this->metadataBuildingContext);

$this->calc->addNode($class1->getClassName(), $class1);
$this->calc->addNode($class2->getClassName(), $class2);
$this->calc->addNode($class3->getClassName(), $class3);
$this->calc->addNode($class4->getClassName(), $class4);

$this->calc->addDependency($class4->getClassName(), $class1->getClassName(), 1);
$this->calc->addDependency($class1->getClassName(), $class2->getClassName(), 1);
$this->calc->addDependency($class4->getClassName(), $class3->getClassName(), 1);
$this->calc->addDependency($class1->getClassName(), $class4->getClassName(), 0);

$sorted = $this->calc->sort();

// There is only multiple valid ordering for this constellation, but
// the class4, class1, class2 ordering is important to break the cycle
// on the nullable link.
$correctOrders = [
[$class4, $class1, $class2, $class3],
[$class4, $class1, $class3, $class2],
[$class4, $class3, $class1, $class2],
];

// We want to perform a strict comparison of the array
$this->assertContains($sorted, $correctOrders, '', false, true, true);
}
}

class NodeClass1
Expand Down
167 changes: 167 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/GH7259Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\ORM\Annotation as ORM;
use Doctrine\Tests\OrmFunctionalTestCase;

final class GH7259Test extends OrmFunctionalTestCase
{
protected function setUp() : void
{
parent::setUp();

$this->setUpEntitySchema([GH7259Space::class, GH7259File::class, GH7259FileVersion::class, GH7259Feed::class]);
}

/**
* @group 7259
*/
public function testPersistFileBeforeVersion() : void
{
$space = new GH7259Space();

$this->em->persist($space);
$this->em->flush();

$feed = new GH7259Feed();
$feed->space = $space;

$file = new GH7259File();
$file->space = $space;
$fileVersion = new GH7259FileVersion();
$fileVersion->file = $file;

$this->em->persist($file);
$this->em->persist($fileVersion);
$this->em->persist($feed);

$this->em->flush();

self::assertNotNull($fileVersion->id);
}

/**
* @group 7259
*/
public function testPersistFileAfterVersion() : void
{
$space = new GH7259Space();

$this->em->persist($space);
$this->em->flush();
$this->em->clear();

$space = $this->em->find(GH7259Space::class, $space->id);

$feed = new GH7259Feed();
$feed->space = $space;

$file = new GH7259File();
$file->space = $space;
$fileVersion = new GH7259FileVersion();
$fileVersion->file = $file;

$this->em->persist($fileVersion);
$this->em->persist($file);
$this->em->persist($feed);

$this->em->flush();

self::assertNotNull($fileVersion->id);
}
}

/**
* @ORM\Entity()
*/
class GH7259File
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*
* @var int
*/
public $id;

/**
* @ORM\ManyToOne(targetEntity=GH7259Space::class)
* @ORM\JoinColumn(nullable=false)
*
* @var GH7259Space|null
*/
public $space;
}

/**
* @ORM\Entity()
*/
class GH7259FileVersion
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*
* @var int
*/
public $id;

/**
* @ORM\ManyToOne(targetEntity=GH7259File::class)
* @ORM\JoinColumn(nullable=false)
*
* @var GH7259File|null
*/
public $file;
}

/**
* @ORM\Entity()
*/
class GH7259Space
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*
* @var int
*/
public $id;

/**
* @ORM\ManyToOne(targetEntity=GH7259File::class)
* @ORM\JoinColumn(nullable=true)
*
* @var GH7259File|null
*/
public $ruleFile;
}

/**
* @ORM\Entity()
*/
class GH7259Feed
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*
* @var int
*/
public $id;

/**
* @ORM\ManyToOne(targetEntity=GH7259Space::class)
* @ORM\JoinColumn(nullable=false)
*
* @var GH7259Space|null
*/
public $space;
}
Loading

0 comments on commit 36aeed4

Please sign in to comment.