Skip to content

Commit

Permalink
Merge pull request #10648 from doctrine/2.14.x-merge-up-into-2.15.x_V…
Browse files Browse the repository at this point in the history
…ZV5I0St

Merge release 2.14.3 into 2.15.x
  • Loading branch information
greg0ire authored Apr 22, 2023
2 parents a78e5bc + a64f315 commit ba9f51a
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 27 deletions.
2 changes: 1 addition & 1 deletion docs/en/reference/attributes-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ Example with partial indexes:
#[Index(name: "search_idx", columns: ["category"],
options: [
"where": "((category IS NOT NULL))"
"where" => "((category IS NOT NULL))"
]
)]
class ECommerceProduct
Expand Down
13 changes: 10 additions & 3 deletions doctrine-mapping.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
<xs:attribute name="type" type="orm:type" default="string" />
<xs:attribute name="column" type="orm:columntoken" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="unique" type="xs:boolean" default="false" />
Expand Down Expand Up @@ -415,7 +415,7 @@
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" />
<xs:attribute name="type" type="orm:type" />
<xs:attribute name="column" type="orm:columntoken" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="association-key" type="xs:boolean" default="false" />
Expand Down Expand Up @@ -447,6 +447,13 @@
</xs:restriction>
</xs:simpleType>

<xs:simpleType name="type" id="type">
<xs:restriction base="xs:token">
<xs:pattern value="([a-zA-Z_u01-uff][a-zA-Z0-9_u01-uff]+)|(\c+)" id="type.class.pattern">
</xs:pattern>
</xs:restriction>
</xs:simpleType>

<xs:complexType name="inverse-join-columns">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
Expand Down Expand Up @@ -631,7 +638,7 @@
<xs:element name="options" type="orm:options" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
<xs:attribute name="type" type="orm:type" default="string" />
<xs:attribute name="column" type="orm:columntoken" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="unique" type="xs:boolean" default="false" />
Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function deferPostLoadInvoking(ClassMetadata $class, $entity): void
}

/**
* This method should me called after any hydration cycle completed.
* This method should be called after any hydration cycle completed.
*
* Method fires all deferred invocations of postLoad events
*/
Expand Down
14 changes: 10 additions & 4 deletions lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use BadMethodCallException;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
Expand Down Expand Up @@ -248,10 +249,15 @@ public function loadCriteria(PersistentCollection $collection, Criteria $criteri
foreach ($parameters as $parameter) {
[$name, $value, $operator] = $parameter;

$field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);
$whereClauses[] = sprintf('te.%s %s ?', $field, $operator);
$params[] = $value;
$paramTypes[] = PersisterHelper::getTypeOfField($name, $targetClass, $this->em)[0];
$field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);

if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) {
$whereClauses[] = sprintf('te.%s %s NULL', $field, $operator === Comparison::EQ ? 'IS' : 'IS NOT');
} else {
$whereClauses[] = sprintf('te.%s %s ?', $field, $operator);
$params[] = $value;
$paramTypes[] = PersisterHelper::getTypeOfField($name, $targetClass, $this->em)[0];
}
}

$tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
Expand Down
16 changes: 9 additions & 7 deletions lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -892,15 +892,17 @@ public function expandCriteriaParameters(Criteria $criteria)

$valueVisitor->dispatch($expression);

[$params, $types] = $valueVisitor->getParamsAndTypes();

foreach ($params as $param) {
$sqlParams = array_merge($sqlParams, $this->getValues($param));
}
[, $types] = $valueVisitor->getParamsAndTypes();

foreach ($types as $type) {
[$field, $value] = $type;
$sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class));
[$field, $value, $operator] = $type;

if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) {
continue;
}

$sqlParams = array_merge($sqlParams, $this->getValues($value));
$sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class));
}

return [$sqlParams, $sqlTypes];
Expand Down
12 changes: 2 additions & 10 deletions lib/Doctrine/ORM/Persisters/SqlValueVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,10 @@ class SqlValueVisitor extends ExpressionVisitor
*/
public function walkComparison(Comparison $comparison)
{
$value = $this->getValueFromComparison($comparison);
$field = $comparison->getField();
$operator = $comparison->getOperator();

if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) {
return null;
} elseif ($operator === Comparison::NEQ && $value === null) {
return null;
}
$value = $this->getValueFromComparison($comparison);

$this->values[] = $value;
$this->types[] = [$field, $value, $operator];
$this->types[] = [$comparison->getField(), $value, $comparison->getOperator()];

return null;
}
Expand Down
26 changes: 26 additions & 0 deletions tests/Doctrine/Tests/Models/GH7717/GH7717Child.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\GH7717;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="gh7717_children")
*/
class GH7717Child
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
public ?int $id = null;

/**
* @ORM\Column(type="string", nullable=true)
*/
public ?string $nullableProperty = null;
}
29 changes: 29 additions & 0 deletions tests/Doctrine/Tests/Models/GH7717/GH7717Parent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\GH7717;

use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="gh7717_parents")
*/
class GH7717Parent
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
public ?int $id = null;

/**
* @ORM\ManyToMany(targetEntity="GH7717Child", cascade={"persist"})
*
* @var Selectable<int, GH7717Child>
*/
public Selectable $children;
}
24 changes: 24 additions & 0 deletions tests/Doctrine/Tests/Models/Project/Project.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\Project;

class Project
{
/**
* @var string
*/
private $id;

/**
* @var string
*/
private $name;

public function __construct(string $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
}
18 changes: 18 additions & 0 deletions tests/Doctrine/Tests/Models/Project/ProjectId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\Project;

class ProjectId
{
/**
* @var string
*/
private $id;

public function __construct(string $id)
{
$this->id = $id;
}
}
24 changes: 24 additions & 0 deletions tests/Doctrine/Tests/Models/Project/ProjectInvalidMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\Project;

class ProjectInvalidMapping
{
/**
* @var string
*/
private $id;

/**
* @var string
*/
private $name;

public function __construct(string $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
}
18 changes: 18 additions & 0 deletions tests/Doctrine/Tests/Models/Project/ProjectName.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\Project;

final class ProjectName
{
/**
* @var string
*/
private $name;

public function __construct(string $name)
{
$this->name = $name;
}
}
45 changes: 45 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/GH7717Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Tests\Models\GH7717\GH7717Child;
use Doctrine\Tests\Models\GH7717\GH7717Parent;
use Doctrine\Tests\OrmFunctionalTestCase;

/**
* @requires PHP 7.4
*/
final class GH7717Test extends OrmFunctionalTestCase
{
public function setUp(): void
{
parent::setUp();

$this->createSchemaForModels(
GH7717Parent::class,
GH7717Child::class
);
}

public function testManyToManyPersisterIsNullComparison(): void
{
$childWithNullProperty = new GH7717Child();
$childWithoutNullProperty = new GH7717Child();
$childWithoutNullProperty->nullableProperty = 'nope';

$parent = new GH7717Parent();
$parent->children = new ArrayCollection([$childWithNullProperty, $childWithoutNullProperty]);

$this->_em->persist($parent);
$this->_em->flush();
$this->_em->clear();

$parent = $this->_em->find(GH7717Parent::class, 1);

$this->assertCount(1, $parent->children->matching(new Criteria(Criteria::expr()->isNull('nullableProperty'))));
}
}
25 changes: 25 additions & 0 deletions tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
use Doctrine\Tests\Models\Generic\BooleanModel;
use Doctrine\Tests\Models\GH7141\GH7141Article;
use Doctrine\Tests\Models\GH7316\GH7316Article;
use Doctrine\Tests\Models\Project\Project;
use Doctrine\Tests\Models\Project\ProjectId;
use Doctrine\Tests\Models\Project\ProjectInvalidMapping;
use Doctrine\Tests\Models\Project\ProjectName;
use Doctrine\Tests\Models\ValueObjects\Name;
use Doctrine\Tests\Models\ValueObjects\Person;

Expand Down Expand Up @@ -239,6 +243,10 @@ public static function dataInvalidSchema(): array
UserMissingAttributes::class,
['The attribute \'name\' is required but missing' => 1],
],
[
ProjectInvalidMapping::class,
['attribute \'type\': [facet \'pattern\'] The value' => 2],
],
];
}

Expand Down Expand Up @@ -279,6 +287,23 @@ public function testInvalidEntityOrMappedSuperClassShouldMentionParentClasses():

$this->createClassMetadata(DDC889Class::class);
}

public function testClassNameInFieldOrId(): void
{
$class = new ClassMetadata(Project::class);
$class->initializeReflection(new RuntimeReflectionService());

$driver = $this->loadDriver();
$driver->loadMetadataForClass(Project::class, $class);

/** @var array{type: string} $id */
$id = $class->getFieldMapping('id');
/** @var array{type: string} $name */
$name = $class->getFieldMapping('name');

self::assertEquals(ProjectId::class, $id['type']);
self::assertEquals(ProjectName::class, $name['type']);
}
}

class CTI
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\Project\Project" table="project">
<id name="id" type="Doctrine\Tests\Models\Project\ProjectId" column="id">
<generator strategy="NONE"/>
</id>
<field name="name" type="Doctrine\Tests\Models\Project\ProjectName" column="name"/>
</entity>
</doctrine-mapping>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\Project\ProjectInvalidMapping" table="project">
<id name="id" type="Doctrine/Tests/Models/Project/Project/ProjectId" column="id">
<generator strategy="NONE"/>
</id>
<field name="name" type="Doctrine/Tests/Models/Project/Project/ProjectName" column="name"/>
</entity>
</doctrine-mapping>
Loading

0 comments on commit ba9f51a

Please sign in to comment.