From 06eafd82ac37a7e6164488b8ea05d064516969e3 Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Wed, 19 Oct 2022 13:36:12 +0200 Subject: [PATCH 01/14] Do not export phpstan stuff (#10154) --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) 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 From d79e61f8d9da630dabdb58069419558e53e3e6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Fri, 21 Oct 2022 12:21:20 +0200 Subject: [PATCH 02/14] Remove wrong sentence --- docs/en/cookbook/validation-of-entities.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/en/cookbook/validation-of-entities.rst b/docs/en/cookbook/validation-of-entities.rst index aa053908519..e297a8ab3b0 100644 --- a/docs/en/cookbook/validation-of-entities.rst +++ b/docs/en/cookbook/validation-of-entities.rst @@ -83,9 +83,6 @@ In XML Mappings: -YAML needs some little change yet, to allow multiple lifecycle -events for one method, this will happen before Beta 1 though. - Now validation is performed whenever you call ``EntityManager#persist($order)`` or when you call ``EntityManager#flush()`` and an order is about to be updated. Any From 4e445feb6c7a1baaf9ab34d67b86997ffca7a069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Wed, 19 Oct 2022 15:42:40 +0200 Subject: [PATCH 03/14] Migrate more documentation towards attributes The previous rework missed some details / left the documentation in an inconsistent state. Searching for "annotation" case insensitively allows to find discrepancies and forgotten things. --- ...-conversion-using-custom-mapping-types.rst | 71 +--- ...nting-the-notify-changetracking-policy.rst | 12 +- .../strategy-cookbook-introduction.rst | 6 +- docs/en/cookbook/validation-of-entities.rst | 28 +- docs/en/reference/advanced-configuration.rst | 12 +- docs/en/reference/association-mapping.rst | 343 +++++++++++++++++- docs/en/reference/basic-mapping.rst | 61 +++- docs/en/reference/caching.rst | 7 +- docs/en/reference/configuration.rst | 6 +- docs/en/reference/events.rst | 55 ++- docs/en/reference/faq.rst | 9 +- docs/en/reference/inheritance-mapping.rst | 127 ++++++- docs/en/reference/namingstrategy.rst | 2 +- docs/en/reference/php-mapping.rst | 6 +- docs/en/reference/second-level-cache.rst | 66 +++- .../transactions-and-concurrency.rst | 26 +- .../reference/working-with-associations.rst | 1 - docs/en/reference/working-with-objects.rst | 2 +- docs/en/tutorials/composite-primary-keys.rst | 108 +++++- docs/en/tutorials/embeddables.rst | 57 ++- docs/en/tutorials/extra-lazy-associations.rst | 19 +- docs/en/tutorials/getting-started.rst | 182 ++++++++-- docs/en/tutorials/ordered-associations.rst | 24 +- ...eld-association-mappings-in-subclasses.rst | 2 +- .../working-with-indexed-associations.rst | 114 +++++- 25 files changed, 1167 insertions(+), 179 deletions(-) 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 ab9ae72c391..040b0c078ff 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; } diff --git a/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst b/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst index f2a8f19c196..0fa27372b9e 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); @@ -73,5 +73,3 @@ The check whether the new value is different from the old one is not mandatory but recommended. That way you can avoid unnecessary updates and also have full control over when you consider a property changed. - - diff --git a/docs/en/cookbook/strategy-cookbook-introduction.rst b/docs/en/cookbook/strategy-cookbook-introduction.rst index 600feb1abac..b7614a38715 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 - * yaml or xml files correctly + * This is a doctrine field, so make sure that you use a + #[Column] attribute or setup your yaml or xml files correctly * @var string */ protected $strategyClassName; @@ -251,5 +251,3 @@ This might look like this: In this example, even some variables are set - like a view object or a specific configuration object. - - 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 e3a76f7e303..943046995fd 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'); @@ -124,18 +125,17 @@ used in the examples. For information on the usage of the AnnotationDriver, XmlDriver or YamlDriver please refer to the dedicated chapters ``Annotation Reference``, ``XML Mapping`` and ``YAML 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 @@ -152,7 +152,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, xml or yaml, 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 diff --git a/docs/en/reference/association-mapping.rst b/docs/en/reference/association-mapping.rst index 8d4b06762e5..0f323de21d7 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 @@ -83,8 +104,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: @@ -111,7 +132,7 @@ references one ``Shipment`` entity. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -186,7 +231,7 @@ object. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -318,7 +395,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 @@ -412,7 +524,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 @@ -514,7 +659,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 @@ -584,7 +756,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 @@ -685,7 +889,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 @@ -903,12 +1148,18 @@ As an example, consider this mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -930,7 +1181,7 @@ mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -964,17 +1225,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 @@ -995,7 +1271,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 @@ -1060,12 +1355,18 @@ attribute on ``JoinColumn`` will be inherited from PHP type. So that: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -1085,7 +1386,17 @@ Is essentially the same as following: .. configuration-block:: - .. code-block:: php + .. code-block:: annotation + + ` - :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:: @@ -157,10 +158,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:: @@ -360,8 +361,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:: @@ -377,6 +377,20 @@ annotation. // ... } + .. code-block:: annotation + + @@ -400,7 +414,7 @@ annotation. fields: # fields here -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. @@ -436,8 +450,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 @@ -461,6 +475,20 @@ besides specifying the sequence's name: // ... } + .. code-block:: annotation + + @@ -522,11 +550,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 ed13e1029cc..19b30b3d837 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 -YAML, XML, Annotations, etc. Instead of parsing this information on -each request we should cache it using one of the cache drivers. +YAML, 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. @@ -199,5 +200,3 @@ not letting your users' requests populate the cache. You can read more about cache slams `in this blog post `_. - - diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index 47b0366a963..57bfd154924 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -55,7 +55,7 @@ access point to ORM functionality provided by Doctrine. 'dbname' => 'foo', ); - $config = ORMSetup::createAnnotationMetadataConfiguration($paths, $isDevMode); + $config = ORMSetup::createAttributeMetadataConfiguration($paths, $isDevMode); $entityManager = EntityManager::create($dbParams, $config); .. note:: @@ -83,9 +83,9 @@ Or if you prefer YAML: .. note:: If you want to use yml mapping you should add yaml dependency to your `composer.json`: - + :: - + "symfony/yaml": "*" Inside the ``ORMSetup`` methods several assumptions are made: diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index 77511d053dc..9958766e777 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -827,7 +827,41 @@ you need to map the listener method using the event type mapping: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + + @@ -980,7 +1015,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 183769a9b02..e19229320ad 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 @@ -477,7 +553,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/namingstrategy.rst b/docs/en/reference/namingstrategy.rst index c48f84f443d..53d75465c2b 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 2c9e425f073..2721d6103d0 100644 --- a/docs/en/reference/php-mapping.rst +++ b/docs/en/reference/php-mapping.rst @@ -90,8 +90,8 @@ Static Function In addition to the PHP files you can also 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 to use the ``StaticPHPDriver``: +and mapping information together but don't want to use attributes or +annotations. For this you just need to use the ``StaticPHPDriver``: .. code-block:: php @@ -325,5 +325,3 @@ entities themselves. - ``setIdentifierValues($entity, $id)`` - ``setFieldValue($entity, $field, $value)`` - ``getFieldValue($entity, $field)`` - - diff --git a/docs/en/reference/second-level-cache.rst b/docs/en/reference/second-level-cache.rst index a589fec7958..e5bb2c2b610 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 @@ -334,7 +358,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 e57d3d84842..ccd91db6c3f 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 @@ -222,7 +233,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 ec3e52e7683..5bc272f4664 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, XML or YAML metadata. In large +Repository in the Attribute, Annotation, XML or YAML 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 c2b48e290d8..cc9a98130fd 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 + + @@ -126,7 +179,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. - Set a key ``associationKey:`` with the field name of the association in YAML. @@ -144,6 +197,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 + + @@ -109,7 +136,7 @@ The following example shows you how to set your prefix to ``myPrefix_``: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -140,7 +178,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 5a317337c5d..94849afccf3 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/yaml": "^5.4", "symfony/cache": "^5.4" }, @@ -143,14 +142,23 @@ step: require_once "vendor/autoload.php"; // Create a simple "default" Doctrine ORM configuration for Annotations - $isDevMode = true; - $proxyDir = null; - $cache = null; - $useSimpleAnnotationReader = false; - $config = ORMSetup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode, $proxyDir, $cache, $useSimpleAnnotationReader); - // or if you prefer YAML or XML - // $config = ORMSetup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode); - // $config = ORMSetup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode); + $config = ORMSetup::createAttributeMetadataConfiguration( + paths: array(__DIR__."/src"), + isDevMode: true, + ); + // or if you prefer annotation, YAML or XML + // $config = ORMSetup::createAnnotationMetadataConfiguration( + // paths: array(__DIR__."/src"), + // isDevMode: true, + // ); + // $config = ORMSetup::createXMLMetadataConfiguration( + // paths: array(__DIR__."/config/xml"), + // isDevMode: true, + //); + // $config = ORMSetup::createYAMLMetadataConfiguration( + // paths: array(__DIR__."/config/yaml"), + // isDevMode: true, + // ); // database configuration parameters $conn = array( @@ -165,10 +173,6 @@ step: The YAML driver is deprecated and will be removed in version 3.0. It is strongly recommended to switch to one of the other mappings. -.. note:: - It is recommended not to use the SimpleAnnotationReader because its - usage will be removed for version 3.0. - The ``require_once`` statement sets up the class autoloading for Doctrine and its dependencies using Composer's autoloader. @@ -494,14 +498,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 or YAML 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 or YAML 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 @@ -1023,7 +1054,7 @@ Lets add metadata mappings for the ``Bug`` entity, as we did for the ``Product`` before: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute @@ -1146,7 +1230,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 @@ -1705,7 +1830,20 @@ we have to adjust the metadata slightly. .. configuration-block:: - .. code-block:: php + .. code-block:: attribute + + + */ + private Collection $groups; + } + .. code-block:: xml @@ -60,7 +76,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 5a2edc866e1..0488c25e464 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 e41738737b3..b09da398249 100644 --- a/docs/en/tutorials/working-with-indexed-associations.rst +++ b/docs/en/tutorials/working-with-indexed-associations.rst @@ -23,6 +23,7 @@ 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. * ``indexBy:`` key-value pair to any association defined in ``manyToMany:`` or ``oneToMany:`` YAML mapping files. @@ -30,7 +31,7 @@ You can map indexed associations by adding: 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 @@ -134,7 +203,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 From ad1d1ca942a4a187618059c988e3930c650f3302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 22 Oct 2022 14:54:01 +0200 Subject: [PATCH 04/14] Remove trailing whitespace --- docs/en/reference/metadata-drivers.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/en/reference/metadata-drivers.rst b/docs/en/reference/metadata-drivers.rst index d7b04f11b14..4b2ed01066c 100644 --- a/docs/en/reference/metadata-drivers.rst +++ b/docs/en/reference/metadata-drivers.rst @@ -120,17 +120,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} */ @@ -198,5 +198,3 @@ iterate over them: foreach ($class->fieldMappings as $fieldMapping) { echo $fieldMapping['fieldName'] . "\n"; } - - From a76b776802be28036eb33a1a19c9fcc28785846f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 22 Oct 2022 14:53:51 +0200 Subject: [PATCH 05/14] Deprecate annotations --- docs/en/reference/advanced-configuration.rst | 6 ++++-- docs/en/reference/basic-mapping.rst | 3 ++- docs/en/reference/metadata-drivers.rst | 14 ++++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/en/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index b4016146369..a487c9e7ea3 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -114,11 +114,13 @@ classes. There are currently 5 available implementations: -- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` - ``Doctrine\ORM\Mapping\Driver\AttributeDriver`` - ``Doctrine\ORM\Mapping\Driver\XmlDriver`` -- ``Doctrine\ORM\Mapping\Driver\YamlDriver`` - ``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 diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 2b514014ca1..1f59e60ddd9 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -45,9 +45,10 @@ Doctrine provides several different ways to specify object-relational mapping metadata: - :doc:`Attributes ` -- :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 attributes, though diff --git a/docs/en/reference/metadata-drivers.rst b/docs/en/reference/metadata-drivers.rst index 4b2ed01066c..8d77bffca0d 100644 --- a/docs/en/reference/metadata-drivers.rst +++ b/docs/en/reference/metadata-drivers.rst @@ -13,11 +13,16 @@ metadata: - **XML files** (XmlDriver) -- **Class DocBlock Annotations** (AnnotationDriver) - **Attributes** (AttributeDriver) -- **YAML files** (YamlDriver) - **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`` @@ -40,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 From 8160e89c5ac9a85c41ad3a7e5f881ef17629317d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 22 Oct 2022 15:05:19 +0200 Subject: [PATCH 06/14] Use correct link --- lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index a0715dd58e9..b30fa67bc0d 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -64,7 +64,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; From fba05675b6f72f0f4b3e2696d0e6e7ca223f9d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 22 Oct 2022 15:13:41 +0200 Subject: [PATCH 07/14] Deprecate methods related to the annotation driver --- UPGRADE.md | 4 ++++ lib/Doctrine/ORM/ORMSetup.php | 16 ++++++++++++++++ psalm.xml | 1 + 3 files changed, 21 insertions(+) diff --git a/UPGRADE.md b/UPGRADE.md index c14fc77b786..052f761cabb 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -5,6 +5,10 @@ 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. diff --git a/lib/Doctrine/ORM/ORMSetup.php b/lib/Doctrine/ORM/ORMSetup.php index 3213431d737..c1f5c7edfe4 100644 --- a/lib/Doctrine/ORM/ORMSetup.php +++ b/lib/Doctrine/ORM/ORMSetup.php @@ -31,6 +31,8 @@ final class ORMSetup /** * Creates a configuration with an annotation metadata driver. * + * @deprecated Use another mapping driver. + * * @param string[] $paths */ public static function createAnnotationMetadataConfiguration( @@ -39,6 +41,12 @@ public static function createAnnotationMetadataConfiguration( ?string $proxyDir = null, ?CacheItemPoolInterface $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)); @@ -48,12 +56,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 $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/psalm.xml b/psalm.xml index 14a5086fc5a..b162b406573 100644 --- a/psalm.xml +++ b/psalm.xml @@ -76,6 +76,7 @@ + From 3ee7d961790b3e8a96a280427a265dc3bda3f0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 22 Oct 2022 15:34:10 +0200 Subject: [PATCH 08/14] Adjust comments (#10160) --- docs/en/tutorials/getting-started.rst | 2 +- lib/Doctrine/ORM/EntityManager.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 94849afccf3..de36bf66474 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -141,7 +141,7 @@ step: require_once "vendor/autoload.php"; - // Create a simple "default" Doctrine ORM configuration for Annotations + // Create a simple "default" Doctrine ORM configuration for Attributes $config = ORMSetup::createAttributeMetadataConfiguration( paths: array(__DIR__."/src"), isDevMode: true, diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 3cc13bfb50d..0e59a760197 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -54,13 +54,13 @@ * 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); + * $config = ORMSetup::createAttributeMetadataConfiguration($paths); + * $dbParams = ['driver' => 'pdo_sqlite', 'memory' => true]; * $entityManager = EntityManager::create($dbParams, $config); * * For more information see From 0b9c949590cdaa782c7065160edd8e7e222715e5 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 23 Oct 2022 01:11:03 +0200 Subject: [PATCH 09/14] Fix build with DBAL 3.5 (#10163) --- psalm.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/psalm.xml b/psalm.xml index 58dc308253e..3e7c4f8334a 100644 --- a/psalm.xml +++ b/psalm.xml @@ -68,8 +68,16 @@ + + + + + + + + From 6b61e262380c51626c33456f46b56409965da6ad Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 23 Oct 2022 02:04:53 +0200 Subject: [PATCH 10/14] Fix calls to AbstractSchemaManager::createSchema() (#10165) --- lib/Doctrine/ORM/Tools/SchemaTool.php | 15 ++++++++++++--- psalm.xml | 5 ----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index aa02054ae4d..3a40e9d2013 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -878,7 +878,7 @@ public function getDropSchemaSQL(array $classes) { $schema = $this->getSchemaFromMetadata($classes); - $deployedSchema = $this->schemaManager->createSchema(); + $deployedSchema = $this->introspectSchema(); foreach ($schema->getTables() as $table) { if (! $deployedSchema->hasTable($table->getName())) { @@ -974,7 +974,7 @@ private function createSchemaForComparison(Schema $toSchema): Schema $previousFilter = $config->getSchemaAssetsFilter(); if ($previousFilter === null) { - return $this->schemaManager->createSchema(); + return $this->introspectSchema(); } // whitelist assets we already know about in $toSchema, use the existing filter otherwise @@ -985,10 +985,19 @@ private function createSchemaForComparison(Schema $toSchema): Schema }); try { - return $this->schemaManager->createSchema(); + 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 b94bb7c4fd1..aed3ec4ba44 100644 --- a/psalm.xml +++ b/psalm.xml @@ -69,11 +69,6 @@ - - - - - From f256d996cc0dc1ce59b4f147a664eaacdcf929ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 22 Oct 2022 21:14:49 +0200 Subject: [PATCH 11/14] Use error style for notifications stderr is not a great name for something that is not meant to be processed (piped into) a program, but to be read by humans. Most commands should use stderr, and some of them should partially use stdout, typically when dumping SQL requests. --- composer.json | 2 +- .../ClearCache/CollectionRegionCommand.php | 2 +- .../Command/ClearCache/EntityRegionCommand.php | 2 +- .../Command/ClearCache/MetadataCommand.php | 2 +- .../Command/ClearCache/QueryCommand.php | 2 +- .../Command/ClearCache/QueryRegionCommand.php | 2 +- .../Command/ClearCache/ResultCommand.php | 2 +- .../Command/ConvertDoctrine1SchemaCommand.php | 2 +- .../Console/Command/ConvertMappingCommand.php | 2 +- .../EnsureProductionSettingsCommand.php | 2 +- .../Command/GenerateEntitiesCommand.php | 2 +- .../Console/Command/GenerateProxiesCommand.php | 2 +- .../Command/GenerateRepositoriesCommand.php | 2 +- .../ORM/Tools/Console/Command/InfoCommand.php | 2 +- .../Console/Command/MappingDescribeCommand.php | 2 +- .../Command/SchemaTool/AbstractCommand.php | 2 +- .../Command/SchemaTool/CreateCommand.php | 10 ++++++---- .../Console/Command/SchemaTool/DropCommand.php | 14 ++++++++------ .../Command/SchemaTool/UpdateCommand.php | 18 ++++++++++-------- .../Console/Command/ValidateSchemaCommand.php | 2 +- 20 files changed, 41 insertions(+), 35 deletions(-) diff --git a/composer.json b/composer.json index b2f7a5699a5..8a224aefc69 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "doctrine/lexer": "^1.2.3", "doctrine/persistence": "^2.4 || ^3", "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0", + "symfony/console": "^3.3 || ^4.0 || ^5.0 || ^6.0", "symfony/polyfill-php72": "^1.23", "symfony/polyfill-php80": "^1.16" }, diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php index b31c3bec2e6..c9f378d7061 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php @@ -67,7 +67,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $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 1a689b37c89..a2826142d70 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php @@ -66,7 +66,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $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 25a76eadf1b..a3e9101d053 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -40,7 +40,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $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 b88e270609b..edfa6ac0ad9 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php @@ -63,7 +63,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $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 8b665760316..b7e163c65bc 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php @@ -65,7 +65,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $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 e9336965958..85a9e5280b4 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -65,7 +65,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $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/ConvertDoctrine1SchemaCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php index 72577f0a8c6..39701a7f22c 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -97,7 +97,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $ui = new SymfonyStyle($input, $output); - $ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); + $ui->getErrorStyle()->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); // Process source directories $fromPaths = array_merge([$input->getArgument('from-path')], $input->getOption('from')); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index 031da1bdd11..3ee83166d76 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -88,7 +88,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $ui = new SymfonyStyle($input, $output); - $ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); + $ui->getErrorStyle()->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); $em = $this->getEntityManager($input); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php index ced57647ec9..38545d92e0d 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -38,7 +38,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $ui->warning('This console command has been deprecated and will be removed in a future version of Doctrine ORM.'); $em = $this->getEntityManager($input); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php index ecd969c25cd..4bebdddb800 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -76,7 +76,7 @@ class is supposed to extend which. You have to adjust the entity */ protected function execute(InputInterface $input, OutputInterface $output) { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); $em = $this->getEntityManager($input); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php index 9e17bc00a2a..e14b98c0fe3 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -47,7 +47,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php index 84cc3fe719b..0bb71f4cd6c 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -48,7 +48,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); $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 e040b81d2e3..3152d51c53c 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php @@ -43,7 +43,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $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 4c7190e9b06..9030c236f53 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 ae4d0fb0f90..c58029cdae8 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -38,7 +38,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $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 cbc1244eec7..16302b271ce 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -57,14 +57,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 d67cad363f1..4cf027ff949 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -67,9 +67,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(); @@ -77,12 +79,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(); @@ -91,12 +93,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 5031387e56e..42f5709c20b 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -78,8 +78,10 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); + $notificationUi = $ui->getErrorStyle(); + 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; } @@ -95,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!', '', @@ -122,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 3d9680fd55c..19b50dbf8ab 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -40,7 +40,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $ui = new SymfonyStyle($input, $output); + $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $validator = new SchemaValidator($em); From ac94d826dcfabe729bcdde8d98e5a36977c3259a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Tue, 18 Oct 2022 22:25:49 +0200 Subject: [PATCH 12/14] Address deprecation of SchemaDiff::toSql() The new method AbstractPlatform::getAlterSchemaSQL() should be preferred when available. --- lib/Doctrine/ORM/Tools/SchemaTool.php | 6 +++++- .../Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 3a40e9d2013..08a998a2025 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -959,7 +959,11 @@ public function getUpdateSchemaSql(array $classes, $saveMode = false) 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); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php index 42f14669ae6..2e674d38d63 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; @@ -82,7 +83,10 @@ public function assertCreatedSchemaNeedsNoUpdates(string ...$classes): void $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 function ($sql) { return ! str_contains($sql, 'DROP'); }); From 7cb96fcf0e3aa151cba463cff4ea2e99a5ba6517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Tue, 18 Oct 2022 22:54:56 +0200 Subject: [PATCH 13/14] Address deprecation of SchemaDiff::toSaveSql() This implies deprecating a feature relying on that method. --- UPGRADE.md | 9 +++++++++ composer.json | 2 +- .../Command/SchemaTool/UpdateCommand.php | 10 +++++++--- lib/Doctrine/ORM/Tools/SchemaTool.php | 20 +++++++++++++++++++ phpstan-dbal2.neon | 3 +++ .../Command/SchemaTool/UpdateCommandTest.php | 5 ++++- 6 files changed, 44 insertions(+), 5 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index 052f761cabb..78fa6cff804 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,14 @@ # 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 diff --git a/composer.json b/composer.json index 8a224aefc69..746d8c73bc3 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "doctrine/lexer": "^1.2.3", "doctrine/persistence": "^2.4 || ^3", "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^3.3 || ^4.0 || ^5.0 || ^6.0", + "symfony/console": "^4.2 || ^5.0 || ^6.0", "symfony/polyfill-php72": "^1.23", "symfony/polyfill-php80": "^1.16" }, diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 42f5709c20b..743041ecac0 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -57,7 +57,7 @@ protected function configure() 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 @@ -73,12 +73,16 @@ protected function configure() */ protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui) { + $notificationUi = $ui->getErrorStyle(); + // Defining if update is complete or not (--complete not defined means $saveMode = true) $saveMode = ! $input->getOption('complete'); - $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); + 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'); + } - $notificationUi = $ui->getErrorStyle(); + $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); if (empty($sqls)) { $notificationUi->success('Nothing to update - your database is already in sync with the current entity metadata.'); diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 08a998a2025..2597ad656ab 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -14,6 +14,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; @@ -32,6 +33,7 @@ use function assert; use function count; use function current; +use function func_num_args; use function implode; use function in_array; use function is_array; @@ -924,6 +926,15 @@ public function getDropSchemaSQL(array $classes) */ public function updateSchema(array $classes, $saveMode = false) { + 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(); @@ -944,6 +955,15 @@ public function updateSchema(array $classes, $saveMode = false) */ public function getUpdateSchemaSql(array $classes, $saveMode = false) { + 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); diff --git a/phpstan-dbal2.neon b/phpstan-dbal2.neon index 852987a3bd4..72285bfb227 100644 --- a/phpstan-dbal2.neon +++ b/phpstan-dbal2.neon @@ -11,6 +11,9 @@ parameters: # Class name will change in DBAL 3. - '/^Class Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform not found\.$/' + # Forward compatibility for DBAL 3.5 + - '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getAlterSchemaSQL\(\).$/' + # Forward compatibility for DBAL 3.4 - '/^Call to an undefined method Doctrine\\DBAL\\Cache\\QueryCacheProfile::[gs]etResultCache\(\)\.$/' - 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..a2df3811075 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()); } From f3f453286fa9926db3b489d6afabb0a0add7efe2 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 24 Oct 2022 00:15:56 +0200 Subject: [PATCH 14/14] Deprecate EntityManager::create() (#9961) --- UPGRADE.md | 7 ++++ ...-conversion-using-custom-mapping-types.rst | 2 +- .../cookbook/dql-user-defined-functions.rst | 4 +-- .../resolve-target-entity-listener.rst | 5 ++- docs/en/cookbook/sql-table-prefixes.rst | 4 +-- docs/en/reference/advanced-configuration.rst | 22 ++++++------ docs/en/reference/configuration.rst | 20 ++++++----- .../reference/dql-doctrine-query-language.rst | 12 +++---- docs/en/reference/events.rst | 4 +-- docs/en/tutorials/getting-started.rst | 9 ++--- lib/Doctrine/ORM/EntityManager.php | 29 +++++++++++++--- psalm.xml | 1 + .../Performance/EntityManagerFactory.php | 9 ++--- .../Tests/Mocks/EntityManagerMock.php | 34 +++++++++++-------- .../Doctrine/Tests/ORM/EntityManagerTest.php | 17 +++++++++- .../Functional/Locking/LockAgentWorker.php | 2 +- .../Tests/ORM/Functional/MergeProxiesTest.php | 2 +- .../ORM/Functional/Ticket/DDC3634Test.php | 2 +- .../ORM/Functional/Ticket/GH7869Test.php | 2 +- .../ORM/Mapping/ClassMetadataFactoryTest.php | 2 +- .../Tests/ORM/PersistentCollectionTest.php | 2 +- .../Tests/ORM/Proxy/ProxyFactoryTest.php | 2 +- .../ORM/Tools/ConvertDoctrine1SchemaTest.php | 2 +- .../Export/ClassMetadataExporterTestCase.php | 2 +- tests/Doctrine/Tests/ORM/UnitOfWorkTest.php | 4 +-- .../Doctrine/Tests/OrmFunctionalTestCase.php | 2 +- tests/Doctrine/Tests/OrmTestCase.php | 2 +- 27 files changed, 126 insertions(+), 79 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index 052f761cabb..1bbe5fd1e93 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -27,6 +27,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 040b0c078ff..67a14c8aa7f 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 @@ -196,7 +196,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/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/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index a487c9e7ea3..0aacc76dd2d 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -41,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 -------------------- @@ -276,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/configuration.rst b/docs/en/reference/configuration.rst index 57bfd154924..831ae91a16a 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -41,22 +41,24 @@ 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::createAttributeMetadataConfiguration($paths, $isDevMode); - $entityManager = EntityManager::create($dbParams, $config); + $connection = DriverManager::getConnection($dbParams, $config); + $entityManager = new EntityManager($connection, $config); .. note:: @@ -68,18 +70,20 @@ 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: @@ -1016,7 +1016,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: diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 998687a4085..d638c8ecec4 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -136,6 +136,7 @@ step: 'pdo_sqlite', 'path' => __DIR__ . '/db.sqlite', - ); + ], $config) // obtaining the entity manager - $entityManager = EntityManager::create($conn, $config); + $entityManager = new EntityManager($connection, $config); .. note:: The YAML driver is deprecated and will be removed in version 3.0. diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 0e59a760197..1956ad75166 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -60,8 +60,8 @@ * $paths = ['/path/to/entity/mapping/files']; * * $config = ORMSetup::createAttributeMetadataConfiguration($paths); - * $dbParams = ['driver' => 'pdo_sqlite', 'memory' => true]; - * $entityManager = EntityManager::create($dbParams, $config); + * $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} @@ -156,7 +156,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 $eventManager = null) { if (! $config->getMetadataDriverImpl()) { throw MissingMappingDriverImplementation::create(); @@ -164,7 +164,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(); @@ -952,6 +952,8 @@ public function initializeObject($obj) /** * 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. * @param Configuration $config The Configuration instance to use. * @param EventManager|null $eventManager The EventManager instance to use. @@ -964,6 +966,15 @@ public function initializeObject($obj) */ public static function create($connection, Configuration $config, ?EventManager $eventManager = null) { + 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); @@ -972,6 +983,8 @@ public static function create($connection, Configuration $config, ?EventManager /** * 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. * @param Configuration $config The Configuration instance to use. * @param EventManager|null $eventManager The EventManager instance to use. @@ -984,6 +997,14 @@ public static function create($connection, Configuration $config, ?EventManager */ protected static function createConnection($connection, Configuration $config, ?EventManager $eventManager = null) { + 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/psalm.xml b/psalm.xml index faf62afc479..949204d6cac 100644 --- a/psalm.xml +++ b/psalm.xml @@ -77,6 +77,7 @@ + diff --git a/tests/Doctrine/Performance/EntityManagerFactory.php b/tests/Doctrine/Performance/EntityManagerFactory.php index be81291cacf..b3e023e661f 100644 --- a/tests/Doctrine/Performance/EntityManagerFactory.php +++ b/tests/Doctrine/Performance/EntityManagerFactory.php @@ -8,6 +8,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 = [], ?Quer } }; - 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 ec59144f2ac..8c3e8356904 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. */ @@ -22,10 +26,19 @@ class EntityManagerMock extends EntityManager /** @var ProxyFactory|null */ private $_proxyFactoryMock; - /** - * {@inheritdoc} - */ - public function getUnitOfWork() + public function __construct(Connection $conn, ?Configuration $config = null, ?EventManager $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(); } @@ -51,19 +64,10 @@ public function getProxyFactory(): ProxyFactory } /** - * Mock factory method to create an EntityManager. - * * {@inheritdoc} */ - public static function create($conn, ?Configuration $config = null, ?EventManager $eventManager = null) + public static function create($connection, Configuration $config, ?EventManager $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/EntityManagerTest.php b/tests/Doctrine/Tests/ORM/EntityManagerTest.php index b3505f08b65..7aeb1b512f5 100644 --- a/tests/Doctrine/Tests/ORM/EntityManagerTest.php +++ b/tests/Doctrine/Tests/ORM/EntityManagerTest.php @@ -31,6 +31,7 @@ use function get_class; use function random_int; +use function sys_get_temp_dir; use function uniqid; class EntityManagerTest extends OrmTestCase @@ -258,12 +259,26 @@ public function transactionalCallback($em): string public function testCreateInvalidConnection(): void { + $config = new Configuration(); + $config->setMetadataDriverImpl($this->createMock(MappingDriver::class)); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid $connection argument of type int given: "1".'); + EntityManager::create(1, $config); + } + public function testNamedConstructorDeprecation(): void + { $config = new Configuration(); $config->setMetadataDriverImpl($this->createMock(MappingDriver::class)); - EntityManager::create(1, $config); + $config->setProxyDir(sys_get_temp_dir()); + $config->setProxyNamespace(__NAMESPACE__ . '\\MyProxies'); + + $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9961'); + + $em = EntityManager::create(['driver' => 'pdo_sqlite', 'memory' => true], $config); + + self::assertInstanceOf(Connection::class, $em->getConnection()); } /** @group #5796 */ diff --git a/tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php b/tests/Doctrine/Tests/ORM/Functional/Locking/LockAgentWorker.php index 7eab7208450..129f8d9cd72 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/MergeProxiesTest.php b/tests/Doctrine/Tests/ORM/Functional/MergeProxiesTest.php index 2acf9843c38..5d66c1316a4 100644 --- a/tests/Doctrine/Tests/ORM/Functional/MergeProxiesTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/MergeProxiesTest.php @@ -257,7 +257,7 @@ private function createEntityManager(): EntityManagerInterface $config ); - $entityManager = EntityManager::create($connection, $config); + $entityManager = new EntityManager($connection, $config); (new SchemaTool($entityManager))->createSchema([$entityManager->getClassMetadata(DateTimeModel::class)]); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3634Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3634Test.php index d0c9d647da6..3fa0adb5f19 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3634Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3634Test.php @@ -47,7 +47,7 @@ public function testSavesVeryLargeIntegerAutoGeneratedValue(): void { $veryLargeId = PHP_INT_MAX . PHP_INT_MAX; - $entityManager = EntityManager::create( + $entityManager = new EntityManager( new DDC3634LastInsertIdMockingConnection($veryLargeId, $this->_em->getConnection()), $this->_em->getConfiguration() ); 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 fc951dfd000..a2b74bdbf5c 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -300,7 +300,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 704799c9e34..ffa7b65f7cc 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php @@ -56,7 +56,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 18b47d334d4..46c1350552f 100644 --- a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php @@ -55,7 +55,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/ConvertDoctrine1SchemaTest.php b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php index 0b92773e2cb..d89218f821a 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php @@ -46,7 +46,7 @@ protected function createEntityManager(MappingDriver $metadataDriver): EntityMan $config->setProxyNamespace('Doctrine\Tests\Proxies'); $config->setMetadataDriverImpl($metadataDriver); - return EntityManagerMock::create($connection, $config); + return new EntityManagerMock($connection, $config); } public function testTest(): void diff --git a/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php b/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php index 2676ceff061..6d27b3c47f9 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php +++ b/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php @@ -66,7 +66,7 @@ protected function createEntityManager($metadataDriver): EntityManagerMock $config->setProxyNamespace('Doctrine\Tests\Proxies'); $config->setMetadataDriverImpl($metadataDriver); - return EntityManagerMock::create($connection, $config); + return new EntityManagerMock($connection, $config); } protected function createMetadataDriver(string $type, string $path): MappingDriver diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index 2a12de35c27..b23452c8e87 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -116,7 +116,7 @@ protected function setUp(): void $this->eventManager = $this->getMockBuilder(EventManager::class)->getMock(); $this->_connectionMock = new Connection([], $driver, null, $this->eventManager); - $this->_emMock = EntityManagerMock::create($this->_connectionMock); + $this->_emMock = new EntityManagerMock($this->_connectionMock); // SUT $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); $this->_emMock->setUnitOfWork($this->_unitOfWork); @@ -841,7 +841,7 @@ public function testCommitThrowOptimisticLockExceptionWhenConnectionCommitReturn $this->_connectionMock = $this->getMockBuilderWithOnlyMethods(ConnectionMock::class, ['commit']) ->setConstructorArgs([[], $driver]) ->getMock(); - $this->_emMock = EntityManagerMock::create($this->_connectionMock); + $this->_emMock = new EntityManagerMock($this->_connectionMock); $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 4e96d5e410d..f0bdef69d38 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -830,7 +830,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 2a99c60aa2d..8cc988c6931 100644 --- a/tests/Doctrine/Tests/OrmTestCase.php +++ b/tests/Doctrine/Tests/OrmTestCase.php @@ -107,7 +107,7 @@ protected function getTestEntityManager(?Connection $connection = null): EntityM ], $config); } - return EntityManagerMock::create($connection, $config); + return new EntityManagerMock($connection, $config); } protected function enableSecondLevelCache(bool $log = true): void