mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a64f315dfe | ||
|
|
6ca319a6f4 | ||
|
|
fceb279947 | ||
|
|
2977933119 | ||
|
|
5ac6fadf29 | ||
|
|
e59ed88251 | ||
|
|
a16aeaeac8 | ||
|
|
fca1ef78a7 |
@@ -575,7 +575,7 @@ Example with partial indexes:
|
||||
|
||||
#[Index(name: "search_idx", columns: ["category"],
|
||||
options: [
|
||||
"where": "((category IS NOT NULL))"
|
||||
"where" => "((category IS NOT NULL))"
|
||||
]
|
||||
)]
|
||||
class ECommerceProduct
|
||||
|
||||
@@ -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" />
|
||||
@@ -414,7 +414,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" />
|
||||
@@ -446,6 +446,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" />
|
||||
@@ -630,7 +637,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" />
|
||||
|
||||
@@ -51,7 +51,7 @@ final class HydrationCompleteHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Persisters\Collection;
|
||||
|
||||
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;
|
||||
@@ -246,10 +247,15 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
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);
|
||||
|
||||
@@ -890,15 +890,17 @@ class BasicEntityPersister implements EntityPersister
|
||||
|
||||
$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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
26
tests/Doctrine/Tests/Models/GH7717/GH7717Child.php
Normal file
26
tests/Doctrine/Tests/Models/GH7717/GH7717Child.php
Normal file
@@ -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
tests/Doctrine/Tests/Models/GH7717/GH7717Parent.php
Normal file
29
tests/Doctrine/Tests/Models/GH7717/GH7717Parent.php
Normal file
@@ -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
tests/Doctrine/Tests/Models/Project/Project.php
Normal file
24
tests/Doctrine/Tests/Models/Project/Project.php
Normal file
@@ -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
tests/Doctrine/Tests/Models/Project/ProjectId.php
Normal file
18
tests/Doctrine/Tests/Models/Project/ProjectId.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
tests/Doctrine/Tests/Models/Project/ProjectName.php
Normal file
18
tests/Doctrine/Tests/Models/Project/ProjectName.php
Normal file
@@ -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
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7717Test.php
Executable file
45
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7717Test.php
Executable file
@@ -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'))));
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,10 @@ use Doctrine\Tests\Models\DDC889\DDC889SuperClass;
|
||||
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;
|
||||
|
||||
@@ -239,6 +243,10 @@ class XmlMappingDriverTest extends MappingDriverTestCase
|
||||
UserMissingAttributes::class,
|
||||
['The attribute \'name\' is required but missing' => 1],
|
||||
],
|
||||
[
|
||||
ProjectInvalidMapping::class,
|
||||
['attribute \'type\': [facet \'pattern\'] The value' => 2],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -279,6 +287,23 @@ class XmlMappingDriverTest extends MappingDriverTestCase
|
||||
|
||||
$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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\Tests;
|
||||
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Driver\AbstractSQLiteDriver\Middleware\EnableForeignKeys;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Exception\DriverException;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
@@ -16,10 +17,12 @@ use Symfony\Component\VarExporter\LazyGhostTrait;
|
||||
use UnexpectedValueException;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function explode;
|
||||
use function fwrite;
|
||||
use function get_debug_type;
|
||||
use function getenv;
|
||||
use function in_array;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
use function str_starts_with;
|
||||
@@ -63,7 +66,13 @@ class TestUtil
|
||||
self::$initialized = true;
|
||||
}
|
||||
|
||||
$connection = DriverManager::getConnection(self::getTestConnectionParameters());
|
||||
$connectionParameters = self::getTestConnectionParameters();
|
||||
$configuration = new Configuration();
|
||||
if (in_array($connectionParameters['driver'], ['pdo_sqlite', 'sqlite3'], true) && class_exists(EnableForeignKeys::class)) {
|
||||
$configuration->setMiddlewares([new EnableForeignKeys()]);
|
||||
}
|
||||
|
||||
$connection = DriverManager::getConnection($connectionParameters, $configuration);
|
||||
assert($connection instanceof DbalExtensions\Connection);
|
||||
|
||||
self::addDbEventSubscribers($connection);
|
||||
@@ -212,6 +221,7 @@ class TestUtil
|
||||
'port',
|
||||
'server',
|
||||
'memory',
|
||||
'path',
|
||||
'ssl_key',
|
||||
'ssl_cert',
|
||||
'ssl_ca',
|
||||
|
||||
Reference in New Issue
Block a user