Compare commits

...

97 Commits

Author SHA1 Message Date
Benjamin Eberlei
242cf1a33d Fix ambiguous case where an entity is also a Traversable (#8371)
* Fix ambiguous case where an entity is also a Traversable

* Address phpcs violations.

* Address phpcs violations.

* Address phpcs violations.

Co-authored-by: Laurent VOULLEMIER <laurent.voullemier@gmail.com>
2020-12-04 20:53:07 +01:00
Grégoire Paris
da225a0db8 Drop step that switches the release branch (#8372)
ORM is a repository where we use the stable branch as the default
branch, that step is not appropriate here.
2020-12-04 20:46:09 +01:00
Benjamin Eberlei
3ef5a30102 [GH-8366] Catch additional Persistence MappingException (#8370) 2020-12-04 20:16:50 +01:00
Grégoire Paris
418587bc25 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-12-03 20:18:13 +01:00
Grégoire Paris
01187c9260 Remove trailing whitespaces (#8360)
Stylistically, it's not great to have them, but more importantly, the
latest symfony/yaml version has issues with trailing whitespaces.
2020-12-03 09:52:14 +01:00
Benjamin Eberlei
404edd418b [GH-8229] Prevent Illegal Inheritance Override (#8348)
* [GH-8229] Prevent AttributeOverride on fields from entities, only allowed for MappedSuperclass

* [GH-8229] Prevent AssociationOverride on fields from entities, only allowed for MappedSuperclass

* Revert "Fix SQL alias generation regression for simple inheritance (#8329)"

This reverts commit f4ebded63c.

* [GH-8229] Finalize checks for illegal attribute/assocation overrides.

* [GH-8229] Revert ccae8f7176 PR #8234

* [GH-8229] Update documentation to clarify only mapped superclass or trait works with overrides

* [GH-8229] Fix style violations introduced by revert

* [GH-8229] Fix style violations introduced by revert

* [GH-8229] Temporarily disable the exception until 2.8.

* Make phpcs happy
2020-11-25 23:04:56 +01:00
Claudio Zizza
011d3c21eb Update functionality of code examples (#8336) 2020-11-24 22:39:42 +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
Roma
b1ac293a50 Add missing backtick in rst markup (#8335) 2020-11-10 20:37:36 +01:00
Romain Grégoire
f4ebded63c Fix SQL alias generation regression for simple inheritance (#8329)
This fixes a regression from 099c5b42e1.
Without the fix, "where part" in SQL is generated with incorrect aliases.
See https://github.com/doctrine/orm/issues/8229#issuecomment-722942180.
2020-11-10 10:53:01 +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
Grégoire Paris
385b5a2f80 Phpunit 8 (#8330)
* Revert to whitelist

coverage requires PHPunit 9, and we don't have that yet.

* Upgrade to PHPUnit 8

This unlocks PCOV usage for coverage

* Upload coverage files to Codecov
2020-11-07 19:41:15 +01:00
Benjamin Eberlei
f7d8b155db Merge branch '2.7' into 2.8.x 2020-11-07 18:37:53 +01:00
Benjamin Eberlei
fa6fe09647 Ignore composer.lock 2020-11-07 18:37:46 +01:00
Benjamin Eberlei
539ffea390 Merge 2.7 into 2.8.x 2020-11-07 18:30:32 +01:00
Benjamin Eberlei
c270eba678 Start moving travis phpunit runs to Github actions. (#8317)
* Move PHPUnit runs from Travis to Github Actions

This removes all artifacts used for TravisCI testing and replaces them
with the existing infrastructure for Github Actions from DBAL component.

In addition some test changes were needed and triggered larger Coding
Style cleanups in 3 test files.

* Remove composer.lock and improve naming in CI workflow.
2020-11-07 18:11:42 +01:00
Michael Käfer
2f0eb95c90 Patch 1 (#8325)
* Update outdated doc parts

- The cache implementation moved from `Common` to `doctrine/cache`
- APCu is mor appropiate nowadays I guess
- AbstractQuery::useResultCache() is deprecated since 2.7

* Fix wrong argument

* Fix wrong arguments and remove useless line
2020-10-31 12:13:48 +01:00
Grégoire Paris
b13b2e8bab Upgrade doctrine/coding-standard (#8321)
* Use a classname that exists

Doctrine\ORM\Mapping\TableGenerator does not exist, only
Doctrine\ORM\Id\TableGenerator does.

* Upgrade doctrine/coding-standard

That library has a dependency on another library that requires composer
plugin API v1. Updating both libs allow to use Composer v2.

* Account for doctrine/reflection deprecation
2020-10-28 11:14:10 +01:00
Simon Podlipsky
4bfc84f035 Rename getIterable() to toIterable() (part 2) (#8293) 2020-10-17 23:55:39 +02:00
Michael Voříšek
ca27cc3f72 Fix EOL of text files (#8310) 2020-10-17 19:04:42 +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
Laurent VOULLEMIER
5fde5801c1 Fix many typos (#8299)
- Some uppercase letters were used in the middle of sentence
- Some dots were missing
- There was two sentences with wrong or missing words
2020-10-13 21:52:04 +02:00
Thomas Landauer
18d96fcc02 Update working-with-indexed-associations.rst (#8298)
* Update working-with-indexed-associations.rst

Fixing broken link

* Update docs/en/tutorials/working-with-indexed-associations.rst

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>
2020-10-12 21:27:18 +02:00
Benjamin Eberlei
4d2908a065 Change <phpunit verbose="true" /> to false for better readability of test output. 2020-10-11 19:14:47 +02:00
Benjamin Eberlei
8d250f5921 Use SHELL_VERBOSITY=3 instead of LOG_LEVEL=DEBUG 2020-10-11 12:03:57 +02:00
Benjamin Eberlei
59fd9b5ea7 Change LOG_LEVEL to debug should expose more info from laminas/automatic-releases. 2020-10-11 00:17:52 +02:00
Benjamin Eberlei
8fcc70cfbe Change LOG_LEVEL to debug should expose more info from laminas/automatic-releases. 2020-10-11 00:14:35 +02:00
Grégoire Paris
7d84a49980 Setup automated release workflow (#8301) 2020-10-10 19:11:26 +02:00
Claudio Zizza
bb64fc953d Move website config to default branch (#8287)
Adds the website config to be compatible with the doctrine/doctrine-website#356 changes
2020-10-02 20:31:00 +02:00
orklah
e0eb82a3b1 psalm fixes (#8286) 2020-09-30 22:00:38 +02:00
orklah
79cdcde9ec rename parameters to match parents (#8284) 2020-09-26 20:24:11 +02:00
orklah
f4524a8bb0 Fix psalm errors and upgrade strictness (#8209)
* Fixes and improvements

* fix param type
2020-09-25 20:44:07 +02:00
Simon Podlipsky
f1365b78d5 Rename getIterable to toIterable (#8268) 2020-09-25 20:42:09 +02:00
Grégoire Paris
d810ea4111 Use inline literals over escaping (#8279)
Escaping underscores does not work as expected.
See https://www.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/dql-custom-walkers.html#modify-the-output-walker-to-generate-vendor-specific-sql
2020-09-22 20:10:05 +02:00
Michael Voříšek
107ba93d79 Convert CRLF to LF in test file (#8276)
* Convert CRLF to LF in test file
* fix cs
2020-09-21 22:51:49 +02:00
Guillaume Simon
706670215d Fix OrderByItem parser to proceed with SimpleArithmetic expression before function (#8277)
There is parser bug for OrderByItem(), where a function is detected
before a SimpleArithmeticExpression while it should be the reverse:
simple arithmetic expressions can start with a function too, and thus
they should be matched first if present.

This fix enables the use of expressions in sorting and with window
functions.
2020-09-20 18:56:57 +02:00
Yohann Durand
ab2b4987b3 Improve formatting (#8028) 2020-09-08 20:56:51 +02:00
Thomas Landauer
717ef9106c Update events.rst (#8257)
* Update events.rst

Improved code formatting

* Update docs/en/reference/events.rst

I was wondering myself what the `#` was about ;-)

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

* Update events.rst

See https://github.com/doctrine/orm/pull/8257#discussion_r482305774

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>
2020-09-03 22:44:28 +02:00
Christoph Ziegenberg
ccae8f7176 Fix for bug #8229 (id column from parent class renamed in child class) (#8234)
This fixes problems with id columns defined in the parent class but renamed in the child class using an attribute override. Before this change always the child column name was used (which was not present in the parent class), now the correct column names are used for the parent table when creating inserts, joins and deletions for it.

Co-authored-by: Crossjoin <crossjoin@users.noreply.github.com>
2020-08-29 13:25:18 +02:00
Grégoire Paris
da18985aca Stop using the DBAL extension of PHPBench (#8252)
It has been removed, and the default XML storage driver is supposed to
be fine for our purposes.
2020-08-29 12:28:40 +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
Simon Podlipsky
1e2ed07731 Use consistent formatting in command line (#8238) 2020-08-12 08:38:00 +02:00
Grégoire Paris
424241f29c Use more accurate terminology (#8236)
I think this was a mistake when writing this documentation, and that the
original author meant to use attribute here, columns do not have
columns, they have attributes.
2020-08-09 22:48:52 +02:00
Grégoire Paris
8230afcde9 Fetch deeper
This is needed to preserve parent information and be able to find the
merge base with the head branch. The assumption here is that the common
ancestor can be found among the 10 parent commits.
2020-08-07 22:11:21 +02:00
Kirill Matasov
7cffba8743 Fix annotation in Expr 2020-08-07 22:11:21 +02:00
Zacharias Luiten
91b9dd90f4 Add DQL support for ORDER BY CASE (#8188) 2020-08-05 22:49:51 +02:00
Benjamin Cremer
7e5fe79349 Change preferred cache driver for metadata and query caches (#8223)
See: https://twitter.com/lcobucci/status/1289087725694484481
2020-08-03 21:40:52 +02:00
njutn95
efd25484f4 Update aggregate-fields.rst (#8215)
Minor typo fix
2020-07-20 21:19:16 +02:00
Andreas Möller
271f3480c8 Fix: Typo (#8213) 2020-07-15 22:46:25 +02:00
orklah
aab589b596 add/fix more types (checked by psalm) (#8199)
* add/fix more psalm types

* remove inexistant SimpleEntityExpression

* Declare template implements for TreeWalkerChainIterator
2020-07-10 22:08:13 +02:00
orklah
190218b267 Change list() syntax to array destructuring (short list syntax) (#8204) 2020-07-07 22:02:34 +02:00
Benjamin Eberlei
181114f2c7 [GH-8106] Move test into existing QueryTest testcase to reduce surface. 2020-07-05 21:51:35 +02:00
tom93
3689b76a86 Fix QueryBuilder::getParameter() on parameter names with colons (#8107)
* Fix type errors

(partially cherry picked from commit 17e7c2a42e)

* Fix QueryBuilder::getParameter() on parameter names with colons

Fixes #8106.

Co-authored-by: Michael Moravec <mail@majkl578.cz>
2020-07-05 21:46:48 +02:00
Igor Pellegrini
75fe18ea5f Add alert to "avoid persisting detached entities" (#8109)
The alert is hidden into the code of ``EntityManager``,
while it's useful to be mentioned in the documentation.
2020-07-05 20:24:08 +02:00
Grégoire Paris
6c73a6b720 Infer return type information with Psalter (#8150)
These changes are a subset of changes done with vendor/bin/psalm --alter
--issues=LessSpecificReturnType
--allow-backwards-incompatible-changes=false
2020-07-05 20:15:39 +02:00
Benjamin Eberlei
775d91c2a3 [GH-8122] Move test from AbstractMappingDriverTest to ClassMetadataFactoryTest case 2020-07-05 20:11:01 +02:00
Gildas Quéméner
64c3f68734 Prohibits class typo in the discriminator map (#8122)
* Prevents incorrect table aliases to be generated

When a defined subclass has a case typo, the query builder will be lost
and will generate exotic table alias. This commit fixes the issue at the
root by prohibiting case typo in discriminator map.

See https://github.com/doctrine/orm/pull/8112 for the consequence of
such typo.

* Controls growing rate of the abstract test class

* Fixes incorrect test case

The Cube class must be autoloaded with the correct case, otherwise
composer autoloader will complain.

* Removes non architecture compliant code

See https://github.com/doctrine/orm/pull/8122/files#r423952247

* Ensures discriminator map is case sensitive
2020-07-05 19:48:38 +02:00
Mohamed Ettaki Talbi
2a2a0b2980 Remove unwanted period (#8198) 2020-07-02 08:36:18 +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
vladyslavstartsev
a438e90046 add dev files to .gitattribute (#8190)
those files probably are not used by end user of the lib
2020-06-19 13:48:45 +02:00
Nicolas Grekas
6a670d7d6d Allow using on PHP 7.1 with Composer 2 (#8184) 2020-06-18 23:05:13 +02:00
Grégoire Paris
765521d257 Use CodeCov instead of Scrutinizer (#8187)
* Document current branch status in the README

* Use CodeCov instead of Scrutinizer
2020-06-17 22:47:25 +02:00
Grégoire Paris
5ced62bf83 Run benchmark on PHP 7.4 (#8186)
phpbench recently dropped compatibilty with PHP 7.1
When this job was introduced, 7.1 was the latest version of PHP we
supported, so it makes sense to bump to 7.4 now.
See https://github.com/phpbench/phpbench/releases/tag/0.17.0
See e07c90df44
2020-06-17 22:06:42 +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
Alexander Berl
93867f8d77 TASK: Replace "Blacklist" example with "Banlist" (#8174) 2020-06-08 23:06:53 +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
645 changed files with 4438 additions and 6319 deletions

49
.doctrine-project.json Normal file
View File

@@ -0,0 +1,49 @@
{
"active": true,
"name": "Object Relational Mapper",
"shortName": "ORM",
"slug": "orm",
"docsSlug": "doctrine-orm",
"versions": [
{
"name": "3.0",
"branchName": "master",
"slug": "latest",
"upcoming": true
},
{
"name": "2.8",
"branchName": "2.8.x",
"slug": "2.8",
"upcoming": true
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"current": true,
"aliases": [
"current",
"stable"
]
},
{
"name": "2.6",
"branchName": "2.6",
"slug": "2.6",
"maintained": false
},
{
"name": "2.5",
"branchName": "2.5",
"slug": "2.5",
"maintained": false
},
{
"name": "2.4",
"branchName": "2.4",
"slug": "2.4",
"maintained": false
}
]
}

4
.gitattributes vendored
View File

@@ -1,5 +1,6 @@
/tests export-ignore
/tools export-ignore
/.github export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
@@ -11,3 +12,6 @@ CONTRIBUTING.md export-ignore
phpunit.xml.dist export-ignore
run-all.sh export-ignore
phpcs.xml.dist export-ignore
phpbench.json export-ignore
phpstan.neon export-ignore
psalm.xml export-ignore

46
.github/workflows/coding-standard.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Static Analysis
on:
pull_request:
jobs:
coding-standards:
name: "Coding Standards"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 10
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: "cs2pr"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Install git-phpcs"
run: "wget https://github.com/diff-sniffer/git/releases/download/0.3.2/git-phpcs.phar"
- name: "Fetch head branch"
run: "git remote set-branches --add origin $GITHUB_BASE_REF && git fetch origin $GITHUB_BASE_REF"
- name: "Run git-phpcs"
run: "php git-phpcs.phar origin/$GITHUB_BASE_REF...$GITHUB_SHA --report=checkstyle | cs2pr"

View File

@@ -0,0 +1,285 @@
name: "Continuous Integration"
on:
pull_request:
push:
env:
fail-fast: true
jobs:
phpunit-smoke-check:
name: "PHPUnit with SQLite"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
deps:
- "normal"
include:
- deps: "low"
php-version: "7.3"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
extensions: "pdo, pdo_sqlite"
coverage: "pcov"
ini-values: "zend.assertions=1"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
if: "${{ matrix.deps == 'normal' }}"
- name: "Install lowest possible dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest --prefer-dist --prefer-lowest"
if: "${{ matrix.deps == 'low' }}"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --coverage-clover=coverage-no-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 0
- name: "Run PHPUnit with Second Level Cache"
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 1
- name: "Upload coverage file"
uses: "actions/upload-artifact@v2"
with:
name: "phpunit-sqlite-${{ matrix.deps }}-${{ matrix.php-version }}-coverage"
path: "coverage*.xml"
phpunit-postgres:
name: "PHPUnit with PostgreSQL"
runs-on: "ubuntu-20.04"
needs: "phpunit-smoke-check"
strategy:
matrix:
php-version:
- "7.4"
postgres-version:
- "9.6"
- "13"
services:
postgres:
image: "postgres:${{ matrix.postgres-version }}"
env:
POSTGRES_PASSWORD: "postgres"
options: >-
--health-cmd "pg_isready"
ports:
- "5432:5432"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
ini-values: "zend.assertions=1"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v2"
with:
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-coverage"
path: "coverage.xml"
phpunit-mariadb:
name: "PHPUnit with MariaDB"
runs-on: "ubuntu-20.04"
needs: "phpunit-smoke-check"
strategy:
matrix:
php-version:
- "7.4"
mariadb-version:
- "10.5"
extension:
- "mysqli"
- "pdo_mysql"
services:
mariadb:
image: "mariadb:${{ matrix.mariadb-version }}"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: "doctrine_tests"
options: >-
--health-cmd "mysqladmin ping --silent"
ports:
- "3306:3306"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
ini-values: "zend.assertions=1"
extensions: "${{ matrix.extension }}"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v2"
with:
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
path: "coverage.xml"
phpunit-mysql:
name: "PHPUnit with MySQL"
runs-on: "ubuntu-20.04"
needs: "phpunit-smoke-check"
strategy:
matrix:
php-version:
- "7.4"
mysql-version:
- "5.7"
- "8.0"
extension:
- "mysqli"
- "pdo_mysql"
services:
mysql:
image: "mysql:${{ matrix.mysql-version }}"
options: >-
--health-cmd "mysqladmin ping --silent"
-e MYSQL_ALLOW_EMPTY_PASSWORD=yes
-e MYSQL_DATABASE=doctrine_tests
ports:
- "3306:3306"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
ini-values: "zend.assertions=1"
extensions: "${{ matrix.extension }}"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 0
- name: "Run PHPUnit with Second Level Cache"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-no-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 1
- name: "Upload coverage files"
uses: "actions/upload-artifact@v2"
with:
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
path: "coverage*.xml"
upload_coverage:
name: "Upload coverage to Codecov"
runs-on: "ubuntu-20.04"
needs:
- "phpunit-smoke-check"
- "phpunit-postgres"
- "phpunit-mariadb"
- "phpunit-mysql"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Download coverage files"
uses: "actions/download-artifact@v2"
with:
path: "reports"
- name: "Upload to Codecov"
uses: "codecov/codecov-action@v1"
with:
directory: reports

View File

@@ -0,0 +1,46 @@
name: "Automatic Releases"
on:
milestone:
types:
- "closed"
jobs:
release:
name: "Git tag, release & create merge-up PR"
runs-on: "ubuntu-20.04"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Release"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:release"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"SHELL_VERBOSITY": "3"
- name: "Create Merge-Up Pull Request"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:create-merge-up-pull-request"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create new milestones"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:create-milestones"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}

View File

@@ -1,11 +1,11 @@
name: CI
name: Static Analysis
on:
pull_request:
jobs:
static-analysis-phpstan:
name: "Static Analysis with PHPStan"
name: "PHPStan"
runs-on: "ubuntu-latest"
strategy:
@@ -32,13 +32,13 @@ jobs:
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer install --no-progress --no-suggest --no-interaction --prefer-dist"
run: "composer update --no-progress --no-suggest --no-interaction --prefer-dist"
- name: "Run a static analysis with phpstan/phpstan"
run: "php vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
static-analysis-psalm:
name: "Static Analysis with Psalm"
name: "Psalm"
runs-on: "ubuntu-latest"
strategy:
@@ -64,46 +64,7 @@ jobs:
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer install --no-interaction --no-progress --no-suggest"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Run a static analysis with vimeo/psalm"
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
coding-standards:
name: "Coding Standards"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: "cs2pr"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer install --no-interaction --no-progress --no-suggest"
- name: "Install git-phpcs"
run: "wget https://github.com/diff-sniffer/git/releases/download/0.3.2/git-phpcs.phar"
- name: "Fetch head branch"
run: "git remote set-branches --add origin $GITHUB_BASE_REF && git fetch origin $GITHUB_BASE_REF"
- name: "Run git-phpcs"
run: "php git-phpcs.phar origin/$GITHUB_BASE_REF...$GITHUB_SHA --report=checkstyle|cs2pr"

2
.gitignore vendored
View File

@@ -14,3 +14,5 @@ lib/Doctrine/DBAL
vendor/
/tests/Doctrine/Performance/history.db
/.phpcs-cache
composer.lock
/.phpunit.result.cache

View File

@@ -1,34 +0,0 @@
build:
nodes:
analysis:
environment:
php:
version: 7.1
cache:
disabled: false
directories:
- ~/.composer/cache
project_setup:
override: true
tests:
override:
- php-scrutinizer-run
before_commands:
- "composer install --no-dev --prefer-source"
tools:
external_code_coverage:
timeout: 3600
filter:
excluded_paths:
- docs
- tools
build_failure_conditions:
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
- 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
- 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection
- 'patches.label("Unused Use Statements").new.exists' # No new unused imports patches allowed

View File

@@ -1,106 +0,0 @@
dist: trusty
sudo: false
language: php
php:
- 7.1
- 7.2
- 7.3
- 7.4
env:
- DB=sqlite
- DB=mysql
- DB=pgsql
before_install:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
- composer self-update
install: travis_retry composer validate --strict && composer update --prefer-dist
script:
- if [[ "$DB" == "mysql" || "$DB" == "mariadb" ]]; then mysql -e "CREATE SCHEMA doctrine_tests; GRANT ALL PRIVILEGES ON doctrine_tests.* to travis@'%'"; fi
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml
- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
jobs:
include:
- stage: Test
env: DB=mariadb
addons:
mariadb: 10.1
- stage: Test
dist: xenial
env: DB=mysql MYSQL_VERSION=5.7
php: 7.1
services:
- mysql
before_script:
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
sudo: required
- stage: Test
dist: xenial
env: DB=mysql MYSQL_VERSION=5.7
php: 7.2
services:
- mysql
before_script:
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
sudo: required
- stage: Test
dist: xenial
env: DB=mysql MYSQL_VERSION=5.7
php: 7.4
services:
- mysql
before_script:
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
sudo: required
- stage: Test
env: DB=sqlite DEPENDENCIES=low
install: travis_retry composer update --prefer-dist --prefer-lowest
- stage: Test
if: type = cron
php: 7.3
env: DB=sqlite DEV_DEPENDENCIES
install:
- composer config minimum-stability dev
- travis_retry composer update --prefer-dist
- stage: Test
env: DB=sqlite COVERAGE
before_script:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,}
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
script:
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --coverage-clover ./build/logs/clover.xml
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml
- stage: Code Quality
env: DB=none BENCHMARK
before_script: wget https://phpbench.github.io/phpbench/phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar.pubkey
script: php phpbench.phar run -l dots --report=default
- stage: Code Quality
if: NOT type = pull_request
env: DB=none CODING_STANDARDS
php: 7.4
install: travis_retry composer install --prefer-dist
script:
- ./vendor/bin/phpcs
allow_failures:
- stage: Code Quality
env: DB=none CODING_STANDARDS
cache:
directories:
- $HOME/.composer/cache

View File

@@ -1,7 +1,7 @@
| [Master][Master] | [2.5][2.5] |
| [Master][Master] | [2.8.x][2.8] |
|:----------------:|:----------:|
| [![Build status][Master image]][Master] | [![Build status][2.5 image]][2.5] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.5 coverage image]][2.5 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://img.shields.io/scrutinizer/coverage/g/doctrine/orm/master.svg?style=flat-square
[Master coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=master
[2.5 image]: https://img.shields.io/travis/doctrine/orm/2.5.svg?style=flat-square
[2.5]: https://github.com/doctrine/orm/tree/2.5
[2.5 coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/2.5.svg?style=flat-square
[2.5 coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=2.5
[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.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

@@ -1,9 +1,9 @@
@echo off
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
GOTO RUN
:USE_PEAR_PATH
set PHPBIN=%PHP_PEAR_PHP_BIN%
:RUN
"%PHPBIN%" "@bin_dir@\doctrine" %*
@echo off
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
GOTO RUN
:USE_PEAR_PATH
set PHPBIN=%PHP_PEAR_PHP_BIN%
:RUN
"%PHPBIN%" "@bin_dir@\doctrine" %*

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<var name="db_driver" value="mysqli"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_port" value="3306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<var name="db_driver" value="pdo_mysql"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_port" value="3306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<var name="db_driver" value="pdo_pgsql"/>
<var name="db_host" value="localhost" />
<var name="db_user" value="postgres" />
<var name="db_password" value="postgres" />
<var name="db_dbname" value="doctrine_tests" />
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>

View File

@@ -13,33 +13,30 @@
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
],
"config": {
"platform": {
"php": "7.1.3"
},
"sort-packages": true
},
"require": {
"php": "^7.1",
"php": "^7.2|^8.0",
"ext-pdo": "*",
"doctrine/annotations": "^1.8",
"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",
"ocramius/package-versions": "^1.2",
"doctrine/persistence": "^2.0",
"symfony/console": "^3.0|^4.0|^5.0"
},
"require-dev": {
"doctrine/coding-standard": "^5.0",
"doctrine/coding-standard": "^8.0",
"phpstan/phpstan": "^0.12.18",
"phpunit/phpunit": "^7.5",
"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"

3618
composer.lock generated

File diff suppressed because it is too large Load Diff

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

@@ -7,4 +7,4 @@ rm build -Rf
sphinx-build en build
sphinx-build -b latex en build/pdf
rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex
rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex

View File

@@ -1,2 +1,2 @@
#!/bin/bash
sudo apt-get update && sudo apt-get install -y python2.7 python-sphinx python-pygments
sudo apt-get update && sudo apt-get install -y python2.7 python-sphinx python-pygments

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.
@@ -22,7 +22,7 @@ into the account can either be of positive or negative money
values. Each account has a credit limit and the account is never
allowed to have a balance below that value.
For simplicity we live in a world were money is composed of
For simplicity we live in a world where money is composed of
integers only. Also we omit the receiver/sender name, stated reason
for transfer and the execution date. These all would have to be
added on the ``Entry`` object.
@@ -32,30 +32,39 @@ Our entities look like:
.. code-block:: php
<?php
namespace Bank\Entities;
use Doctrine\ORM\Mapping as ORM;
/**
* @Entity
* @ORM\Entity
*/
class Account
{
/** @Id @GeneratedValue @Column(type="integer") */
private $id;
/** @Column(type="string", unique=true) */
private $no;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id;
/**
* @OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
* @ORM\Column(type="string", unique=true)
*/
private $entries;
private string $no;
/**
* @Column(type="integer")
* @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
*/
private $maxCredit = 0;
private array $entries;
public function __construct($no, $maxCredit = 0)
/**
* @ORM\Column(type="integer")
*/
private int $maxCredit = 0;
public function __construct(string $no, int $maxCredit = 0)
{
$this->no = $no;
$this->maxCredit = $maxCredit;
@@ -64,31 +73,35 @@ Our entities look like:
}
/**
* @Entity
* @ORM\Entity
*/
class Entry
{
/** @Id @GeneratedValue @Column(type="integer") */
private $id;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id;
/**
* @ManyToOne(targetEntity="Account", inversedBy="entries")
* @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
*/
private $account;
private Account $account;
/**
* @Column(type="integer")
* @ORM\Column(type="integer")
*/
private $amount;
private int $amount;
public function __construct($account, $amount)
public function __construct(Account $account, int $amount)
{
$this->account = $account;
$this->amount = $amount;
// more stuff here, from/to whom, stated reason, execution date and such
}
public function getAmount()
public function getAmount(): Amount
{
return $this->amount;
}
@@ -146,12 +159,14 @@ collection, which means we can compute this value at runtime:
class Account
{
// .. previous code
public function getBalance()
public function getBalance(): int
{
$balance = 0;
foreach ($this->entries as $entry) {
$balance += $entry->getAmount();
}
return $balance;
}
}
@@ -175,13 +190,12 @@ relation with this method:
<?php
class Account
{
public function addEntry($amount)
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
$e = new Entry($this, $amount);
$this->entries[] = $e;
return $e;
}
}
@@ -190,7 +204,10 @@ Now look at the following test-code for our entities:
.. code-block:: php
<?php
class AccountTest extends \PHPUnit_Framework_TestCase
use PHPUnit\Framework\TestCase;
class AccountTest extends TestCase
{
public function testAddEntry()
{
@@ -208,7 +225,7 @@ Now look at the following test-code for our entities:
{
$account = new Account("123456", $maxCredit = 200);
$this->setExpectedException("Exception");
$this->expectException(Exception::class);
$account->addEntry(-1000);
}
}
@@ -219,9 +236,12 @@ To enforce our rule we can now implement the assertion in
.. code-block:: php
<?php
class Account
{
private function assertAcceptEntryAllowed($amount)
// .. previous code
private function assertAcceptEntryAllowed(int $amount): void
{
$futureBalance = $this->getBalance() + $amount;
$allowedMinimalBalance = ($this->maxCredit * -1);
@@ -266,23 +286,22 @@ entries collection) we want to add an aggregate field called
class Account
{
/**
* @Column(type="integer")
* @ORM\Column(type="integer")
*/
private $balance = 0;
private int $balance = 0;
public function getBalance()
public function getBalance(): int
{
return $this->balance;
}
public function addEntry($amount)
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
$e = new Entry($this, $amount);
$this->entries[] = $e;
$this->balance += $amount;
return $e;
}
}
@@ -306,12 +325,15 @@ potentially lead to inconsistent state. See this example:
.. code-block:: php
<?php
use Bank\Entities\Account;
// The Account $accId has a balance of 0 and a max credit limit of 200:
// request 1 account
$account1 = $em->find('Bank\Entities\Account', $accId);
$account1 = $em->find(Account::class, $accId);
// request 2 account
$account2 = $em->find('Bank\Entities\Account', $accId);
$account2 = $em->find(Account::class, $accId);
$account1->addEntry(-200);
$account2->addEntry(-200);
@@ -332,10 +354,14 @@ Optimistic locking is as easy as adding a version column:
.. code-block:: php
<?php
class Account
{
/** @Column(type="integer") @Version */
private $version;
/**
* @ORM\Column(type="integer")
* @ORM\Version
*/
private int $version;
}
The previous example would then throw an exception in the face of
@@ -349,9 +375,11 @@ the database using a FOR UPDATE.
.. code-block:: php
<?php
use Bank\Entities\Account;
use Doctrine\DBAL\LockMode;
$account = $em->find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ);
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ);
Keeping Updates and Deletes in Sync
-----------------------------------
@@ -372,5 +400,3 @@ field that offers serious performance benefits over iterating all
the related objects that make up an aggregate value. Finally I
showed how you can ensure that your aggregate fields do not get out
of sync due to race-conditions and concurrent access.

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
@@ -167,7 +167,7 @@ can be set via ``Query::setHint($name, $value)`` as shown in the
previous example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint.
We will implement a custom Output Walker that allows to specify the
SQL\_NO\_CACHE query hint.
``SQL_NO_CACHE`` query hint.
.. code-block:: php
@@ -180,7 +180,7 @@ SQL\_NO\_CACHE query hint.
Our ``MysqlWalker`` will extend the default ``SqlWalker``. We will
modify the generation of the SELECT clause, adding the
SQL\_NO\_CACHE on those queries that need it:
``SQL_NO_CACHE`` on those queries that need it:
.. code-block:: php

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

@@ -1,113 +1,113 @@
@ECHO OFF
REM Command file for Sphinx documentation
set SPHINXBUILD=sphinx-build
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end
@ECHO OFF
REM Command file for Sphinx documentation
set SPHINXBUILD=sphinx-build
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

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

@@ -18,9 +18,9 @@ This chapter is split into three different sections.
One tip for working with relations is to read the relation from left to right, where the left word refers to the current Entity. For example:
- OneToMany - One instance of the current Entity has Many instances (references) to the refered Entity.
- ManyToOne - Many instances of the current Entity refer to One instance of the refered Entity.
- OneToOne - One instance of the current Entity refers to One instance of the refered Entity.
- OneToMany - One instance of the current Entity has Many instances (references) to the referred Entity.
- ManyToOne - Many instances of the current Entity refer to One instance of the referred Entity.
- OneToOne - One instance of the current Entity refers to One instance of the referred Entity.
See below for all the possible relations.

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

@@ -1,8 +1,8 @@
Caching
=======
Doctrine provides cache drivers in the ``Common`` package for some
of the most popular caching implementations such as APC, Memcache
Doctrine provides cache drivers in the ``doctrine/cache`` package for some
of the most popular caching implementations such as APCu, Memcache
and Xcache. We also provide an ``ArrayCache`` driver which stores
the data in a PHP array. Obviously, when using ``ArrayCache``, the
cache does not persist between requests, but this is useful for
@@ -306,7 +306,7 @@ the result cache.
<?php
$query = $em->createQuery('select u from \Entities\User u');
$query->useResultCache(true);
$query->enableResultCache();
You can also configure an individual query to use a different
result cache driver.
@@ -317,19 +317,18 @@ result cache driver.
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
'/path/to/writable/directory'
);
$config = new \Doctrine\ORM\Configuration();
$query->setResultCacheDriver($cacheDriver);
.. note::
Setting the result cache driver on the query will
automatically enable the result cache for the query. If you want to
disable it pass false to ``useResultCache()``.
disable it use ``disableResultCache()``.
::
<?php
$query->useResultCache(false);
$query->disableResultCache();
If you want to set the time the cache has to live you can use the
@@ -350,12 +349,12 @@ yourself with the ``setResultCacheId()`` method.
$query->setResultCacheId('my_custom_id');
You can also set the lifetime and cache ID by passing the values as
the second and third argument to ``useResultCache()``.
the first and second argument to ``enableResultCache()``.
.. code-block:: php
<?php
$query->useResultCache(true, 3600, 'my_custom_id');
$query->enableResultCache(3600, 'my_custom_id');
Metadata Cache
~~~~~~~~~~~~~~

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,29 +467,25 @@ 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
<?php
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
$query = $em->createQuery('SELECT u FROM User u JOIN Banlist b WITH u.email = b.email');
.. note::
The differences between WHERE, WITH and HAVING clauses may be
@@ -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:
@@ -145,61 +145,65 @@ An example for a correct notation can be found in the example
Lifecycle Events
----------------
The EntityManager and UnitOfWork trigger a bunch of events during
the life-time of their registered entities.
The ``EntityManager`` and ``UnitOfWork`` classes trigger a bunch of
events during the life-time of their registered entities.
- preRemove - The preRemove event occurs for a given entity before
the respective EntityManager remove operation for that entity is
executed. It is not called for a DQL DELETE statement.
- postRemove - The postRemove event occurs for an entity after the
- ``preRemove`` - The ``preRemove`` event occurs for a given entity
before the respective ``EntityManager`` remove operation for that
entity is executed. It is not called for a DQL ``DELETE`` statement.
- ``postRemove`` - The ``postRemove`` event occurs for an entity after the
entity has been deleted. It will be invoked after the database
delete operations. It is not called for a DQL DELETE statement.
- prePersist - The prePersist event occurs for a given entity
before the respective EntityManager persist operation for that
delete operations. It is not called for a DQL ``DELETE`` statement.
- ``prePersist`` - The ``prePersist`` event occurs for a given entity
before the respective ``EntityManager`` persist operation for that
entity is executed. It should be noted that this event is only triggered on
*initial* persist of an entity (i.e. it does not trigger on future updates).
- postPersist - The postPersist event occurs for an entity after
- ``postPersist`` - The ``postPersist`` event occurs for an entity after
the entity has been made persistent. It will be invoked after the
database insert operations. Generated primary key values are
available in the postPersist event.
- preUpdate - The preUpdate event occurs before the database
update operations to entity data. It is not called for a DQL UPDATE statement
nor when the computed changeset is empty.
- postUpdate - The postUpdate event occurs after the database
update operations to entity data. It is not called for a DQL UPDATE statement.
- postLoad - The postLoad event occurs for an entity after the
entity has been loaded into the current EntityManager from the
- ``preUpdate`` - The ``preUpdate`` event occurs before the database
update operations to entity data. It is not called for a DQL
``UPDATE`` statement nor when the computed changeset is empty.
- ``postUpdate`` - The ``postUpdate`` event occurs after the database
update operations to entity data. It is not called for a DQL
``UPDATE`` statement.
- ``postLoad`` - The postLoad event occurs for an entity after the
entity has been loaded into the current ``EntityManager`` from the
database or after the refresh operation has been applied to it.
- loadClassMetadata - The loadClassMetadata event occurs after the
- ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
mapping metadata for a class has been loaded from a mapping source
(annotations/xml/yaml). This event is not a lifecycle callback.
- onClassMetadataNotFound - Loading class metadata for a particular
- ``onClassMetadataNotFound`` - Loading class metadata for a particular
requested class name failed. Manipulating the given event args instance
allows providing fallback metadata even when no actual metadata exists
or could be found. This event is not a lifecycle callback.
- preFlush - The preFlush event occurs at the very beginning of a flush
operation.
- onFlush - The onFlush event occurs after the change-sets of all
- ``preFlush`` - The ``preFlush`` event occurs at the very beginning of
a flush operation.
- ``onFlush`` - The ``onFlush`` event occurs after the change-sets of all
managed entities are computed. This event is not a lifecycle
callback.
- postFlush - The postFlush event occurs at the end of a flush operation. This
- ``postFlush`` - The ``postFlush`` event occurs at the end of a flush operation. This
event is not a lifecycle callback.
- onClear - The onClear event occurs when the EntityManager#clear() operation is
invoked, after all references to entities have been removed from the unit of
work. This event is not a lifecycle callback.
- ``onClear`` - The ``onClear`` event occurs when the
``EntityManager#clear()`` operation is invoked, after all references
to entities have been removed from the unit of work. This event is not
a lifecycle callback.
.. 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::
Note that the postRemove event or any events triggered after an entity removal
Note that the ``postRemove`` event or any events triggered after an entity removal
can receive an uninitializable proxy in case you have configured an entity to
cascade remove relations. In this case, you should load yourself the proxy in
the associated pre event.
@@ -217,18 +221,18 @@ 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.
The EventArgs instance received by the listener gives access to the entity,
EntityManager and other relevant data.
The ``EventArgs`` instance received by the listener gives access to the entity,
``EntityManager`` instance and other relevant data.
.. note::
All Lifecycle events that happen during the ``flush()`` of
an EntityManager have very specific constraints on the allowed
an ``EntityManager`` have very specific constraints on the allowed
operations that can be executed. Please read the
:ref:`reference-events-implementing-listeners` section very carefully
to understand which operations are allowed in which lifecycle event.
@@ -379,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.
@@ -410,9 +412,9 @@ sit at a level above the entities and allow you to implement re-usable
behaviors across different entity classes.
Note that they require much more detailed knowledge about the inner
workings of the EntityManager and UnitOfWork. Please read the
:ref:`reference-events-implementing-listeners` section carefully if you
are trying to write your own listener.
workings of the ``EntityManager`` and ``UnitOfWork`` classes. Please
read the :ref:`reference-events-implementing-listeners` section
carefully if you are trying to write your own listener.
For event subscribers, there are no surprises. They declare the
lifecycle events in their ``getSubscribedEvents`` method and provide
@@ -501,16 +503,16 @@ Implementing Event Listeners
----------------------------
This section explains what is and what is not allowed during
specific lifecycle events of the UnitOfWork. Although you get
passed the EntityManager in all of these events, you have to follow
these restrictions very carefully since operations in the wrong
event may produce lots of different errors, such as inconsistent
specific lifecycle events of the ``UnitOfWork`` class. Although you get
passed the ``EntityManager`` instance in all of these events, you have
to follow these restrictions very carefully since operations in the
wrong event may produce lots of different errors, such as inconsistent
data and lost updates/persists/removes.
For the described events that are also lifecycle callback events
the restrictions apply as well, with the additional restriction
that (prior to version 2.4) you do not have access to the
EntityManager or UnitOfWork APIs inside these events.
``EntityManager`` or ``UnitOfWork`` APIs inside these events.
prePersist
~~~~~~~~~~
@@ -586,8 +588,8 @@ entities and their associations have been computed. This means, the
- Collections scheduled for update
- Collections scheduled for removal
To make use of the onFlush event you have to be familiar with the
internal UnitOfWork API, which grants you access to the previously
To make use of the ``onFlush`` event you have to be familiar with the
internal ``UnitOfWork`` API, which grants you access to the previously
mentioned sets. See this example:
.. code-block:: php
@@ -735,7 +737,7 @@ Restrictions for this event:
the event to modify primitive field values, e.g. use
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
- Any calls to ``EntityManager#persist()`` or
``EntityManager#remove()``, even in combination with the UnitOfWork
``EntityManager#remove()``, even in combination with the ``UnitOfWork``
API are strongly discouraged and don't work as expected outside the
flush operation.
@@ -757,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.
@@ -991,4 +991,3 @@ process and manipulate the instance.
}
}

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

@@ -20,12 +20,17 @@ Metadata and Query caches
As already mentioned earlier in the chapter about configuring
Doctrine, it is strongly discouraged to use Doctrine without a
Metadata and Query cache (preferably with APC or Memcache as the
cache driver). Operating Doctrine without these caches means
Metadata and Query cache.
Operating Doctrine without these caches means
Doctrine will need to load your mapping information on every single
request and has to parse each DQL query on every single request.
This is a waste of resources.
The preferred cache driver for metadata and query caches is ``PhpFileCache``.
This driver serializes cache items and writes them to a file.
This allows for opcode caching to be used and provides high performance in most scenarios.
See :ref:`integrating-with-the-orm`
Alternative Query Result Formats
@@ -38,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.
@@ -289,9 +289,15 @@ column and cascading on delete.
Overrides
---------
Used to override a mapping for an entity field or relationship.
May be applied to an entity that extends a mapped superclass
to override a relationship or field mapping defined by the mapped superclass.
Used to override a mapping for an entity field or relationship. Can only be
applied to an entity that extends a mapped superclass or uses a trait to
override a relationship or field mapping defined by the mapped superclass or
trait.
It is not possible to override attributes or associations in entity to entity
inheritance scenarios, because this can cause unforseen edge case behavior and
increases complexity in ORM internal classes.
Association Override
@@ -584,7 +590,7 @@ Things to note:
- The "attribute override" specifies the overrides base on the property name.
- The column type *CANNOT* be changed. If the column type is not equal you get a ``MappingException``
- The override can redefine all the columns except the type.
- The override can redefine all the attributes except the type.
Query the Type
--------------

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

@@ -77,11 +77,10 @@ A query region might be something like :
Cache Regions
-------------
``Doctrine\ORM\Cache\Region\DefaultRegion`` It's the default implementation.
``Doctrine\ORM\Cache\Region\DefaultRegion`` is the default implementation.
A simplest cache region compatible with all doctrine-cache drivers but does not support locking.
``Doctrine\ORM\Cache\Region`` and ``Doctrine\ORM\Cache\ConcurrentRegion``
Defines contracts that should be implemented by a cache provider.
define contracts that should be implemented by a cache provider.
It allows you to provide your own cache implementation that might take advantage of specific cache driver.
@@ -91,11 +90,8 @@ If you want to support locking for ``READ_WRITE`` strategies you should implemen
Cache region
~~~~~~~~~~~~
Defines a contract for accessing a particular region.
``Doctrine\ORM\Cache\Region``
Defines a contract for accessing a particular cache region.
``Doctrine\ORM\Cache\Region`` defines a contract for accessing a particular
cache region.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Region.html>`_.
@@ -107,9 +103,7 @@ By default, Doctrine provides a very simple implementation based on file locks `
If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region.
``Doctrine\ORM\Cache\ConcurrentRegion``
Defines contract for concurrently managed data region.
``Doctrine\ORM\Cache\ConcurrentRegion`` defines a contract for concurrently managed data region.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/ConcurrentRegion.html>`_.
@@ -177,7 +171,7 @@ Doctrine allows you to specify configurations and some points of extension for t
Enable Second Level Cache
~~~~~~~~~~~~~~~~~~~~~~~~~
To enable the second-level-cache, you should provide a cache factory
To enable the second-level-cache, you should provide a cache factory.
``\Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation.
.. code-block:: php
@@ -203,11 +197,16 @@ Cache Factory is the main point of extension.
It allows you to provide a specific implementation of the following components :
* ``QueryCache`` Store and retrieve query cache results.
* ``CachedEntityPersister`` Store and retrieve entity results.
* ``CachedCollectionPersister`` Store and retrieve query results.
* ``EntityHydrator`` Transform an entity into a cache entry and cache entry into entities
* ``CollectionHydrator`` Transform a collection into a cache entry and cache entry into collection
``QueryCache``
stores and retrieves query cache results.
``CachedEntityPersister``
stores and retrieves entity results.
``CachedCollectionPersister``
stores and retrieves query results.
``EntityHydrator``
transforms entities into a cache entries and cache entries into entities
``CollectionHydrator``
transforms collections into cache entries and cache entries into collections
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
@@ -225,8 +224,8 @@ To specify a default lifetime for all regions or specify a different lifetime fo
$regionConfig = $cacheConfig->getRegionsConfiguration();
// Cache Region lifetime
$regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region; In seconds
$regionConfig->setDefaultLifetime(7200); // Default time to live; In seconds
$regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region (in seconds)
$regionConfig->setDefaultLifetime(7200); // Default time to live (in seconds)
Cache Log
@@ -267,8 +266,9 @@ By providing a cache logger you should be able to get information about all cach
// Get the total number of cached entries *not* found in all regions.
$logger->getMissCount();
If you want to get more information you should implement ``\Doctrine\ORM\Cache\Logging\CacheLogger``.
and collect all information you want.
If you want to get more information you should implement
``\Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
all the information you want.
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
@@ -277,8 +277,11 @@ Entity cache definition
-----------------------
* Entity cache configuration allows you to define the caching strategy and region for an entity.
* ``usage`` Specifies the caching strategy: ``READ_ONLY``, ``NONSTRICT_READ_WRITE``, ``READ_WRITE``. see :ref:`reference-second-level-cache-mode`
* ``region`` Optional value that specifies the name of the second level cache region.
* ``usage`` specifies the caching strategy: ``READ_ONLY``,
``NONSTRICT_READ_WRITE``, ``READ_WRITE``.
See :ref:`reference-second-level-cache-mode`.
* ``region`` is an optional value that specifies the name of the second
level cache region.
.. configuration-block::
@@ -579,7 +582,8 @@ The Cache Mode controls how a particular query interacts with the second-level c
DELETE / UPDATE queries
~~~~~~~~~~~~~~~~~~~~~~~
DQL UPDATE / DELETE statements are ported directly into a database and bypass the second-level cache,
DQL UPDATE / DELETE statements are ported directly into a database and bypass
the second-level cache.
Entities that are already cached will NOT be invalidated.
However the cached data could be evicted using the cache API or an special query hint.
@@ -622,7 +626,7 @@ Using the repository query cache
--------------------------------
As well as ``Query Cache`` all persister queries store only identifier values for an individual query.
All persister use a single timestamps cache region keeps track of the last update for each persister,
All persisters use a single timestamp cache region to keep track of the last update for each persister,
When a query is loaded from cache, the timestamp region is checked for the last update for that persister.
Using the last update timestamps as part of the query key invalidate the cache key when an update occurs.
@@ -641,7 +645,7 @@ Using the last update timestamps as part of the query key invalidate the cache k
$em->clear();
// Reload from database.
// At this point the query cache key if not logger valid, the select goes straight
// At this point the query cache key is no longer valid, the select goes straight to the database
$entities = $em->getRepository('Entity\Country')->findAll();
Cache API
@@ -728,4 +732,5 @@ Paginator
~~~~~~~~~
Count queries generated by ``Doctrine\ORM\Tools\Pagination\Paginator`` are not cached by second-level cache.
Although entities and query result are cached count queries will hit the database every time.
Although entities and query result are cached, count queries will hit the
database every time.

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();
@@ -250,6 +250,12 @@ as follows:
- If X is a detached entity, an exception will be thrown on
flush.
.. caution::
Do not pass detached entities to the persist operation. The persist operation always
considers entities that are not yet known to the ``EntityManager`` as new entities
(refer to the ``STATE_NEW`` constant inside the ``UnitOfWork``).
Removing entities
-----------------
@@ -269,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:
@@ -312,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()``.
@@ -647,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.
@@ -694,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'));
@@ -736,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');
@@ -751,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.
@@ -794,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();
@@ -839,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()
@@ -866,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

@@ -5,19 +5,19 @@ Extra Lazy Associations
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:
@@ -291,6 +287,5 @@ Outlook into the Future
~~~~~~~~~~~~~~~~~~~~~~~
For the inverse side of a many-to-many associations there will be a way to persist the keys and the order
as a third and fourth parameter into the join table. This feature is discussed in `DDC-213 <http://www.doctrine-project.org/jira/browse/DDC-213>`_
as a third and fourth parameter into the join table. This feature is discussed in `#2817 <https://github.com/doctrine/orm/issues/2817>`_
This feature cannot be implemented for one-to-many associations, because they are never the owning side.

View File

@@ -19,14 +19,30 @@
namespace Doctrine\ORM;
use Doctrine\Common\Util\ClassUtils;
use Countable;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
use Doctrine\Persistence\Mapping\MappingException;
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.
@@ -72,6 +88,7 @@ abstract class AbstractQuery
* The parameter map of this query.
*
* @var ArrayCollection|Parameter[]
* @psalm-var ArrayCollection<int, Parameter>
*/
protected $parameters;
@@ -240,7 +257,7 @@ abstract class AbstractQuery
*
* @param integer $lifetime
*
* @return \Doctrine\ORM\AbstractQuery This query instance.
* @return static This query instance.
*/
public function setLifetime($lifetime)
{
@@ -260,7 +277,7 @@ abstract class AbstractQuery
/**
* @param integer $cacheMode
*
* @return \Doctrine\ORM\AbstractQuery This query instance.
* @return static This query instance.
*/
public function setCacheMode($cacheMode)
{
@@ -321,11 +338,13 @@ abstract class AbstractQuery
*/
public function getParameter($key)
{
$key = Query\Parameter::normalizeName($key);
$filteredParameters = $this->parameters->filter(
function (Query\Parameter $parameter) use ($key) : bool {
$parameterName = $parameter->getName();
return $key === $parameterName || (string) $key === (string) $parameterName;
return $key === $parameterName;
}
);
@@ -338,11 +357,14 @@ abstract class AbstractQuery
* @param ArrayCollection|mixed[] $parameters
*
* @return static This query instance.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*/
public function setParameters($parameters)
{
// BC compatibility with 2.3-
if (is_array($parameters)) {
/** @psalm-var ArrayCollection<int, Parameter> $parameterCollection */
$parameterCollection = new ArrayCollection();
foreach ($parameters as $key => $value) {
@@ -388,9 +410,11 @@ abstract class AbstractQuery
*
* @param mixed $value
*
* @return array|string
* @return mixed[]|string|int|float|bool
*
* @throws \Doctrine\ORM\ORMInvalidArgumentException
*
* @psalm-return array|scalar
*/
public function processParameterValue($value)
{
@@ -399,14 +423,11 @@ abstract class AbstractQuery
}
if ($value instanceof Collection) {
$value = $value->toArray();
$value = iterator_to_array($value);
}
if (is_array($value)) {
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
$value = $this->processArrayParameterValue($value);
return $value;
}
@@ -425,10 +446,51 @@ 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.
$value = $this->potentiallyProcessIterable($value);
} catch (MappingException $e) {
// as previous, but depending on MappingDriver this exception from Persistence
// is thrown and not the ORM one.
$value = $this->potentiallyProcessIterable($value);
}
return $value;
}
/**
* If no mapping is detected, trying to resolve the value as a Traversable
*
* @param mixed $value
*
* @return mixed
*/
private function potentiallyProcessIterable($value)
{
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
$value = $this->processArrayParameterValue($value);
}
return $value;
}
/**
* Process a parameter value which was previously identified as an array
*
* @param mixed[] $value
*
* @return mixed[]
*/
private function processArrayParameterValue(array $value): array
{
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
return $value;
@@ -468,7 +530,7 @@ abstract class AbstractQuery
*/
private function translateNamespaces(Query\ResultSetMapping $rsm)
{
$translate = function ($alias) {
$translate = function ($alias) : string {
return $this->_em->getClassMetadata($alias)->getName();
};
@@ -629,7 +691,7 @@ abstract class AbstractQuery
/**
* Defines how long the result cache will be active before expire.
*
* @param integer $lifetime How long the cache entry is valid.
* @param int|null $lifetime How long the cache entry is valid.
*
* @return static This query instance.
*/
@@ -908,6 +970,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.
*
@@ -915,6 +979,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);
}
@@ -929,6 +998,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.
*
@@ -964,10 +1060,11 @@ abstract class AbstractQuery
$this->setParameters($parameters);
}
$setCacheEntry = function() {};
$setCacheEntry = static function () : void {
};
if ($this->_hydrationCacheProfile !== null) {
list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
[$cacheKey, $realCacheKey] = $this->getHydrationCacheId();
$queryCacheProfile = $this->getHydrationCacheProfile();
$cache = $queryCacheProfile->getResultCacheDriver();
@@ -981,7 +1078,7 @@ abstract class AbstractQuery
$result = [];
}
$setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
$setCacheEntry = static function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) : void {
$result[$realCacheKey] = $data;
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
@@ -1068,7 +1165,7 @@ abstract class AbstractQuery
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
*
* @return array ($key, $hash)
* @return array<string, string> ($key, $hash)
*/
protected function getHydrationCacheId()
{
@@ -1161,4 +1258,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

@@ -60,9 +60,6 @@ class DefaultCache implements Cache
*/
private $defaultQueryCache;
/**
* {@inheritdoc}
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;

View File

@@ -348,7 +348,9 @@ class DefaultQueryCache implements QueryCache
* @param array $assoc
* @param mixed $assocValue
*
* @return array|null
* @return mixed[]|null
*
* @psalm-return array{targetEntity: string, type: mixed, list?: array[], identifier?: array}|null
*/
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue)
{

View File

@@ -33,17 +33,17 @@ use Doctrine\ORM\Cache\QueryCacheKey;
class StatisticsCacheLogger implements CacheLogger
{
/**
* @var array
* @var int[]
*/
private $cacheMissCountMap = [];
/**
* @var array
* @var int[]
*/
private $cacheHitCountMap = [];
/**
* @var array
* @var int[]
*/
private $cachePutCountMap = [];
@@ -142,7 +142,7 @@ class StatisticsCacheLogger implements CacheLogger
*
* @param string $regionName The name of the cache region.
*
* @return integer
* @return int
*/
public function getRegionHitCount($regionName)
{
@@ -154,7 +154,7 @@ class StatisticsCacheLogger implements CacheLogger
*
* @param string $regionName The name of the cache region.
*
* @return integer
* @return int
*/
public function getRegionMissCount($regionName)
{
@@ -166,7 +166,7 @@ class StatisticsCacheLogger implements CacheLogger
*
* @param string $regionName The name of the cache region.
*
* @return integer
* @return int
*/
public function getRegionPutCount($regionName)
{
@@ -222,7 +222,7 @@ class StatisticsCacheLogger implements CacheLogger
/**
* Get the total number of put in cache.
*
* @return integer
* @return int
*/
public function getPutCount()
{
@@ -232,7 +232,7 @@ class StatisticsCacheLogger implements CacheLogger
/**
* Get the total number of entries successfully retrieved from cache.
*
* @return integer
* @return int
*/
public function getHitCount()
{
@@ -242,7 +242,7 @@ class StatisticsCacheLogger implements CacheLogger
/**
* Get the total number of cached entries *not* found in cache.
*
* @return integer
* @return int
*/
public function getMissCount()
{

View File

@@ -144,7 +144,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
* @param \Doctrine\ORM\PersistentCollection $collection
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key
*
* @return \Doctrine\ORM\PersistentCollection|null
* @return object[]|null
*/
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key)
{

View File

@@ -283,17 +283,17 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/**
* Generates a string of currently query
*
* @param array $query
* @param string $criteria
* @param array $orderBy
* @param integer $limit
* @param integer $offset
* @param string $query
* @param string[]|Criteria $criteria
* @param string[] $orderBy
* @param int $limit
* @param int $offset
*
* @return string
*/
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
{
list($params) = ($criteria instanceof Criteria)
[$params] = $criteria instanceof Criteria
? $this->persister->expandCriteriaParameters($criteria)
: $this->persister->expandParameters($criteria);
@@ -544,19 +544,18 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/**
* {@inheritdoc}
*/
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection)
{
$persister = $this->uow->getCollectionPersister($assoc);
$hasCache = ($persister instanceof CachedPersister);
$key = null;
if ( ! $hasCache) {
return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll);
return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);
}
$ownerId = $this->uow->getEntityIdentifier($coll->getOwner());
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
$list = $persister->loadCollectionCache($coll, $key);
$list = $persister->loadCollectionCache($collection, $key);
if ($list !== null) {
if ($this->cacheLogger) {
@@ -566,7 +565,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
return $list;
}
$list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll);
$list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);
$persister->storeCollectionCache($key, $list);
@@ -580,18 +579,18 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/**
* {@inheritdoc}
*/
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection)
{
$persister = $this->uow->getCollectionPersister($assoc);
$hasCache = ($persister instanceof CachedPersister);
if ( ! $hasCache) {
return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll);
return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);
}
$ownerId = $this->uow->getEntityIdentifier($coll->getOwner());
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
$list = $persister->loadCollectionCache($coll, $key);
$list = $persister->loadCollectionCache($collection, $key);
if ($list !== null) {
if ($this->cacheLogger) {
@@ -601,7 +600,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
return $list;
}
$list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll);
$list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);
$persister->storeCollectionCache($key, $list);

View File

@@ -100,6 +100,12 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
$this->queuedCache['update'][] = $entity;
}
/**
* @param object $entity
* @param bool $isChanged
*
* @return bool
*/
private function updateCache($entity, $isChanged)
{
$class = $this->metadataFactory->getMetadataFor(get_class($entity));

View File

@@ -249,6 +249,8 @@ class FileLockRegion implements ConcurrentRegion
/**
* {@inheritdoc}
*
* @return bool
*/
public function unlock(CacheKey $key, Lock $lock)
{

View File

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

View File

@@ -65,7 +65,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
*/
public function beginTransaction()
{
return $this->wrapped->beginTransaction();
$this->wrapped->beginTransaction();
}
/**
@@ -81,7 +81,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
*/
public function commit()
{
return $this->wrapped->commit();
$this->wrapped->commit();
}
/**
@@ -89,7 +89,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
*/
public function rollback()
{
return $this->wrapped->rollback();
$this->wrapped->rollback();
}
/**
@@ -153,7 +153,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
*/
public function close()
{
return $this->wrapped->close();
$this->wrapped->close();
}
/**
@@ -169,15 +169,15 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
*/
public function lock($entity, $lockMode, $lockVersion = null)
{
return $this->wrapped->lock($entity, $lockMode, $lockVersion);
$this->wrapped->lock($entity, $lockMode, $lockVersion);
}
/**
* {@inheritdoc}
*/
public function find($entityName, $id, $lockMode = null, $lockVersion = null)
public function find($className, $id, $lockMode = null, $lockVersion = null)
{
return $this->wrapped->find($entityName, $id, $lockMode, $lockVersion);
return $this->wrapped->find($className, $id, $lockMode, $lockVersion);
}
/**
@@ -185,7 +185,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
*/
public function flush($entity = null)
{
return $this->wrapped->flush($entity);
$this->wrapped->flush($entity);
}
/**

View File

@@ -31,6 +31,7 @@ use Doctrine\Common\Util\ClassUtils;
use Doctrine\Persistence\Mapping\MappingException;
use Doctrine\Persistence\ObjectRepository;
use Throwable;
use function ltrim;
use const E_USER_DEPRECATED;
use function trigger_error;
@@ -327,7 +328,7 @@ use function trigger_error;
*/
public function createNamedNativeQuery($name)
{
list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
[$sql, $rsm] = $this->config->getNamedNativeQuery($name);
return $this->createNativeQuery($sql, $rsm);
}
@@ -373,7 +374,7 @@ use function trigger_error;
/**
* Finds an Entity by its identifier.
*
* @param string $entityName The class name of the entity to find.
* @param string $className The class name of the entity to find.
* @param mixed $id The identity of the entity to find.
* @param integer|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
@@ -387,10 +388,14 @@ use function trigger_error;
* @throws ORMInvalidArgumentException
* @throws TransactionRequiredException
* @throws ORMException
*
* @template T
* @psalm-param class-string<T> $entityName
* @psalm-return ?T
*/
public function find($entityName, $id, $lockMode = null, $lockVersion = null)
public function find($className, $id, $lockMode = null, $lockVersion = null)
{
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
$class = $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
if ($lockMode !== null) {
$this->checkLockRequirements($lockMode, $class);
@@ -732,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

@@ -38,7 +38,7 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
private $em;
/**
* @var string
* @var string|null
*/
private $entityClass;

View File

@@ -40,7 +40,7 @@ abstract class AbstractIdGenerator
* By default, this method returns FALSE. Generators that have this requirement
* must override this method and return TRUE.
*
* @return boolean
* @return bool
*/
public function isPostInsertGenerator()
{

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;
@@ -228,14 +277,14 @@ abstract class AbstractHydrator
*
* Template method.
*
* @param array $data The row data.
* @param array $result The result to fill.
* @param mixed[] $row The row data.
* @param mixed[] $result The result to fill.
*
* @return void
*
* @throws HydrationException
*/
protected function hydrateRowData(array $data, array &$result)
protected function hydrateRowData(array $row, array &$result)
{
throw new HydrationException("hydrateRowData() not implemented by this hydrator.");
}
@@ -260,8 +309,19 @@ abstract class AbstractHydrator
* @param array &$id Dql-Alias => ID-Hash.
* @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value?
*
* @return array An array with all the fields (name => value) of the data row,
* grouped by their component alias.
* @return array<string, array<string, mixed>> An array with all the fields
* (name => value) of the data
* row, grouped by their
* component alias.
*
* @psalm-return array{
* data: array<array-key, array>,
* newObjects?: array<array-key, array{
* class: mixed,
* args?: array
* }>,
* scalars?: array
* }
*/
protected function gatherRowData(array $data, array &$id, array &$nonemptyComponents)
{

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

@@ -47,8 +47,8 @@ class ScalarHydrator extends AbstractHydrator
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $data, array &$result)
protected function hydrateRowData(array $row, array &$result)
{
$result[] = $this->gatherScalarRowData($data);
$result[] = $this->gatherScalarRowData($row);
}
}

View File

@@ -22,6 +22,7 @@ namespace Doctrine\ORM\Internal\Hydration;
use PDO;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use function array_keys;
use function in_array;
class SimpleObjectHydrator extends AbstractHydrator
@@ -77,7 +78,7 @@ class SimpleObjectHydrator extends AbstractHydrator
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $sqlResult, array &$result)
protected function hydrateRowData(array $row, array &$result)
{
$entityName = $this->class->name;
$data = [];
@@ -92,27 +93,27 @@ class SimpleObjectHydrator extends AbstractHydrator
$discrColumnName = $metaMappingDiscrColumnName;
}
if ( ! isset($sqlResult[$discrColumnName])) {
if (! isset($row[$discrColumnName])) {
throw HydrationException::missingDiscriminatorColumn($entityName, $discrColumnName, key($this->_rsm->aliasMap));
}
if ($sqlResult[$discrColumnName] === '') {
if ($row[$discrColumnName] === '') {
throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
}
$discrMap = $this->class->discriminatorMap;
if ( ! isset($discrMap[$sqlResult[$discrColumnName]])) {
throw HydrationException::invalidDiscriminatorValue($sqlResult[$discrColumnName], array_keys($discrMap));
if (! isset($discrMap[$row[$discrColumnName]])) {
throw HydrationException::invalidDiscriminatorValue($row[$discrColumnName], array_keys($discrMap));
}
$entityName = $discrMap[$sqlResult[$discrColumnName]];
$discrColumnValue = $sqlResult[$discrColumnName];
$entityName = $discrMap[$row[$discrColumnName]];
$discrColumnValue = $row[$discrColumnName];
unset($sqlResult[$discrColumnName]);
unset($row[$discrColumnName]);
}
foreach ($sqlResult as $column => $value) {
foreach ($row as $column => $value) {
// An ObjectHydrator should be used instead of SimpleObjectHydrator
if (isset($this->_rsm->relationMap[$column])) {
throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column));

View File

@@ -89,7 +89,7 @@ final class HydrationCompleteHandler
$this->deferredPostLoadInvocations = [];
foreach ($toInvoke as $classAndEntity) {
list($class, $invoke, $entity) = $classAndEntity;
[$class, $invoke, $entity] = $classAndEntity;
$this->listenersInvoker->invoke(
$class,

View File

@@ -58,7 +58,7 @@ class AssociationBuilder
/**
* @param string $fieldName
*
* @return AssociationBuilder
* @return static
*/
public function mappedBy($fieldName)
{
@@ -70,7 +70,7 @@ class AssociationBuilder
/**
* @param string $fieldName
*
* @return AssociationBuilder
* @return static
*/
public function inversedBy($fieldName)
{
@@ -80,7 +80,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function cascadeAll()
{
@@ -90,7 +90,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function cascadePersist()
{
@@ -100,7 +100,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function cascadeRemove()
{
@@ -110,7 +110,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function cascadeMerge()
{
@@ -120,7 +120,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function cascadeDetach()
{
@@ -130,7 +130,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function cascadeRefresh()
{
@@ -140,7 +140,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function fetchExtraLazy()
{
@@ -150,7 +150,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function fetchEager()
{
@@ -160,7 +160,7 @@ class AssociationBuilder
}
/**
* @return AssociationBuilder
* @return static
*/
public function fetchLazy()
{
@@ -179,7 +179,7 @@ class AssociationBuilder
* @param string|null $onDelete
* @param string|null $columnDef
*
* @return AssociationBuilder
* @return static
*/
public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
{
@@ -198,7 +198,7 @@ class AssociationBuilder
/**
* Sets field as primary key.
*
* @return self
* @return static
*/
public function makePrimaryKey()
{
@@ -210,7 +210,7 @@ class AssociationBuilder
/**
* Removes orphan entities when detached from their parent.
*
* @return self
* @return static
*/
public function orphanRemoval()
{

View File

@@ -57,7 +57,7 @@ class ClassMetadataBuilder
/**
* Marks the class as mapped superclass.
*
* @return ClassMetadataBuilder
* @return static
*/
public function setMappedSuperClass()
{
@@ -70,7 +70,7 @@ class ClassMetadataBuilder
/**
* Marks the class as embeddable.
*
* @return ClassMetadataBuilder
* @return static
*/
public function setEmbeddable()
{
@@ -107,7 +107,7 @@ class ClassMetadataBuilder
*
* @param string $repositoryClassName
*
* @return ClassMetadataBuilder
* @return static
*/
public function setCustomRepositoryClass($repositoryClassName)
{
@@ -119,7 +119,7 @@ class ClassMetadataBuilder
/**
* Marks class read only.
*
* @return ClassMetadataBuilder
* @return static
*/
public function setReadOnly()
{
@@ -133,7 +133,7 @@ class ClassMetadataBuilder
*
* @param string $name
*
* @return ClassMetadataBuilder
* @return static
*/
public function setTable($name)
{
@@ -148,7 +148,7 @@ class ClassMetadataBuilder
* @param array $columns
* @param string $name
*
* @return ClassMetadataBuilder
* @return static
*/
public function addIndex(array $columns, $name)
{
@@ -167,7 +167,7 @@ class ClassMetadataBuilder
* @param array $columns
* @param string $name
*
* @return ClassMetadataBuilder
* @return static
*/
public function addUniqueConstraint(array $columns, $name)
{
@@ -186,7 +186,7 @@ class ClassMetadataBuilder
* @param string $name
* @param string $dqlQuery
*
* @return ClassMetadataBuilder
* @return static
*/
public function addNamedQuery($name, $dqlQuery)
{
@@ -203,7 +203,7 @@ class ClassMetadataBuilder
/**
* Sets class as root of a joined table inheritance hierarchy.
*
* @return ClassMetadataBuilder
* @return static
*/
public function setJoinedTableInheritance()
{
@@ -215,7 +215,7 @@ class ClassMetadataBuilder
/**
* Sets class as root of a single table inheritance hierarchy.
*
* @return ClassMetadataBuilder
* @return static
*/
public function setSingleTableInheritance()
{
@@ -231,7 +231,7 @@ class ClassMetadataBuilder
* @param string $type
* @param int $length
*
* @return ClassMetadataBuilder
* @return static
*/
public function setDiscriminatorColumn($name, $type = 'string', $length = 255)
{
@@ -252,7 +252,7 @@ class ClassMetadataBuilder
* @param string $name
* @param string $class
*
* @return ClassMetadataBuilder
* @return static
*/
public function addDiscriminatorMapClass($name, $class)
{
@@ -264,7 +264,7 @@ class ClassMetadataBuilder
/**
* Sets deferred explicit change tracking policy.
*
* @return ClassMetadataBuilder
* @return static
*/
public function setChangeTrackingPolicyDeferredExplicit()
{
@@ -276,7 +276,7 @@ class ClassMetadataBuilder
/**
* Sets notify change tracking policy.
*
* @return ClassMetadataBuilder
* @return static
*/
public function setChangeTrackingPolicyNotify()
{
@@ -291,7 +291,7 @@ class ClassMetadataBuilder
* @param string $methodName
* @param string $event
*
* @return ClassMetadataBuilder
* @return static
*/
public function addLifecycleEvent($methodName, $event)
{
@@ -307,7 +307,7 @@ class ClassMetadataBuilder
* @param string $type
* @param array $mapping
*
* @return ClassMetadataBuilder
* @return static
*/
public function addField($name, $type, array $mapping = [])
{

View File

@@ -74,7 +74,7 @@ class FieldBuilder
*
* @param int $length
*
* @return FieldBuilder
* @return static
*/
public function length($length)
{
@@ -88,7 +88,7 @@ class FieldBuilder
*
* @param bool $flag
*
* @return FieldBuilder
* @return static
*/
public function nullable($flag = true)
{
@@ -102,7 +102,7 @@ class FieldBuilder
*
* @param bool $flag
*
* @return FieldBuilder
* @return static
*/
public function unique($flag = true)
{
@@ -116,7 +116,7 @@ class FieldBuilder
*
* @param string $name
*
* @return FieldBuilder
* @return static
*/
public function columnName($name)
{
@@ -130,7 +130,7 @@ class FieldBuilder
*
* @param int $p
*
* @return FieldBuilder
* @return static
*/
public function precision($p)
{
@@ -144,7 +144,7 @@ class FieldBuilder
*
* @param int $s
*
* @return FieldBuilder
* @return static
*/
public function scale($s)
{
@@ -167,7 +167,7 @@ class FieldBuilder
/**
* Sets field as primary key.
*
* @return FieldBuilder
* @return static
*/
public function makePrimaryKey()
{
@@ -182,7 +182,7 @@ class FieldBuilder
* @param string $name
* @param mixed $value
*
* @return FieldBuilder
* @return static
*/
public function option($name, $value)
{
@@ -194,7 +194,7 @@ class FieldBuilder
/**
* @param string $strategy
*
* @return FieldBuilder
* @return static
*/
public function generatedValue($strategy = 'AUTO')
{
@@ -206,7 +206,7 @@ class FieldBuilder
/**
* Sets field versioned.
*
* @return FieldBuilder
* @return static
*/
public function isVersionField()
{
@@ -222,7 +222,7 @@ class FieldBuilder
* @param int $allocationSize
* @param int $initialValue
*
* @return FieldBuilder
* @return static
*/
public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1)
{
@@ -240,7 +240,7 @@ class FieldBuilder
*
* @param string $def
*
* @return FieldBuilder
* @return static
*/
public function columnDefinition($def)
{

View File

@@ -42,7 +42,7 @@ class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
/**
* @param string $name
*
* @return ManyToManyAssociationBuilder
* @return static
*/
public function setJoinTable($name)
{
@@ -61,7 +61,7 @@ class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
* @param string|null $onDelete
* @param string|null $columnDef
*
* @return ManyToManyAssociationBuilder
* @return static
*/
public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
{

View File

@@ -32,7 +32,7 @@ class OneToManyAssociationBuilder extends AssociationBuilder
/**
* @param array $fieldNames
*
* @return OneToManyAssociationBuilder
* @return static
*/
public function setOrderBy(array $fieldNames)
{
@@ -44,7 +44,7 @@ class OneToManyAssociationBuilder extends AssociationBuilder
/**
* @param string $fieldName
*
* @return OneToManyAssociationBuilder
* @return static
*/
public function setIndexBy($fieldName)
{

View File

@@ -31,8 +31,8 @@ use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Persistence\Mapping\ReflectionService;
use ReflectionClass;
use ReflectionException;
use function assert;
use function interface_exists;
/**
@@ -278,6 +278,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
if ( ! $class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
foreach ($class->subClasses as $subClass) {
if ((new ReflectionClass($subClass))->name !== $subClass) {
throw MappingException::invalidClassInDiscriminatorMap($subClass, $class->name);
}
}
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
@@ -798,6 +803,3 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
return $this->targetPlatform;
}
}
interface_exists(ClassMetadataInterface::class);
interface_exists(ReflectionService::class);

View File

@@ -28,7 +28,10 @@ use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ReflectionService;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionProperty;
use RuntimeException;
use function count;
use function explode;
/**
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
@@ -394,6 +397,8 @@ class ClassMetadataInfo implements ClassMetadata
* Whether a unique constraint should be generated for the column.
*
* @var array
*
* @psalm-var array<string, array{type: string, fieldName: string, columnName: string, inherited: class-string}>
*/
public $fieldMappings = [];
@@ -641,7 +646,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* The ReflectionProperty instances of the mapped class.
*
* @var \ReflectionProperty[]
* @var ReflectionProperty[]|null[]
*/
public $reflFields = [];
@@ -668,7 +673,9 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets the ReflectionProperties of the mapped class.
*
* @return array An array of ReflectionProperty instances.
* @return ReflectionProperty[]|null[] An array of ReflectionProperty instances.
*
* @psalm-return array<ReflectionProperty|null>
*/
public function getReflectionProperties()
{
@@ -680,7 +687,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $name
*
* @return \ReflectionProperty
* @return ReflectionProperty
*/
public function getReflectionProperty($name)
{
@@ -690,7 +697,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets the ReflectionProperty for the single identifier field.
*
* @return \ReflectionProperty
* @return ReflectionProperty
*
* @throws BadMethodCallException If the class has a composite identifier.
*/
@@ -806,7 +813,7 @@ class ClassMetadataInfo implements ClassMetadata
* - reflClass (ReflectionClass)
* - reflFields (ReflectionProperty array)
*
* @return array The names of all the fields that should be serialized.
* @return string[] The names of all the fields that should be serialized.
*/
public function __sleep()
{
@@ -1097,7 +1104,10 @@ class ClassMetadataInfo implements ClassMetadata
* @param string $fieldName
* @param array $cache
*
* @return array
* @return mixed[]
*
* @psalm-param array{usage: mixed, region: mixed} $cache
* @psalm-return array{usage: mixed, region: mixed}
*/
public function getAssociationCacheDefaults($fieldName, array $cache)
{
@@ -1446,9 +1456,25 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param array $mapping The mapping.
*
* @return array The updated mapping.
* @return mixed[] The updated mapping.
*
* @throws MappingException If something is wrong with the mapping.
*
* @psalm-return array{
* mappedBy: mixed,
* inversedBy: mixed,
* isOwningSide: bool,
* sourceEntity: string,
* targetEntity: string,
* fieldName: mixed,
* fetch: mixed,
* cascade: array<array-key,string>,
* isCascadeRemove: bool,
* isCascadePersist: bool,
* isCascadeRefresh: bool,
* isCascadeMerge: bool,
* isCascadeDetach: bool
* }
*/
protected function _validateAndCompleteAssociationMapping(array $mapping)
{
@@ -1566,10 +1592,12 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param array $mapping The mapping to validate & complete.
*
* @return array The validated & completed mapping.
* @return mixed[] The validated & completed mapping.
*
* @throws RuntimeException
* @throws MappingException
*
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool, isCascadeRemove: bool}
*/
protected function _validateAndCompleteOneToOneMapping(array $mapping)
{
@@ -1657,10 +1685,27 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param array $mapping The mapping to validate and complete.
*
* @return array The validated and completed mapping.
* @return mixed[] The validated and completed mapping.
*
* @throws MappingException
* @throws InvalidArgumentException
*
* @psalm-return array{
* mappedBy: mixed,
* inversedBy: mixed,
* isOwningSide: bool,
* sourceEntity: string,
* targetEntity: string,
* fieldName: mixed,
* fetch: int|mixed,
* cascade: array<array-key,string>,
* isCascadeRemove: bool,
* isCascadePersist: bool,
* isCascadeRefresh: bool,
* isCascadeMerge: bool,
* isCascadeDetach: bool,
* orphanRemoval: bool
* }
*/
protected function _validateAndCompleteOneToManyMapping(array $mapping)
{
@@ -1684,9 +1729,11 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param array $mapping The mapping to validate & complete.
*
* @return array The validated & completed mapping.
* @return mixed[] The validated & completed mapping.
*
* @throws \InvalidArgumentException
*
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool}
*/
protected function _validateAndCompleteManyToManyMapping(array $mapping)
{
@@ -1863,7 +1910,9 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param array|null $fieldNames
*
* @return array
* @return mixed[]
*
* @psalm-return list<string>
*/
public function getColumnNames(array $fieldNames = null)
{
@@ -2144,6 +2193,13 @@ class ClassMetadataInfo implements ClassMetadata
$mapping = $this->associationMappings[$fieldName];
//if (isset($mapping['inherited']) && (count($overrideMapping) !== 1 || ! isset($overrideMapping['fetch']))) {
// TODO: Deprecate overriding the fetch mode via association override for 3.0,
// users should do this with a listener and a custom attribute/annotation
// TODO: Enable this exception in 2.8
//throw MappingException::illegalOverrideOfInheritedProperty($this->name, $fieldName);
//}
if (isset($overrideMapping['joinColumns'])) {
$mapping['joinColumns'] = $overrideMapping['joinColumns'];
}
@@ -2202,6 +2258,11 @@ class ClassMetadataInfo implements ClassMetadata
$mapping = $this->fieldMappings[$fieldName];
//if (isset($mapping['inherited'])) {
// TODO: Enable this exception in 2.8
//throw MappingException::illegalOverrideOfInheritedProperty($this->name, $fieldName);
//}
if (isset($mapping['id'])) {
$overrideMapping['id'] = $mapping['id'];
}
@@ -2299,7 +2360,7 @@ class ClassMetadataInfo implements ClassMetadata
if (isset($table['name'])) {
// Split schema and table name from a table name like "myschema.mytable"
if (strpos($table['name'], '.') !== false) {
list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2);
[$this->table['schema'], $table['name']] = explode('.', $table['name'], 2);
}
if ($table['name'][0] === '`') {
@@ -2525,7 +2586,7 @@ class ClassMetadataInfo implements ClassMetadata
if (!isset($field['column'])) {
$fieldName = $field['name'];
if (strpos($fieldName, '.')) {
list(, $fieldName) = explode('.', $fieldName);
[, $fieldName] = explode('.', $fieldName);
}
$resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;

View File

@@ -40,7 +40,8 @@ use function interface_exists;
class AnnotationDriver extends AbstractAnnotationDriver
{
/**
* {@inheritDoc}
* @var int[]
* @psalm-var array<class-string, int>
*/
protected $entityAnnotationClasses = [
Mapping\Entity::class => 1,
@@ -274,7 +275,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
}
// Evaluate annotations on properties/fields
/* @var $property \ReflectionProperty */
foreach ($class->getProperties() as $property) {
if ($metadata->isMappedSuperclass && ! $property->isPrivate()
||
@@ -342,7 +342,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
'initialValue' => $seqGeneratorAnnot->initialValue
]
);
} else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
} elseif ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Id\TableGenerator')) {
throw MappingException::tableIdGeneratorNotImplemented($className);
} else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\CustomIdGenerator::class)) {
$metadata->setCustomGeneratorDefinition(
@@ -505,7 +505,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
$hasMapping = false;
$listenerClass = new \ReflectionClass($listenerClassName);
/* @var $method \ReflectionMethod */
foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
// find method callbacks.
$callbacks = $this->getMethodCallbacks($method);
@@ -525,7 +524,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
// Evaluate @HasLifecycleCallbacks annotation
if (isset($classAnnotations[Mapping\HasLifecycleCallbacks::class])) {
/* @var $method \ReflectionMethod */
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
foreach ($this->getMethodCallbacks($method) as $value) {
$metadata->addLifecycleCallback($value[0], $value[1]);
@@ -558,7 +556,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
*
* @param \ReflectionMethod $method
*
* @return array
* @return callable[]
*/
private function getMethodCallbacks(\ReflectionMethod $method)
{
@@ -606,7 +604,17 @@ class AnnotationDriver extends AbstractAnnotationDriver
* Parse the given JoinColumn as array
*
* @param Mapping\JoinColumn $joinColumn
* @return array
*
* @return mixed[]
*
* @psalm-return array{
* name: string,
* unique: bool,
* nullable: bool,
* onDelete: mixed,
* columnDefinition: string,
* referencedColumnName: string
* }
*/
private function joinColumnToArray(Mapping\JoinColumn $joinColumn)
{
@@ -626,7 +634,20 @@ class AnnotationDriver extends AbstractAnnotationDriver
* @param string $fieldName
* @param Mapping\Column $column
*
* @return array
* @return mixed[]
*
* @psalm-return array{
* fieldName: string,
* type: mixed,
* scale: int,
* length: int,
* unique: bool,
* nullable: bool,
* precision: int,
* options?: mixed[],
* columnName?: string,
* columnDefinition?: string
* }
*/
private function columnToArray($fieldName, Mapping\Column $column)
{
@@ -672,5 +693,3 @@ class AnnotationDriver extends AbstractAnnotationDriver
return new self($reader, $paths);
}
}
interface_exists(ClassMetadata::class);

View File

@@ -19,18 +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.
@@ -80,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();
}
/**
@@ -169,6 +176,11 @@ class DatabaseDriver implements MappingDriver
}
}
public function setInflector(Inflector $inflector) : void
{
$this->inflector = $inflector;
}
/**
* {@inheritDoc}
*/
@@ -388,6 +400,22 @@ class DatabaseDriver implements MappingDriver
* @param \Doctrine\DBAL\Schema\Column $column
*
* @return array
*
* @psalm-return array{
* fieldName: string,
* columnName: string,
* type: string,
* nullable: bool,
* options?: array{
* unsigned?: bool,
* fixed?: bool,
* comment?: string,
* default?: string
* },
* precision?: int,
* scale?: int,
* length?: int|null
* }
*/
private function buildFieldMapping($tableName, Column $column)
{
@@ -488,7 +516,9 @@ class DatabaseDriver implements MappingDriver
*
* @param \Doctrine\DBAL\Schema\Table $table
*
* @return array
* @return ForeignKeyConstraint[]
*
* @psalm-return array<string, ForeignKeyConstraint>
*/
private function getTableForeignKeys(Table $table)
{
@@ -502,7 +532,7 @@ class DatabaseDriver implements MappingDriver
*
* @param \Doctrine\DBAL\Schema\Table $table
*
* @return array
* @return string[]
*/
private function getTablePrimaryKeys(Table $table)
{
@@ -528,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));
}
/**
@@ -553,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

@@ -671,13 +671,12 @@ class XmlDriver extends FileDriver
*
* @param SimpleXMLElement $options The XML element.
*
* @return array The options array.
* @return mixed[] The options array.
*/
private function _parseOptions(SimpleXMLElement $options)
{
$array = [];
/* @var $option SimpleXMLElement */
foreach ($options as $option) {
if ($option->count()) {
$value = $this->_parseOptions($option->children());
@@ -706,7 +705,16 @@ class XmlDriver extends FileDriver
*
* @param SimpleXMLElement $joinColumnElement The XML element.
*
* @return array The mapping array.
* @return mixed[] The mapping array.
*
* @psalm-return array{
* name: string,
* referencedColumnName: string,
* unique?: bool,
* nullable?: bool,
* onDelete?: string,
* columnDefinition?: string
* }
*/
private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
{
@@ -735,12 +743,24 @@ class XmlDriver extends FileDriver
}
/**
* Parses the given field as array.
*
* @param SimpleXMLElement $fieldMapping
*
* @return array
*/
* Parses the given field as array.
*
* @return mixed[]
*
* @psalm-return array{
* fieldName: string,
* type?: string,
* columnName?: string,
* length?: int,
* precision?: int,
* scale?: int,
* unique?: bool,
* nullable?: bool,
* version?: bool,
* columnDefinition?: string,
* options?: array
* }
*/
private function columnToArray(SimpleXMLElement $fieldMapping)
{
$mapping = [
@@ -795,7 +815,9 @@ class XmlDriver extends FileDriver
*
* @param SimpleXMLElement $cacheMapping
*
* @return array
* @return mixed[]
*
* @psalm-return array{usage: mixed, region: string|null}
*/
private function cacheToArray(SimpleXMLElement $cacheMapping)
{
@@ -821,12 +843,13 @@ class XmlDriver extends FileDriver
*
* @param SimpleXMLElement $cascadeElement The cascade element.
*
* @return array The list of cascade options.
* @return string[] The list of cascade options.
*
* @psalm-return list<string>
*/
private function _getCascadeMappings(SimpleXMLElement $cascadeElement)
{
$cascades = [];
/* @var $action SimpleXmlElement */
foreach ($cascadeElement->children() as $action) {
// According to the JPA specifications, XML uses "cascade-persist"
// instead of "persist". Here, both variations
@@ -880,5 +903,3 @@ class XmlDriver extends FileDriver
return ($flag == "true" || $flag == "1");
}
}
interface_exists(ClassMetadata::class);

View File

@@ -684,7 +684,17 @@ class YamlDriver extends FileDriver
*
* @param array $joinColumnElement The array join column element.
*
* @return array The mapping array.
* @return mixed[] The mapping array.
*
* @psalm-return array{
* referencedColumnName?: string,
* name?: string,
* fieldName?: string,
* unique?: bool,
* nullable?: bool,
* onDelete?: mixed,
* columnDefinition?: mixed
* }
*/
private function joinColumnToArray($joinColumnElement)
{
@@ -726,7 +736,21 @@ class YamlDriver extends FileDriver
* @param string $fieldName
* @param array $column
*
* @return array
* @return mixed[]
*
* @psalm-return array{
* fieldName: string,
* type?: string,
* columnName?: mixed,
* length?: mixed,
* precision?: mixed,
* scale?: mixed,
* unique?: bool,
* options?: mixed,
* nullable?: mixed,
* version?: mixed,
* columnDefinition?: mixed
* }
*/
private function columnToArray($fieldName, $column)
{
@@ -787,9 +811,12 @@ class YamlDriver extends FileDriver
/**
* Parse / Normalize the cache configuration
*
* @param array $cacheMapping
* @param mixed[] $cacheMapping
*
* @return array
* @return mixed[]
*
* @psalm-param array{usage: mixed, region: null|string} $cacheMapping
* @psalm-return array{usage: mixed, region: null|string}
*/
private function cacheToArray($cacheMapping)
{
@@ -818,5 +845,3 @@ class YamlDriver extends FileDriver
return Yaml::parse(file_get_contents($file));
}
}
interface_exists(ClassMetadata::class);

View File

@@ -19,6 +19,8 @@
namespace Doctrine\ORM\Mapping;
use function sprintf;
/**
* A MappingException indicates that something is wrong with the mapping setup.
*
@@ -820,4 +822,16 @@ class MappingException extends \Doctrine\ORM\ORMException
)
);
}
public static function illegalOverrideOfInheritedProperty($className, $propertyName)
{
return new self(
sprintf(
'Override for %s::%s is only allowed for attributes/associations ' .
'declared on a mapped superclass or a trait.',
$className,
$propertyName
)
);
}
}

View File

@@ -52,9 +52,11 @@ final class ReflectionPropertiesGetter
}
/**
* @param $className
* @param string $className
*
* @return ReflectionProperty[] indexed by property internal name
*
* @psalm-param class-string $className
*/
public function getProperties($className)
{
@@ -79,8 +81,10 @@ final class ReflectionPropertiesGetter
* @param string $className
*
* @return ReflectionClass[]
*
* @psalm-return list<ReflectionClass>
*/
private function getHierarchyClasses($className)
private function getHierarchyClasses($className) : array
{
$classes = [];
$parentClassName = $className;
@@ -97,13 +101,17 @@ final class ReflectionPropertiesGetter
return $classes;
}
// phpcs:disable SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod
/**
* @param ReflectionClass $reflectionClass
*
* @return ReflectionProperty[]
*
* @psalm-return array<string, ReflectionProperty>
*/
private function getClassProperties(ReflectionClass $reflectionClass)
private function getClassProperties(ReflectionClass $reflectionClass) : array
{
// phpcs:enable SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod
$properties = $reflectionClass->getProperties();
return array_filter(

View File

@@ -37,9 +37,9 @@ final class NativeQuery extends AbstractQuery
*
* @param string $sql
*
* @return NativeQuery This query instance.
* @return self This query instance.
*/
public function setSQL($sql)
public function setSQL($sql) : self
{
$this->_sql = $sql;

View File

@@ -110,7 +110,7 @@ class ORMException extends Exception
* @param string $given
* @param string $expected
*
* @return \Doctrine\ORM\ORMInvalidArgumentException
* @return \Doctrine\ORM\ORMException
*/
public static function unexpectedAssociationValue($class, $association, $given, $expected)
{

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