Compare commits

...

44 Commits
2.7.5 ... 2.8.0

Author SHA1 Message Date
Grégoire Paris
418587bc25 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-12-03 20:18:13 +01:00
Benjamin Eberlei
3d46e07887 PHP8 Support (#8303)
* Update doctrine/dbal to 2.12 for PHP 8 support.

* Change Query\Parser::match to Query\Parser::matchToken including DQL functions.

* Fix phpunit constraint to 9.4, adjust @group usage to workaround PHPUnit bug.

* Fix PHPUnit API related changes.

* Add PHP 8 support for EntityGenerator namespace detection.

* Use new assertEqualsWithDetla for QueryDqlFunctionTest with date comparisons

* Replace ReflectionParameter::getClass usage with non-deprecated ::getType instead.

* Revert "Change Query\Parser::match to Query\Parser::matchToken including DQL functions."

This reverts commit 279070491d50deaa4d41e17b28bb5a68f5a22796.

* More matchToken => match reverts

* Housekeeping: phpcs

* Housekeeping: phpcs

* Housekeeping: phpcs

* Housekeeping: phpcs

* Add PHP 8 testrunner, update composer.json and small fix in OrmFunctionalTestCase for new PHPUnit behvaior

* Update doctrine/coding-standard to 8.x

* Update rule names for doctrine/coding-standard v8.0

* Update to Psalm 4.

* Not failOnWarning anymore.

* Fix phpcs

* fix phpcs

* remove 7.2 for now until we can support in DBAL.

* Relax doctrine/dbal requirement and add 7.2 CI support again.
2020-11-15 13:24:31 +01:00
Benjamin Eberlei
51bc596502 Update to Psalm 4. (#8332) 2020-11-08 10:38:21 +01:00
Grégoire Paris
95f1b48422 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-11-07 20:19:45 +01:00
Benjamin Eberlei
f7d8b155db Merge branch '2.7' into 2.8.x 2020-11-07 18:37:53 +01:00
Benjamin Eberlei
539ffea390 Merge 2.7 into 2.8.x 2020-11-07 18:30:32 +01:00
Simon Podlipsky
4bfc84f035 Rename getIterable() to toIterable() (part 2) (#8293) 2020-10-17 23:55:39 +02:00
Mateusz Sip
53dc5b2ac3 Detect associations inside embeddables (#8291) 2020-10-17 18:49:16 +02:00
orklah
f1219f1418 Add psalm template support to several types (#8289) 2020-10-17 12:01:52 +02:00
Grégoire Paris
072066f746 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-10-17 11:07:05 +02:00
Simon Podlipsky
f1365b78d5 Rename getIterable to toIterable (#8268) 2020-09-25 20:42:09 +02:00
Simon Podlipsky
60cd524443 Introduce getIterable() on AbstractQuery (#7885) 2020-08-14 11:12:40 +02:00
Simon Podlipsky
045d1f3bf2 Bump Doctrine CS to v6 (#8241) 2020-08-13 09:10:43 +02:00
Grégoire Paris
1ae6f18fe9 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-08-12 22:26:19 +02:00
Zacharias Luiten
91b9dd90f4 Add DQL support for ORDER BY CASE (#8188) 2020-08-05 22:49:51 +02:00
Grégoire Paris
505d658e3d Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-06-21 13:50:48 +02:00
Grégoire Paris
bee8decd18 Require persistence 2 and common 3 (#8166)
This allows us to drop the autoload-calls-based BC layer.
2020-06-17 14:56:17 +02:00
Laurent VOULLEMIER
6bce7e9cab Add iterable support for IN criterias (#8162) 2020-06-03 18:40:53 +02:00
Grégoire Paris
4d8418fe6f Merge remote-tracking branch 'origin/2.7' into drop-persistence-bc-layer 2020-06-02 18:40:48 +02:00
Benjamin Eberlei
850d57e791 [GH-8137] Update doctrine/inflector dependency to 1.4|2.0 (#8147)
* [GH-8137] Update doctrine/inflector dependency to 1.4|2.0 and resolve deprecations.

* [GH-8137] Address review comments

* Address phpcs issues.

* More CS

* Codingstyle
2020-05-17 19:58:02 +02:00
Grégoire Paris
c3dd71704b Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-05-16 15:14:51 +02:00
Philippe Le Van (@plv)
0b305e5bd3 Add possibility to use sql comments in the DQL syntax (#8140) 2020-05-15 08:04:58 +02:00
Gabriel Birke
061207861b Improve CLI config documentation (#8130)
Change code examples for `cli-config.php` to use
`ConsoleRunner::createHelperSet` instead of constructing the helper set
array yourself.
Remove DBAL-specific documentation

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>
2020-05-11 21:43:26 +02:00
Grégoire Paris
3c91792dd8 Address deprecation about PHPUnit annotations (#8070)
These changes have been done using Rector, this might not result in the
best placement for method calls, but it is not worse than the current
situation.

I used the configuration documented at
b398e8740c/docs/HowItWorks.md (221-order-of-rectors)

Fixes #8069
2020-03-16 08:56:59 +01:00
Claudio Zizza
431d0a3c5e Remove ORM 2.0 version constraints from docs (#8002) 2020-03-06 21:47:40 +01:00
Soliman
1da002ca2f Throw OptimisticLockException when connection::commit() returns… (#7946)
* Throw OptimisticLockException when connection::commit() returns false

* Update unit tests

* Fix doctrine persistence version to avoid deprecations changes

* Apply changes from 2.8.x

* Update from 2.8.x
2020-02-29 23:06:47 +01:00
Benjamin Eberlei
ef639d4de6 Remove nightly builds from .travis.yml 2020-02-15 15:34:05 +01:00
Benjamin Eberlei
31f4dd671a Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-02-13 21:31:29 +01:00
Benjamin Eberlei
60c4867ed3 Merge branch 2.7 into 2.8.x 2020-02-12 23:42:36 +01:00
Benjamin Eberlei
0ee1716b26 Merge branch 2.7 into 2.8.x 2020-02-12 23:38:27 +01:00
Claudio Zizza
a236a83fa8 Merge pull request #7993 from SenseException/readme2-8
Update version and links in readme
2020-01-20 11:14:33 +01:00
Claudio Zizza
37f1bd7606 Update version and links in readme 2020-01-17 21:42:59 +01:00
Grégoire Paris
af4cb282ba Merge pull request #7978 from alexeyshockov/patch-2
Stable PHP 7.4 in Travis
2020-01-17 07:49:48 +01:00
Benjamin Eberlei
ce4914ba0e Merge 2.7 to 2.8.x 2020-01-17 00:08:23 +01:00
Alexey Shokov
bdfd6c1677 Stable PHP 7.4 in Travis 2020-01-16 23:27:20 +01:00
Benjamin Eberlei
8e0157d97d Merge branch '2.7' into 2.8.x 2020-01-15 22:57:06 +01:00
Benjamin Eberlei
1767f4b8e7 Merge branch '2.7' into 2.8.x 2020-01-15 22:02:55 +01:00
Benjamin Eberlei
ca95b0ee13 Merge branch '2.7' into 2.8.x 2020-01-08 19:39:17 +01:00
Luís Cobucci
4aa09861dd Merge pull request #7941 from Grafikart/feat-typed-functions
Allow DQL functions to specify return type
2019-12-12 19:50:23 +01:00
Grafikart
24e9a7caaf Allow defining return types for DQL functions 2019-12-12 14:19:09 +01:00
Luís Cobucci
d90df59118 Merge pull request #7948 from beberlei/TravisSmokeTesting
Add stage that runs before Test to allow fast failures on Sqlite/phpcs
2019-12-12 11:54:41 +01:00
Benjamin Eberlei
f9103a7b41 Add another stage that runs before Test to allow fast failures on Sqlite, Quality + PHPCS-differ 2019-12-12 11:43:24 +01:00
Luís Cobucci
9891477094 Merge pull request #7928 from kokspflanze/2.7-patch-deprecated
Deprecate EntityRepository#clear()
2019-12-09 21:56:41 +01:00
kokspflanze
59e3a55110 Deprecate EntityRepository#clear() 2019-12-09 21:42:11 +01:00
127 changed files with 1581 additions and 727 deletions

View File

@@ -18,6 +18,7 @@ jobs:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
deps:
- "normal"
include:

View File

@@ -1,7 +1,7 @@
| [Master][Master] | [2.7][2.7] |
| [Master][Master] | [2.8.x][2.8] |
|:----------------:|:----------:|
| [![Build status][Master image]][Master] | [![Build status][2.7 image]][2.7] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.7 coverage image]][2.7 coverage] |
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
@@ -13,14 +13,14 @@ without requiring unnecessary code duplication.
## More resources:
* [Website](http://www.doctrine-project.org)
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
[Master image]: https://img.shields.io/travis/doctrine/orm/master.svg?style=flat-square
[Master]: https://travis-ci.org/doctrine/orm
[Master coverage image]: https://codecov.io/gh/doctrine/orm/branch/master/graph/badge.svg
[Master coverage]: https://codecov.io/gh/doctrine/orm/branch/master
[2.7 image]: https://img.shields.io/travis/doctrine/orm/2.7.svg?style=flat-square
[2.7]: https://github.com/doctrine/orm/tree/2.7
[2.7 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.7/graph/badge.svg
[2.7 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.7
[2.8 image]: https://img.shields.io/travis/doctrine/orm/2.8.svg?style=flat-square
[2.8]: https://github.com/doctrine/orm/tree/2.8
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8/graph/badge.svg
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8

View File

@@ -1,3 +1,10 @@
# Upgrade to 2.8
## Minor BC BREAK: Failed commit now throw OptimisticLockException
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
# Upgrade to 2.7
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods
@@ -30,7 +37,7 @@ Method `Doctrine\ORM\AbstractQuery#useResultCache()` is deprecated because it is
and `disableResultCache()`. It will be removed in 3.0.
## Deprecated code generators and related console commands
These console commands have been deprecated:
* `orm:convert-mapping`

View File

@@ -5,7 +5,6 @@
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<var name="db_driver" value="mysqli"/>

View File

@@ -5,7 +5,6 @@
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<var name="db_driver" value="pdo_mysql"/>

View File

@@ -5,7 +5,6 @@
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<var name="db_driver" value="pdo_pgsql"/>

View File

@@ -5,7 +5,6 @@
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<!-- necessary change for some CLI/console output test assertions -->

View File

@@ -16,27 +16,27 @@
"sort-packages": true
},
"require": {
"php": "^7.1",
"php": "^7.2|^8.0",
"ext-pdo": "*",
"composer/package-versions-deprecated": "^1.8",
"doctrine/annotations": "^1.11.1",
"doctrine/cache": "^1.9.1",
"doctrine/collections": "^1.5",
"doctrine/common": "^2.11 || ^3.0",
"doctrine/dbal": "^2.9.3",
"doctrine/common": "^3.0",
"doctrine/dbal": "^2.10.0",
"doctrine/event-manager": "^1.1",
"doctrine/inflector": "^1.0",
"doctrine/inflector": "^1.4|^2.0",
"doctrine/instantiator": "^1.3",
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^1.3.3 || ^2.0",
"doctrine/persistence": "^2.0",
"symfony/console": "^3.0|^4.0|^5.0"
},
"require-dev": {
"doctrine/coding-standard": "^6.0",
"doctrine/coding-standard": "^8.0",
"phpstan/phpstan": "^0.12.18",
"phpunit/phpunit": "^8.0",
"phpunit/phpunit": "^8.5|^9.4",
"symfony/yaml": "^3.4|^4.0|^5.0",
"vimeo/psalm": "^3.11"
"vimeo/psalm": "4.1.1"
},
"suggest": {
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"

View File

@@ -1,4 +1,4 @@
The Doctrine2 documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
The Doctrine ORM documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
Creative Commons Legal Code

View File

@@ -6,7 +6,7 @@ Aggregate Fields
You will often come across the requirement to display aggregate
values of data that can be computed by using the MIN, MAX, COUNT or
SUM SQL functions. For any ORM this is a tricky issue
traditionally. Doctrine 2 offers several ways to get access to
traditionally. Doctrine ORM offers several ways to get access to
these values and this article will describe all of them from
different perspectives.

View File

@@ -3,45 +3,45 @@ Persisting the Decorator Pattern
.. sectionauthor:: Chris Woodford <chris.woodford@gmail.com>
This recipe will show you a simple example of how you can use
Doctrine 2 to persist an implementation of the
This recipe will show you a simple example of how you can use
Doctrine ORM to persist an implementation of the
`Decorator Pattern <http://en.wikipedia.org/wiki/Decorator_pattern>`_
Component
---------
The ``Component`` class needs to be persisted, so it's going to
be an ``Entity``. As the top of the inheritance hierarchy, it's going
to have to define the persistent inheritance. For this example, we
will use Single Table Inheritance, but Class Table Inheritance
would work as well. In the discriminator map, we will define two
concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
The ``Component`` class needs to be persisted, so it's going to
be an ``Entity``. As the top of the inheritance hierarchy, it's going
to have to define the persistent inheritance. For this example, we
will use Single Table Inheritance, but Class Table Inheritance
would work as well. In the discriminator map, we will define two
concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
.. code-block:: php
<?php
namespace Test;
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"cc" = "Test\Component\ConcreteComponent",
* @DiscriminatorMap({"cc" = "Test\Component\ConcreteComponent",
"cd" = "Test\Decorator\ConcreteDecorator"})
*/
abstract class Component
{
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/** @Column(type="string", nullable=true) */
protected $name;
/**
* Get id
* @return integer $id
@@ -50,7 +50,7 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
{
return $this->id;
}
/**
* Set name
* @param string $name
@@ -59,7 +59,7 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
{
$this->name = $name;
}
/**
* Get name
* @return string $name
@@ -68,33 +68,33 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
{
return $this->name;
}
}
ConcreteComponent
-----------------
The ``ConcreteComponent`` class is pretty simple and doesn't do much
more than extend the abstract ``Component`` class (only for the
The ``ConcreteComponent`` class is pretty simple and doesn't do much
more than extend the abstract ``Component`` class (only for the
purpose of keeping this example simple).
.. code-block:: php
<?php
namespace Test\Component;
use Test\Component;
/** @Entity */
class ConcreteComponent extends Component
{}
Decorator
---------
The ``Decorator`` class doesn't need to be persisted, but it does
need to define an association with a persisted ``Entity``. We can
The ``Decorator`` class doesn't need to be persisted, but it does
need to define an association with a persisted ``Entity``. We can
use a ``MappedSuperclass`` for this.
.. code-block:: php
@@ -102,17 +102,17 @@ use a ``MappedSuperclass`` for this.
<?php
namespace Test;
/** @MappedSuperclass */
abstract class Decorator extends Component
{
/**
* @OneToOne(targetEntity="Test\Component", cascade={"all"})
* @JoinColumn(name="decorates", referencedColumnName="id")
*/
protected $decorates;
/**
* initialize the decorator
* @param Component $c
@@ -121,7 +121,7 @@ use a ``MappedSuperclass`` for this.
{
$this->setDecorates($c);
}
/**
* (non-PHPdoc)
* @see Test.Component::getName()
@@ -130,7 +130,7 @@ use a ``MappedSuperclass`` for this.
{
return 'Decorated ' . $this->getDecorates()->getName();
}
/**
* the component being decorated
* @return Component
@@ -139,7 +139,7 @@ use a ``MappedSuperclass`` for this.
{
return $this->decorates;
}
/**
* sets the component being decorated
* @param Component $c
@@ -148,52 +148,52 @@ use a ``MappedSuperclass`` for this.
{
$this->decorates = $c;
}
}
All operations on the ``Decorator`` (i.e. persist, remove, etc) will
cascade from the ``Decorator`` to the ``Component``. This means that
when we persist a ``Decorator``, Doctrine will take care of
persisting the chain of decorated objects for us. A ``Decorator`` can
be treated exactly as a ``Component`` when it comes time to
All operations on the ``Decorator`` (i.e. persist, remove, etc) will
cascade from the ``Decorator`` to the ``Component``. This means that
when we persist a ``Decorator``, Doctrine will take care of
persisting the chain of decorated objects for us. A ``Decorator`` can
be treated exactly as a ``Component`` when it comes time to
persisting it.
The ``Decorator's`` constructor accepts an instance of a
``Component``, as defined by the ``Decorator`` pattern. The
setDecorates/getDecorates methods have been defined as protected to
hide the fact that a ``Decorator`` is decorating a ``Component`` and
keeps the ``Component`` interface and the ``Decorator`` interface
The ``Decorator's`` constructor accepts an instance of a
``Component``, as defined by the ``Decorator`` pattern. The
setDecorates/getDecorates methods have been defined as protected to
hide the fact that a ``Decorator`` is decorating a ``Component`` and
keeps the ``Component`` interface and the ``Decorator`` interface
identical.
To illustrate the intended result of the ``Decorator`` pattern, the
getName() method has been overridden to append a string to the
To illustrate the intended result of the ``Decorator`` pattern, the
getName() method has been overridden to append a string to the
``Component's`` getName() method.
ConcreteDecorator
-----------------
The final class required to complete a simple implementation of the
Decorator pattern is the ``ConcreteDecorator``. In order to further
illustrate how the ``Decorator`` can alter data as it moves through
the chain of decoration, a new field, "special", has been added to
this class. The getName() has been overridden and appends the value
of the getSpecial() method to its return value.
The final class required to complete a simple implementation of the
Decorator pattern is the ``ConcreteDecorator``. In order to further
illustrate how the ``Decorator`` can alter data as it moves through
the chain of decoration, a new field, "special", has been added to
this class. The getName() has been overridden and appends the value
of the getSpecial() method to its return value.
.. code-block:: php
<?php
namespace Test\Decorator;
use Test\Decorator;
/** @Entity */
class ConcreteDecorator extends Decorator
{
/** @Column(type="string", nullable=true) */
protected $special;
/**
* Set special
* @param string $special
@@ -202,7 +202,7 @@ of the getSpecial() method to its return value.
{
$this->special = $special;
}
/**
* Get special
* @return string $special
@@ -211,7 +211,7 @@ of the getSpecial() method to its return value.
{
return $this->special;
}
/**
* (non-PHPdoc)
* @see Test.Component::getName()
@@ -219,55 +219,55 @@ of the getSpecial() method to its return value.
public function getName()
{
return '[' . $this->getSpecial()
. '] ' . parent::getName();
. '] ' . parent::getName();
}
}
Examples
--------
Here is an example of how to persist and retrieve your decorated
Here is an example of how to persist and retrieve your decorated
objects
.. code-block:: php
<?php
use Test\Component\ConcreteComponent,
Test\Decorator\ConcreteDecorator;
// assumes Doctrine 2 is configured and an instance of
// assumes Doctrine ORM is configured and an instance of
// an EntityManager is available as $em
// create a new concrete component
$c = new ConcreteComponent();
$c->setName('Test Component 1');
$em->persist($c); // assigned unique ID = 1
// create a new concrete decorator
$c = new ConcreteComponent();
$c->setName('Test Component 2');
$d = new ConcreteDecorator($c);
$d->setSpecial('Really');
$em->persist($d);
$em->persist($d);
// assigns c as unique ID = 2, and d as unique ID = 3
$em->flush();
$c = $em->find('Test\Component', 1);
$d = $em->find('Test\Component', 3);
echo get_class($c);
// prints: Test\Component\ConcreteComponent
echo $c->getName();
// prints: Test Component 1
echo get_class($d)
// prints: Test Component 1
echo get_class($d)
// prints: Test\Component\ConcreteDecorator
echo $d->getName();
// prints: [Really] Decorated Test Component 2

View File

@@ -1,4 +1,4 @@
Extending DQL in Doctrine 2: Custom AST Walkers
Extending DQL in Doctrine ORM: Custom AST Walkers
===============================================
.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
@@ -12,7 +12,7 @@ the Doctrine ORM.
In Doctrine 1 the DQL language was not implemented using a real
parser. This made modifications of the DQL by the user impossible.
Doctrine 2 in contrast has a real parser for the DQL language,
Doctrine ORM in contrast has a real parser for the DQL language,
which transforms the DQL statement into an
`Abstract Syntax Tree <http://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
and generates the appropriate SQL statement for it. Since this

View File

@@ -10,7 +10,7 @@ change it during the life of your project. This decision for a
specific vendor potentially allows you to make use of powerful SQL
features that are unique to the vendor.
It is worth to mention that Doctrine 2 also allows you to handwrite
It is worth to mention that Doctrine ORM also allows you to handwrite
your SQL instead of extending the DQL parser. Extending DQL is sort of an
advanced extension point. You can map arbitrary SQL to your objects
and gain access to vendor specific functionalities using the
@@ -240,7 +240,7 @@ functionalities in DQL, we would be excited to see user extensions
that add vendor specific function packages, for example more math
functions, XML + GIS Support, Hashing functions and so on.
For 2.0 we will come with the current set of functions, however for
For ORM we will come with the current set of functions, however for
a future version we will re-evaluate if we can abstract even more
vendor sql functions and extend the DQL languages scope.

View File

@@ -1,12 +1,12 @@
Mysql Enums
===========
The type system of Doctrine 2 consists of flyweights, which means there is only
The type system of Doctrine ORM consists of flyweights, which means there is only
one instance of any given type. Additionally types do not contain state. Both
assumptions make it rather complicated to work with the Enum Type of MySQL that
is used quite a lot by developers.
When using Enums with a non-tweaked Doctrine 2 application you will get
When using Enums with a non-tweaked Doctrine ORM application you will get
errors from the Schema-Tool commands due to the unknown database type "enum".
By default Doctrine does not map the MySQL enum type to a Doctrine type.
This is because Enums contain state (their allowed values) and Doctrine

View File

@@ -1,13 +1,11 @@
Keeping your Modules independent
=================================
.. versionadded:: 2.2
One of the goals of using modules is to create discrete units of functionality
that do not have many (if any) dependencies, allowing you to use that
functionality in other applications without including unnecessary items.
Doctrine 2.2 includes a new utility called the ``ResolveTargetEntityListener``,
Doctrine ORM includes a new utility called the ``ResolveTargetEntityListener``,
that functions by intercepting certain calls inside Doctrine and rewrite
targetEntity parameters in your metadata mapping at runtime. It means that
in your bundle you are able to use an interface or abstract class in your

View File

@@ -30,7 +30,7 @@ highly uncomfortable because of the following:
every panel-type? This wouldn't be flexible. You might be tempted
to add an AbstractPanelEntity and an AbstractBlockEntity that use
class inheritance. Your page could then only confer to the
AbstractPanelType and Doctrine 2 would do the rest for you, i.e.
AbstractPanelType and Doctrine ORM would do the rest for you, i.e.
load the right entities. But - you'll for sure have lots of panels
and blocks, and even worse, you'd have to edit the discriminator
map *manually* every time you or another developer implements a new
@@ -152,7 +152,7 @@ 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 2)
* 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
@@ -161,7 +161,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
protected $strategyClassName;
/**
* This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine 2.
* This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine ORM.
*
* @var BlockStrategyInterface
*/
@@ -199,7 +199,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
$strategy->setBlockEntity($this);
}
Now, the important point is that $strategyClassName is a Doctrine 2
Now, the important point is that $strategyClassName is a Doctrine ORM
field, i.e. Doctrine will persist this value. This is only the
class name of your strategy and not an instance!

View File

@@ -3,7 +3,7 @@ Validation of Entities
.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
Doctrine 2 does not ship with any internal validators, the reason
Doctrine ORM does not ship with any internal validators, the reason
being that we think all the frameworks out there already ship with
quite decent ones that can be integrated into your Domain easily.
What we offer are hooks to execute any kind of validation.

View File

@@ -3,7 +3,7 @@ Working with DateTime Instances
There are many nitty gritty details when working with PHPs DateTime instances. You have to know their inner
workings pretty well not to make mistakes with date handling. This cookbook entry holds several
interesting pieces of information on how to work with PHP DateTime instances in Doctrine 2.
interesting pieces of information on how to work with PHP DateTime instances in ORM.
DateTime changes are detected by Reference
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -58,7 +58,7 @@ Handling different Timezones with the DateTime Type
If you first come across the requirement to save different timezones you may be still optimistic about how
to manage this mess,
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2)
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine ORM)
that supports timezones correctly. Correctly here means that you can cover all the use-cases that
can come up with timezones. If you don't believe me you should read up on `Storing DateTime
in Databases <http://derickrethans.nl/storing-date-time-in-database.html>`_.
@@ -67,7 +67,7 @@ The problem is simple. Not a single database vendor saves the timezone, only the
However with frequent daylight saving and political timezone changes you can have a UTC offset that moves
in different offset directions depending on the real location.
The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine 2. However there is a workaround
The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine ORM. However there is a workaround
that even allows correct date-time handling with timezones:
1. Always convert any DateTime instance to UTC.

View File

@@ -11,15 +11,15 @@ steps of configuration.
<?php
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Configuration;
// ...
if ($applicationMode == "development") {
$cache = new \Doctrine\Common\Cache\ArrayCache;
} else {
$cache = new \Doctrine\Common\Cache\ApcCache;
}
$config = new Configuration;
$config->setMetadataCacheImpl($cache);
$driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
@@ -27,18 +27,18 @@ steps of configuration.
$config->setQueryCacheImpl($cache);
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
$config->setProxyNamespace('MyProject\Proxies');
if ($applicationMode == "development") {
$config->setAutoGenerateProxyClasses(true);
} else {
$config->setAutoGenerateProxyClasses(false);
}
$connectionOptions = array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite'
);
$em = EntityManager::create($connectionOptions, $config);
.. note::
@@ -259,7 +259,7 @@ In a production environment, it is highly recommended to use
AUTOGENERATE_NEVER to allow for optimal performances. The other
options are interesting in development environment.
Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean
``setAutoGenerateProxyClasses`` can accept a boolean
value. This is still possible, ``FALSE`` being equivalent to
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
@@ -299,7 +299,7 @@ Proxy Objects
A proxy object is an object that is put in place or used instead of
the "real" object. A proxy object can add behavior to the object
being proxied without that object being aware of it. In Doctrine 2,
being proxied without that object being aware of it. In ORM,
proxy objects are used to realize several features but mainly for
transparent lazy-loading.
@@ -309,7 +309,7 @@ of the objects. This is an essential property as without it there
would always be fragile partial objects at the outer edges of your
object graph.
Doctrine 2 implements a variant of the proxy pattern where it
Doctrine ORM implements a variant of the proxy pattern where it
generates classes that extend your entity classes and adds
lazy-loading capabilities to them. Doctrine can then give you an
instance of such a proxy class whenever you request an object of
@@ -411,7 +411,7 @@ be found.
Multiple Metadata Sources
-------------------------
When using different components using Doctrine 2 you may end up
When using different components using Doctrine ORM you may end up
with them using two different metadata drivers, for example XML and
YAML. You can use the DriverChain Metadata implementations to
aggregate these drivers based on namespaces:

View File

@@ -5,29 +5,29 @@ You've probably used docblock annotations in some form already,
most likely to provide documentation metadata for a tool like
``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a
tool to embed metadata inside the documentation section which can
then be processed by some tool. Doctrine 2 generalizes the concept
then be processed by some tool. Doctrine ORM generalizes the concept
of docblock annotations so that they can be used for any kind of
metadata and so that it is easy to define new docblock annotations.
In order to allow more involved annotation values and to reduce the
chances of clashes with other docblock annotations, the Doctrine 2
chances of clashes with other docblock annotations, the Doctrine ORM
docblock annotations feature an alternative syntax that is heavily
inspired by the Annotation syntax introduced in Java 5.
The implementation of these enhanced docblock annotations is
located in the ``Doctrine\Common\Annotations`` namespace and
therefore part of the Common package. Doctrine 2 docblock
therefore part of the Common package. Doctrine ORM docblock
annotations support namespaces and nested annotations among other
things. The Doctrine 2 ORM defines its own set of docblock
things. The Doctrine ORM ORM defines its own set of docblock
annotations for supplying object-relational mapping metadata.
.. note::
If you're not comfortable with the concept of docblock
annotations, don't worry, as mentioned earlier Doctrine 2 provides
annotations, don't worry, as mentioned earlier Doctrine ORM provides
XML and YAML alternatives and you could easily implement your own
favourite mechanism for defining ORM metadata.
In this chapter a reference of every Doctrine 2 Annotation is given
In this chapter a reference of every Doctrine ORM Annotation is given
with short explanations on their context and usage.
Index
@@ -213,7 +213,7 @@ Optional attributes:
~~~~~~~~~~~~~~~~~~~~~
The Change Tracking Policy annotation allows to specify how the
Doctrine 2 UnitOfWork should detect changes in properties of
Doctrine ORM UnitOfWork should detect changes in properties of
entities during flush. By default each entity is checked according
to a deferred implicit strategy, which means upon flush UnitOfWork
compares all the properties of an entity to a previously stored
@@ -254,7 +254,7 @@ Example:
<?php
/**
* @Id
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="CUSTOM")
* @CustomIdGenerator(class="My\Namespace\MyIdGenerator")
@@ -388,7 +388,7 @@ Optional attributes:
EntityRepository. Use of repositories for entities is encouraged to keep
specialized DQL and SQL operations separated from the Model/Domain
Layer.
- **readOnly**: (>= 2.1) Specifies that this entity is marked as read only and not
- **readOnly**: Specifies that this entity is marked as read only and not
considered for change-tracking. Entities of this type can be persisted
and removed though.
@@ -812,7 +812,7 @@ The @MappedSuperclass annotation cannot be used in conjunction with
Optional attributes:
- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository.
- **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository.
That will be inherited for all subclasses of that Mapped Superclass.
Example:
@@ -1231,7 +1231,7 @@ Optional attributes:
- **indexes**: Array of @Index annotations
- **uniqueConstraints**: Array of @UniqueConstraint annotations.
- **schema**: (>= 2.5) Name of the schema the table lies in.
- **schema**: Name of the schema the table lies in.
Example:

View File

@@ -2,29 +2,29 @@ Architecture
============
This chapter gives an overview of the overall architecture,
terminology and constraints of Doctrine 2. It is recommended to
terminology and constraints of Doctrine ORM. It is recommended to
read this chapter carefully.
Using an Object-Relational Mapper
---------------------------------
As the term ORM already hints at, Doctrine 2 aims to simplify the
As the term ORM already hints at, Doctrine ORM aims to simplify the
translation between database rows and the PHP object model. The
primary use case for Doctrine are therefore applications that
utilize the Object-Oriented Programming Paradigm. For applications
that do not primarily work with objects Doctrine 2 is not suited very
that do not primarily work with objects Doctrine ORM is not suited very
well.
Requirements
------------
Doctrine 2 requires a minimum of PHP 7.1. For greatly improved
Doctrine ORM requires a minimum of PHP 7.1. For greatly improved
performance it is also recommended that you use APC with PHP.
Doctrine 2 Packages
Doctrine ORM Packages
-------------------
Doctrine 2 is divided into three main packages.
Doctrine ORM is divided into three main packages.
- Common
- DBAL (includes Common)
@@ -166,8 +166,8 @@ did not find a way yet, if you did, please contact us).
The EntityManager
~~~~~~~~~~~~~~~~~
The ``EntityManager`` class is a central access point to the ORM
functionality provided by Doctrine 2. The ``EntityManager`` API is
The ``EntityManager`` class is a central access point to the
functionality provided by Doctrine ORM. The ``EntityManager`` API is
used to manage the persistence of your objects and to query for
persistent objects.

View File

@@ -423,7 +423,7 @@ performance of Doctrine. The allocationSize specifies by how much
values the sequence is incremented whenever the next value is
retrieved. If this is larger than 1 (one) Doctrine can generate
identifier values for the allocationSizes amount of entities. In
the above example with ``allocationSize=100`` Doctrine 2 would only
the above example with ``allocationSize=100`` Doctrine ORM would only
need to access the sequence once to generate the identifiers for
100 new entities.
@@ -451,7 +451,7 @@ need to access the sequence once to generate the identifiers for
Composite Keys
~~~~~~~~~~~~~~
With Doctrine 2 you can use composite primary keys, using ``@Id`` on more then
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
@@ -484,15 +484,11 @@ according to the used database platform.
.. _reference-basic-mapping-custom-mapping-types:
.. versionadded: 2.3
For more control over column quoting the ``Doctrine\ORM\Mapping\QuoteStrategy`` interface
was introduced in 2.3. It is invoked for every column, table, alias and other
was introduced in ORM. It is invoked for every column, table, alias and other
SQL names. You can implement the QuoteStrategy and set it by calling
``Doctrine\ORM\Configuration#setQuoteStrategy()``.
.. versionadded: 2.4
The ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name.
You can use it with the following code:

View File

@@ -75,7 +75,7 @@ Iterating results
~~~~~~~~~~~~~~~~~
An alternative solution for bulk updates is to use the
``Query#iterate()`` facility to iterate over the query results step
``Query#toIterable()`` facility to iterate over the query results step
by step instead of loading the whole result into memory at once.
The following example shows how to do this, combining the iteration
with the batching strategy that was already used for bulk inserts:
@@ -86,9 +86,7 @@ with the batching strategy that was already used for bulk inserts:
$batchSize = 20;
$i = 1;
$q = $em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
foreach ($iterableResult as $row) {
$user = $row[0];
foreach ($q->toIterable() as $user) {
$user->increaseCredit();
$user->calculateNewBonuses();
if (($i % $batchSize) === 0) {
@@ -137,7 +135,7 @@ Iterating results
~~~~~~~~~~~~~~~~~
An alternative solution for bulk deletes is to use the
``Query#iterate()`` facility to iterate over the query results step
``Query#toIterable()`` facility to iterate over the query results step
by step instead of loading the whole result into memory at once.
The following example shows how to do this:
@@ -147,9 +145,8 @@ The following example shows how to do this:
$batchSize = 20;
$i = 1;
$q = $em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
while (($row = $iterableResult->next()) !== false) {
$em->remove($row[0]);
foreach($q->toIterable() as $row) {
$em->remove($row);
if (($i % $batchSize) === 0) {
$em->flush(); // Executes all deletions.
$em->clear(); // Detaches all objects from Doctrine!
@@ -168,20 +165,18 @@ The following example shows how to do this:
Iterating Large Results for Data-Processing
-------------------------------------------
You can use the ``iterate()`` method just to iterate over a large
result and no UPDATE or DELETE intention. The ``IterableResult``
instance returned from ``$query->iterate()`` implements the
Iterator interface so you can process a large result without memory
You can use the ``toIterable()`` method just to iterate over a large
result and no UPDATE or DELETE intention. ``$query->toIterable()`` returns ``iterable``
so you can process a large result without memory
problems using the following approach:
.. code-block:: php
<?php
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
foreach ($iterableResult as $row) {
// do stuff with the data in the row, $row[0] is always the object
foreach ($q->toIterable() as $row) {
// do stuff with the data in the row
// detach from Doctrine, so that it can be Garbage-Collected immediately
$this->_em->detach($row[0]);
}

View File

@@ -30,7 +30,7 @@ Deferred Explicit
The deferred explicit policy is similar to the deferred implicit
policy in that it detects changes through a property-by-property
comparison at commit time. The difference is that Doctrine 2 only
comparison at commit time. The difference is that Doctrine ORM only
considers entities that have been explicitly marked for change detection
through a call to EntityManager#persist(entity) or through a save
cascade. All other entities are skipped. This policy therefore

View File

@@ -112,8 +112,6 @@ You need to register your applications EntityManager to the console tool
to make use of the tasks by creating a ``cli-config.php`` file with the
following content:
On Doctrine 2.4 and above:
.. code-block:: php
<?php
@@ -126,19 +124,3 @@ On Doctrine 2.4 and above:
$entityManager = GetEntityManager();
return ConsoleRunner::createHelperSet($entityManager);
On Doctrine 2.3 and below:
.. code-block:: php
<?php
// cli-config.php
require_once 'my_bootstrap.php';
// Any way to access the EntityManager from your application
$em = GetMyEntityManager();
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));

View File

@@ -458,8 +458,6 @@ Get all users that have no phonenumber
Get all instances of a specific type, for use with inheritance
hierarchies:
.. versionadded:: 2.1
.. code-block:: php
<?php
@@ -469,24 +467,20 @@ hierarchies:
Get all users visible on a given website that have chosen certain gender:
.. versionadded:: 2.2
.. code-block:: php
<?php
$query = $em->createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)');
.. versionadded:: 2.4
Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys:
The IDENTITY() DQL function also works for composite primary keys
.. code-block:: php
<?php
$query = $em->createQuery("SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1");
Joins between entities without associations were not possible until version
2.4, where you can generate an arbitrary join with the following syntax:
Joins between entities without associations are available,
where you can generate an arbitrary join with the following syntax:
.. code-block:: php
@@ -534,8 +528,6 @@ You use the partial syntax when joining as well:
"NEW" Operator Syntax
^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 2.4
Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries.
- When using ``SELECT NEW`` you don't need to specify a mapped entity.
@@ -657,6 +649,16 @@ The same restrictions apply for the reference of related entities.
of the query. Additionally Deletes of specified entities are *NOT*
cascaded to related entities even if specified in the metadata.
Comments in queries
-------------------
We can use comments with the SQL syntax of comments.
.. code-block:: sql
SELECT u FROM MyProject\Model\User u
-- my comment
WHERE u.age > 20 -- comment at the end of a line
Functions, Operators, Aggregates
--------------------------------
@@ -900,7 +902,7 @@ Class Table Inheritance
is an inheritance mapping strategy where each class in a hierarchy
is mapped to several tables: its own table and the tables of all
parent classes. The table of a child class is linked to the table
of a parent class through a foreign key constraint. Doctrine 2
of a parent class through a foreign key constraint. Doctrine ORM
implements this strategy through the use of a discriminator column
in the topmost table of the hierarchy because this is the easiest
way to achieve polymorphic queries with Class Table Inheritance.

View File

@@ -1,7 +1,7 @@
Events
======
Doctrine 2 features a lightweight event system that is part of the
Doctrine ORM features a lightweight event system that is part of the
Common package. Doctrine uses it to dispatch system events, mainly
:ref:`lifecycle events <reference-events-lifecycle-events>`.
You can also use it for your own custom events.
@@ -70,7 +70,7 @@ method.
<?php
$evm->removeEventListener(array(self::preFoo, self::postFoo), $this);
The Doctrine 2 event system also has a simple concept of event
The Doctrine ORM event system also has a simple concept of event
subscribers. We can define a simple ``TestEventSubscriber`` class
which implements the ``\Doctrine\Common\EventSubscriber`` interface
and implements a ``getSubscribedEvents()`` method which returns an
@@ -124,7 +124,7 @@ Now you can test the ``$eventSubscriber`` instance to see if the
Naming convention
~~~~~~~~~~~~~~~~~
Events being used with the Doctrine 2 EventManager are best named
Events being used with the Doctrine ORM EventManager are best named
with camelcase and the value of the corresponding constant should
be the name of the constant itself, even with spelling. This has
several reasons:
@@ -195,10 +195,10 @@ events during the life-time of their registered entities.
.. warning::
Note that, when using ``Doctrine\ORM\AbstractQuery#iterate()``, ``postLoad``
Note that, when using ``Doctrine\ORM\AbstractQuery#toIterable()``, ``postLoad``
events will be executed immediately after objects are being hydrated, and therefore
associations are not guaranteed to be initialized. It is not safe to combine
usage of ``Doctrine\ORM\AbstractQuery#iterate()`` and ``postLoad`` event
usage of ``Doctrine\ORM\AbstractQuery#toIterable()`` and ``postLoad`` event
handlers.
.. warning::
@@ -221,7 +221,7 @@ These can be hooked into by two different types of event
listeners:
- Lifecycle Callbacks are methods on the entity classes that are
called when the event is triggered. As of v2.4 they receive some kind
called when the event is triggered. They receive some kind
of ``EventArgs`` instance.
- Lifecycle Event Listeners and Subscribers are classes with specific callback
methods that receives some kind of ``EventArgs`` instance.
@@ -383,9 +383,7 @@ defined on your ``User`` model.
Lifecycle Callbacks Event Argument
-----------------------------------
.. versionadded:: 2.4
Since 2.4 the triggered event is given to the lifecycle-callback.
The triggered event is also given to the lifecycle-callback.
With the additional argument you have access to the
``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
@@ -761,8 +759,6 @@ EntityManager.
Entity listeners
----------------
.. versionadded:: 2.4
An entity listener is a lifecycle listener class used for an entity.
- The entity listener's mapping may be applied to an entity class or mapped superclass.

View File

@@ -80,7 +80,7 @@ You can solve this exception by:
How can I filter an association?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Natively you can't filter associations in 2.0 and 2.1. You should use DQL queries to query for the filtered set of entities.
You should use DQL queries to query for the filtered set of entities.
I call clear() on a One-To-Many collection but the entities are not deleted
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -98,7 +98,7 @@ How can I add columns to a many-to-many table?
The many-to-many association is only supporting foreign keys in the table definition
To work with many-to-many tables containing extra columns you have to use the
foreign keys as primary keys feature of Doctrine introduced in version 2.1.
foreign keys as primary keys feature of Doctrine ORM.
See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`.
@@ -128,10 +128,10 @@ See the previous question for a solution to this task.
Inheritance
-----------
Can I use Inheritance with Doctrine 2?
Can I use Inheritance with Doctrine ORM?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2.
Yes, you can use Single- or Joined-Table Inheritance in ORM.
See the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>` for
the details.

View File

@@ -1,9 +1,7 @@
Filters
=======
.. versionadded:: 2.2
Doctrine 2.2 features a filter system that allows the developer to add SQL to
Doctrine ORM features a filter system that allows the developer to add SQL to
the conditional clauses of queries, regardless the place where the SQL is
generated (e.g. from a DQL query, or by loading associated entities).

View File

@@ -43,7 +43,7 @@ in scenarios where data is loaded for read-only purposes.
Read-Only Entities
------------------
Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping
You can mark entities as read only (See metadata mapping
references for details). This means that the entity marked as read only is never considered
for updates, which means when you call flush on the EntityManager these entities are skipped
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing

View File

@@ -185,7 +185,7 @@ Class Table Inheritance
is an inheritance mapping strategy where each class in a hierarchy
is mapped to several tables: its own table and the tables of all
parent classes. The table of a child class is linked to the table
of a parent class through a foreign key constraint. Doctrine 2
of a parent class through a foreign key constraint. Doctrine ORM
implements this strategy through the use of a discriminator column
in the topmost table of the hierarchy because this is the easiest
way to achieve polymorphic queries with Class Table Inheritance.

View File

@@ -6,7 +6,7 @@ Therefore we think it is very important to be honest about the
current limitations to our users. Much like every other piece of
software Doctrine2 is not perfect and far from feature complete.
This section should give you an overview of current limitations of
Doctrine 2 as well as critical known issues that you should know
Doctrine ORM as well as critical known issues that you should know
about.
Current Limitations
@@ -107,7 +107,7 @@ to the same entity.
Behaviors
~~~~~~~~~
Doctrine 2 will **never** include a behavior system like Doctrine 1
Doctrine ORM will **never** include a behavior system like Doctrine 1
in the core library. We don't think behaviors add more value than
they cost pain and debugging hell. Please see the many different
blog posts we have written on this topics:
@@ -115,9 +115,9 @@ blog posts we have written on this topics:
- `Doctrine2 "Behaviors" in a Nutshell <http://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_
- `A re-usable Versionable behavior for Doctrine2 <http://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_
- `Write your own ORM on top of Doctrine2 <http://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_
- `Doctrine 2 Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
- `Doctrine ORM Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
Doctrine 2 has enough hooks and extension points so that **you** can
Doctrine ORM has enough hooks and extension points so that **you** can
add whatever you want on top of it. None of this will ever become
core functionality of Doctrine2 however, you will have to rely on
third party extensions for magical behaviors.
@@ -126,9 +126,9 @@ Nested Set
~~~~~~~~~~
NestedSet was offered as a behavior in Doctrine 1 and will not be
included in the core of Doctrine 2. However there are already two
included in the core of Doctrine ORM. However there are already two
extensions out there that offer support for Nested Set with
Doctrine 2:
ORM:
- `Doctrine2 Hierarchical-Structural Behavior <http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
@@ -149,9 +149,9 @@ Identifier Quoting and Legacy Databases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For compatibility reasons between all the supported vendors and
edge case problems Doctrine 2 does **NOT** do automatic identifier
edge case problems Doctrine ORM does **NOT** do automatic identifier
quoting. This can lead to problems when trying to get
legacy-databases to work with Doctrine 2.
legacy-databases to work with Doctrine ORM.
- You can quote column-names as described in the

View File

@@ -147,7 +147,7 @@ ClassMetadata
-------------
The last piece you need to know and understand about metadata in
Doctrine 2 is the API of the ``ClassMetadata`` classes. You need to
Doctrine ORM is the API of the ``ClassMetadata`` classes. You need to
be familiar with them in order to implement your own drivers but
more importantly to retrieve mapping information for a certain
entity when needed.

View File

@@ -1,8 +1,6 @@
Implementing a NamingStrategy
==============================
.. versionadded:: 2.3
Using a naming strategy you can provide rules for generating database identifiers,
column or table names. This feature helps
reduce the verbosity of the mapping document, eliminating repetitive noise (eg: ``TABLE_``).

View File

@@ -80,9 +80,7 @@ with inheritance hierarchies.
The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well.
.. versionadded:: 2.4
Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause
The ``SELECT`` clause can be generated
from a ``ResultSetMappingBuilder``. You can either cast the builder
object to ``(string)`` and the DQL aliases are used as SQL table aliases
or use the ``generateSelectClause($tableAliases)`` method and pass

View File

@@ -1,7 +1,7 @@
PHP Mapping
===========
Doctrine 2 also allows you to provide the ORM metadata in the form
Doctrine ORM also allows you to provide the ORM metadata in the form
of plain PHP code using the ``ClassMetadata`` API. You can write
the code in PHP files or inside of a static function named
``loadMetadata($class)`` on the entity class itself.

View File

@@ -354,6 +354,7 @@ a querybuilder instance into a Query object:
// Execute Query
$result = $query->getResult();
$iterableResult = $query->toIterable();
$single = $query->getSingleResult();
$array = $query->getArrayResult();
$scalar = $query->getScalarResult();

View File

@@ -5,7 +5,7 @@ Doctrine Console
----------------
The Doctrine Console is a Command Line Interface tool for simplifying common
administration tasks during the development of a project that uses Doctrine 2.
administration tasks during the development of a project that uses ORM.
Take a look at the :doc:`Installation and Configuration <configuration>`
chapter for more information how to setup the console command.
@@ -27,64 +27,34 @@ Configuration
~~~~~~~~~~~~~
Whenever the ``doctrine`` command line tool is invoked, it can
access all Commands that were registered by developer. There is no
access all Commands that were registered by a developer. There is no
auto-detection mechanism at work. The Doctrine binary
already registers all the commands that currently ship with
Doctrine DBAL and ORM. If you want to use additional commands you
have to register them yourself.
All the commands of the Doctrine Console require access to the ``EntityManager``
or ``DBAL`` Connection. You have to inject them into the console application
using so called Helper-Sets. This requires either the ``db``
or the ``em`` helpers to be defined in order to work correctly.
All the commands of the Doctrine Console require access to the
``EntityManager``. You have to inject it into the console application with
``ConsoleRunner::createHelperSet``. Whenever you invoke the Doctrine
binary, it searches the current directory for the file ``cli-config.php``.
This file contains the project-specific configuration.
Whenever you invoke the Doctrine binary the current folder is searched for a
``cli-config.php`` file. This file contains the project specific configuration:
Here is an example of a the project-specific ``cli-config.php``:
.. code-block:: php
<?php
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($conn)
));
$cli->setHelperSet($helperSet);
use Doctrine\ORM\Tools\Console\ConsoleRunner;
When dealing with the ORM package, the EntityManagerHelper is
required:
// replace this with the path to your own project bootstrap file.
require_once 'bootstrap.php';
.. code-block:: php
// replace with mechanism to retrieve EntityManager in your app
$entityManager = GetEntityManager();
<?php
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
$cli->setHelperSet($helperSet);
return ConsoleRunner::createHelperSet($entityManager);
The HelperSet instance has to be generated in a separate file (i.e.
``cli-config.php``) that contains typical Doctrine bootstrap code
and predefines the needed HelperSet attributes mentioned above. A
sample ``cli-config.php`` file looks as follows:
.. code-block:: php
<?php
// cli-config.php
require_once 'my_bootstrap.php';
// Any way to access the EntityManager from your application
$em = GetMyEntityManager();
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
It is important to define a correct HelperSet that Doctrine binary
script will ultimately use. The Doctrine Binary will automatically
find the first instance of HelperSet in the global variable
namespace and use this.
.. note::
.. note::
You have to adjust this snippet for your specific application or framework
and use their facilities to access the Doctrine EntityManager and
@@ -375,7 +345,7 @@ First you need to retrieve the metadata instances with the
$em->getConnection()->getSchemaManager()
)
);
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();
@@ -412,7 +382,7 @@ You can also reverse engineer a database using the
Runtime vs Development Mapping Validation
-----------------------------------------
For performance reasons Doctrine 2 has to skip some of the
For performance reasons Doctrine ORM has to skip some of the
necessary validation of metadata mappings. You have to execute
this validation in your development workflow to verify the
associations are correctly defined.
@@ -517,4 +487,3 @@ HelperSet, like it is described in the configuration section.
// Runs console application
$cli->run();

View File

@@ -16,13 +16,13 @@ transaction. Without any explicit transaction demarcation from your
side, this quickly results in poor performance because transactions
are not cheap.
For the most part, Doctrine 2 already takes care of proper
For the most part, Doctrine ORM already takes care of proper
transaction demarcation for you: All the write operations
(INSERT/UPDATE/DELETE) are queued until ``EntityManager#flush()``
is invoked which wraps all of these changes in a single
transaction.
However, Doctrine 2 also allows (and encourages) you to take over
However, Doctrine ORM also allows (and encourages) you to take over
and control transaction demarcation yourself.
These are two ways to deal with transactions when using the
@@ -154,7 +154,7 @@ occurred you should do that with a new ``EntityManager``.
Locking Support
---------------
Doctrine 2 offers support for Pessimistic- and Optimistic-locking
Doctrine ORM offers support for Pessimistic- and Optimistic-locking
strategies natively. This allows to take very fine-grained control
over what kind of locking is required for your Entities in your
application.
@@ -367,7 +367,7 @@ And the change headline action (POST Request):
Pessimistic Locking
~~~~~~~~~~~~~~~~~~~
Doctrine 2 supports Pessimistic Locking at the database level. No
Doctrine ORM supports Pessimistic Locking at the database level. No
attempt is being made to implement pessimistic locking inside
Doctrine, rather vendor-specific and ANSI-SQL commands are used to
acquire row-level locks. Every Entity can be part of a pessimistic
@@ -376,11 +376,11 @@ lock, there is no special metadata required to use this feature.
However for Pessimistic Locking to work you have to disable the
Auto-Commit Mode of your Database and start a transaction around
your pessimistic lock use-case using the "Approach 2: Explicit
Transaction Demarcation" described above. Doctrine 2 will throw an
Transaction Demarcation" described above. Doctrine ORM will throw an
Exception if you attempt to acquire an pessimistic lock and no
transaction is running.
Doctrine 2 currently supports two pessimistic lock modes:
Doctrine ORM currently supports two pessimistic lock modes:
- Pessimistic Write

View File

@@ -407,7 +407,7 @@ There are two approaches to handle this problem in your code:
Transitive persistence / Cascade Operations
-------------------------------------------
Doctrine 2 provides a mechanism for transitive persistence through cascading of certain operations.
Doctrine ORM provides a mechanism for transitive persistence through cascading of certain operations.
Each association to another entity or a collection of
entities can be configured to automatically cascade the following operations to the associated entities:
``persist``, ``remove``, ``merge``, ``detach``, ``refresh`` or ``all``.

View File

@@ -27,7 +27,7 @@ Work that have not yet been persisted are lost.
.. note::
Doctrine does NEVER touch the public API of methods in your entity
Doctrine does NEVER touch the public API of methods in your entity
classes (like getters and setters) nor the constructor method.
Instead, it uses reflection to get/set data from/to your entity objects.
When Doctrine fetches data from DB and saves it back,
@@ -48,12 +48,12 @@ headline "Hello World" with the ID 1234:
<?php
$article = $entityManager->find('CMS\Article', 1234);
$article->setHeadline('Hello World dude!');
$article2 = $entityManager->find('CMS\Article', 1234);
echo $article2->getHeadline();
In this case the Article is accessed from the entity manager twice,
but modified in between. Doctrine 2 realizes this and will only
but modified in between. Doctrine ORM realizes this and will only
ever give you access to one instance of the Article with ID 1234,
no matter how often do you retrieve it from the EntityManager and
even no matter what kind of Query method you are using (find,
@@ -100,25 +100,25 @@ from newly opened EntityManager.
{
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @Column(type="string") */
private $headline;
/** @ManyToOne(targetEntity="User") */
private $author;
/** @OneToMany(targetEntity="Comment", mappedBy="article") */
private $comments;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getAuthor() { return $this->author; }
public function getComments() { return $this->comments; }
}
$article = $em->find('Article', 1);
This code only retrieves the ``Article`` instance with id 1 executing
@@ -139,22 +139,22 @@ your code. See the following code:
<?php
$article = $em->find('Article', 1);
// accessing a method of the user instance triggers the lazy-load
echo "Author: " . $article->getAuthor()->getName() . "\n";
// Lazy Loading Proxies pass instanceof tests:
if ($article->getAuthor() instanceof User) {
// a User Proxy is a generated "UserProxy" class
}
// accessing the comments as an iterator triggers the lazy-load
// retrieving ALL the comments of this article from the database
// using a single SELECT statement
foreach ($article->getComments() as $comment) {
echo $comment->getText() . "\n\n";
}
// Article::$comments passes instanceof tests for the Collection interface
// But it will NOT pass for the ArrayCollection interface
if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) {
@@ -174,7 +174,7 @@ methods along the lines of the ``getName()`` method shown below:
{
// lazy loading code
}
public function getName()
{
$this->_load();
@@ -275,7 +275,7 @@ which means that its persistent state will be deleted once
for and appear in query and collection results. See
the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
for more information.
Example:
@@ -318,7 +318,7 @@ Deleting an object with all its associated objects can be achieved
in multiple ways with very different performance impacts.
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM
will fetch this association. If its a Single association it will
pass this entity to
``EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()``.
@@ -653,7 +653,7 @@ just created via the "new" operator).
Querying
--------
Doctrine 2 provides the following ways, in increasing level of
Doctrine ORM provides the following ways, in increasing level of
power and flexibility, to query for persistent objects. You should
always start with the simplest one that suits your needs.
@@ -700,13 +700,13 @@ methods on a repository as follows:
<?php
// $em instanceof EntityManager
// All users that are 20 years old
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20));
// All users that are 20 years old and have a surname of 'Miller'
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller'));
// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
@@ -742,7 +742,7 @@ examples are equivalent:
<?php
// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
// A single user by its nickname (__call magic)
$user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb');
@@ -757,8 +757,6 @@ Additionally, you can just count the result of the provided conditions when you
By Criteria
~~~~~~~~~~~
.. versionadded:: 2.3
The Repository implement the ``Doctrine\Common\Collections\Selectable``
interface. That means you can build ``Doctrine\Common\Collections\Criteria``
and pass them to the ``matching($criteria)`` method.
@@ -800,7 +798,7 @@ A DQL query is represented by an instance of the
<?php
// $em instanceof EntityManager
// All users with an age between 20 and 30 (inclusive).
$q = $em->createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30");
$users = $q->getResult();
@@ -845,18 +843,18 @@ in a central location.
<?php
namespace MyDomain\Model;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
*/
class User
{
}
class UserRepository extends EntityRepository
{
public function getAllAdminUsers()
@@ -872,7 +870,7 @@ You can access your repository now by calling:
<?php
// $em instanceof EntityManager
$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();

View File

@@ -8,9 +8,7 @@ The XML driver is backed by an XML Schema document that describes
the structure of a mapping document. The most recent version of the
XML Schema document is available online at
`https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd <https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd>`_.
In order to point to the latest version of the document of a
particular stable release branch, just append the release number,
i.e.: doctrine-mapping-2.0.xsd The most convenient way to work with
The most convenient way to work with
XML mapping files is to use an IDE/editor that can provide
code-completion based on such an XML Schema document. The following
is an outline of a XML mapping document with the proper xmlns/xsi
@@ -208,10 +206,10 @@ Optional attributes:
- **inheritance-type** - The type of inheritance, defaults to none. A
more detailed description follows in the
*Defining Inheritance Mappings* section.
- **read-only** - (>= 2.1) Specifies that this entity is marked as read only and not
- **read-only** - Specifies that this entity is marked as read only and not
considered for change-tracking. Entities of this type can be persisted
and removed though.
- **schema** - (>= 2.5) The schema the table lies in, for platforms that support schemas
- **schema** - The schema the table lies in, for platforms that support schemas
Defining Fields
~~~~~~~~~~~~~~~
@@ -293,7 +291,7 @@ Defining Identity and Generator Strategies
An entity has to have at least one ``<id />`` element. For
composite keys you can specify more than one id-element, however
surrogate keys are recommended for use with Doctrine 2. The Id
surrogate keys are recommended for use with Doctrine ORM. The Id
field allows to define properties of the identifier and allows a
subset of the ``<field />`` element attributes:

View File

@@ -1,11 +1,9 @@
Composite and Foreign Keys as Primary Key
=========================================
.. versionadded:: 2.1
Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept
and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases.
For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as
Doctrine ORM supports composite primary keys natively. Composite keys are a very powerful relational database concept
and we took good care to make sure Doctrine ORM supports as many of the composite primary key use-cases.
For Doctrine ORM composite keys of primitive data-types are supported, even foreign keys as
primary keys are supported.
This tutorial shows how the semantics of composite primary keys work and how they map to the database.
@@ -19,7 +17,7 @@ the ID fields have to have their values set before you call ``EntityManager#pers
Primitive Types only
~~~~~~~~~~~~~~~~~~~~
Even in version 2.0 you can have composite keys as long as they only consist of the primitive types
You can have composite keys as long as they only consist of the primitive types
``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name
and year of production as primary keys:
@@ -120,10 +118,6 @@ and to ``year`` to the related entities.
Identity through foreign Entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
Identity through foreign entities is only supported with Doctrine 2.1
There are tons of use-cases where the identity of an Entity should be determined by the entity
of one or many parent entities.

View File

@@ -3,21 +3,21 @@ Extra Lazy Associations
.. versionadded:: 2.1
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog.
where posts can be commented, you always have to assume that a post draws hundreds of comments.
In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This
In Doctrine ORM if you accessed an association it would always get loaded completely into memory. This
can lead to pretty serious performance problems, if your associations contain several hundreds or thousands
of entities.
With Doctrine 2.1 a feature called **Extra Lazy** is introduced for associations. Associations
Doctrine ORM includes a feature called **Extra Lazy** for associations. Associations
are marked as **Lazy** by default, which means the whole collection object for an association is populated
the first time its accessed. If you mark an association as extra lazy the following methods on collections
can be called without triggering a full load of the collection:
- ``Collection#contains($entity)``
- ``Collection#containsKey($key)`` (available with Doctrine 2.5)
- ``Collection#containsKey($key)``
- ``Collection#count()``
- ``Collection#get($key)`` (available with Doctrine 2.4)
- ``Collection#get($key)``
- ``Collection#slice($offset, $length = null)``
For each of the above methods the following semantics apply:
@@ -25,7 +25,7 @@ For each of the above methods the following semantics apply:
- For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database.
- For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed.
Additionally even with Doctrine 2.0 the following methods do not trigger the collection load:
Additionally even with Doctrine ORM the following methods do not trigger the collection load:
- ``Collection#add($entity)``
- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does

View File

@@ -20,8 +20,7 @@ development is said to use the *Database First* approach to Doctrine.
In this workflow you would modify the database schema first and then
regenerate the PHP code to use with this schema. You need a flexible
code-generator for this task and up to Doctrine 2.2, the code generator hasn't
been flexible enough to achieve this.
code-generator for this task.
We spinned off a subproject, Doctrine CodeGenerator, that will fill this gap and
allow you to do *Database First* development.

View File

@@ -23,15 +23,10 @@ installed:
The code of this tutorial is `available on Github <https://github.com/doctrine/doctrine2-orm-tutorial>`_.
.. note::
This tutorial assumes you work with **Doctrine 2.6** and above.
Some of the code will not work with lower versions.
What is Doctrine?
-----------------
Doctrine 2 is an `object-relational mapper (ORM) <https://en.wikipedia.org/wiki/Object-relational_mapping>`_
Doctrine ORM is an `object-relational mapper (ORM) <https://en.wikipedia.org/wiki/Object-relational_mapping>`_
for PHP 7.1+ that provides transparent persistence for PHP objects. It uses the Data Mapper
pattern at the heart, aiming for a complete separation of your domain/business
logic from the persistence in a relational database management system.
@@ -123,7 +118,7 @@ Obtaining the EntityManager
Doctrine's public interface is through the ``EntityManager``. This class
provides access points to the complete lifecycle management for your entities,
and transforms entities from and back to persistence. You have to
configure and create it to use your entities with Doctrine 2. I
configure and create it to use your entities with Doctrine ORM. I
will show the configuration steps and then discuss them step by
step:
@@ -133,9 +128,9 @@ step:
// bootstrap.php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
require_once "vendor/autoload.php";
// Create a simple "default" Doctrine ORM configuration for Annotations
$isDevMode = true;
$proxyDir = null;
@@ -145,13 +140,13 @@ step:
// or if you prefer yaml or XML
//$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
//$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
// database configuration parameters
$conn = array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/db.sqlite',
);
// obtaining the entity manager
$entityManager = EntityManager::create($conn, $config);
@@ -193,7 +188,7 @@ defined entity classes and their metadata. For this tool to work, a
<?php
// cli-config.php
require_once "bootstrap.php";
return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);
Now call the Doctrine command-line tool:
@@ -299,14 +294,14 @@ but you only need to choose one.
*/
class Product
{
/**
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
/**
* @ORM\Column(type="string")
/**
* @ORM\Column(type="string")
*/
protected $name;
@@ -562,7 +557,7 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Entity
* @ORM\Table(name="users")
*/
class User
@@ -610,7 +605,7 @@ foreign keys through their own identities.
For every foreign key you either have a Doctrine ManyToOne or OneToOne
association. On the inverse sides of these foreign keys you can have
OneToMany associations. Obviously you can have ManyToMany associations
that connect two tables with each other through a join table with
that connect two tables with each other through a join table with
two foreign keys.
Now that you know the basics about references in Doctrine, we can extend the
@@ -679,8 +674,8 @@ careful to implement a bidirectional reference in the domain model.
The concept of owning or inverse side of a relation is central to
this notion and should always be kept in mind. The following
assumptions are made about relations and have to be followed to be
able to work with Doctrine 2. These assumptions are not unique to
Doctrine 2 but are best practices in handling database relations
able to work with Doctrine ORM. These assumptions are not unique to
Doctrine ORM but are best practices in handling database relations
and Object-Relational Mapping.
- In a one-to-one relation, the entity holding the foreign key of
@@ -832,14 +827,14 @@ the ``Product`` before:
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Entity
* @ORM\Table(name="bugs")
*/
class Bug
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
@@ -936,8 +931,8 @@ the ``Product`` before:
Here we have the entity, id and primitive type definitions.
For the "created" field we have used the ``datetime`` type,
which translates the YYYY-mm-dd HH:mm:ss database format
For the "created" field we have used the ``datetime`` type,
which translates the YYYY-mm-dd HH:mm:ss database format
into a PHP DateTime instance and back.
After the field definitions, the two qualified references to the
@@ -975,8 +970,8 @@ Finally, we'll add metadata mappings for the ``User`` entity.
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @var int
*/
@@ -1215,10 +1210,10 @@ The console output of this script is then:
As a last resort you can still use Native SQL and a description of the
result set to retrieve entities from the database. DQL boils down to a
Native SQL statement and a ``ResultSetMapping`` instance itself. Using
Native SQL you could even use stored procedures for data retrieval, or
make use of advanced non-portable database queries like PostgreSql's
result set to retrieve entities from the database. DQL boils down to a
Native SQL statement and a ``ResultSetMapping`` instance itself. Using
Native SQL you could even use stored procedures for data retrieval, or
make use of advanced non-portable database queries like PostgreSql's
recursive queries.
@@ -1231,7 +1226,7 @@ objects only from Doctrine however. For a simple list view like the
previous one we only need read access to our entities and can
switch the hydration from objects to simple PHP arrays instead.
Hydration can be an expensive process so only retrieving what you need can
Hydration can be an expensive process so only retrieving what you need can
yield considerable performance benefits for read-only requests.
Implementing the same list view as before using array hydration we

View File

@@ -1,9 +1,7 @@
Pagination
==========
.. versionadded:: 2.2
Starting with version 2.2 Doctrine ships with a Paginator for DQL queries. It
Doctrine ORM ships with a Paginator for DQL queries. It
has a very simple API and implements the SPL interfaces ``Countable`` and
``IteratorAggregate``.

View File

@@ -1,13 +1,9 @@
Working with Indexed Associations
=================================
.. note::
This feature is available from version 2.1 of Doctrine.
Doctrine 2 collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in
Doctrine ORM collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in
the first version of Doctrine keys retrieved from the database were always numerical unless ``INDEX BY``
was used. Starting with Doctrine 2.1 you can index your collections by a value in the related entity.
was used. You can index your collections by a value in the related entity.
This is a first step towards full ordered hashmap support through the Doctrine ORM.
The feature works like an implicit ``INDEX BY`` for the selected association but has several
downsides also:

View File

@@ -19,14 +19,28 @@
namespace Doctrine\ORM;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Collections\Collection;
use Countable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\Persistence\Mapping\MappingException;
use Traversable;
use function array_map;
use function array_shift;
use function count;
use function is_array;
use function is_numeric;
use function is_object;
use function is_scalar;
use function iterator_count;
use function iterator_to_array;
use function ksort;
use function reset;
use function serialize;
use function sha1;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@@ -406,8 +420,8 @@ abstract class AbstractQuery
return $value;
}
if ($value instanceof Collection) {
$value = $value->toArray();
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
}
if (is_array($value)) {
@@ -433,7 +447,7 @@ abstract class AbstractQuery
if ($value === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
}
} catch (MappingException | ORMMappingException $e) {
} catch (ORMMappingException $e) {
// Silence any mapping exceptions. These can occur if the object in
// question is not a mapped entity, in which case we just don't do
// any preparation on the value.
@@ -916,6 +930,8 @@ abstract class AbstractQuery
* Executes the query and returns an IterableResult that can be used to incrementally
* iterate over the result.
*
* @deprecated
*
* @param ArrayCollection|array|null $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
*
@@ -923,6 +939,11 @@ abstract class AbstractQuery
*/
public function iterate($parameters = null, $hydrationMode = null)
{
@trigger_error(
'Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
E_USER_DEPRECATED
);
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
@@ -937,6 +958,33 @@ abstract class AbstractQuery
return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints);
}
/**
* Executes the query and returns an iterable that can be used to incrementally
* iterate over the result.
*
* @param ArrayCollection|mixed[] $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
*
* @return iterable<mixed>
*/
public function toIterable(iterable $parameters = [], $hydrationMode = null) : iterable
{
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
if (($this->isCountable($parameters) && count($parameters) !== 0)
|| ($parameters instanceof Traversable && iterator_count($parameters) !== 0)
) {
$this->setParameters($parameters);
}
$rsm = $this->getResultSetMapping();
$stmt = $this->_doExecute();
return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt, $rsm, $this->_hints);
}
/**
* Executes the query.
*
@@ -1170,4 +1218,10 @@ abstract class AbstractQuery
return sha1($query . '-' . serialize($params) . '-' . serialize($hints));
}
/** @param iterable<mixed> $subject */
private function isCountable(iterable $subject) : bool
{
return $subject instanceof Countable || is_array($subject);
}
}

View File

@@ -937,5 +937,3 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['defaultQueryHints'][$name] = $value;
}
}
interface_exists(MappingDriver::class);

View File

@@ -388,6 +388,10 @@ use function trigger_error;
* @throws ORMInvalidArgumentException
* @throws TransactionRequiredException
* @throws ORMException
*
* @template T
* @psalm-param class-string<T> $entityName
* @psalm-return ?T
*/
public function find($className, $id, $lockMode = null, $lockVersion = null)
{
@@ -733,6 +737,10 @@ use function trigger_error;
* @param string $entityName The name of the entity.
*
* @return ObjectRepository|EntityRepository The repository class.
*
* @template T
* @psalm-param class-string<T> $entityName
* @psalm-return EntityRepository<T>
*/
public function getRepository($entityName)
{

View File

@@ -153,6 +153,10 @@ interface EntityManagerInterface extends ObjectManager
* @return object|null The entity reference.
*
* @throws ORMException
*
* @template T
* @psalm-param class-string<T> $entityName
* @psalm-return ?T
*/
public function getReference($entityName, $id);

View File

@@ -21,9 +21,13 @@ namespace Doctrine\ORM;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Selectable;
use Doctrine\Common\Inflector\Inflector;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Persistence\ObjectRepository;
use const E_USER_DEPRECATED;
use function lcfirst;
use function trigger_error;
/**
* An EntityRepository serves as a repository for entities with generic as well as
@@ -37,6 +41,10 @@ use Doctrine\Persistence\ObjectRepository;
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*
* @template T
* @template-implements Selectable<int,T>
* @template-implements ObjectRepository<T>
*/
class EntityRepository implements ObjectRepository, Selectable
{
@@ -55,8 +63,13 @@ class EntityRepository implements ObjectRepository, Selectable
*/
protected $_class;
/** @var Inflector */
private static $inflector;
/**
* Initializes a new <tt>EntityRepository</tt>.
*
* @psalm-param Mapping\ClassMetadata<T>
*/
public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
{
@@ -129,9 +142,13 @@ class EntityRepository implements ObjectRepository, Selectable
* Clears the repository, causing all managed entities to become detached.
*
* @return void
*
* @deprecated 2.8 This method is being removed from the ORM and won't have any replacement
*/
public function clear()
{
@trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
$this->_em->clear($this->_class->rootEntityName);
}
@@ -145,6 +162,8 @@ class EntityRepository implements ObjectRepository, Selectable
* @param int|null $lockVersion The lock version.
*
* @return object|null The entity instance or NULL if the entity can not be found.
*
* @psalm-return ?T
*/
public function find($id, $lockMode = null, $lockVersion = null)
{
@@ -155,6 +174,8 @@ class EntityRepository implements ObjectRepository, Selectable
* Finds all entities in the repository.
*
* @return array The entities.
*
* @psalm-return list<T>
*/
public function findAll()
{
@@ -170,6 +191,8 @@ class EntityRepository implements ObjectRepository, Selectable
* @param int|null $offset
*
* @return array The objects.
*
* @psalm-return list<T>
*/
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
{
@@ -185,6 +208,8 @@ class EntityRepository implements ObjectRepository, Selectable
* @param array|null $orderBy
*
* @return object|null The entity instance or NULL if the entity can not be found.
*
* @psalm-return ?T
*/
public function findOneBy(array $criteria, array $orderBy = null)
{
@@ -277,6 +302,8 @@ class EntityRepository implements ObjectRepository, Selectable
* @param \Doctrine\Common\Collections\Criteria $criteria
*
* @return \Doctrine\Common\Collections\Collection
*
* @psalm-return \Doctrine\Common\Collections\Collection<int, T>
*/
public function matching(Criteria $criteria)
{
@@ -302,7 +329,11 @@ class EntityRepository implements ObjectRepository, Selectable
throw ORMException::findByRequiresParameter($method . $by);
}
$fieldName = lcfirst(Inflector::classify($by));
if (self::$inflector === null) {
self::$inflector = InflectorFactory::create()->build();
}
$fieldName = lcfirst(self::$inflector->classify($by));
if (! ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName))) {
throw ORMException::invalidMagicCall($this->_entityName, $fieldName, $method . $by);

View File

@@ -84,5 +84,3 @@ class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs
return $this->className;
}
}
interface_exists(ClassMetadata::class);

View File

@@ -19,14 +19,20 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker;
use PDO;
use const E_USER_DEPRECATED;
use function array_map;
use function end;
use function in_array;
use function trigger_error;
/**
* Base class for all hydrators. A hydrator is a class that provides some form
@@ -110,6 +116,8 @@ abstract class AbstractHydrator
/**
* Initiates a row-by-row hydration.
*
* @deprecated
*
* @param object $stmt
* @param object $resultSetMapping
* @param array $hints
@@ -118,6 +126,11 @@ abstract class AbstractHydrator
*/
public function iterate($stmt, $resultSetMapping, array $hints = [])
{
@trigger_error(
'Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
E_USER_DEPRECATED
);
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
@@ -131,6 +144,42 @@ abstract class AbstractHydrator
return new IterableResult($this);
}
/**
* Initiates a row-by-row hydration.
*
* @param mixed[] $hints
*
* @return iterable<mixed>
*/
public function toIterable(Statement $stmt, ResultSetMapping $resultSetMapping, array $hints = []) : iterable
{
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
$evm = $this->_em->getEventManager();
$evm->addEventListener([Events::onClear], $this);
$this->prepare();
$result = [];
while (true) {
$row = $this->_stmt->fetch(FetchMode::ASSOCIATIVE);
if ($row === false || $row === null) {
$this->cleanup();
break;
}
$this->hydrateRowData($row, $result);
yield end($result);
}
}
/**
* Hydrates all rows returned by the passed statement instance at once.
*
@@ -159,7 +208,7 @@ abstract class AbstractHydrator
/**
* Hydrates a single row returned by the current statement instance during
* row-by-row hydration with {@link iterate()}.
* row-by-row hydration with {@link iterate()} or {@link toIterable()}.
*
* @return mixed
*/
@@ -167,7 +216,7 @@ abstract class AbstractHydrator
{
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
if ( ! $row) {
if ($row === false || $row === null) {
$this->cleanup();
return false;

View File

@@ -23,8 +23,7 @@ namespace Doctrine\ORM\Internal\Hydration;
* Represents a result structure that can be iterated over, hydrating row-by-row
* during the iteration. An IterableResult is obtained by AbstractHydrator#iterate().
*
* @author robo
* @since 2.0
* @deprecated
*/
class IterableResult implements \Iterator
{

View File

@@ -33,7 +33,6 @@ use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Persistence\Mapping\ReflectionService;
use ReflectionClass;
use ReflectionException;
use function assert;
use function interface_exists;
/**
@@ -804,6 +803,3 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
return $this->targetPlatform;
}
}
interface_exists(ClassMetadataInterface::class);
interface_exists(ReflectionService::class);

View File

@@ -693,5 +693,3 @@ class AnnotationDriver extends AbstractAnnotationDriver
return new self($reader, $paths);
}
}
interface_exists(ClassMetadata::class);

View File

@@ -19,19 +19,21 @@
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Inflector\Inflector;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Types\Type;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use function interface_exists;
use function preg_replace;
use function strtolower;
/**
* The DatabaseDriver reverse engineers the mapping metadata from a database.
@@ -81,12 +83,16 @@ class DatabaseDriver implements MappingDriver
*/
private $namespace;
/** @var Inflector */
private $inflector;
/**
* @param AbstractSchemaManager $schemaManager
*/
public function __construct(AbstractSchemaManager $schemaManager)
{
$this->_sm = $schemaManager;
$this->inflector = InflectorFactory::create()->build();
}
/**
@@ -170,6 +176,11 @@ class DatabaseDriver implements MappingDriver
}
}
public function setInflector(Inflector $inflector) : void
{
$this->inflector = $inflector;
}
/**
* {@inheritDoc}
*/
@@ -547,7 +558,7 @@ class DatabaseDriver implements MappingDriver
return $this->namespace . $this->classNamesForTables[$tableName];
}
return $this->namespace . Inflector::classify(strtolower($tableName));
return $this->namespace . $this->inflector->classify(strtolower($tableName));
}
/**
@@ -572,8 +583,6 @@ class DatabaseDriver implements MappingDriver
$columnName = preg_replace('/_id$/', '', $columnName);
}
return Inflector::camelize($columnName);
return $this->inflector->camelize($columnName);
}
}
interface_exists(ClassMetadata::class);

View File

@@ -903,5 +903,3 @@ class XmlDriver extends FileDriver
return ($flag == "true" || $flag == "1");
}
}
interface_exists(ClassMetadata::class);

View File

@@ -845,5 +845,3 @@ class YamlDriver extends FileDriver
return Yaml::parse(file_get_contents($file));
}
}
interface_exists(ClassMetadata::class);

View File

@@ -210,5 +210,3 @@ class ProxyFactory extends AbstractProxyFactory
};
}
}
interface_exists(ClassMetadata::class);

View File

@@ -694,6 +694,14 @@ final class Query extends AbstractQuery
return parent::iterate($parameters, $hydrationMode);
}
/** {@inheritDoc} */
public function toIterable(iterable $parameters = [], $hydrationMode = self::HYDRATE_OBJECT) : iterable
{
$this->setHint(self::HINT_INTERNAL_ITERATION, true);
return parent::toIterable($parameters, $hydrationMode);
}
/**
* {@inheritdoc}
*/

View File

@@ -24,7 +24,7 @@ use Doctrine\ORM\Query\Lexer;
/**
* "ABS" "(" SimpleArithmeticExpression ")"
*
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>

View File

@@ -19,6 +19,8 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\AST\TypedExpression;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\AST\AggregateExpression;
@@ -29,7 +31,7 @@ use Doctrine\ORM\Query\AST\AggregateExpression;
* @since 2.6
* @author Mathew Davies <thepixeldeveloper@icloud.com>
*/
final class CountFunction extends FunctionNode
final class CountFunction extends FunctionNode implements TypedExpression
{
/**
* @var AggregateExpression
@@ -51,4 +53,9 @@ final class CountFunction extends FunctionNode
{
$this->aggregateExpression = $parser->AggregateExpression();
}
public function getReturnType() : Type
{
return Type::getType(Type::INTEGER);
}
}

View File

@@ -24,7 +24,7 @@ use Doctrine\ORM\Query\AST\Node;
/**
* Abstract Function Node.
*
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>

View File

@@ -19,6 +19,8 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\AST\TypedExpression;
use Doctrine\ORM\Query\Lexer;
/**
@@ -32,7 +34,7 @@ use Doctrine\ORM\Query\Lexer;
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class LengthFunction extends FunctionNode
class LengthFunction extends FunctionNode implements TypedExpression
{
public $stringPrimary;
@@ -60,4 +62,9 @@ class LengthFunction extends FunctionNode
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getReturnType() : Type
{
return Type::getType(Type::INTEGER);
}
}

View File

@@ -24,7 +24,7 @@ use Doctrine\ORM\Query\Lexer;
/**
* "SQRT" "(" SimpleArithmeticExpression ")"
*
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\DBAL\Types\Type;
/**
* Provides an API for resolving the type of a Node
*/
interface TypedExpression
{
public function getReturnType() : Type;
}

View File

@@ -19,6 +19,10 @@
namespace Doctrine\ORM\Query;
use Traversable;
use function is_iterable;
use function iterator_to_array;
/**
* This class is used to generate DQL expressions via a set of PHP static functions.
*
@@ -442,7 +446,11 @@ class Expr
*/
public function in($x, $y)
{
if (is_array($y)) {
if (is_iterable($y)) {
if ($y instanceof Traversable) {
$y = iterator_to_array($y);
}
foreach ($y as &$literal) {
if ( ! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
@@ -463,7 +471,11 @@ class Expr
*/
public function notIn($x, $y)
{
if (is_array($y)) {
if (is_iterable($y)) {
if ($y instanceof Traversable) {
$y = iterator_to_array($y);
}
foreach ($y as &$literal) {
if ( ! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);

View File

@@ -145,7 +145,7 @@ class Lexer extends AbstractLexer
*/
protected function getNonCatchablePatterns()
{
return ['\s+', '(.)'];
return ['\s+', '--.*', '(.)'];
}
/**

View File

@@ -1495,7 +1495,7 @@ class Parser
/**
* OrderByItem ::= (
* SimpleArithmeticExpression | SingleValuedPathExpression |
* SimpleArithmeticExpression | SingleValuedPathExpression | CaseExpression |
* ScalarExpression | ResultVariable | FunctionDeclaration
* ) ["ASC" | "DESC"]
*
@@ -1525,6 +1525,10 @@ class Parser
$expr = $this->ScalarExpression();
break;
case $this->lexer->lookahead['type'] === Lexer::T_CASE:
$expr = $this->CaseExpression();
break;
case $this->isFunction():
$expr = $this->FunctionDeclaration();
break;

View File

@@ -1349,10 +1349,20 @@ class SqlWalker implements TreeWalker
$this->scalarResultAliasMap[$resultAlias] = $columnAlias;
if ( ! $hidden) {
// We cannot resolve field type here; assume 'string'.
$this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
if ($hidden) {
break;
}
if (! $expr instanceof Query\AST\TypedExpression) {
// Conceptually we could resolve field type here by traverse through AST to retrieve field type,
// but this is not a feasible solution; assume 'string'.
$this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
break;
}
$this->rsm->addScalarResult($columnAlias, $resultAlias, $expr->getReturnType()->getName());
break;
case ($expr instanceof AST\Subselect):

View File

@@ -20,8 +20,8 @@
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\Common\Inflector\Inflector;
use Doctrine\DBAL\Types\Type;
use Doctrine\Inflector\InflectorFactory;
use Symfony\Component\Yaml\Yaml;
/**
@@ -293,6 +293,8 @@ class ConvertDoctrine1Schema
return;
}
$inflector = InflectorFactory::create()->build();
foreach ($model['relations'] as $name => $relation) {
if ( ! isset($relation['alias'])) {
$relation['alias'] = $name;
@@ -301,7 +303,7 @@ class ConvertDoctrine1Schema
$relation['class'] = $name;
}
if ( ! isset($relation['local'])) {
$relation['local'] = Inflector::tableize($relation['class']);
$relation['local'] = $inflector->tableize($relation['class']);
}
if ( ! isset($relation['foreign'])) {
$relation['foreign'] = 'id';

View File

@@ -20,11 +20,13 @@
namespace Doctrine\ORM\Tools;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Inflector\Inflector;
use Doctrine\DBAL\Types\Type;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use ReflectionClass;
use const E_USER_DEPRECATED;
use const PHP_VERSION_ID;
use function str_replace;
use function trigger_error;
use function var_export;
@@ -336,6 +338,9 @@ public function __construct(<params>)
}
';
/** @var Inflector */
protected $inflector;
/**
* Constructor.
*/
@@ -344,6 +349,7 @@ public function __construct(<params>)
@trigger_error(self::class . ' is deprecated and will be removed in Doctrine ORM 3.0', E_USER_DEPRECATED);
$this->annotationsPrefix = 'ORM\\';
$this->inflector = InflectorFactory::create()->build();
}
/**
@@ -593,6 +599,11 @@ public function __construct(<params>)
$this->backupExisting = $bool;
}
public function setInflector(Inflector $inflector) : void
{
$this->inflector = $inflector;
}
/**
* @param string $type
*
@@ -809,6 +820,8 @@ public function __construct(<params>)
/**
* @todo this won't work if there is a namespace in brackets and a class outside of it.
*
* @psalm-suppress UndefinedConstant
*
* @param string $src
*
* @return void
@@ -832,6 +845,8 @@ public function __construct(<params>)
if ($inNamespace) {
if (in_array($token[0], [T_NS_SEPARATOR, T_STRING], true)) {
$lastSeenNamespace .= $token[1];
} elseif (PHP_VERSION_ID >= 80000 && ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)) {
$lastSeenNamespace .= $token[1];
} elseif (is_string($token) && in_array($token, [';', '{'], true)) {
$inNamespace = false;
}
@@ -1380,11 +1395,12 @@ public function __construct(<params>)
*/
protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
{
$methodName = $type . Inflector::classify($fieldName);
$variableName = Inflector::camelize($fieldName);
$methodName = $type . $this->inflector->classify($fieldName);
$variableName = $this->inflector->camelize($fieldName);
if (in_array($type, ["add", "remove"])) {
$methodName = Inflector::singularize($methodName);
$variableName = Inflector::singularize($variableName);
$methodName = $this->inflector->singularize($methodName);
$variableName = $this->inflector->singularize($variableName);
}
if ($this->hasMethod($methodName, $metadata)) {

View File

@@ -19,6 +19,7 @@
namespace Doctrine\ORM\Tools\Pagination;
use ArrayIterator;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Parser;
@@ -33,6 +34,8 @@ use function array_sum;
* @author Pablo Díez <pablodip@gmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @license New BSD
*
* @template T
*/
class Paginator implements \Countable, \IteratorAggregate
{
@@ -134,6 +137,8 @@ class Paginator implements \Countable, \IteratorAggregate
/**
* {@inheritdoc}
*
* @return ArrayIterator<mixed, T>
*/
public function getIterator()
{
@@ -156,7 +161,7 @@ class Paginator implements \Countable, \IteratorAggregate
// don't do this for an empty id array
if ($foundIdRows === []) {
return new \ArrayIterator([]);
return new ArrayIterator([]);
}
$whereInQuery = $this->cloneQuery($this->query);
@@ -179,7 +184,7 @@ class Paginator implements \Countable, \IteratorAggregate
;
}
return new \ArrayIterator($result);
return new ArrayIterator($result);
}
/**

View File

@@ -22,6 +22,7 @@ namespace Doctrine\ORM\Tools;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\DBAL\Types\Type;
use function count;
/**
* Performs strict validation of the mapping schema
@@ -96,6 +97,12 @@ class SchemaValidator
}
}
if ($class->isEmbeddedClass && count($class->associationMappings) > 0) {
$ce[] = "Embeddable '" . $class->name . "' does not support associations";
return $ce;
}
foreach ($class->associationMappings as $fieldName => $assoc) {
if (!class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) {
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.';

View File

@@ -47,6 +47,7 @@ use InvalidArgumentException;
use Throwable;
use UnexpectedValueException;
use function get_class;
use function is_object;
use function spl_object_hash;
/**
@@ -425,10 +426,18 @@ class UnitOfWork implements PropertyChangedListener
}
}
$conn->commit();
// Commit failed silently
if ($conn->commit() === false) {
$object = is_object($entity) ? $entity : null;
throw new OptimisticLockException('Commit failed', $object);
}
} catch (Throwable $e) {
$this->em->close();
$conn->rollBack();
if ($conn->isTransactionActive()) {
$conn->rollBack();
}
$this->afterTransactionRolledBack();

View File

@@ -44,5 +44,3 @@ final class HierarchyDiscriminatorResolver
return $discriminators;
}
}
interface_exists(ClassMetadata::class);

View File

@@ -16,7 +16,7 @@
<exclude-pattern>*/tests/Doctrine/Tests/Proxies/__CG__/*</exclude-pattern>
<rule ref="Doctrine">
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint"/>
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingAnyTypeHint" />
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint"/>
<exclude name="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException"/>
<exclude name="SlevomatCodingStandard.ControlStructures.EarlyExit"/>
@@ -37,10 +37,14 @@
<exclude-pattern>lib/Doctrine/ORM/Tools/ToolEvents.php</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversablePropertyTypeHintSpecification">
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification">
<exclude-pattern>lib/Doctrine/ORM/Annotation/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator">
<exclude-pattern>lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php</exclude-pattern>
</rule>
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
<exclude-pattern>lib/Doctrine/ORM/Query/Parser.php</exclude-pattern>
</rule>
@@ -105,7 +109,7 @@
<exclude-pattern>lib/Doctrine/ORM/EntityManagerInterface.php</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingPropertyTypeHint">
<rule name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingAnyTypeHint">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
@@ -113,7 +117,7 @@
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableParameterTypeHintSpecification">
<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
@@ -121,11 +125,11 @@
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversablePropertyTypeHintSpecification">
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableReturnTypeHintSpecification">
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Query;
use PHPUnit\Framework\Assert;
use function is_array;
use function iterator_to_array;
final class IterableTester
{
public static function assertResultsAreTheSame(Query $query) : void
{
$result = $query->getResult();
$iterable = $query->toIterable();
Assert::assertSame($result, self::iterableToArray($iterable));
$result = $query->getResult(AbstractQuery::HYDRATE_ARRAY);
$iterable = $query->toIterable([], AbstractQuery::HYDRATE_ARRAY);
Assert::assertSame($result, self::iterableToArray($iterable));
}
/**
* Copy the iterable into an array. If the iterable is already an array, return it.
*
* @return mixed[]
*/
public static function iterableToArray(iterable $iterable) : array
{
return is_array($iterable) ? $iterable : iterator_to_array($iterable, true);
}
}

View File

@@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
@@ -73,6 +74,10 @@ class AdvancedAssociationTest extends OrmFunctionalTestCase
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
$this->_em->clear();
// test2 - eager load in DQL query with double-join back and forth
$query = $this->_em->createQuery("SELECT p,t,pp FROM Doctrine\Tests\ORM\Functional\Phrase p JOIN p.type t JOIN t.phrases pp");
$res = $query->getResult();
@@ -100,6 +105,10 @@ class AdvancedAssociationTest extends OrmFunctionalTestCase
$this->assertInstanceOf(Definition::class, $definitions[0]);
$this->assertEquals(2, $definitions->count());
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testManyToMany()
@@ -123,6 +132,10 @@ class AdvancedAssociationTest extends OrmFunctionalTestCase
$types = $res[0]->getTypes();
$this->assertInstanceOf(Type::class, $types[0]);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
}

View File

@@ -2,10 +2,12 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\Models\Company\CompanyEmployee,
Doctrine\Tests\Models\Company\CompanyManager,
Doctrine\Tests\Models\Company\CompanyCar;
use Doctrine\Tests\OrmFunctionalTestCase;
use function count;
/**
* Functional Query tests.
@@ -37,6 +39,24 @@ class AdvancedDqlQueryTest extends OrmFunctionalTestCase
$this->assertEquals(600000, $result[1]['avgSalary']);
}
public function testCommentsInDQL()
{
//same test than testAggregateWithHavingClause but with comments into the DQL
$dql = "SELECT p.department, AVG(p.salary) AS avgSalary -- comment end of line
-- comment with 'strange chars', & $
FROM Doctrine\\Tests\\Models\\Company\\CompanyEmployee p
-- comment beginning of line GROUP BY
GROUP BY p.department HAVING SUM(p.salary) > 200000 ORDER BY p.department -- comment end of line";
$result = $this->_em->createQuery($dql)->getScalarResult();
$this->assertEquals(2, count($result));
$this->assertEquals('IT', $result[0]['department']);
$this->assertEquals(150000, $result[0]['avgSalary']);
$this->assertEquals('IT2', $result[1]['department']);
$this->assertEquals(600000, $result[1]['avgSalary']);
}
public function testUnnamedScalarResultsAreOneBased()
{
$dql = 'SELECT p.department, AVG(p.salary) '.
@@ -74,11 +94,58 @@ class AdvancedDqlQueryTest extends OrmFunctionalTestCase
$this->assertEquals(1, $result[3]['friends']);
}
public function testOrderBySimpleCaseExpression() : void
{
$dql = <<<'DQL'
SELECT p.name
FROM Doctrine\Tests\Models\Company\CompanyEmployee p
ORDER BY CASE p.name
WHEN 'Jonathan W.' THEN 1
WHEN 'Roman B.' THEN 2
WHEN 'Guilherme B.' THEN 3
ELSE 4
END DESC
DQL;
$result = $this->_em->createQuery($dql)->getScalarResult();
self::assertCount(4, $result);
self::assertEquals('Benjamin E.', $result[0]['name']);
self::assertEquals('Guilherme B.', $result[1]['name']);
self::assertEquals('Roman B.', $result[2]['name']);
self::assertEquals('Jonathan W.', $result[3]['name']);
}
public function testOrderByGeneralCaseExpression() : void
{
$dql = <<<'DQL'
SELECT p.name
FROM Doctrine\Tests\Models\Company\CompanyEmployee p
ORDER BY CASE
WHEN p.name='Jonathan W.' THEN 1
WHEN p.name='Roman B.' THEN 2
WHEN p.name='Guilherme B.' THEN 3
ELSE 4
END DESC
DQL;
$result = $this->_em->createQuery($dql)->getScalarResult();
self::assertCount(4, $result);
self::assertEquals('Benjamin E.', $result[0]['name']);
self::assertEquals('Guilherme B.', $result[1]['name']);
self::assertEquals('Roman B.', $result[2]['name']);
self::assertEquals('Jonathan W.', $result[3]['name']);
}
public function testIsNullAssociation()
{
$dql = 'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p '.
$dql = 'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p ' .
'WHERE p.spouse IS NULL';
$result = $this->_em->createQuery($dql)->getResult();
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
$this->assertEquals(2, count($result));
$this->assertTrue($result[0]->getId() > 0);
@@ -86,35 +153,56 @@ class AdvancedDqlQueryTest extends OrmFunctionalTestCase
$this->assertTrue($result[1]->getId() > 0);
$this->assertNull($result[1]->getSpouse());
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testSelectSubselect()
{
$dql = 'SELECT p, (SELECT c.brand FROM Doctrine\Tests\Models\Company\CompanyCar c WHERE p.car = c) brandName '.
$dql = 'SELECT p, (SELECT c.brand FROM Doctrine\Tests\Models\Company\CompanyCar c WHERE p.car = c) brandName ' .
'FROM Doctrine\Tests\Models\Company\CompanyManager p';
$result = $this->_em->createQuery($dql)->getArrayResult();
$query = $this->_em->createQuery($dql);
$result = $query->getArrayResult();
$this->assertEquals(1, count($result));
$this->assertEquals("Caramba", $result[0]['brandName']);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testInSubselect()
{
$dql = "SELECT p.name FROM Doctrine\Tests\Models\Company\CompanyPerson p ".
"WHERE p.name IN (SELECT n.name FROM Doctrine\Tests\Models\Company\CompanyPerson n WHERE n.name = 'Roman B.')";
$result = $this->_em->createQuery($dql)->getScalarResult();
$dql = <<<DQL
SELECT p.name FROM Doctrine\Tests\Models\Company\CompanyPerson p
WHERE p.name IN (SELECT n.name FROM Doctrine\Tests\Models\Company\CompanyPerson n WHERE n.name = 'Roman B.')
DQL;
$query = $this->_em->createQuery($dql);
$result = $query->getScalarResult();
$this->assertEquals(1, count($result));
$this->assertEquals('Roman B.', $result[0]['name']);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testGroupByMultipleFields()
{
$dql = 'SELECT p.department, p.name, count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p '.
$dql = 'SELECT p.department, p.name, count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'GROUP BY p.department, p.name';
$result = $this->_em->createQuery($dql)->getResult();
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
$this->assertEquals(4, count($result));
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testUpdateAs()
@@ -122,8 +210,14 @@ class AdvancedDqlQueryTest extends OrmFunctionalTestCase
$dql = 'UPDATE Doctrine\Tests\Models\Company\CompanyEmployee AS p SET p.salary = 1';
$this->_em->createQuery($dql)->execute();
$this->assertTrue(count($this->_em->createQuery(
'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1')->getResult()) > 0);
$query = $this->_em->createQuery(
'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1'
);
self::assertGreaterThan(0, $query->getResult());
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testDeleteAs()

View File

@@ -10,6 +10,7 @@ use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\Proxy;
use Doctrine\ORM\Query;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsComment;
@@ -230,6 +231,10 @@ class BasicFunctionalTest extends OrmFunctionalTestCase
//$this->assertNull($users[0]->phonenumbers);
//$this->assertNull($users[0]->articles);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
$usersArray = $query->getArrayResult();
$this->assertTrue(is_array($usersArray));

View File

@@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\Proxy;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\Models\Company\CompanyAuction;
use Doctrine\Tests\Models\Company\CompanyEmployee;
use Doctrine\Tests\Models\Company\CompanyEvent;
@@ -62,15 +63,23 @@ class ClassTableInheritanceTest extends OrmFunctionalTestCase
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
$this->_em->clear();
$query = $this->_em->createQuery('select p from ' . CompanyEmployee::class . ' p');
$entities = $query->getResult();
$this->assertCount(1, $entities);
$this->assertInstanceOf(CompanyEmployee::class, $entities[0]);
self::assertCount(1, $entities);
self::assertInstanceOf(CompanyEmployee::class, $entities[0]);
$this->assertTrue(is_numeric($entities[0]->getId()));
$this->assertEquals('Guilherme Blanco', $entities[0]->getName());
$this->assertEquals(100000, $entities[0]->getSalary());
self::assertEquals('Guilherme Blanco', $entities[0]->getName());
self::assertEquals(100000, $entities[0]->getSalary());
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
$this->_em->clear();
@@ -170,6 +179,10 @@ class ClassTableInheritanceTest extends OrmFunctionalTestCase
$this->assertInstanceOf(CompanyEmployee::class, $result[0]->getSpouse());
$this->assertEquals('John Smith', $result[0]->getSpouse()->getName());
$this->assertSame($result[0], $result[0]->getSpouse()->getSpouse());
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testSelfReferencingManyToMany()
@@ -242,6 +255,10 @@ class ClassTableInheritanceTest extends OrmFunctionalTestCase
$this->assertInstanceOf(CompanyRaffle::class, $events[0]);
$this->assertInstanceOf(CompanyAuction::class, $events[1]);
}
$this->_em->clear();
IterableTester::assertResultsAreTheSame($q);
}
public function testLazyLoading2()
@@ -264,6 +281,10 @@ class ClassTableInheritanceTest extends OrmFunctionalTestCase
$this->_em->clear();
IterableTester::assertResultsAreTheSame($q);
$this->_em->clear();
$q = $this->_em->createQuery('select a from ' . CompanyOrganization::class . ' a where a.id = ?1');
$q->setParameter(1, $org->getId());
@@ -276,6 +297,10 @@ class ClassTableInheritanceTest extends OrmFunctionalTestCase
// mainEvent should have been loaded because it can't be lazy
$this->assertInstanceOf(CompanyAuction::class, $mainEvent);
$this->assertNotInstanceOf(Proxy::class, $mainEvent);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($q);
}
/**
@@ -286,10 +311,14 @@ class ClassTableInheritanceTest extends OrmFunctionalTestCase
$this->_em->createQuery('UPDATE ' . CompanyEmployee::class . ' AS p SET p.salary = 1')
->execute();
$result = $this->_em->createQuery('SELECT count(p.id) FROM ' . CompanyEmployee::class . ' p WHERE p.salary = 1')
->getResult();
$query = $this->_em->createQuery('SELECT count(p.id) FROM ' . CompanyEmployee::class . ' p WHERE p.salary = 1');
$result = $query->getResult();
$this->assertGreaterThan(0, count($result));
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
/**

View File

@@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query\QueryException;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\Models\Navigation\NavCountry;
use Doctrine\Tests\Models\Navigation\NavPointOfInterest;
use Doctrine\Tests\Models\Navigation\NavTour;
@@ -86,11 +87,16 @@ class CompositePrimaryKeyTest extends OrmFunctionalTestCase
$this->_em->clear();
$dql = "SELECT IDENTITY(p.poi, 'long') AS long, IDENTITY(p.poi, 'lat') AS lat FROM Doctrine\Tests\Models\Navigation\NavPhotos p";
$result = $this->_em->createQuery($dql)->getResult();
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
$this->assertCount(1, $result);
$this->assertEquals(200, $result[0]['long']);
$this->assertEquals(100, $result[0]['lat']);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testManyToManyCompositeRelation()
@@ -112,7 +118,10 @@ class CompositePrimaryKeyTest extends OrmFunctionalTestCase
'INNER JOIN t.pois p INNER JOIN p.country c';
$tours = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(1, count($tours));
$query = $this->_em->createQuery($dql);
$tours = $query->getResult();
self::assertCount(1, $tours);
$pois = $tours[0]->getPointOfInterests();
@@ -133,6 +142,10 @@ class CompositePrimaryKeyTest extends OrmFunctionalTestCase
->getResult();
$this->assertEquals(1, count($tours));
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testSpecifyUnknownIdentifierPrimaryKeyFails()

View File

@@ -1118,5 +1118,18 @@ class EntityRepositoryTest extends OrmFunctionalTestCase
$this->assertTrue(in_array($user, [$user1, $user2]));
}
}
public function testDeprecatedClear() : void
{
$repository = $this->_em->getRepository(CmsAddress::class);
$this->expectDeprecationMessageSame(
'Method Doctrine\ORM\EntityRepository::clear() is deprecated and will be removed in Doctrine ORM 3.0.'
);
$this->expectDeprecationMessageSame(
'Calling Doctrine\ORM\EntityManager::clear() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.'
);
$repository->clear();
}
}

View File

@@ -8,6 +8,7 @@ use Doctrine\ORM\Event\PreFlushEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Query;
use Doctrine\Tests\OrmFunctionalTestCase;
use function sprintf;
class LifecycleCallbackTest extends OrmFunctionalTestCase
{
@@ -232,10 +233,9 @@ WHERE
e.id IN (%s, %s)
DQL;
$result = $this
->_em
->createQuery(sprintf($dql, $e1->getId(), $e2->getId()))
->iterate();
$query = $this->_em->createQuery(sprintf($dql, $e1->getId(), $e2->getId()));
$result = $query->iterate();
foreach ($result as $entity) {
$this->assertTrue($entity[0]->postLoadCallbackInvoked);
@@ -243,6 +243,15 @@ DQL;
break;
}
$iterableResult = $query->toIterable();
foreach ($iterableResult as $entity) {
self::assertTrue($entity->postLoadCallbackInvoked);
self::assertFalse($entity->postLoadCascaderNotNull);
break;
}
}
/**
* @group DDC-54
@@ -256,10 +265,11 @@ DQL;
$this->_em->flush();
$this->_em->clear();
$result = $this
->_em
->createQuery('SELECT e FROM Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity AS e')
->iterate(null, Query::HYDRATE_SIMPLEOBJECT);
$query = $this->_em->createQuery(
'SELECT e FROM Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity AS e'
);
$result = $query->iterate(null, Query::HYDRATE_SIMPLEOBJECT);
foreach ($result as $entity) {
$this->assertTrue($entity[0]->postLoadCallbackInvoked);
@@ -267,6 +277,15 @@ DQL;
break;
}
$result = $query->toIterable([], Query::HYDRATE_SIMPLEOBJECT);
foreach ($result as $entity) {
self::assertTrue($entity->postLoadCallbackInvoked);
self::assertFalse($entity->postLoadCascaderNotNull);
break;
}
}
/**

View File

@@ -26,7 +26,7 @@ class OneToOneInverseSideLoadAfterDqlQueryTest extends OrmFunctionalTestCase
}
/**
* @group 6759
* @group GH-6759
*/
public function testInverseSideOneToOneLoadedAfterDqlQuery(): void
{

View File

@@ -312,8 +312,7 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
self::assertEqualsWithDelta(
(new \DateTimeImmutable($result['now']))->modify(sprintf('+%d %s', $amount, $unit)),
new \DateTimeImmutable($result['add']),
$delta,
''
$delta
);
}
@@ -342,8 +341,7 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
self::assertEqualsWithDelta(
(new \DateTimeImmutable($result['now']))->modify(sprintf('-%d %s', $amount, $unit)),
new \DateTimeImmutable($result['sub']),
$delta,
''
$delta
);
}

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;
final class QueryIterableTest extends OrmFunctionalTestCase
{
protected function setUp() : void
{
$this->useModelSet('cms');
parent::setUp();
}
public function testAlias() : void
{
$user = new CmsUser();
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$query = $this->_em->createQuery('SELECT u AS user FROM Doctrine\Tests\Models\CMS\CmsUser u');
$users = $query->getResult();
self::assertCount(1, $users);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
public function testAliasInnerJoin() : void
{
$user = new CmsUser();
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$address = new CmsAddress();
$address->country = 'Germany';
$address->city = 'Berlin';
$address->zip = '12345';
$address->user = $user;
$user->address = $address;
$this->_em->persist($user);
$this->_em->flush();
$query = $this->_em->createQuery('SELECT u AS user, a AS address FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.address a');
$users = $query->getResult();
self::assertCount(1, $users);
$this->_em->clear();
IterableTester::assertResultsAreTheSame($query);
}
}

View File

@@ -8,6 +8,7 @@ use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\Proxy\Proxy;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\UnexpectedResultException;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\Models\CMS\CmsUser,
Doctrine\Tests\Models\CMS\CmsArticle,
Doctrine\Tests\Models\CMS\CmsPhonenumber;
@@ -15,6 +16,8 @@ use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Parameter;
use Doctrine\Tests\OrmFunctionalTestCase;
use function count;
use function iterator_to_array;
/**
* Functional Query tests.
@@ -217,26 +220,27 @@ class QueryTest extends OrmFunctionalTestCase
$query = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a WHERE a.topic = ?1');
$articles = $query->iterate(new ArrayCollection([new Parameter(1, 'Doctrine 2')]), Query::HYDRATE_ARRAY);
$found = [];
$expectedArticle = [
'id' => $articleId,
'topic' => 'Doctrine 2',
'text' => 'This is an introduction to Doctrine 2.',
'version' => 1,
];
foreach ($articles AS $article) {
$found[] = $article;
}
$articles = iterator_to_array($articles, false);
$this->assertEquals(1, count($found));
$this->assertSame(
[
[
[
'id' => $articleId,
'topic' => 'Doctrine 2',
'text' => 'This is an introduction to Doctrine 2.',
'version' => 1,
],
],
],
$found
self::assertCount(1, $articles);
self::assertEquals([[$expectedArticle]], $articles);
$articles = $query->toIterable(
new ArrayCollection([new Parameter(1, 'Doctrine 2')]),
Query::HYDRATE_ARRAY
);
$articles = IterableTester::iterableToArray($articles);
self::assertCount(1, $articles);
self::assertEquals([$expectedArticle], $articles);
}
public function testIterateResult_IterativelyBuildUpUnitOfWork()
@@ -275,8 +279,23 @@ class QueryTest extends OrmFunctionalTestCase
$this->assertSame(["Doctrine 2", "Symfony 2"], $topics);
$this->assertSame(2, $iteratedCount);
$this->_em->flush();
$this->_em->clear();
$articles = $query->toIterable();
$iteratedCount = 0;
$topics = [];
foreach ($articles as $article) {
$topics[] = $article->topic;
$identityMap = $this->_em->getUnitOfWork()->getIdentityMap();
$identityMapCount = count($identityMap[CmsArticle::class]);
self::assertGreaterThan($iteratedCount, $identityMapCount);
$iteratedCount++;
}
self::assertSame(['Doctrine 2', 'Symfony 2'], $topics);
self::assertSame(2, $iteratedCount);
}
public function testIterateResultClearEveryCycle()
@@ -295,9 +314,9 @@ class QueryTest extends OrmFunctionalTestCase
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a");
$articles = $query->iterate();
$query = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a');
$articles = $query->iterate();
$iteratedCount = 0;
$topics = [];
foreach($articles AS $row) {
@@ -309,6 +328,20 @@ class QueryTest extends OrmFunctionalTestCase
$iteratedCount++;
}
self::assertSame(['Doctrine 2', 'Symfony 2'], $topics);
self::assertSame(2, $iteratedCount);
$articles = $query->toIterable();
$iteratedCount = 0;
$topics = [];
foreach ($articles as $article) {
$topics[] = $article->topic;
$this->_em->clear();
$iteratedCount++;
}
$this->assertSame(["Doctrine 2", "Symfony 2"], $topics);
$this->assertSame(2, $iteratedCount);
@@ -317,9 +350,17 @@ class QueryTest extends OrmFunctionalTestCase
public function testIterateResult_FetchJoinedCollection_ThrowsException()
{
$this->expectException('Doctrine\ORM\Query\QueryException');
$this->expectException(QueryException::class);
$query = $this->_em->createQuery("SELECT u, a FROM ' . CmsUser::class . ' u JOIN u.articles a");
$articles = $query->iterate();
$query->iterate();
}
public function testToIterableResultFetchJoinedCollectionThrowsException() : void
{
$this->expectException(QueryException::class);
$query = $this->_em->createQuery("SELECT u, a FROM ' . CmsUser::class . ' u JOIN u.articles a");
$query->toIterable();
}
public function testGetSingleResultThrowsExceptionOnNoResult()

View File

@@ -16,7 +16,6 @@ class DDC144Test extends OrmFunctionalTestCase
$this->_em->getClassMetadata(DDC144Operand::class),
]
);
}
/**

View File

@@ -5,7 +5,7 @@ namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Events;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\Models\CMS\CmsUser;
use ReflectionClass;
use ReflectionObject;
/**
* @group DDC-3123
@@ -38,8 +38,9 @@ class DDC3123Test extends \Doctrine\Tests\OrmFunctionalTestCase
->expects($this->once())
->method(Events::postFlush)
->will($this->returnCallback(function () use ($uow, $test) {
$class = new ReflectionClass(UnitOfWork::class);
$property = $class->getProperty('extraUpdates');
$reflection = new ReflectionObject($uow);
$property = $reflection->getProperty('extraUpdates');
$property->setAccessible(true);
$test->assertEmpty(
$property->getValue($uow),

View File

@@ -13,9 +13,9 @@ class DDC3303Test extends OrmFunctionalTestCase
}
/**
* @group 4097
* @group 4277
* @group 5867
* @group GH-4097
* @group GH-4277
* @group GH-5867
*
* When using an embedded field in an inheritance, private properties should also be inherited.
*/

View File

@@ -27,7 +27,8 @@ class DDC3597Test extends OrmFunctionalTestCase
/**
* @group DDC-3597
*/
public function testSaveImageEntity() {
public function testSaveImageEntity(): void
{
$imageEntity = new DDC3597Image('foobar');
$imageEntity->setFormat('JPG');
$imageEntity->setSize(123);

View File

@@ -6,7 +6,7 @@ use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group 2947
* @group GH-2947
*/
class GH2947Test extends OrmFunctionalTestCase
{

View File

@@ -25,7 +25,7 @@ final class GH5562Test extends OrmFunctionalTestCase
}
/**
* @group 5562
* @group GH-5562
*/
public function testCacheShouldBeUpdatedWhenAssociationChanges()
{

View File

@@ -9,7 +9,7 @@ use Doctrine\ORM\Id\AbstractIdGenerator;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group 5804
* @group GH-5804
*/
final class GH5804Test extends OrmFunctionalTestCase
{

View File

@@ -8,7 +8,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group 5887
* @group GH-5887
*/
class GH5887Test extends OrmFunctionalTestCase
{

Some files were not shown because too many files have changed in this diff Show More