diff --git a/.gitattributes b/.gitattributes index 7ae25b3212a..1b71dc4e5b3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,5 +16,8 @@ phpcs.xml.dist export-ignore phpbench.json export-ignore phpstan.neon export-ignore phpstan-baseline.neon export-ignore +phpstan-dbal2.neon export-ignore +phpstan-params.neon export-ignore +phpstan-persistence2.neon export-ignore psalm.xml export-ignore psalm-baseline.xml export-ignore diff --git a/UPGRADE.md b/UPGRADE.md index df6fcd889f6..269f62ddc58 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -494,11 +494,24 @@ Use `toIterable()` instead. # Upgrade to 2.14 +## Deprecated incomplete schema updates + +Using `orm:schema-tool:update` without passing the `--complete` flag is +deprecated. Use schema asset filtering if you need to preserve assets not +managed by DBAL. + +Likewise, calling `SchemaTool::updateSchema()` or +`SchemaTool::getUpdateSchemaSql()` with a second argument is deprecated. + ## Deprecated annotation mapping driver. Please switch to one of the other mapping drivers. Native attributes which PHP supports since version 8.0 are probably your best option. +As a consequence, the following methods are deprecated: +- `ORMSetup::createAnnotationMetadataConfiguration` +- `ORMSetup::createDefaultAnnotationDriver` + ## Deprecated `Doctrine\ORM\Proxy\Proxy` interface. Use `Doctrine\Persistence\Proxy` instead to check whether proxies are initialized. @@ -517,6 +530,13 @@ It will be removed in 3.0. Use one of the dedicated event classes instead: # Upgrade to 2.13 +## Deprecated `EntityManager::create()` + +The constructor of `EntityManager` is now public and should be used instead of the `create()` method. +However, the constructor expects a `Connection` while `create()` accepted an array with connection parameters. +You can pass that array to DBAL's `Doctrine\DBAL\DriverManager::getConnection()` method to bootstrap the +connection. + ## Deprecated `QueryBuilder` methods and constants. 1. The `QueryBuilder::getState()` method has been deprecated as the builder state is an internal concern. diff --git a/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst b/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst index 2a44f3725c7..8e7812e21a7 100644 --- a/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst +++ b/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst @@ -32,59 +32,39 @@ The entity class: namespace Geo\Entity; - /** - * @Entity - */ + use Geo\ValueObject\Point; + + #[Entity] class Location { - /** - * @Column(type="point") - * - * @var \Geo\ValueObject\Point - */ - private $point; - - /** - * @Column(type="string") - * - * @var string - */ - private $address; - - /** - * @param \Geo\ValueObject\Point $point - */ - public function setPoint(\Geo\ValueObject\Point $point) + #[Column(type: 'point')] + private Point $point; + + #[Column] + private string $address; + + public function setPoint(Point $point): void { $this->point = $point; } - /** - * @return \Geo\ValueObject\Point - */ - public function getPoint() + public function getPoint(): Point { return $this->point; } - /** - * @param string $address - */ - public function setAddress($address) + public function setAddress(string $address): void { $this->address = $address; } - /** - * @return string - */ - public function getAddress() + public function getAddress(): string { return $this->address; } } -We use the custom type ``point`` in the ``@Column`` docblock annotation of the +We use the custom type ``point`` in the ``#[Column]`` attribute of the ``$point`` field. We will create this custom mapping type in the next chapter. The point class: @@ -97,29 +77,18 @@ The point class: class Point { - - /** - * @param float $latitude - * @param float $longitude - */ - public function __construct($latitude, $longitude) - { - $this->latitude = $latitude; - $this->longitude = $longitude; + public function __construct( + private float $latitude, + private float $longitude, + ) { } - /** - * @return float - */ - public function getLatitude() + public function getLatitude(): float { return $this->latitude; } - /** - * @return float - */ - public function getLongitude() + public function getLongitude(): float { return $this->longitude; } @@ -222,7 +191,7 @@ Example usage addCustomNumericFunction($name, $class); $config->addCustomDatetimeFunction($name, $class); - $em = EntityManager::create($dbParams, $config); + $em = new EntityManager($connection, $config); The ``$name`` is the name the function will be referred to in the DQL query. ``$class`` is a string of a class-name which has to @@ -247,5 +247,3 @@ vendor sql functions and extend the DQL languages scope. Code for this Extension to DQL and other Doctrine Extensions can be found `in the GitHub DoctrineExtensions repository `_. - - diff --git a/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst b/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst index 3634a10c1bd..e56393c4609 100644 --- a/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst +++ b/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst @@ -29,15 +29,15 @@ implement the ``NotifyPropertyChanged`` interface from the listeners[] = $listener; } - + /** Notifies listeners of a change. */ protected function onPropertyChanged($propName, $oldValue, $newValue) { if ($this->listeners) { @@ -55,12 +55,12 @@ listeners: .. code-block:: php data) { // check: is it actually modified? $this->onPropertyChanged('data', $this->data, $data); diff --git a/docs/en/cookbook/resolve-target-entity-listener.rst b/docs/en/cookbook/resolve-target-entity-listener.rst index 82771684b81..e3c5550b102 100644 --- a/docs/en/cookbook/resolve-target-entity-listener.rst +++ b/docs/en/cookbook/resolve-target-entity-listener.rst @@ -127,7 +127,8 @@ the targetEntity resolution will occur reliably: // Add the ResolveTargetEntityListener $evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel); - $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + $connection = \Doctrine\DBAL\DriverManager::createConnection($connectionOptions, $config, $evm); + $em = new \Doctrine\ORM\EntityManager($connection, $config, $evm); Final Thoughts -------------- @@ -136,5 +137,3 @@ With the ``ResolveTargetEntityListener``, we are able to decouple our bundles, keeping them usable by themselves, but still being able to define relationships between different objects. By using this method, I've found my bundles end up being easier to maintain independently. - - diff --git a/docs/en/cookbook/sql-table-prefixes.rst b/docs/en/cookbook/sql-table-prefixes.rst index dc4ae6952df..f1c2c4486ff 100644 --- a/docs/en/cookbook/sql-table-prefixes.rst +++ b/docs/en/cookbook/sql-table-prefixes.rst @@ -81,6 +81,4 @@ before the prefix has been set. $tablePrefix = new \DoctrineExtensions\TablePrefix('prefix_'); $evm->addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $tablePrefix); - $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); - - + $em = new \Doctrine\ORM\EntityManager($connection, $config, $evm); diff --git a/docs/en/cookbook/strategy-cookbook-introduction.rst b/docs/en/cookbook/strategy-cookbook-introduction.rst index e593b9c870b..bb60c39801c 100644 --- a/docs/en/cookbook/strategy-cookbook-introduction.rst +++ b/docs/en/cookbook/strategy-cookbook-introduction.rst @@ -154,8 +154,8 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg * This var contains the classname of the strategy * that is used for this blockitem. (This string (!) value will be persisted by Doctrine ORM) * - * This is a doctrine field, so make sure that you use an @column annotation or setup your - * xml files correctly + * This is a doctrine field, so make sure that you use a + #[Column] attribute or setup your xml files correctly * @var string */ protected $strategyClassName; diff --git a/docs/en/cookbook/validation-of-entities.rst b/docs/en/cookbook/validation-of-entities.rst index e297a8ab3b0..4f286490a70 100644 --- a/docs/en/cookbook/validation-of-entities.rst +++ b/docs/en/cookbook/validation-of-entities.rst @@ -36,12 +36,12 @@ are allowed to: public function assertCustomerAllowedBuying() { $orderLimit = $this->customer->getOrderLimit(); - + $amount = 0; foreach ($this->orderLines as $line) { $amount += $line->getAmount(); } - + if ($amount > $orderLimit) { throw new CustomerOrderLimitExceededException(); } @@ -53,7 +53,21 @@ code, enforcing it at any time is important so that customers with a unknown reputation don't owe your business too much money. We can enforce this constraint in any of the metadata drivers. -First Annotations: +First Attributes: + +.. code-block:: php + + plannedShipDate instanceof DateTime)) { throw new ValidateException(); } - + if ($this->plannedShipDate->format('U') < time()) { throw new ValidateException(); } - + if ($this->customer == null) { throw new OrderRequiresCustomerException(); } diff --git a/docs/en/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index 9efd9cb4268..d2e2d281763 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -12,6 +12,7 @@ steps of configuration. use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; + use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\ORMSetup; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\PhpFilesAdapter; @@ -28,7 +29,7 @@ steps of configuration. $config = new Configuration; $config->setMetadataCache($metadataCache); - $driverImpl = ORMSetup::createDefaultAnnotationDriver('/path/to/lib/MyProject/Entities'); + $driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities']); $config->setMetadataDriverImpl($driverImpl); $config->setQueryCache($queryCache); $config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies'); @@ -40,12 +41,12 @@ steps of configuration. $config->setAutoGenerateProxyClasses(false); } - $connectionOptions = array( + $connection = DriverManager::getConnection([ 'driver' => 'pdo_sqlite', - 'path' => 'database.sqlite' - ); + 'path' => 'database.sqlite', + ], $config); - $em = EntityManager::create($connectionOptions, $config); + $em = new EntityManager($connection, $config); Doctrine and Caching -------------------- @@ -113,28 +114,30 @@ classes. There are currently 4 available implementations: -- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` - ``Doctrine\ORM\Mapping\Driver\AttributeDriver`` - ``Doctrine\ORM\Mapping\Driver\XmlDriver`` - ``Doctrine\ORM\Mapping\Driver\DriverChain`` +- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` (deprecated and will + be removed in ``doctrine/orm`` 3.0) +- ``Doctrine\ORM\Mapping\Driver\YamlDriver`` (deprecated and will be + removed in ``doctrine/orm`` 3.0) Throughout the most part of this manual the AttributeDriver is used in the examples. For information on the usage of the AnnotationDriver or XmlDriver please refer to the dedicated chapters ``Annotation Reference`` and ``XML Mapping``. -The annotation driver can be configured with a factory method on -the ``Doctrine\ORM\Configuration``: +The attribute driver can be injected in the ``Doctrine\ORM\Configuration``: .. code-block:: php setMetadataDriverImpl($driverImpl); -The path information to the entities is required for the annotation +The path information to the entities is required for the attribute driver, because otherwise mass-operations on all entities through the console could not work correctly. All of metadata drivers accept either a single directory as a string or an array of @@ -151,7 +154,7 @@ Metadata Cache (***RECOMMENDED***) $config->getMetadataCache(); Gets or sets the cache adapter to use for caching metadata -information, that is, all the information you supply via +information, that is, all the information you supply via attributes, annotations or xml, so that they do not need to be parsed and loaded from scratch on every single request which is a waste of resources. The cache implementation must implement the PSR-6 @@ -273,15 +276,13 @@ proxy sets an exclusive file lock which can cause serious performance bottlenecks in systems with regular concurrent requests. -Connection Options ------------------- +Connection +---------- -The ``$connectionOptions`` passed as the first argument to -``EntityManager::create()`` has to be either an array or an -instance of ``Doctrine\DBAL\Connection``. If an array is passed it -is directly passed along to the DBAL Factory -``Doctrine\DBAL\DriverManager::getConnection()``. The DBAL -configuration is explained in the +The ``$connection`` passed as the first argument to he constructor of +``EntityManager`` has to be an instance of ``Doctrine\DBAL\Connection``. +You can use the factory ``Doctrine\DBAL\DriverManager::getConnection()`` +to create such a connection. The DBAL configuration is explained in the `DBAL section `_. Proxy Objects diff --git a/docs/en/reference/association-mapping.rst b/docs/en/reference/association-mapping.rst index 492d64905a2..5a740319a9d 100644 --- a/docs/en/reference/association-mapping.rst +++ b/docs/en/reference/association-mapping.rst @@ -37,7 +37,7 @@ A many-to-one association is the most common association between objects. Exampl .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -71,8 +92,8 @@ A many-to-one association is the most common association between objects. Exampl The above ``#[JoinColumn]`` is optional as it would default to ``address_id`` and ``id`` anyways. You can omit it and let it use the defaults. - Likewise, inside the ``#[ManyToOne]`` annotation you can omit the - ``targetEntity`` attribute and it will default to ``Address``. + Likewise, inside the ``#[ManyToOne]`` attribute you can omit the + ``targetEntity`` argument and it will default to ``Address``. Generated MySQL Schema: @@ -99,7 +120,7 @@ references one ``Shipment`` entity. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -163,7 +208,7 @@ object. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -279,7 +356,7 @@ bidirectional many-to-one. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + * @OneToMany(targetEntity="Feature", mappedBy="product") + */ + private Collection $features; + // ... + + public function __construct() { + $this->features = new ArrayCollection(); + } + } + + /** @Entity */ + class Feature + { + // ... + /** + * Many features have one product. This is the owning side. + * @ManyToOne(targetEntity="Product", inversedBy="features") + * @JoinColumn(name="product_id", referencedColumnName="id") + */ + private Product|null $product = null; + // ... + } + .. code-block:: xml @@ -355,7 +467,7 @@ The following example sets up such a unidirectional one-to-many association: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + private Collection $phonenumbers; + + public function __construct() + { + $this->phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity */ + class Phonenumber + { + // ... + } + .. code-block:: xml @@ -439,7 +584,7 @@ database perspective is known as an adjacency list approach. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + private Collection $children; + + /** + * Many Categories have One Category. + * @ManyToOne(targetEntity="Category", inversedBy="children") + * @JoinColumn(name="parent_id", referencedColumnName="id") + */ + private Category|null $parent = null; + // ... + + public function __construct() { + $this->children = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + .. code-block:: xml @@ -496,7 +668,7 @@ entities: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + private Collection $groups; + + // ... + + public function __construct() { + $this->groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** @Entity */ + class Group + { + // ... + } + .. code-block:: xml @@ -581,7 +785,7 @@ one is bidirectional. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + private Collection $groups; + + public function __construct() { + $this->groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity */ + class Group + { + // ... + /** + * Many Groups have Many Users. + * @ManyToMany(targetEntity="User", mappedBy="groups") + * @var Collection + */ + private Collection $users; + + public function __construct() { + $this->users = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + .. code-block:: xml @@ -775,12 +1020,18 @@ As an example, consider this mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -794,7 +1045,7 @@ mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -817,17 +1078,32 @@ similar defaults. As an example, consider this mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute */ #[ManyToMany(targetEntity: Group::class)] private Collection $groups; // ... } + .. code-block:: annotation + + + */ + private Collection $groups; + // ... + } + .. code-block:: xml @@ -840,7 +1116,7 @@ This is essentially the same as the following, more verbose, mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + private Collection $groups; + // ... + } + .. code-block:: xml @@ -889,12 +1184,18 @@ attribute on ``JoinColumn`` will be inherited from PHP type. So that: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -907,7 +1208,17 @@ Is essentially the same as following: .. configuration-block:: - .. code-block:: php + .. code-block:: annotation + + ` -- :doc:`Docblock Annotations ` - :doc:`XML ` - :doc:`PHP code ` +- :doc:`Docblock Annotations ` (deprecated and + will be removed in ``doctrine/orm`` 3.0) - :doc:`YAML ` (deprecated and will be removed in ``doctrine/orm`` 3.0.) -This manual will usually show mapping metadata via docblock annotations, though -many examples also show the equivalent configuration in YAML and XML. +This manual will usually show mapping metadata via attributes, though +many examples also show the equivalent configuration in annotations, +YAML and XML. .. note:: @@ -144,10 +146,10 @@ Property Mapping The next step is mapping its properties to columns in the table. -To configure a property use the ``Column`` docblock annotation. The ``type`` -attribute specifies the :ref:`Doctrine Mapping Type ` -to use for the field. If the type is not specified, ``string`` is used as the -default. +To configure a property use the ``Column`` attribute. The ``type`` +argument specifies the :ref:`Doctrine Mapping Type +` to use for the field. If the type is not +specified, ``string`` is used as the default. .. configuration-block:: @@ -334,8 +336,7 @@ Identifiers / Primary Keys -------------------------- Every entity class must have an identifier/primary key. You can select -the field that serves as the identifier with the ``@Id`` -annotation. +the field that serves as the identifier with the ``#[Id]`` attribute. .. configuration-block:: @@ -351,6 +352,20 @@ annotation. // ... } + .. code-block:: annotation + + @@ -362,7 +377,7 @@ annotation. -In most cases using the automatic generator strategy (``@GeneratedValue``) is +In most cases using the automatic generator strategy (``#[GeneratedValue]``) is what you want. It defaults to the identifier generation mechanism your current database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL and Oracle and so on. @@ -396,8 +411,8 @@ Here is the list of possible generation strategies: - ``NONE``: Tells Doctrine that the identifiers are assigned (and thus generated) by your code. The assignment must take place before a new entity is passed to ``EntityManager#persist``. NONE is the - same as leaving off the @GeneratedValue entirely. -- ``CUSTOM``: With this option, you can use the ``@CustomIdGenerator`` annotation. + same as leaving off the ``#[GeneratedValue]`` entirely. +- ``CUSTOM``: With this option, you can use the ``#[CustomIdGenerator]`` attribute. It will allow you to pass a :doc:`class of your own to generate the identifiers.<_annref_customidgenerator>` Sequence Generator @@ -421,6 +436,20 @@ besides specifying the sequence's name: // ... } + .. code-block:: annotation + + @@ -468,11 +497,12 @@ need to access the sequence once to generate the identifiers for Composite Keys ~~~~~~~~~~~~~~ -With Doctrine ORM you can use composite primary keys, using ``@Id`` on more then -one column. Some restrictions exist opposed to using a single identifier in -this case: The use of the ``@GeneratedValue`` annotation is not supported, -which means you can only use composite keys if you generate the primary key -values yourself before calling ``EntityManager#persist()`` on the entity. +With Doctrine ORM you can use composite primary keys, using ``#[Id]`` on +more than one column. Some restrictions exist opposed to using a single +identifier in this case: The use of the ``#[GeneratedValue]`` attribute +is not supported, which means you can only use composite keys if you +generate the primary key values yourself before calling +``EntityManager#persist()`` on the entity. More details on composite primary keys are discussed in a :doc:`dedicated tutorial <../tutorials/composite-primary-keys>`. diff --git a/docs/en/reference/caching.rst b/docs/en/reference/caching.rst index 940ebf7e37b..37ce9c00919 100644 --- a/docs/en/reference/caching.rst +++ b/docs/en/reference/caching.rst @@ -109,8 +109,9 @@ Metadata Cache ~~~~~~~~~~~~~~ Your class metadata can be parsed from a few different sources like -XML, Annotations, etc. Instead of parsing this information on -each request we should cache it using one of the cache drivers. +XML, Attributes, Annotations etc. Instead of parsing this +information on each request we should cache it using one of the cache +drivers. Just like the query and result cache we need to configure it first. diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index 095c57bd767..356099016ce 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -41,33 +41,36 @@ access point to ORM functionality provided by Doctrine. // bootstrap.php require_once "vendor/autoload.php"; + use Doctrine\DBAL\DriverManager; use Doctrine\ORM\EntityManager; use Doctrine\ORM\ORMSetup; - $paths = array("/path/to/entity-files"); + $paths = ['/path/to/entity-files']; $isDevMode = false; // the connection configuration - $dbParams = array( + $dbParams = [ 'driver' => 'pdo_mysql', 'user' => 'root', 'password' => '', 'dbname' => 'foo', - ); + ]; - $config = ORMSetup::createAnnotationMetadataConfiguration($paths, $isDevMode); - $entityManager = EntityManager::create($dbParams, $config); + $config = ORMSetup::createAttributeMetadataConfiguration($paths, $isDevMode); + $connection = DriverManager::getConnection($dbParams, $config); + $entityManager = new EntityManager($connection, $config); Or if you prefer XML: .. code-block:: php createQuery('SELECT u FROM ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id'); - $query->setParameters(array( + $query->setParameters([ 'name' => 'Bob', 'name2' => 'Alice', 'id' => 321, - )); + ]); $users = $query->getResult(); // array of ForumUser objects With COUNT DISTINCT: @@ -794,7 +794,7 @@ You can register custom DQL functions in your ORM Configuration: $config->addCustomNumericFunction($name, $class); $config->addCustomDatetimeFunction($name, $class); - $em = EntityManager::create($dbParams, $config); + $em = new EntityManager($connection, $config); The functions have to return either a string, numeric or datetime value depending on the registered function type. As an example we @@ -806,8 +806,8 @@ classes have to implement the base class : addEventListener([Events::preUpdate], new MyEventListener()); $eventManager->addEventSubscriber(new MyEventSubscriber()); - $entityManager = EntityManager::create($dbOpts, $config, $eventManager); + $entityManager = new EntityManager($connection, $config, $eventManager); You can also retrieve the event manager instance after the EntityManager was created: @@ -809,7 +809,46 @@ you need to map the listener method using the event type mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + + @@ -940,7 +980,7 @@ Implementing your own resolver: // Configure the listener resolver only before instantiating the EntityManager $configurations->setEntityListenerResolver(new MyEntityListenerResolver); - EntityManager::create(.., $configurations, ..); + $entityManager = new EntityManager(.., $configurations, ..); .. _reference-events-load-class-metadata: @@ -949,7 +989,7 @@ Load ClassMetadata Event ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the mapping metadata for a class has been loaded from a mapping source -(annotations/xml/yaml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. +(attributes/annotations/xml/yaml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. You can hook in to this process and manipulate the instance. This event is not a lifecycle callback. diff --git a/docs/en/reference/faq.rst b/docs/en/reference/faq.rst index 06edf9458b9..249761f400a 100644 --- a/docs/en/reference/faq.rst +++ b/docs/en/reference/faq.rst @@ -13,10 +13,11 @@ Database Schema How do I set the charset and collation for MySQL tables? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can't set these values inside the annotations, yml or xml mapping files. To make a database -work with the default charset and collation you should configure MySQL to use it as default charset, -or create the database with charset and collation details. This way they get inherited to all newly -created database tables and columns. +You can't set these values with attributes, annotations or inside yml or +xml mapping files. To make a database work with the default charset and +collation you should configure MySQL to use it as default charset, or +create the database with charset and collation details. This way they +get inherited to all newly created database tables and columns. Entity Classes -------------- diff --git a/docs/en/reference/inheritance-mapping.rst b/docs/en/reference/inheritance-mapping.rst index 611f202172d..4d5b1c0e173 100644 --- a/docs/en/reference/inheritance-mapping.rst +++ b/docs/en/reference/inheritance-mapping.rst @@ -97,7 +97,7 @@ Example: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + protected Collection $groups; + + /** + * @ManyToOne(targetEntity="Address") + * @JoinColumn(name="address_id", referencedColumnName="id") + */ + protected Address|null $address = null; + } + + // admin mapping + namespace MyProject\Model; + /** + * @Entity + * @AssociationOverrides({ + * @AssociationOverride(name="groups", + * joinTable=@JoinTable( + * name="users_admingroups", + * joinColumns=@JoinColumn(name="adminuser_id"), + * inverseJoinColumns=@JoinColumn(name="admingroup_id") + * ) + * ), + * @AssociationOverride(name="address", + * joinColumns=@JoinColumn( + * name="adminaddress_id", referencedColumnName="id" + * ) + * ) + * }) + */ + class Admin extends User + { + } + .. code-block:: xml @@ -417,7 +493,7 @@ Could be used by an entity that extends a mapped superclass to override a field .. configuration-block:: - .. code-block:: php + .. code-block:: attribute diff --git a/docs/en/reference/metadata-drivers.rst b/docs/en/reference/metadata-drivers.rst index a64eb5b1e3d..8d77bffca0d 100644 --- a/docs/en/reference/metadata-drivers.rst +++ b/docs/en/reference/metadata-drivers.rst @@ -13,10 +13,16 @@ metadata: - **XML files** (XmlDriver) -- **Class DocBlock Annotations** (AnnotationDriver) - **Attributes** (AttributeDriver) - **PHP Code in files or static functions** (PhpDriver) +There are also two deprecated ways to do this: + +- **Class DocBlock Annotations** (AnnotationDriver) +- **YAML files** (YamlDriver) + +They will be removed in 3.0, make sure to avoid them. + Something important to note about the above drivers is they are all an intermediate step to the same end result. The mapping information is populated to ``Doctrine\ORM\Mapping\ClassMetadata`` @@ -39,8 +45,9 @@ an entity. If you want to use one of the included core metadata drivers you need to -configure it. If you pick the annotation driver, you will additionally -need to install ``doctrine/annotations``. All the drivers are in the +configure it. If you pick the annotation driver despite it being +deprecated, you will additionally need to install +``doctrine/annotations``. All the drivers are in the ``Doctrine\ORM\Mapping\Driver`` namespace: .. code-block:: php @@ -119,17 +126,17 @@ the ``FileDriver`` implementation for you to extend from: * {@inheritdoc} */ protected $_fileExtension = '.dcm.ext'; - + /** * {@inheritdoc} */ public function loadMetadataForClass($className, ClassMetadata $metadata) { $data = $this->_loadMappingFile($file); - + // populate ClassMetadata instance from $data } - + /** * {@inheritdoc} */ diff --git a/docs/en/reference/namingstrategy.rst b/docs/en/reference/namingstrategy.rst index 3379448b0fc..9ea3e772f33 100644 --- a/docs/en/reference/namingstrategy.rst +++ b/docs/en/reference/namingstrategy.rst @@ -7,7 +7,7 @@ reduce the verbosity of the mapping document, eliminating repetitive noise (eg: .. warning - The naming strategy is always overridden by entity mapping such as the `Table` annotation. + The naming strategy is always overridden by entity mapping such as the `Table` attribute. Configuring a naming strategy ----------------------------- diff --git a/docs/en/reference/php-mapping.rst b/docs/en/reference/php-mapping.rst index fbfaf2aaf55..d6718af864b 100644 --- a/docs/en/reference/php-mapping.rst +++ b/docs/en/reference/php-mapping.rst @@ -13,7 +13,8 @@ programatically specify your mapping information inside of a static function defined on the entity class itself. This is useful for cases where you want to keep your entity and mapping -information together but don't want to use annotations. For this you just need +information together but don't want to use attributes or +annotations. For this you just need to use the ``StaticPHPDriver``: .. code-block:: php diff --git a/docs/en/reference/second-level-cache.rst b/docs/en/reference/second-level-cache.rst index 9da8dc3f544..e4e576f39a6 100644 --- a/docs/en/reference/second-level-cache.rst +++ b/docs/en/reference/second-level-cache.rst @@ -277,7 +277,7 @@ level cache region. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -316,7 +340,7 @@ It caches the primary keys of association and cache each element will be cached .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + protected Collection $cities; + + // other properties and methods + } + .. code-block:: xml diff --git a/docs/en/reference/transactions-and-concurrency.rst b/docs/en/reference/transactions-and-concurrency.rst index ebbb98d82ef..0f17cf230c4 100644 --- a/docs/en/reference/transactions-and-concurrency.rst +++ b/docs/en/reference/transactions-and-concurrency.rst @@ -189,7 +189,7 @@ example we'll use an integer. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -213,7 +224,7 @@ timestamp or datetime): .. configuration-block:: - .. code-block:: php + .. code-block:: attribute diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index fb1b465b2ac..b765419b1e8 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -137,7 +137,6 @@ relations of the ``User``: return $this->commentsRead; } - /** @param Collection $c */ public function setFirstComment(Comment $c): void { $this->firstComment = $c; } diff --git a/docs/en/reference/working-with-objects.rst b/docs/en/reference/working-with-objects.rst index 5d45261b1df..573f62cd500 100644 --- a/docs/en/reference/working-with-objects.rst +++ b/docs/en/reference/working-with-objects.rst @@ -834,7 +834,7 @@ By default the EntityManager returns a default implementation of ``Doctrine\ORM\EntityRepository`` when you call ``EntityManager#getRepository($entityClass)``. You can overwrite this behaviour by specifying the class name of your own Entity -Repository in the Annotation or XML metadata. In large +Repository in the Attribute, Annotation or XML metadata. In large applications that require lots of specialized DQL queries using a custom repository is one recommended way of grouping these queries in a central location. diff --git a/docs/en/tutorials/composite-primary-keys.rst b/docs/en/tutorials/composite-primary-keys.rst index 9bca21a123f..ead3bab07d6 100644 --- a/docs/en/tutorials/composite-primary-keys.rst +++ b/docs/en/tutorials/composite-primary-keys.rst @@ -50,6 +50,59 @@ and year of production as primary keys: } } + .. code-block:: annotation + + name = $name; + $this->year = $year; + } + + public function getModelName(): string + { + return $this->name; + } + + public function getYearOfProduction(): int + { + return $this->year; + } + } + + .. code-block:: annotation + + @@ -116,7 +169,7 @@ of one or many parent entities. The semantics of mapping identity through foreign entities are easy: - Only allowed on Many-To-One or One-To-One associations. -- Plug an ``@Id`` annotation onto every association. +- Plug an ``#[Id]`` attribute onto every association. - Set an attribute ``association-key`` with the field name of the association in XML. Use-Case 1: Dynamic Attributes @@ -133,6 +186,57 @@ We keep up the example of an Article with arbitrary attributes, the mapping look use Doctrine\Common\Collections\ArrayCollection; + /** + * @Entity + */ + class Article + { + /** @Id @Column(type="integer") @GeneratedValue */ + private int|null $id = null; + /** @Column(type="string") */ + private string $title; + + /** + * @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute") + * @var Collection + */ + private Collection $attributes; + + public function addAttribute($name, $value): void + { + $this->attributes[$name] = new ArticleAttribute($name, $value, $this); + } + } + + /** + * @Entity + */ + class ArticleAttribute + { + /** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */ + private Article|null $article; + + /** @Id @Column(type="string") */ + private string $attribute; + + /** @Column(type="string") */ + private string $value; + + public function __construct($name, $value, $article) + { + $this->attribute = $name; + $this->value = $value; + $this->article = $article; + } + } + + .. code-block:: attribute + + @@ -93,7 +120,7 @@ The following example shows you how to set your prefix to ``myPrefix_``: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -115,7 +153,7 @@ directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML): .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + public Collection $users; + } + .. code-block:: xml diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index a3b2bdcf8fb..9067fd1f480 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -83,7 +83,6 @@ that directory with the following contents: "require": { "doctrine/orm": "^2.11.0", "doctrine/dbal": "^3.2", - "doctrine/annotations": "1.13.2", "symfony/cache": "^5.4" }, "autoload": { @@ -132,32 +131,35 @@ step: 'pdo_sqlite', 'path' => __DIR__ . '/db.sqlite', - ); + ], $config) // obtaining the entity manager - $entityManager = EntityManager::create($conn, $config); - -.. note:: - It is recommended not to use the SimpleAnnotationReader because its - usage will be removed for version 3.0. + $entityManager = new EntityManager($connection, $config); The ``require_once`` statement sets up the class autoloading for Doctrine and its dependencies using Composer's autoloader. @@ -484,14 +486,14 @@ the ``Product`` entity to Doctrine using a metadata language. The metadata language describes how entities, their properties and references should be persisted and what constraints should be applied to them. -Metadata for an Entity can be configured using DocBlock annotations directly -in the Entity class itself, or in an external XML file. This Getting -Started guide will demonstrate metadata mappings using all three methods, -but you only need to choose one. +Metadata for an Entity can be configured using attributes directly in +the Entity class itself, or in an external XML file. This +Getting Started guide will demonstrate metadata mappings using all three +methods, but you only need to choose one. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -994,7 +1023,7 @@ Lets add metadata mappings for the ``Bug`` entity, as we did for the ``Product`` before: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -1083,7 +1165,7 @@ Finally, we'll add metadata mappings for the ``User`` entity. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute An ArrayCollection of Bug objects. + */ + private Collection $reportedBugs; + + /** + * @ORM\OneToMany(targetEntity="Bug", mappedBy="engineer") + * @var Collection An ArrayCollection of Bug objects. + */ + private Collection $assignedBugs; + + // .. (other code) + } .. code-block:: xml @@ -1616,7 +1739,20 @@ we have to adjust the metadata slightly. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + + + */ + private Collection $groups; + } + .. code-block:: xml @@ -43,7 +59,7 @@ The DQL Snippet in OrderBy is only allowed to consist of unqualified, unquoted field names and of an optional ASC/DESC positional statement. Multiple Fields are separated by a comma (,). The referenced field names have to exist on the ``targetEntity`` -class of the ``@ManyToMany`` or ``@OneToMany`` annotation. +class of the ``#[ManyToMany]`` or ``#[OneToMany]`` annotation. The semantics of this feature can be described as follows: diff --git a/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst b/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst index b69a7aa700e..8d36b30ef66 100644 --- a/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst +++ b/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst @@ -50,7 +50,7 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example The docblock is showing metadata override of the attribute and association type. It basically changes the names of the columns mapped for a property ``foo`` and for the association ``bar`` which relates to Bar class shown above. Here is the trait -which has mapping metadata that is overridden by the annotation above: +which has mapping metadata that is overridden by the attribute above: .. code-block:: php diff --git a/docs/en/tutorials/working-with-indexed-associations.rst b/docs/en/tutorials/working-with-indexed-associations.rst index bc3c9e4f1f3..46ecdbab136 100644 --- a/docs/en/tutorials/working-with-indexed-associations.rst +++ b/docs/en/tutorials/working-with-indexed-associations.rst @@ -23,13 +23,14 @@ Mapping Indexed Associations You can map indexed associations by adding: + * ``indexBy`` argument to any ``#[OneToMany]`` or ``#[ManyToMany]`` attribute. * ``indexBy`` attribute to any ``@OneToMany`` or ``@ManyToMany`` annotation. * ``index-by`` attribute to any ```` or ```` xml element. The code and mappings for the Market entity looks like this: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + */ + private Collection $stocks; + + public function __construct($name) + { + $this->name = $name; + $this->stocks = new ArrayCollection(); + } + + public function getId(): int|null + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function addStock(Stock $stock): void + { + $this->stocks[$stock->getSymbol()] = $stock; + } + + public function getStock($symbol): Stock + { + if (!isset($this->stocks[$symbol])) { + throw new \InvalidArgumentException("Symbol is not traded on this market."); + } + + return $this->stocks[$symbol]; + } + + /** @return array */ + public function getStocks(): array + { + return $this->stocks->toArray(); + } + } + .. code-block:: xml @@ -115,7 +184,7 @@ The ``Stock`` entity doesn't contain any special instructions that are new, but here are the code and mappings for it: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute symbol = $symbol; + $this->market = $market; + $market->addStock($this); + } + + public function getSymbol(): string + { + return $this->symbol; + } + } + .. code-block:: xml diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 5621dd697d6..5229c42bb98 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -11,6 +11,7 @@ use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\LockMode; +use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Exception\EntityManagerClosed; use Doctrine\ORM\Exception\InvalidHydrationMode; use Doctrine\ORM\Exception\ManagerException; @@ -43,14 +44,14 @@ * the static create() method. The quickest way to obtain a fully * configured EntityManager is: * - * use Doctrine\ORM\Tools\Setup; + * use Doctrine\ORM\Tools\ORMSetup; * use Doctrine\ORM\EntityManager; * - * $paths = array('/path/to/entity/mapping/files'); + * $paths = ['/path/to/entity/mapping/files']; * - * $config = Setup::createAnnotationMetadataConfiguration($paths); - * $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true); - * $entityManager = EntityManager::create($dbParams, $config); + * $config = ORMSetup::createAttributeMetadataConfiguration($paths); + * $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true], $config); + * $entityManager = new EntityManager($connection, $config); * * For more information see * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/configuration.html} @@ -123,7 +124,7 @@ class EntityManager implements EntityManagerInterface * Creates a new EntityManager that operates on the given database connection * and uses the given Configuration and EventManager implementations. */ - public function __construct(Connection $conn, Configuration $config) + public function __construct(Connection $conn, Configuration $config, EventManager|null $eventManager = null) { if (! $config->getMetadataDriverImpl()) { throw MissingMappingDriverImplementation::create(); @@ -131,7 +132,7 @@ public function __construct(Connection $conn, Configuration $config) $this->conn = $conn; $this->config = $config; - $this->eventManager = $conn->getEventManager(); + $this->eventManager = $eventManager ?? $conn->getEventManager(); $metadataFactoryClassName = $config->getClassMetadataFactoryName(); @@ -635,6 +636,8 @@ public function initializeObject(object $obj): void /** * Factory method to create EntityManager instances. * + * @deprecated Use {@see DriverManager::getConnection()} to bootstrap the connection and call the constructor. + * * @param mixed[]|Connection $connection An array with the connection parameters or an existing Connection instance. * @psalm-param array|Connection $connection * @@ -643,6 +646,15 @@ public function initializeObject(object $obj): void */ public static function create(array|Connection $connection, Configuration $config, EventManager|null $eventManager = null): EntityManager { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/9961', + '%s() is deprecated. To boostrap a DBAL connection, call %s::getConnection() instead. Use the constructor to create an instance of %s.', + __METHOD__, + DriverManager::class, + self::class, + ); + $connection = static::createConnection($connection, $config, $eventManager); return new EntityManager($connection, $config); @@ -651,6 +663,8 @@ public static function create(array|Connection $connection, Configuration $confi /** * Factory method to create Connection instances. * + * @deprecated Use {@see DriverManager::getConnection()} to bootstrap the connection. + * * @param mixed[]|Connection $connection An array with the connection parameters or an existing Connection instance. * @psalm-param array|Connection $connection * @@ -659,6 +673,14 @@ public static function create(array|Connection $connection, Configuration $confi */ protected static function createConnection(array|Connection $connection, Configuration $config, EventManager|null $eventManager = null): Connection { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/9961', + '%s() is deprecated, call %s::getConnection() instead.', + __METHOD__, + DriverManager::class, + ); + if (is_array($connection)) { return DriverManager::getConnection($connection, $config, $eventManager ?? new EventManager()); } diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 5788832f988..01086c818dc 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -63,7 +63,7 @@ public function __construct($reader, $paths = null) { Deprecation::trigger( 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/10096', + 'https://github.com/doctrine/orm/issues/10098', 'The annotation mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to the attribute or XML driver.', ); $this->reader = $reader; diff --git a/lib/Doctrine/ORM/ORMSetup.php b/lib/Doctrine/ORM/ORMSetup.php index 1800d920ab1..ac19e8ef606 100644 --- a/lib/Doctrine/ORM/ORMSetup.php +++ b/lib/Doctrine/ORM/ORMSetup.php @@ -6,6 +6,7 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\PsrCachedReader; +use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\Driver\XmlDriver; @@ -29,6 +30,8 @@ final class ORMSetup /** * Creates a configuration with an annotation metadata driver. * + * @deprecated Use another mapping driver. + * * @param string[] $paths */ public static function createAnnotationMetadataConfiguration( @@ -37,6 +40,12 @@ public static function createAnnotationMetadataConfiguration( string|null $proxyDir = null, CacheItemPoolInterface|null $cache = null, ): Configuration { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/issues/10098', + '%s is deprecated and will be removed in Doctrine ORM 3.0', + __METHOD__, + ); $config = self::createConfiguration($isDevMode, $proxyDir, $cache); $config->setMetadataDriverImpl(self::createDefaultAnnotationDriver($paths)); @@ -46,12 +55,20 @@ public static function createAnnotationMetadataConfiguration( /** * Adds a new default annotation driver with a correctly configured annotation reader. * + * @deprecated Use another mapping driver. + * * @param string[] $paths */ public static function createDefaultAnnotationDriver( array $paths = [], CacheItemPoolInterface|null $cache = null, ): AnnotationDriver { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/issues/10098', + '%s is deprecated and will be removed in Doctrine ORM 3.0', + __METHOD__, + ); if (! class_exists(AnnotationReader::class)) { throw new LogicException(sprintf( 'The annotation metadata driver cannot be enabled because the "doctrine/annotations" library' diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php index c0fc8434498..b4c6efab337 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php @@ -58,7 +58,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $ownerClass = $input->getArgument('owner-class'); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php index 758f68a53ed..c5f2d659eaf 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php @@ -57,7 +57,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $entityClass = $input->getArgument('entity-class'); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php index 4ba30e93a07..147795b229f 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -31,7 +31,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $cacheDriver = $em->getConfiguration()->getMetadataCache(); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php index ad3367b2202..83edd7a2619 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php @@ -30,7 +30,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $cache = $em->getConfiguration()->getQueryCache(); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php index 06ab676138a..e80fb906b62 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php @@ -56,7 +56,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $name = $input->getArgument('region-name'); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php index 2a6d845917a..4f84e0bd140 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -45,7 +45,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $cache = $em->getConfiguration()->getResultCache(); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php index 65cb9a7f55c..5a407de6ce6 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -39,7 +39,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php index 743d540b955..deebb58a7b6 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php @@ -34,7 +34,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $entityManager = $this->getEntityManager($input); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php index ee1e851b972..3afe57997d8 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php @@ -62,7 +62,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $entityManager = $this->getEntityManager($input); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php index b6da9cd1af9..b1e44606ec9 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -29,7 +29,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $metadatas = $em->getMetadataFactory()->getAllMetadata(); if (empty($metadatas)) { - $ui->success('No Metadata Classes to process.'); + $ui->getErrorStyle()->success('No Metadata Classes to process.'); return 0; } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php index b90fc92b344..d9cf440bce5 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -53,14 +53,16 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ return 0; } - $ui->caution('This operation should not be executed in a production environment!'); + $notificationUi = $ui->getErrorStyle(); - $ui->text('Creating database schema...'); - $ui->newLine(); + $notificationUi->caution('This operation should not be executed in a production environment!'); + + $notificationUi->text('Creating database schema...'); + $notificationUi->newLine(); $schemaTool->createSchema($metadatas); - $ui->success('Database schema created successfully!'); + $notificationUi->success('Database schema created successfully!'); return 0; } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index 2c4d9cdc494..69434b97ba5 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -63,9 +63,11 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ return 0; } + $notificationUi = $ui->getErrorStyle(); + if ($force) { - $ui->text('Dropping database schema...'); - $ui->newLine(); + $notificationUi->text('Dropping database schema...'); + $notificationUi->newLine(); if ($isFullDatabaseDrop) { $schemaTool->dropDatabase(); @@ -73,12 +75,12 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ $schemaTool->dropSchema($metadatas); } - $ui->success('Database schema dropped successfully!'); + $notificationUi->success('Database schema dropped successfully!'); return 0; } - $ui->caution('This operation should not be executed in a production environment!'); + $notificationUi->caution('This operation should not be executed in a production environment!'); if ($isFullDatabaseDrop) { $sqls = $schemaTool->getDropDatabaseSQL(); @@ -87,12 +89,12 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ } if (empty($sqls)) { - $ui->success('Nothing to drop. The database is empty!'); + $notificationUi->success('Nothing to drop. The database is empty!'); return 0; } - $ui->text( + $notificationUi->text( [ sprintf('The Schema-Tool would execute "%s" queries to update the database.', count($sqls)), '', diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 746294bab63..e458df56fdd 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -54,7 +54,7 @@ protected function configure(): void task will drop all database assets (e.g. tables, etc) that are *not* described by the current metadata. In other words, without this option, this task leaves untouched any "extra" tables that exist in the database, but which aren't -described by any metadata. +described by any metadata. Not passing that option is deprecated. Hint: If you have a database with tables that should not be managed by the ORM, you can use a DBAL functionality to filter the tables and sequences down @@ -69,13 +69,19 @@ protected function configure(): void */ protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int { + $notificationUi = $ui->getErrorStyle(); + // Defining if update is complete or not (--complete not defined means $saveMode = true) $saveMode = ! $input->getOption('complete'); + if ($saveMode) { + $notificationUi->warning('Not passing the "--complete" option to "orm:schema-tool:update" is deprecated and will not be supported when using doctrine/dbal 4'); + } + $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); if (empty($sqls)) { - $ui->success('Nothing to update - your database is already in sync with the current entity metadata.'); + $notificationUi->success('Nothing to update - your database is already in sync with the current entity metadata.'); return 0; } @@ -91,25 +97,25 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ if ($force) { if ($dumpSql) { - $ui->newLine(); + $notificationUi->newLine(); } - $ui->text('Updating database schema...'); - $ui->newLine(); + $notificationUi->text('Updating database schema...'); + $notificationUi->newLine(); $schemaTool->updateSchema($metadatas, $saveMode); $pluralization = count($sqls) === 1 ? 'query was' : 'queries were'; - $ui->text(sprintf(' %s %s executed', count($sqls), $pluralization)); - $ui->success('Database schema updated successfully!'); + $notificationUi->text(sprintf(' %s %s executed', count($sqls), $pluralization)); + $notificationUi->success('Database schema updated successfully!'); } if ($dumpSql || $force) { return 0; } - $ui->caution( + $notificationUi->caution( [ 'This operation should not be executed in a production environment!', '', @@ -118,7 +124,7 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ ], ); - $ui->text( + $notificationUi->text( [ sprintf('The Schema-Tool would execute "%s" queries to update the database.', count($sqls)), '', diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php index 7cddcad6101..0df11609bfd 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -32,7 +32,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $validator = new SchemaValidator($em); diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 6ccd3f5caa8..b4b8bb20ca4 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -12,6 +12,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets; +use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\MappingException; @@ -31,6 +32,7 @@ use function class_exists; use function count; use function current; +use function func_num_args; use function implode; use function in_array; use function is_array; @@ -856,10 +858,7 @@ public function getDropSchemaSQL(array $classes): array { $schema = $this->getSchemaFromMetadata($classes); - $method = method_exists(AbstractSchemaManager::class, 'introspectSchema') ? - 'introspectSchema' : - 'createSchema'; - $deployedSchema = $this->schemaManager->$method(); + $deployedSchema = $this->introspectSchema(); foreach ($schema->getTables() as $table) { if (! $deployedSchema->hasTable($table->getName())) { @@ -903,6 +902,15 @@ public function getDropSchemaSQL(array $classes): array */ public function updateSchema(array $classes, bool $saveMode = false): void { + if (func_num_args() > 1) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/10153', + 'Passing $saveMode to %s() is deprecated and will not be possible in Doctrine ORM 3.0.', + __METHOD__, + ); + } + $updateSchemaSql = $this->getUpdateSchemaSql($classes, $saveMode); $conn = $this->em->getConnection(); @@ -923,6 +931,15 @@ public function updateSchema(array $classes, bool $saveMode = false): void */ public function getUpdateSchemaSql(array $classes, bool $saveMode = false): array { + if (func_num_args() > 1) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/10153', + 'Passing $saveMode to %s() is deprecated and will not be possible in Doctrine ORM 3.0.', + __METHOD__, + ); + } + $toSchema = $this->getSchemaFromMetadata($classes); $fromSchema = $this->createSchemaForComparison($toSchema); $comparator = $this->schemaManager->createComparator(); @@ -932,7 +949,11 @@ public function getUpdateSchemaSql(array $classes, bool $saveMode = false): arra return $schemaDiff->toSaveSql($this->platform); } - return $schemaDiff->toSql($this->platform); + if (! method_exists(AbstractPlatform::class, 'getAlterSchemaSQL')) { + return $schemaDiff->toSql($this->platform); + } + + return $this->platform->getAlterSchemaSQL($schemaDiff); } /** @@ -951,7 +972,7 @@ private function createSchemaForComparison(Schema $toSchema): Schema 'createSchema'; if ($previousFilter === null) { - return $this->schemaManager->$method(); + return $this->introspectSchema(); } // whitelist assets we already know about in $toSchema, use the existing filter otherwise @@ -962,10 +983,19 @@ private function createSchemaForComparison(Schema $toSchema): Schema }); try { - return $this->schemaManager->$method(); + return $this->introspectSchema(); } finally { // restore schema assets filter $config->setSchemaAssetsFilter($previousFilter); } } + + private function introspectSchema(): Schema + { + $method = method_exists($this->schemaManager, 'introspectSchema') + ? 'introspectSchema' + : 'createSchema'; + + return $this->schemaManager->$method(); + } } diff --git a/psalm.xml b/psalm.xml index 0d8b7b55afc..677db491384 100644 --- a/psalm.xml +++ b/psalm.xml @@ -29,7 +29,12 @@ + + + + + diff --git a/tests/Doctrine/Performance/EntityManagerFactory.php b/tests/Doctrine/Performance/EntityManagerFactory.php index b9b283c435a..62710b6573f 100644 --- a/tests/Doctrine/Performance/EntityManagerFactory.php +++ b/tests/Doctrine/Performance/EntityManagerFactory.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\PDO\SQLite\Driver; +use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Result; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; @@ -34,11 +35,11 @@ public static function getEntityManager(array $schemaClassNames): EntityManagerI realpath(__DIR__ . '/Models/GeoNames'), ])); - $entityManager = EntityManager::create( - [ + $entityManager = new EntityManager( + DriverManager::getConnection([ 'driverClass' => Driver::class, 'memory' => true, - ], + ], $config), $config, ); @@ -71,6 +72,6 @@ public function executeQuery(string $sql, array $params = [], $types = [], Query } }; - return EntityManager::create($connection, $config, $connection->getEventManager()); + return new EntityManager($connection, $config); } } diff --git a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php index 7a96b78d6e9..cfeb23a3cef 100644 --- a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php +++ b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php @@ -4,13 +4,17 @@ namespace Doctrine\Tests\Mocks; +use BadMethodCallException; use Doctrine\Common\EventManager; +use Doctrine\DBAL\Connection; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\ORMSetup; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\UnitOfWork; +use function sprintf; + /** * Special EntityManager mock used for testing purposes. */ @@ -19,6 +23,18 @@ class EntityManagerMock extends EntityManager private UnitOfWork|null $_uowMock = null; private ProxyFactory|null $_proxyFactoryMock = null; + public function __construct(Connection $conn, Configuration|null $config = null, EventManager|null $eventManager = null) + { + if ($config === null) { + $config = new Configuration(); + $config->setProxyDir(__DIR__ . '/../Proxies'); + $config->setProxyNamespace('Doctrine\Tests\Proxies'); + $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver()); + } + + parent::__construct($conn, $config, $eventManager); + } + public function getUnitOfWork(): UnitOfWork { return $this->_uowMock ?? parent::getUnitOfWork(); @@ -45,19 +61,10 @@ public function getProxyFactory(): ProxyFactory } /** - * Mock factory method to create an EntityManager. - * * {@inheritdoc} */ - public static function create($conn, Configuration|null $config = null, EventManager|null $eventManager = null): self + public static function create($connection, Configuration|null $config, EventManager|null $eventManager = null): self { - if ($config === null) { - $config = new Configuration(); - $config->setProxyDir(__DIR__ . '/../Proxies'); - $config->setProxyNamespace('Doctrine\Tests\Proxies'); - $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver()); - } - - return new EntityManagerMock($conn, $config); + throw new BadMethodCallException(sprintf('Call to deprecated method %s().', __METHOD__)); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php b/tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php index 3000341ffed..fc277a11aeb 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php +++ b/tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php @@ -123,7 +123,7 @@ protected function createEntityManager(Connection $conn): EntityManagerInterface $config->setQueryCache(new ArrayAdapter()); - return EntityManager::create($conn, $config); + return new EntityManager($conn, $config); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php index 2b9d0b0e5c8..60ef9714dc9 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional\SchemaTool; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Comparator; @@ -76,7 +77,10 @@ public function assertCreatedSchemaNeedsNoUpdates(string ...$classes): void $comparator = $sm->createComparator(); $schemaDiff = $comparator->compareSchemas($fromSchema, $toSchema); - $sql = $schemaDiff->toSql($this->_em->getConnection()->getDatabasePlatform()); + $sql = method_exists(AbstractPlatform::class, 'getAlterSchemaSQL') ? + $this->_em->getConnection()->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff) : + $schemaDiff->toSql($this->_em->getConnection()->getDatabasePlatform()); + $sql = array_filter($sql, static fn ($sql) => ! str_contains($sql, 'DROP')); self::assertCount(0, $sql, 'SQL: ' . implode(PHP_EOL, $sql)); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php index 213670b6e56..11a5eec69e2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7869Test.php @@ -33,7 +33,7 @@ public function testDQLDeferredEagerLoad(): void $connection->method('getEventManager') ->willReturn(new EventManager()); - $em = new class (EntityManagerMock::create($connection)) extends EntityManagerDecorator { + $em = new class (new EntityManagerMock($connection)) extends EntityManagerDecorator { /** @var int */ public $getClassMetadataCalls = 0; diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php index 3327da34047..b5089b31dc0 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -248,7 +248,7 @@ protected function createEntityManager(MappingDriver $metadataDriver, $conn = nu $config->setMetadataDriverImpl($metadataDriver); - return EntityManagerMock::create($conn, $config); + return new EntityManagerMock($conn, $config); } protected function createTestFactory(): ClassMetadataFactoryTestSubject diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php index ecfa8fdcdc0..bd4ad9d1d08 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php @@ -50,7 +50,7 @@ protected function setUp(): void $connection->method('executeQuery') ->willReturn($this->createMock(Result::class)); - $this->_emMock = EntityManagerMock::create($connection); + $this->_emMock = new EntityManagerMock($connection); $this->setUpPersistentCollection(); } diff --git a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php index 4ed24d43111..50e5d070efa 100644 --- a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php @@ -49,7 +49,7 @@ protected function setUp(): void $connection->method('getEventManager') ->willReturn(new EventManager()); - $this->emMock = EntityManagerMock::create($connection); + $this->emMock = new EntityManagerMock($connection); $this->uowMock = new UnitOfWorkMock($this->emMock); $this->emMock->setUnitOfWork($this->uowMock); $this->proxyFactory = new ProxyFactory($this->emMock, sys_get_temp_dir(), 'Proxies', ProxyFactory::AUTOGENERATE_ALWAYS); diff --git a/tests/Doctrine/Tests/ORM/Tools/Console/Command/SchemaTool/UpdateCommandTest.php b/tests/Doctrine/Tests/ORM/Tools/Console/Command/SchemaTool/UpdateCommandTest.php index df94ca4256e..88ea2cf1617 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Console/Command/SchemaTool/UpdateCommandTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Console/Command/SchemaTool/UpdateCommandTest.php @@ -12,7 +12,10 @@ class UpdateCommandTest extends AbstractCommandTest public function testItPrintsTheSql(): void { $tester = $this->getCommandTester(UpdateCommand::class); - $tester->execute(['--dump-sql' => true]); + $tester->execute( + ['--dump-sql' => true], + ['capture_stderr_separately' => true], + ); self::$sharedConn->executeStatement($tester->getDisplay()); } diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index ea14100ccbf..340d366ef84 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -89,7 +89,7 @@ protected function setUp(): void $this->eventManager = $this->getMockBuilder(EventManager::class)->getMock(); $this->connection = new Connection([], $driver, null, $this->eventManager); - $this->_emMock = EntityManagerMock::create($this->connection); + $this->_emMock = new EntityManagerMock($this->connection); // SUT $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); $this->_emMock->setUnitOfWork($this->_unitOfWork); @@ -600,7 +600,7 @@ public function testCommitThrowOptimisticLockExceptionWhenConnectionCommitFails( ->onlyMethods(['commit']) ->setConstructorArgs([[], $driver]) ->getMock(); - $this->_emMock = EntityManagerMock::create($this->connection); + $this->_emMock = new EntityManagerMock($this->connection); $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); $this->_emMock->setUnitOfWork($this->_unitOfWork); diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 195acbbecaf..e8817d1827a 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -966,7 +966,7 @@ protected function getEntityManager( $evm->addEventListener(['onFlush'], new DebugUnitOfWorkListener()); } - return EntityManager::create($conn, $config); + return new EntityManager($conn, $config); } final protected function createSchemaManager(): AbstractSchemaManager diff --git a/tests/Doctrine/Tests/OrmTestCase.php b/tests/Doctrine/Tests/OrmTestCase.php index a4bebfdebc1..7cb9de1aefb 100644 --- a/tests/Doctrine/Tests/OrmTestCase.php +++ b/tests/Doctrine/Tests/OrmTestCase.php @@ -119,7 +119,7 @@ private function buildTestEntityManagerWithPlatform(Connection $connection): Ent $config->setSecondLevelCacheConfiguration($cacheConfig); } - return EntityManagerMock::create($connection, $config); + return new EntityManagerMock($connection, $config); } protected function enableSecondLevelCache(bool $log = true): void