Compare commits

..

13 Commits

Author SHA1 Message Date
Grégoire Paris ccfc97c32f Merge pull request #11187 from jwage/remove-xml-validation-disabling-deprecation
Remove XML validation disabling deprecation.
2024-01-26 20:41:16 +01:00
Jonathan H. Wage d386b43be3 Remove XML validation disabling deprecation. 2024-01-26 09:59:03 -06:00
Grégoire Paris 0970ce7072 Merge pull request #11186 from derrabus/chore/readme-versions
Update branches in README
2024-01-26 08:34:24 +01:00
Alexander M. Turek 624c56be72 Update branches in README 2024-01-26 00:52:35 +01:00
Grégoire Paris fbc8e6741e Merge pull request #11176 from thePanz/fix-11173-get-name-on-null-non-backed-enum
Throw exception when trying to use non-backed enum types
2024-01-23 07:50:20 +01:00
thePanz 7151db3cb8 Throw exception when trying to use non-backed enum types 2024-01-22 13:04:58 +01:00
Grégoire Paris ac24c11808 Modernize code in documentation (#11179)
Somehow, there still were code samples relying on annotations.
2024-01-20 21:53:48 +01:00
Grégoire Paris dd478d8662 Merge pull request #11178 from greg0ire/remove-ref-jira
Remove references to JIRA
2024-01-20 14:06:15 +01:00
Grégoire Paris 0b3cd72609 Remove references to JIRA 2024-01-20 13:45:04 +01:00
Grégoire Paris 85034699cb Merge pull request #11171 from greg0ire/extract-class
Make Doctrine\Tests\ORM\Internal\Node autoloadable
2024-01-18 21:33:06 +01:00
Grégoire Paris d98186e2c4 Make Doctrine\Tests\ORM\Internal\Node autoloadable
It is used in several tests.
2024-01-18 21:19:28 +01:00
Grégoire Paris a0ed37954b Merge pull request #11167 from bobvandevijver/fix-eager-iterable-loading-test
Use foreach on iterable to prevent table locks during tests
2024-01-18 16:47:11 +01:00
Bob van de Vijver 4875f4c878 Use foreach on iterable to prevent table locks during tests 2024-01-18 10:24:40 +01:00
15 changed files with 182 additions and 134 deletions
+11 -11
View File
@@ -1,7 +1,7 @@
| [3.0.x][3.0] | [2.16.x][2.16] | [2.15.x][2.15] |
| [3.0.x][3.0] | [2.18.x][2.18] | [2.17.x][2.17] |
|:----------------:|:----------------:|:----------:|
| [![Build status][3.0 image]][3.0] | [![Build status][2.16 image]][2.16] | [![Build status][2.15 image]][2.15] |
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.16 coverage image]][2.16 coverage] | [![Coverage Status][2.15 coverage image]][2.15 coverage] |
| [![Build status][3.0 image]][3.0] | [![Build status][2.18 image]][2.18] | [![Build status][2.17 image]][2.17] |
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.18 coverage image]][2.18 coverage] | [![Coverage Status][2.17 coverage image]][2.17 coverage] |
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
@@ -22,11 +22,11 @@ without requiring unnecessary code duplication.
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
[2.16 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.16.x
[2.16]: https://github.com/doctrine/orm/tree/2.16.x
[2.16 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.16.x/graph/badge.svg
[2.16 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.16.x
[2.15 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.15.x
[2.15]: https://github.com/doctrine/orm/tree/2.15.x
[2.15 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.15.x/graph/badge.svg
[2.15 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.15.x
[2.18 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.18.x
[2.18]: https://github.com/doctrine/orm/tree/2.18.x
[2.18 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.18.x/graph/badge.svg
[2.18 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.18.x
[2.17 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.17.x
[2.17]: https://github.com/doctrine/orm/tree/2.17.x
[2.17 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.17.x/graph/badge.svg
[2.17 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.17.x
+2 -3
View File
@@ -13,6 +13,5 @@ understand the assumptions we make.
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/security.html)
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/security.html)
If you find a Security bug in Doctrine, please report it on Jira and change the
Security Level to "Security Issues". It will be visible to Doctrine Core
developers and you only.
If you find a Security bug in Doctrine, please follow our
[Security reporting guidelines](https://www.doctrine-project.org/policies/security.html#reporting).
+50 -77
View File
@@ -36,71 +36,50 @@ Our entities look like:
namespace Bank\Entities;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity]
class Account
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id;
/**
* @ORM\Column(type="string", unique=true)
*/
private string $no;
/**
* @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
*/
private array $entries;
/**
* @ORM\Column(type="integer")
*/
private int $maxCredit = 0;
public function __construct(string $no, int $maxCredit = 0)
{
$this->no = $no;
$this->maxCredit = $maxCredit;
$this->entries = new \Doctrine\Common\Collections\ArrayCollection();
#[ORM\OneToMany(targetEntity: Entry::class, mappedBy: 'account', cascade: ['persist'])]
private Collection $entries;
public function __construct(
#[ORM\Column(type: 'string', unique: true)]
private string $no,
#[ORM\Column(type: 'integer')]
private int $maxCredit = 0,
) {
$this->entries = new ArrayCollection();
}
}
/**
* @ORM\Entity
*/
#[ORM\Entity]
class Entry
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id;
/**
* @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
*/
private Account $account;
/**
* @ORM\Column(type="integer")
*/
private int $amount;
public function __construct(Account $account, int $amount)
{
$this->account = $account;
$this->amount = $amount;
public function __construct(
#[ORM\ManyToOne(targetEntity: Account::class, inversedBy: 'entries')]
private Account $account,
#[ORM\Column(type: 'integer')]
private int $amount,
) {
// more stuff here, from/to whom, stated reason, execution date and such
}
public function getAmount(): Amount
{
return $this->amount;
@@ -193,9 +172,8 @@ relation with this method:
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
$e = new Entry($this, $amount);
$this->entries[] = $e;
$this->entries[] = new Entry($this, $amount);
}
}
@@ -213,18 +191,18 @@ Now look at the following test-code for our entities:
{
$account = new Account("123456", maxCredit: 200);
$this->assertEquals(0, $account->getBalance());
$account->addEntry(500);
$this->assertEquals(500, $account->getBalance());
$account->addEntry(-700);
$this->assertEquals(-200, $account->getBalance());
}
public function testExceedMaxLimit()
{
$account = new Account("123456", maxCredit: 200);
$this->expectException(Exception::class);
$account->addEntry(-1000);
}
@@ -285,22 +263,19 @@ entries collection) we want to add an aggregate field called
<?php
class Account
{
/**
* @ORM\Column(type="integer")
*/
#[ORM\Column(type: 'integer')]
private int $balance = 0;
public function getBalance(): int
{
return $this->balance;
}
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
$e = new Entry($this, $amount);
$this->entries[] = $e;
$this->entries[] = new Entry($this, $amount);
$this->balance += $amount;
}
}
@@ -331,13 +306,13 @@ potentially lead to inconsistent state. See this example:
// The Account $accId has a balance of 0 and a max credit limit of 200:
// request 1 account
$account1 = $em->find(Account::class, $accId);
// request 2 account
$account2 = $em->find(Account::class, $accId);
$account1->addEntry(-200);
$account2->addEntry(-200);
// now request 1 and 2 both flush the changes.
The aggregate field ``Account::$balance`` is now -200, however the
@@ -357,10 +332,8 @@ Optimistic locking is as easy as adding a version column:
class Account
{
/**
* @ORM\Column(type="integer")
* @ORM\Version
*/
#[ORM\Column(type: 'integer')]
#[ORM\Version]
private int $version;
}
@@ -47,10 +47,8 @@ A Customer entity
use Acme\CustomerModule\Entity\Customer as BaseCustomer;
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
/**
* @ORM\Entity
* @ORM\Table(name="customer")
*/
#[ORM\Entity]
#[ORM\Table(name: 'customer')]
class Customer extends BaseCustomer implements InvoiceSubjectInterface
{
// In our example, any methods defined in the InvoiceSubjectInterface
@@ -69,19 +67,12 @@ An Invoice entity
use Doctrine\ORM\Mapping AS ORM;
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
/**
* Represents an Invoice.
*
* @ORM\Entity
* @ORM\Table(name="invoice")
*/
#[ORM\Entity]
#[ORM\Table(name: 'invoice')]
class Invoice
{
/**
* @ORM\ManyToOne(targetEntity="Acme\InvoiceModule\Model\InvoiceSubjectInterface")
* @var InvoiceSubjectInterface
*/
protected $subject;
#[ORM\ManyToOne(targetEntity: InvoiceSubjectInterface::class)]
protected InvoiceSubjectInterface $subject;
}
An InvoiceSubjectInterface
+2 -3
View File
@@ -12,9 +12,8 @@ page only handles Security issues in the ORM.
- `DBAL Security Page <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
If you find a Security bug in Doctrine, please report it on Jira and change the
Security Level to "Security Issues". It will be visible to Doctrine Core
developers and you only.
If you find a Security bug in Doctrine, please follow our
`Security reporting guidelines <https://www.doctrine-project.org/policies/security.html#reporting>`_.
User input and Doctrine ORM
---------------------------
@@ -57,7 +57,15 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
$mapping['enumType'] = $type->getName();
$reflection = new ReflectionEnum($type->getName());
$type = $reflection->getBackingType();
if (! $reflection->isBacked()) {
throw MappingException::backedEnumTypeRequired(
$field->class,
$mapping['fieldName'],
$mapping['enumType']
);
}
$type = $reflection->getBackingType();
assert($type instanceof ReflectionNamedType);
}
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
@@ -56,15 +55,6 @@ class XmlDriver extends FileDriver
);
}
if (! $isXsdValidationEnabled) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/6728',
'Using XML mapping driver with XSD validation disabled is deprecated'
. ' and will not be supported in Doctrine ORM 3.0.'
);
}
if ($isXsdValidationEnabled && ! extension_loaded('dom')) {
throw new LogicException(
'XSD validation cannot be enabled because the DOM extension is missing.'
@@ -954,6 +954,16 @@ class MappingException extends ORMException
return new self(sprintf('Enum types require PHP 8.1 in %s::$%s', $className, $fieldName));
}
public static function backedEnumTypeRequired(string $className, string $fieldName, string $enumType): self
{
return new self(sprintf(
'Attempting to map a non-backed enum type %s in entity %s::$%s. Please use backed enums only',
$enumType,
$className,
$fieldName
));
}
public static function nonEnumTypeMapped(string $className, string $fieldName, string $enumType): self
{
return new self(sprintf(
@@ -257,7 +257,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
$innerSql
);
// http://www.doctrine-project.org/jira/browse/DDC-1958
// https://github.com/doctrine/orm/issues/2630
$sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause);
// Apply the limit and offset.
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\Enums;
use Doctrine\ORM\Mapping\Column;
class FaultySwitch
{
#[Column(type: 'string')]
public string $value;
/**
* The following line is ignored on psalm and phpstan so that we can test
* that the mapping is throwing an exception when a non-backed enum is used.
*
* @psalm-suppress InvalidArgument
*/
#[Column(enumType: SwitchStatus::class)]
public SwitchStatus $status;
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\Enums;
enum SwitchStatus
{
case ON;
case OFF;
}
@@ -18,6 +18,10 @@ class EagerFetchCollectionTest extends OrmFunctionalTestCase
parent::setUp();
$this->createSchemaForModels(EagerFetchOwner::class, EagerFetchChild::class);
// Ensure tables are empty
$this->_em->getRepository(EagerFetchChild::class)->createQueryBuilder('o')->delete()->getQuery()->execute();
$this->_em->getRepository(EagerFetchOwner::class)->createQueryBuilder('o')->delete()->getQuery()->execute();
}
public function testEagerFetchMode(): void
@@ -91,9 +95,11 @@ class EagerFetchCollectionTest extends OrmFunctionalTestCase
$this->_em->clear();
$iterable = $this->_em->getRepository(EagerFetchOwner::class)->createQueryBuilder('o')->getQuery()->toIterable();
$owner = $iterable->current();
$this->assertCount(2, $owner->children);
// There is only a single record, but use a foreach to ensure the iterator is marked as finished and the table lock is released
foreach ($iterable as $owner) {
$this->assertCount(2, $owner->children);
}
}
protected function createOwnerWithChildren(int $children): EagerFetchOwner
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Internal;
class Node
{
/** @var string */
public $name;
public function __construct(string $name)
{
$this->name = $name;
}
}
@@ -287,14 +287,3 @@ class TopologicalSortTest extends OrmTestCase
}, array_values($this->topologicalSort->sort()));
}
}
class Node
{
/** @var string */
public $name;
public function __construct(string $name)
{
$this->name = $name;
}
}
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\ORM\Mapping\DefaultTypedFieldMapper;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Tests\Models\Enums\FaultySwitch;
use Doctrine\Tests\OrmTestCase;
use ReflectionClass;
/**
* @requires PHP >= 8.1
*/
class TypedEnumFieldMapperTest extends OrmTestCase
{
private static function defaultTypedFieldMapper(): DefaultTypedFieldMapper
{
return new DefaultTypedFieldMapper();
}
public function testNotBackedEnumThrows(): void
{
$reflectionClass = new ReflectionClass(FaultySwitch::class);
$this->expectException(MappingException::class);
$this->expectExceptionMessage(
'Attempting to map a non-backed enum type Doctrine\Tests\Models\Enums\SwitchStatus in entity Doctrine\Tests\Models\Enums\FaultySwitch::$status. Please use backed enums only'
);
self::defaultTypedFieldMapper()->validateAndComplete(['fieldName' => 'status'], $reflectionClass->getProperty('status'));
}
}