Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merge 2.16.x up into 2.17.x #11023

Merged
merged 7 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions docs/en/reference/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,34 @@ performance it is also recommended that you use APC with PHP.
Doctrine ORM Packages
-------------------

Doctrine ORM is divided into three main packages.
Doctrine ORM is divided into four main packages.

- Common
- DBAL (includes Common)
- ORM (includes DBAL+Common)
- `Collections <https://www.doctrine-project.org/projects/doctrine-collections/en/stable/index.html>`_
- `Event Manager <https://www.doctrine-project.org/projects/doctrine-event-manager/en/stable/index.html>`_
- `Persistence <https://www.doctrine-project.org/projects/doctrine-persistence/en/stable/index.html>`_
- `DBAL <https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/index.html>`_
- ORM (depends on DBAL+Persistence+Collections)

This manual mainly covers the ORM package, sometimes touching parts
of the underlying DBAL and Common packages. The Doctrine code base
of the underlying DBAL and Persistence packages. The Doctrine code base
is split in to these packages for a few reasons and they are to...


- ...make things more maintainable and decoupled
- ...allow you to use the code in Doctrine Common without the ORM
or DBAL
- ...allow you to use the code in Doctrine Persistence and Collections
without the ORM or DBAL
- ...allow you to use the DBAL without the ORM

The Common Package
~~~~~~~~~~~~~~~~~~
Collection, Event Manager and Persistence
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The Common package contains highly reusable components that have no
dependencies beyond the package itself (and PHP, of course). The
root namespace of the Common package is ``Doctrine\Common``.
The Collection, Event Manager and Persistence packages contain highly
reusable components that have no dependencies beyond the packages
themselves (and PHP, of course). The root namespace of the Persistence
package is ``Doctrine\Persistence``. The root namespace of the
Collection package is ``Doctrine\Common\Collections``, for historical
reasons. The root namespace of the Event Manager package is just
``Doctrine\Common``, also for historical reasons.

The DBAL Package
~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -199,5 +205,3 @@ typical implementation of the
to keep track of all the things that need to be done the next time
``flush`` is invoked. You usually do not directly interact with a
``UnitOfWork`` but with the ``EntityManager`` instead.


2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Query/AST/ConditionalFactor.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*
* @link www.doctrine-project.org
*/
class ConditionalFactor extends Node
class ConditionalFactor extends Node implements Phase2OptimizableConditional
{
/** @var bool */
public $not = false;
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
*
* @link www.doctrine-project.org
*/
class ConditionalPrimary extends Node
class ConditionalPrimary extends Node implements Phase2OptimizableConditional
{
/** @var Node|null */
public $simpleConditionalExpression;

/** @var ConditionalExpression|null */
/** @var ConditionalExpression|Phase2OptimizableConditional|null */
public $conditionalExpression;

/** @return bool */
Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Query/AST/ConditionalTerm.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*
* @link www.doctrine-project.org
*/
class ConditionalTerm extends Node
class ConditionalTerm extends Node implements Phase2OptimizableConditional
{
/** @var mixed[] */
public $conditionalFactors = [];
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Query/AST/HavingClause.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

class HavingClause extends Node
{
/** @var ConditionalExpression */
/** @var ConditionalExpression|Phase2OptimizableConditional */
public $conditionalExpression;

/** @param ConditionalExpression $conditionalExpression */
/** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */
public function __construct($conditionalExpression)
{
$this->conditionalExpression = $conditionalExpression;
Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Query/AST/Join.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Join extends Node
/** @var Node|null */
public $joinAssociationDeclaration = null;

/** @var ConditionalExpression|null */
/** @var ConditionalExpression|Phase2OptimizableConditional|null */
public $conditionalExpression = null;

/**
Expand Down
17 changes: 17 additions & 0 deletions lib/Doctrine/ORM/Query/AST/Phase2OptimizableConditional.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Doctrine\ORM\Query\AST;

/**
* Marks types that can be used in place of a ConditionalExpression as a phase
* 2 optimization.
*
* @internal
*
* @psalm-inheritors ConditionalPrimary|ConditionalFactor|ConditionalTerm
*/
interface Phase2OptimizableConditional
{
}
6 changes: 3 additions & 3 deletions lib/Doctrine/ORM/Query/AST/WhenClause.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
*/
class WhenClause extends Node
{
/** @var ConditionalExpression */
/** @var ConditionalExpression|Phase2OptimizableConditional */
public $caseConditionExpression;

/** @var mixed */
public $thenScalarExpression = null;

/**
* @param ConditionalExpression $caseConditionExpression
* @param mixed $thenScalarExpression
* @param ConditionalExpression|Phase2OptimizableConditional $caseConditionExpression
* @param mixed $thenScalarExpression
*/
public function __construct($caseConditionExpression, $thenScalarExpression)
{
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Query/AST/WhereClause.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
*/
class WhereClause extends Node
{
/** @var ConditionalExpression|ConditionalTerm */
/** @var ConditionalExpression|Phase2OptimizableConditional */
public $conditionalExpression;

/** @param ConditionalExpression $conditionalExpression */
/** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */
public function __construct($conditionalExpression)
{
$this->conditionalExpression = $conditionalExpression;
Expand Down
5 changes: 5 additions & 0 deletions lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ protected function addAllClassFields($class, $alias, $columnAliasMap = [])
}

$this->addFieldResult($alias, $columnAlias, $propertyName);

$enumType = $classMetadata->getFieldMapping($propertyName)['enumType'] ?? null;
if (! empty($enumType)) {
$this->addEnumResult($columnAlias, $enumType);
}
}

foreach ($classMetadata->associationMappings as $associationMapping) {
Expand Down
12 changes: 6 additions & 6 deletions lib/Doctrine/ORM/Query/SqlWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -1010,9 +1010,9 @@ private function generateRangeVariableDeclarationSQL(
/**
* Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
*
* @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
* @param int $joinType
* @param AST\ConditionalExpression $condExpr
* @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
* @param int $joinType
* @param AST\ConditionalExpression|AST\Phase2OptimizableConditional $condExpr
* @psalm-param AST\Join::JOIN_TYPE_* $joinType
*
* @return string
Expand Down Expand Up @@ -2048,7 +2048,7 @@ public function walkWhereClause($whereClause)
/**
* Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL.
*
* @param AST\ConditionalExpression $condExpr
* @param AST\ConditionalExpression|AST\Phase2OptimizableConditional $condExpr
*
* @return string
*
Expand All @@ -2068,7 +2068,7 @@ public function walkConditionalExpression($condExpr)
/**
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
*
* @param AST\ConditionalTerm $condTerm
* @param AST\ConditionalTerm|AST\ConditionalFactor|AST\ConditionalPrimary $condTerm
*
* @return string
*
Expand All @@ -2088,7 +2088,7 @@ public function walkConditionalTerm($condTerm)
/**
* Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.
*
* @param AST\ConditionalFactor $factor
* @param AST\ConditionalFactor|AST\ConditionalPrimary $factor
*
* @return string The SQL.
*
Expand Down
6 changes: 1 addition & 5 deletions lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Doctrine\ORM\Query\AST\ArithmeticExpression;
use Doctrine\ORM\Query\AST\ConditionalExpression;
use Doctrine\ORM\Query\AST\ConditionalFactor;
use Doctrine\ORM\Query\AST\ConditionalPrimary;
use Doctrine\ORM\Query\AST\ConditionalTerm;
use Doctrine\ORM\Query\AST\InListExpression;
Expand Down Expand Up @@ -96,10 +95,7 @@ public function walkSelectStatement(SelectStatement $AST)
),
]
);
} elseif (
$AST->whereClause->conditionalExpression instanceof ConditionalExpression
|| $AST->whereClause->conditionalExpression instanceof ConditionalFactor
) {
} else {
$tmpPrimary = new ConditionalPrimary();
$tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
$AST->whereClause->conditionalExpression = new ConditionalTerm(
Expand Down
15 changes: 5 additions & 10 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,11 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Query/SqlWalker.php

-
message: "#^Parameter \\#1 \\$condTerm of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkConditionalTerm\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalFactor\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalPrimary\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalTerm, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Phase2OptimizableConditional given\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/SqlWalker.php

-
message: "#^Result of && is always false\\.$#"
count: 1
Expand Down Expand Up @@ -595,16 +600,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php

-
message: "#^Instanceof between \\*NEVER\\* and Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalFactor will always evaluate to false\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php

-
message: "#^Instanceof between Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalExpression and Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalPrimary will always evaluate to false\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php

-
message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
count: 1
Expand Down
27 changes: 0 additions & 27 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2022,18 +2022,13 @@
</PossiblyFalseArgument>
<PossiblyInvalidArgument>
<code>$AST</code>
<code>$conditionalExpression</code>
<code>$expr</code>
<code>$pathExp</code>
<code><![CDATA[$this->ConditionalExpression()]]></code>
<code><![CDATA[$this->ConditionalExpression()]]></code>
<code><![CDATA[$this->lexer->getLiteral($token)]]></code>
<code><![CDATA[$this->lexer->getLiteral($token)]]></code>
<code><![CDATA[$this->lexer->getLiteral($token)]]></code>
</PossiblyInvalidArgument>
<PossiblyInvalidPropertyAssignmentValue>
<code><![CDATA[$this->ConditionalExpression()]]></code>
<code><![CDATA[$this->ConditionalExpression()]]></code>
<code><![CDATA[$this->SimpleArithmeticExpression()]]></code>
</PossiblyInvalidPropertyAssignmentValue>
<PossiblyNullArgument>
Expand Down Expand Up @@ -2145,11 +2140,6 @@
<ImplicitToStringCast>
<code>$expr</code>
</ImplicitToStringCast>
<InvalidArgument>
<code>$condExpr</code>
<code>$condTerm</code>
<code>$factor</code>
</InvalidArgument>
<InvalidNullableReturnType>
<code>string</code>
</InvalidNullableReturnType>
Expand All @@ -2158,7 +2148,6 @@
</MoreSpecificImplementedParamType>
<PossiblyInvalidArgument>
<code><![CDATA[$aggExpression->pathExpression]]></code>
<code><![CDATA[$whereClause->conditionalExpression]]></code>
</PossiblyInvalidArgument>
<PossiblyNullArgument>
<code><![CDATA[$AST->whereClause]]></code>
Expand Down Expand Up @@ -2194,7 +2183,6 @@
</PossiblyUndefinedArrayOffset>
<RedundantConditionGivenDocblockType>
<code>$whereClause !== null</code>
<code><![CDATA[($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary)]]></code>
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Query/TreeWalkerAdapter.php">
Expand Down Expand Up @@ -2633,21 +2621,6 @@
<code>$orderByClause</code>
</PropertyNotSetInConstructor>
</file>
<file src="lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php">
<DocblockTypeContradiction>
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalExpression
|| $AST->whereClause->conditionalExpression instanceof ConditionalFactor]]></code>
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalFactor]]></code>
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalPrimary]]></code>
</DocblockTypeContradiction>
<PossiblyInvalidPropertyAssignmentValue>
<code><![CDATA[$AST->whereClause->conditionalExpression]]></code>
</PossiblyInvalidPropertyAssignmentValue>
<RedundantConditionGivenDocblockType>
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalExpression
|| $AST->whereClause->conditionalExpression instanceof ConditionalFactor]]></code>
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Tools/SchemaTool.php">
<ArgumentTypeCoercion>
<code>$classes</code>
Expand Down
41 changes: 41 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Doctrine\Tests\ORM\Functional;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type as DBALType;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
Expand All @@ -31,6 +33,8 @@
use Doctrine\Tests\OrmFunctionalTestCase;
use InvalidArgumentException;

use function class_exists;

class NativeQueryTest extends OrmFunctionalTestCase
{
use SQLResultCasing;
Expand Down Expand Up @@ -75,6 +79,43 @@ public function testBasicNativeQuery(): void
self::assertEquals('Roman', $users[0]->name);
}

public function testNativeQueryWithArrayParameter(): void
{
$user = new CmsUser();
$user->name = 'William Shatner';
$user->username = 'wshatner';
$user->status = 'dev';
$this->_em->persist($user);
$user = new CmsUser();
$user->name = 'Leonard Nimoy';
$user->username = 'lnimoy';
$user->status = 'dev';
$this->_em->persist($user);
$user = new CmsUser();
$user->name = 'DeForest Kelly';
$user->username = 'dkelly';
$user->status = 'dev';
$this->_em->persist($user);
$this->_em->flush();

$this->_em->clear();

$rsm = new ResultSetMapping();
$rsm->addEntityResult(CmsUser::class, 'u');
$rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'id'), 'id');
$rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'name'), 'name');

$query = $this->_em->createNativeQuery('SELECT id, name FROM cms_users WHERE username IN (?) ORDER BY username', $rsm);
$query->setParameter(1, ['wshatner', 'lnimoy'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY);

$users = $query->getResult();

self::assertCount(2, $users);
self::assertInstanceOf(CmsUser::class, $users[0]);
self::assertEquals('Leonard Nimoy', $users[0]->name);
self::assertEquals('William Shatner', $users[1]->name);
}

public function testBasicNativeQueryWithMetaResult(): void
{
$user = new CmsUser();
Expand Down
Loading