Compare commits

...

114 Commits
2.9.1 ... 2.9.4

Author SHA1 Message Date
Vincent Langlet
b19a13f4ed Override getAssociationTargetClass phpdoc (#8907)
The upstream interface now allows null to be returned, but this
implementation never returns null, and consumers are expecting it not
to.
2021-08-11 22:53:03 +02:00
Grégoire Paris
1e2c0ce72d Merge pull request #8909 from greg0ire/fix-build
Fix build
2021-08-11 22:08:05 +02:00
Grégoire Paris
6a6bcc1e2b Ignore error caused by upstream package
See https://github.com/doctrine/collections/pull/282
2021-08-11 21:48:09 +02:00
Grégoire Paris
e2f54f6fa6 Remove phpstan-specific annotation
This is a direct consequence of https://github.com/doctrine/collections/pull/274
2021-08-11 21:48:09 +02:00
Grégoire Paris
e16a768916 Adapt baseline to new error message
The signature has become more precise, and so has the error message.
2021-08-11 21:48:03 +02:00
Grégoire Paris
3b9e04e971 Merge pull request #8905 from nicolas-grekas/ret-types
Add explicit `@return` type next to `#[ReturnTypeWillChange]`
2021-08-11 00:13:16 +02:00
Nicolas Grekas
fca1f5240d Add explicit @return type next to #[ReturnTypeWillChange] 2021-08-10 18:51:46 +02:00
Grégoire Paris
4fa2f6baa4 Merge pull request #8896 from derrabus/bugfix/dont-pass-null
Don't pass null as parameter
2021-08-09 08:19:12 +02:00
Grégoire Paris
245563e1cf Merge pull request #8894 from derrabus/bugfix/return-type-will-change
Fix return types for PHP 8.1
2021-08-09 08:18:46 +02:00
Grégoire Paris
a5436be939 Merge pull request #8899 from simPod/psalm
Regenerate psalm baseline
2021-08-08 17:13:19 +02:00
Simon Podlipsky
1246b3b5c3 Regenerate psalm baseline 2021-08-08 16:24:49 +02:00
Alexander M. Turek
3464591763 Don't pass null as parameter
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-08-08 01:39:43 +02:00
Alexander M. Turek
dc960d7d96 Fix return types for PHP 8.1
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-08-08 01:10:58 +02:00
Grégoire Paris
7736429e9b Merge pull request #8892 from derrabus/bump/phpstan
Bump PHPStan to 0.12.94
2021-08-08 00:08:21 +02:00
Alexander M. Turek
7c6bea1307 Bump PHPStan to 0.12.94
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-08-07 22:12:21 +02:00
Grégoire Paris
106ed8009a Merge pull request #8883 from dopeh/patch-1
Fix getting-started example by including cache
2021-08-05 00:20:07 +02:00
Pep
b2e00f6086 Fix getting-started example by including cache
The getting started example does not work without a valid cache library, this adds symfony/cache.
2021-08-04 23:04:46 +02:00
Grégoire Paris
055b646d9a Merge pull request #8769 from greg0ire/remove-unhelpful-template
Make templating Psalm-specific
2021-08-04 07:42:29 +02:00
Grégoire Paris
c1c3c89836 Merge pull request #8855 from piowin/GH8443-failing-test
Failing test for GH8443
2021-08-03 07:53:38 +02:00
Grégoire Paris
6fc0176f87 Merge pull request #8872 from simPod/fix-build
Fix CI SA failures
2021-08-02 23:48:02 +02:00
Simon Podlipsky
42126dc1bd Fix CI SA failures 2021-08-02 18:30:31 +02:00
Grégoire Paris
bc9e0b3d2c Merge pull request #8860 from t-richard/patch-1
Fix typo in "Working with Objects"
2021-07-24 14:55:58 +02:00
Thibault RICHARD
aa9d0148d5 Fix typo in "Working with Objects" 2021-07-24 14:13:29 +02:00
21skills
86703cbc73 Extra brackets if no cti joins fix
Continuation of a problem from
https://github.com/doctrine/orm/pull/6812
The same problem appears if you add WITH condition to the joined entity with discriminator
2021-07-23 21:27:05 +02:00
piowin
0504c535f1 Failing test for GH8443 2021-07-23 21:27:05 +02:00
Grégoire Paris
3c4009df38 Merge pull request #8847 from greg0ire/adapt-tests-to-new-wrapping
Adapt tests to new way of wrapping
2021-07-18 12:04:40 +02:00
Grégoire Paris
0a1be2cc21 Adapt tests to new way of wrapping
Namespacing is configured here, which means instead of just one layer,
we have a PSR cache wrapped in a doctrine cache with namespacing, itself
wrapped again with a PSR cache.
2021-07-18 11:00:46 +02:00
Benjamin Eberlei
95408cd8e4 [docs] Fix sentence about how nullable types affecting column definitions. (#8835) 2021-07-08 22:22:34 +02:00
Thomas Landauer
182bdaac6b Adding fields to Index (#8830)
* Adding `fields` to Index

I guess this was just forgotten here.
Wording is taken from https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/reference/annotations-reference.html#index

* Update attributes-reference.rst
2021-07-08 22:06:57 +02:00
Grégoire Paris
3c805b22b4 Merge pull request #8825 from greg0ire/github-templates
Backport Github issue and PR templates
2021-07-06 08:34:55 +02:00
Grégoire Paris
6a41ab56ce Backport Github issue and PR templates 2021-07-05 23:14:47 +02:00
fridde
802dd54f07 Referenced new support for PHP8 attributes (#8823) 2021-07-05 22:06:04 +02:00
Grégoire Paris
836c0d3803 Merge pull request #8814 from greg0ire/backport-7158
Proposed corrected typo in demo code.
2021-07-05 10:11:50 +02:00
Grégoire Paris
aa3ed91dd2 Merge pull request #8821 from greg0ire/fix-phpstan 2021-07-05 10:01:59 +02:00
Grégoire Paris
f542dde131 Pin PHPStan to current version
We want to control upgrades to avoid suddenly failing builds.
2021-07-05 09:22:54 +02:00
Grégoire Paris
a165d4af7c Adapt ignore rules to new PHPStan version 2021-07-05 09:13:37 +02:00
Cathy Guilbaud
ff13059ba2 Proposed corrected typo in demo code.
changed $payed to $paid.
2021-07-04 17:34:22 +02:00
Grégoire Paris
c3953435dd Merge pull request #8806 from greg0ire/resurrect-phpbench
Resurrect phpbench
2021-07-03 23:21:21 +02:00
Grégoire Paris
8f2aef5fa3 Merge pull request #8810 from greg0ire/update-baseline
Update Psalm baseline
2021-07-03 21:00:26 +02:00
Grégoire Paris
05be0e8bbd Update Psalm baseline
It seems some deprecations have been addressed recently.
2021-07-03 20:46:37 +02:00
Grégoire Paris
64cf6edea6 Use consistent style for the OR operator 2021-07-03 12:07:43 +02:00
Grégoire Paris
7608a40463 Run phpbench in the CI 2021-07-03 12:05:45 +02:00
Grégoire Paris
b1f6f9bfc3 Make phpbench runnable again
There seems to have been a new major version.
2021-07-03 11:57:32 +02:00
Grégoire Paris
3fe980de96 Merge pull request #8801 from derrabus/bugfix/ignore-return-type-will-change
Ignore errors about missing ReturnTypeWillChange class
2021-06-30 23:02:01 +02:00
Grégoire Paris
f5d988de4e Merge pull request #8784 from greg0ire/undeprecate-passing-null
Undeprecate targetEntity omission for some types
2021-06-30 21:42:13 +02:00
Grégoire Paris
e840aef630 Merge pull request #8798 from greg0ire/backport-7776
Remove hack to access class scope inside closures
2021-06-30 08:20:29 +02:00
Gabriel Caruso
2936bac7ec Remove hack to access class scope inside closures
This is possible since 5.4.
2021-06-29 23:56:37 +02:00
Grégoire Paris
1f6bfe1754 Merge pull request #8800 from jderusse/fix-exception
Fix exception not thrown by "getEntityIdentifier"
2021-06-29 23:50:41 +02:00
Alexander M. Turek
b0c7993dd6 Ignore errors about missing ReturnTypeWillChange class
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-06-29 22:50:16 +02:00
Jérémy Derussé
796af72650 Fix exception not thrown by "getEntityIdentifier" 2021-06-29 22:32:29 +02:00
Claudio Zizza
233d9b0276 Update session examples in docs (#8795)
The session examples contained approaches that aren't favorable for an application
and needed an overhaul using either scalar values or an DTO instead of the whole
entity with detach() and merge().
2021-06-28 23:23:40 +02:00
Grégoire Paris
b6d7826dc7 Merge pull request #8789 from greg0ire/backport-7292
Use a more concrete, less confusing example
2021-06-28 23:09:24 +02:00
Grégoire Paris
b3ee7141eb Use a more concrete, less confusing example
I picked a toothbrush because you usually do not share it with other
persons, feel free to propose something else.
2021-06-28 00:03:29 +02:00
Grégoire Paris
73aa6e8352 Merge pull request #8791 from greg0ire/backport-docs-batch-2
Backport docs batch 2
2021-06-28 00:00:10 +02:00
Igor Pellegrini
f3e87d2c2f Fix bullet list not rendering correctly on Github
As *rst*, if reading the tutorial on the Github repository, the items
are not seen as elements of a bullet list, hence they are not rendered
and they are collapsed in a single line of text.

Fix won't affect how it renders on
[doctrine-project.org](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html).
2021-06-27 10:42:17 +02:00
Catalin Ciobanu
53ba6b9732 Updated docblock 2021-06-26 23:04:56 +02:00
Jachim Coudenys
b3f580bf5e Fix the link to the 'Change Tracking Policies' documentation 2021-06-26 23:04:12 +02:00
Alex Rock Ancelet
774b5cbdd4 document using DTOs and entities as Value Objects 2021-06-26 22:51:22 +02:00
Gabriel Caruso
7fa3e6ec7c Use HTTPS instead of HTTP 2021-06-26 22:43:16 +02:00
Grégoire Paris
e743981f8d Merge pull request #8790 from greg0ire/backport-docs-batch
Backport docs batch
2021-06-26 22:18:30 +02:00
Haralan Dobrev
a469514ef0 Remove obsolete backslashes from underscores
These backslashes were not correctly migrated when updating the website
and it prevents search for some important terms like the query hints.
2021-06-26 18:57:58 +02:00
Jachim Coudenys
9fb13dbe28 Fix the result cache documentation (it caches raw data, not hydrated entities) 2021-06-26 17:12:28 +02:00
Adrian Föder
fc97041e49 [TASK] Streamline code block type hints for readability
This corrects a mistake with a given type hint in
the PHP code blocks as well as adds further for
consistency and brevity.
2021-06-26 16:14:05 +02:00
Claudio Zizza
f8f3b196a1 Extend documentation of arbitrary joins 2021-06-26 16:13:56 +02:00
Claudio Zizza
c01961840e Fix of variable typo in cache usage example 2021-06-26 16:13:47 +02:00
Andreas Möller
6ef1367cde Fix: Consistently format inversedBy and mappedBy attributes 2021-06-26 16:13:39 +02:00
Adrian Föder
3572b49e6a [FEATURE] Mention and link identifier generation strategies 2021-06-26 16:13:30 +02:00
Maciej Malarz
587c5f5ad6 Fix Criteria's orderBy example 2021-06-26 16:13:23 +02:00
someniatko
4f0a04e0eb Better PHPDoc for AbstractQuery cache methods
Updated useResultCache() and setResultCacheLifetime() docs to clearly state $lifetime is measured in seconds;
Added description to useResultCache()'s params. Renamed $bool => $useCache.
2021-06-26 16:13:19 +02:00
Andreas Möller
4451019dc0 Fix: Attributes vs. annotations 2021-06-26 15:56:33 +02:00
Gabriel Ostrolucký
c021426613 Fix #2935 [DDC-2236] Add note that Paginator might screw aggreg. queries 2021-06-26 15:56:09 +02:00
Gabriel Ostrolucký
243c8bff1f Fix EBNF of InExpression (StateFieldPath -> Arithmetic) 2021-06-26 15:55:18 +02:00
Gert de Pagter
5bbad8c403 Require ctype extension 2021-06-26 15:53:07 +02:00
Thomas Landauer
6c6b919788 Added UNIQUE INDEX to generated SQL schema
When I tried the example code, I got this unique index too.

Plus my `ALTER TABLE` statement was a little different from the one in the docs:
> ALTER TABLE cart ADD CONSTRAINT FK_BA388B79395C3F3 FOREIGN KEY (customer_id) REFERENCES customer (id)
Should I change that too?
2021-06-26 15:51:51 +02:00
Thomas Bisignani
b37c433080 [Documentation] Fixed comments syntax 2021-06-26 15:50:34 +02:00
Claudio Zizza
173f31a14a Add readOnly attribute to annotation example 2021-06-26 15:48:33 +02:00
Guido Sangiovanni
319acb1076 typo in class description
fixed typo in class description
2021-06-26 15:46:45 +02:00
Arman
242d2c1c41 Remove year from license 2021-06-26 15:46:14 +02:00
Jérôme Vasseur
5c95ce5c21 Fix EntityRepository constructor phpdoc
Remove redundant phpdoc from EntityRepository constructor
2021-06-26 15:45:02 +02:00
Grégoire Paris
7a78fd2900 Undeprecate targetEntity omission for some types
Some relationship types are attached to properties that can be typed
since php 7.4.
To avoid repeating the property type in targetEntity, an autodetection
feature has been developed that allows building the mapping from that
type information and annotations.
Omitting the targetEntity stays deprecated for ManyToMany as there is no
auto-detection for that yet (although one could be built by analyzing
phpdoc annotations).
2021-06-24 23:41:05 +02:00
Grégoire Paris
f233e4cf6b Merge pull request #8781 from derrabus/bugfix/static-analysis
Fix Psalm/PHPStan errors
2021-06-23 08:20:28 +02:00
Alexander M. Turek
b9f7e09401 Fix Psalm/PHPStan errors 2021-06-23 01:12:54 +02:00
Julio Montoya
5e91eea726 Link to correct section (#8778) 2021-06-22 13:12:54 +02:00
Julio Montoya
10922a5329 Spell "entity" properly (#8777) 2021-06-22 13:09:57 +02:00
Grégoire Paris
fbf793af0e Merge pull request #8543 from simonberger/add_missing_param_and_return_attributes
Add missing @param and @return PHPDoc attributes
2021-06-20 21:51:38 +02:00
Simon Berger
a26ae0648f Add missing @param and @Result PHPDoc attributes 2021-06-20 21:42:50 +02:00
Grégoire Paris
8bb564d5fe Limit template annotations to Psalm
Phpstan requires that the template can be resolved in all cases, while
Psalm seems to tolerate this ambiguous situation.
See https://github.com/phpstan/phpstan/issues/5175#issuecomment-861437050
2021-06-16 08:13:57 +02:00
Matthias Pigulla
7827869191 Make ResolveTargetEntityListener deal with the DiscriminatorMap as well (#8402) 2021-06-15 08:08:15 +02:00
Philipp Fritsche
82e77cf508 Bugfix: handle repeatable attributes (#8756)
* fix: handle repeatable attributes

* Restore interface compatibility

A reader is supposed to only ever return objects. By introducing
RepeatableAttributeCollection, we fulfill the interface and improve
clarity.

* refactor: accurate type declarations and returns

* refactor: remove unused use

* Ignore AttributeReader in phpstan and psalm to pass on CI running PHP 7.4.

* test: isTransient

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-06-13 12:29:22 +02:00
Grégoire Paris
1518b40dd2 Merge pull request #8758 from greg0ire/restore-bc-for-annotations
Restore bc for annotations
2021-06-10 19:12:13 +02:00
Grégoire Paris
10e41ec8bc Deprecate required of mandatory arguments 2021-06-09 23:04:16 +02:00
Grégoire Paris
303e346390 Restore backwards-compatibility
There used to be no constructor in this class, adding one with mandatory
arguments was technically a BC-break.

Fixes #8753
2021-06-09 23:04:16 +02:00
Grégoire Paris
fc7db8f59e Merge pull request #8747 from greg0ire/fix-attributes-syntax
Use correct named argument syntax in docs
2021-06-07 22:41:13 +02:00
Grégoire Paris
ae7f04ea53 Use correct named argument syntax in docs 2021-06-07 21:06:44 +02:00
Grégoire Paris
b8808099ea Merge pull request #8742 from derrabus/bugfix/return-type-will-change
Add ReturnTypeWillChange to ReflectionEmbeddedProperty
2021-06-05 23:52:49 +02:00
Alexander M. Turek
6432a3eeb2 Add ReturnTypeWillChange to ReflectionEmbeddedProperty 2021-06-05 22:51:55 +02:00
Grégoire Paris
3a0f60d6c6 Merge pull request #8734 from VincentLanglet/fixMetadata
Fix metadata constructor inference by phpstan
2021-06-05 21:48:17 +02:00
Grégoire Paris
ee19cf5cfd Merge pull request #8740 from VincentLanglet/fixMetada2
Make ClassMetadata covariant
2021-06-03 10:21:39 +02:00
Vincent Langlet
66daafd597 Make ClassMetadata covariant 2021-06-03 09:04:46 +02:00
Vincent Langlet
249c4fe61b Remove currentWorkingDirectory 2021-06-01 09:28:45 +02:00
Vincent Langlet
89673c60bf Fix metadata constructor inference by phpstan 2021-05-31 23:53:58 +02:00
Yup
75b4b88c5b Add automatic type detection for Embedded. (#8724)
* Add automatic type detection for Embedded.

* Inline statement.

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-05-31 11:53:14 +02:00
Benjamin Eberlei
d9e59d6862 [GH-8723] Remove use of nullability to automatically detect nullable status (#8732)
* [GH-8723] Remove use of nullability to automatically detect nullable status.

* [GH-8723] Make Column::$nullable default to false again, fix tests.
2021-05-31 10:19:16 +02:00
Yup
5fa94969de Adapt flush($argument) in documentation as it's deprecated. (#8728) 2021-05-29 22:22:19 +02:00
Juan Iglesias
f2c3ddac97 Add note about performance and inheritance mapping (#8704)
Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>
2021-05-27 08:27:55 +02:00
Grégoire Paris
46f0da9ffa Merge pull request #8710 from franmomu/recompute
Handle generic parameters in UnitOfWork
2021-05-25 13:29:21 +02:00
Fran Moreno
1e832a6782 Add generics to parameters 2021-05-25 13:01:47 +02:00
Grégoire Paris
56bdb44efd Merge pull request #8722 from alcaeus/fix-metadata-cache-clear 2021-05-25 12:55:00 +02:00
Andreas Braun
fffac44991 Bump doctrine/cache patch dependency to fix build with lowest deps 2021-05-25 11:58:11 +02:00
Andreas Braun
e42b3d6584 Fix metadata cache compatibility layer 2021-05-25 10:38:00 +02:00
Grégoire Paris
7ab2c3abbd Merge pull request #8708 from VincentLanglet/patch-2
Fix ClassMetadaInfo template inference
2021-05-25 08:49:50 +02:00
Grégoire Paris
498c816b65 Merge pull request #8717 from greg0ire/update-branch-metadata
Mark 2.8.x as unmaintained, and 2.9.x as current
2021-05-25 00:01:21 +02:00
Vincent Langlet
eec740079d Fix ClassMetadataInfo template inference 2021-05-24 21:52:40 +02:00
Grégoire Paris
c359715a97 Mark 2.8.x as unmaintained, and 2.9.x as current 2021-05-24 21:32:26 +02:00
157 changed files with 1596 additions and 822 deletions

View File

@@ -21,6 +21,7 @@
"name": "2.9",
"branchName": "2.9.x",
"slug": "2.9",
"current": true,
"aliases": [
"current",
"stable"
@@ -30,7 +31,7 @@
"name": "2.8",
"branchName": "2.8.x",
"slug": "2.8",
"current": true
"maintained": false
},
{
"name": "2.7",

37
.github/ISSUE_TEMPLATE/BC_Break.md vendored Normal file
View File

@@ -0,0 +1,37 @@
---
name: 💥 BC Break
about: Have you encountered an issue during upgrade? 💣
---
<!--
Before reporting a BC break, please consult the upgrading document to make sure it's not an expected change: https://github.com/doctrine/orm/blob/2.9.x/UPGRADE.md
-->
### BC Break Report
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes
| Version | x.y.z
#### Summary
<!-- Provide a summary describing the problem you are experiencing. -->
#### Previous behavior
<!-- What was the previous (working) behavior? -->
#### Current behavior
<!-- What is the current (broken) behavior? -->
#### How to reproduce
<!--
Provide steps to reproduce the BC break.
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
Adding a failing Unit or Functional Test would help us a lot - you can submit it in a Pull Request separately, referencing this bug report.
-->

34
.github/ISSUE_TEMPLATE/Bug.md vendored Normal file
View File

@@ -0,0 +1,34 @@
---
name: 🐞 Bug Report
about: Something is broken? 🔨
---
### Bug Report
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes/no
| Version | x.y.z
#### Summary
<!-- Provide a summary describing the problem you are experiencing. -->
#### Current behavior
<!-- What is the current (buggy) behavior? -->
#### How to reproduce
<!--
Provide steps to reproduce the bug.
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
Adding a failing Unit or Functional Test would help us a lot - you can submit one in a Pull Request separately, referencing this bug report.
-->
#### Expected behavior
<!-- What was the expected (correct) behavior? -->

View File

@@ -0,0 +1,18 @@
---
name: 🎉 Feature Request
about: You have a neat idea that should be implemented? 🎩
---
### Feature Request
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| New Feature | yes
| RFC | yes/no
| BC Break | yes/no
#### Summary
<!-- Provide a summary of the feature you would like to see implemented. -->

View File

@@ -0,0 +1,6 @@
---
name: ❓ Support Question
about: Have a problem that you can't figure out? 🤔
---
Please use https://github.com/doctrine/orm/discussions instead.

View File

@@ -0,0 +1,19 @@
---
name: 🐞 Failing Test
about: You found a bug and have a failing Unit or Functional test? 🔨
---
### Failing Test
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes/no
| Version | x.y.z
#### Summary
<!-- Provide a summary of the failing scenario. -->

View File

@@ -0,0 +1,18 @@
---
name: ⚙ Improvement
about: You have some improvement to make Doctrine better? 🎁
---
### Improvement
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| New Feature | yes
| RFC | yes/no
| BC Break | yes/no
#### Summary
<!-- Provide a summary of the improvement you are submitting. -->

View File

@@ -0,0 +1,26 @@
---
name: 🎉 New Feature
about: You have implemented some neat idea that you want to make part of Doctrine? 🎩
---
<!--
Thank you for submitting new feature!
Pick the target branch based according to these criteria:
* submitting a bugfix: target the lowest active stable branch: 2.9.x
* submitting a new feature: target the next minor branch: 2.10.x
* submitting a BC-breaking change: target the next major branch: 3.0.x
-->
### New Feature
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| New Feature | yes
| RFC | yes/no
| BC Break | yes/no
#### Summary
<!-- Provide a summary of the feature you have implemented. -->

49
.github/workflows/phpbench.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: "Performance benchmark"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
env:
fail-fast: true
jobs:
phpbench:
name: "PHPBench"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.4"
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"
- name: "Run PHPBench"
run: "vendor/bin/phpbench run --report=default"

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.0"
steps:
- name: "Checkout code"
@@ -44,7 +44,7 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.0"
steps:
- name: "Checkout code"

View File

@@ -1,4 +1,4 @@
Copyright (c) 2006-2015 Doctrine Project
Copyright (c) Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -16,30 +16,32 @@
"sort-packages": true
},
"require": {
"php": "^7.1|^8.0",
"php": "^7.1 ||^8.0",
"ext-ctype": "*",
"ext-pdo": "*",
"composer/package-versions-deprecated": "^1.8",
"doctrine/annotations": "^1.13",
"doctrine/cache": "^1.11|^2.0",
"doctrine/cache": "^1.12.1 || ^2.1.1",
"doctrine/collections": "^1.5",
"doctrine/common": "^3.0.3",
"doctrine/dbal": "^2.13.0",
"doctrine/deprecations": "^0.5.3",
"doctrine/event-manager": "^1.1",
"doctrine/inflector": "^1.4|^2.0",
"doctrine/inflector": "^1.4 || ^2.0",
"doctrine/instantiator": "^1.3",
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^2.2",
"psr/cache": "^1 || ^2 || ^3",
"symfony/console": "^3.0|^4.0|^5.0|^6.0"
"symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0"
},
"require-dev": {
"doctrine/coding-standard": "^9.0",
"phpstan/phpstan": "^0.12.83",
"phpunit/phpunit": "^7.5|^8.5|^9.4",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "0.12.94",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.0",
"symfony/cache": "^4.4|^5.2",
"symfony/yaml": "^3.4|^4.0|^5.0|^6.0",
"symfony/cache": "^4.4 || ^5.2",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.7.0"
},
"suggest": {
@@ -52,6 +54,7 @@
"autoload-dev": {
"psr-4": {
"Doctrine\\Tests\\": "tests/Doctrine/Tests",
"Doctrine\\StaticAnalysis\\": "tests/Doctrine/StaticAnalysis",
"Doctrine\\Performance\\": "tests/Doctrine/Performance"
}
},

View File

@@ -1,4 +1,4 @@
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)
The Doctrine ORM documentation is licensed under [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
Creative Commons Legal Code
@@ -359,5 +359,4 @@ Creative Commons Notice
available upon request from time to time. For the avoidance of doubt,
this trademark restriction does not form part of this License.
Creative Commons may be contacted at http://creativecommons.org/.
Creative Commons may be contacted at https://creativecommons.org/.

View File

@@ -193,7 +193,7 @@ object into a string representation before saving to the database (in the
value from the database (in the ``convertToPHPValue`` method).
The format of the string representation format is called
`Well-known text (WKT) <http://en.wikipedia.org/wiki/Well-known_text>`_.
`Well-known text (WKT) <https://en.wikipedia.org/wiki/Well-known_text>`_.
The advantage of this format is, that it is both human readable and parsable by MySQL.
Internally, MySQL stores geometry values in a binary format that is not

View File

@@ -5,7 +5,7 @@ Persisting the Decorator Pattern
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>`_
`Decorator Pattern <https://en.wikipedia.org/wiki/Decorator_pattern>`_
Component
---------

View File

@@ -14,7 +14,7 @@ In Doctrine 1 the DQL language was not implemented using a real
parser. This made modifications of the DQL by the user impossible.
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>`_
`Abstract Syntax Tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
and generates the appropriate SQL statement for it. Since this
process is deterministic Doctrine heavily caches the SQL that is
generated from any given DQL query, which reduces the performance

View File

@@ -132,7 +132,7 @@ dql statement.
The ``ArithmeticPrimary`` method call is the most common
denominator of valid EBNF tokens taken from the
`DQL EBNF grammar <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
`DQL EBNF grammar <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
that matches our requirements for valid input into the DateDiff Dql
function. Picking the right tokens for your methods is a tricky
business, but the EBNF grammar is pretty helpful finding it, as is
@@ -246,6 +246,6 @@ vendor sql functions and extend the DQL languages scope.
Code for this Extension to DQL and other Doctrine Extensions can be
found
`in the GitHub DoctrineExtensions repository <http://github.com/beberlei/DoctrineExtensions>`_.
`in the GitHub DoctrineExtensions repository <https://github.com/beberlei/DoctrineExtensions>`_.

View File

@@ -3,42 +3,69 @@ Entities in the Session
There are several use-cases to save entities in the session, for example:
1. User object
1. User data
2. Multi-step forms
To achieve this with Doctrine you have to pay attention to some details to get
this working.
Merging entity into an EntityManager
------------------------------------
Updating an entity
------------------
In Doctrine an entity objects has to be "managed" by an EntityManager to be
updateable. Entities saved into the session are not managed in the next request
anymore. This means that you have to register these entities with an
EntityManager again if you want to change them or use them as part of
references between other entities. You can achieve this by calling
``EntityManager#merge()``.
updatable. Entities saved into the session are not managed in the next request
anymore. This means that you have to update the entities with the stored session
data after you fetch the entities from the EntityManager again.
For a representative User object the code to get turn an instance from
the session into a managed Doctrine object looks like this:
For a representative User object the code to get data from the session into a
managed Doctrine object can look like these examples:
Working with scalars
~~~~~~~~~~~~~~~~~~~~
In simpler applications there is no need to work with objects in sessions and you can use
separate session elements.
.. code-block:: php
<?php
require_once 'bootstrap.php';
$em = GetEntityManager(); // creates an EntityManager
session_start();
if (isset($_SESSION['user']) && $_SESSION['user'] instanceof User) {
$user = $_SESSION['user'];
$user = $em->merge($user);
if (isset($_SESSION['userId']) && is_int($_SESSION['userId'])) {
$userId = $_SESSION['userId'];
$em = GetEntityManager(); // creates an EntityManager
$user = $em->find(User::class, $userId);
$user->setValue($_SESSION['storedValue']);
$em->flush();
}
.. note::
Working with custom data transfer objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A frequent mistake is not to get the merged user object from the return
value of ``EntityManager#merge()``. The entity object passed to merge is
not necessarily the same object that is returned from the method.
If objects are needed, we discourage the storage of entity objects in the session. It's
preferable to use a `DTO (data transfer object) <https://en.wikipedia.org/wiki/Data_transfer_object>`_
instead and merge the DTO data later with the entity.
.. code-block:: php
<?php
require_once 'bootstrap.php';
session_start();
if (isset($_SESSION['user']) && $_SESSION['user'] instanceof UserDto) {
$userDto = $_SESSION['user'];
$em = GetEntityManager(); // creates an EntityManager
$userEntity = $em->find(User::class, $userDto->getId());
$userEntity->populateFromDto($userDto);
$em->flush();
}
Serializing entity into the session
-----------------------------------
@@ -47,22 +74,20 @@ Entities that are serialized into the session normally contain references to
other entities as well. Think of the user entity has a reference to their
articles, groups, photos or many other different entities. If you serialize
this object into the session then you don't want to serialize the related
entities as well. This is why you should call ``EntityManager#detach()`` on this
object or implement the __sleep() magic method on your entity.
entities as well. This is why you shouldn't serialize an entity and use
only the needed values of it. This can happen with the help of a DTO.
.. code-block:: php
<?php
require_once 'bootstrap.php';
$em = GetEntityManager(); // creates an EntityManager
$user = $em->find("User", 1);
$em->detach($user);
$_SESSION['user'] = $user;
$userDto = new UserDto($user->getId(), $user->getFirstName(), $user->getLastName());
// or "UserDto::createFrom($user);", but don't store an entity in a property. Only its values without relations.
.. note::
$_SESSION['user'] = $userDto;
When you called detach on your objects they get "unmanaged" with that
entity manager. This means you cannot use them as part of write operations
during ``EntityManager#flush()`` anymore in this request.

View File

@@ -6,7 +6,7 @@ Implementing ArrayAccess for Domain Objects
This recipe will show you how to implement ArrayAccess for your
domain objects in order to allow more uniform access, for example
in templates. In these examples we will implement ArrayAccess on a
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
for all our domain objects.
Option 1

View File

@@ -7,10 +7,10 @@ The NOTIFY change-tracking policy is the most effective
change-tracking policy provided by Doctrine but it requires some
boilerplate code. This recipe will show you how this boilerplate
code should look like. We will implement it on a
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
for all our domain objects.
.. warning::
.. note::
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
(`Details <https://github.com/doctrine/orm/issues/8383>`_)

View File

@@ -4,7 +4,7 @@ Implementing Wakeup or Clone
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
As explained in the
`restrictions for entity classes in the manual <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
`restrictions for entity classes in the manual <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
it is usually not allowed for an entity to implement ``__wakeup``
or ``__clone``, because Doctrine makes special use of them.
However, it is quite easy to make use of these methods in a safe
@@ -23,7 +23,7 @@ implementation code in an identity check as follows:
class MyEntity
{
private $id; // This is the identifier of the entity.
//...
// ...
public function __wakeup()
{
@@ -34,7 +34,7 @@ implementation code in an identity check as follows:
// otherwise do nothing, do NOT throw an exception!
}
//...
// ...
}
Safely implementing __clone
@@ -48,7 +48,7 @@ Safely implementing ``__clone`` is pretty much the same:
class MyEntity
{
private $id; // This is the identifier of the entity.
//...
// ...
public function __clone()
{
@@ -59,7 +59,7 @@ Safely implementing ``__clone`` is pretty much the same:
// otherwise do nothing, do NOT throw an exception!
}
//...
// ...
}
Summary

View File

@@ -61,7 +61,7 @@ to manage this mess,
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>`_.
in Databases <https://derickrethans.nl/storing-date-time-in-database.html>`_.
The problem is simple. Not a single database vendor saves the timezone, only the differences to UTC.
However with frequent daylight saving and political timezone changes you can have a UTC offset that moves

View File

@@ -41,6 +41,7 @@ Mapping Objects onto a Database
* **Drivers**:
:doc:`Docblock Annotations <reference/annotations-reference>` |
:doc:`Attributes <reference/attributes-reference>` |
:doc:`XML <reference/xml-mapping>` |
:doc:`YAML <reference/yaml-mapping>` |
:doc:`PHP <reference/php-mapping>`

View File

@@ -50,7 +50,7 @@ steps of configuration.
conversions with the query cache. These 2 caches require only an
absolute minimum of memory yet they heavily improve the runtime
performance of Doctrine. The recommended cache driver to use with
Doctrine is `APC <http://www.php.net/apc>`_. APC provides you with
Doctrine is `APC <https://php.net/apc>`_. APC provides you with
an opcode-cache (which is highly recommended anyway) and a very
fast in-memory cache storage that you can use for the metadata and
query caches as seen in the previous code snippet.

View File

@@ -113,7 +113,7 @@ Optional attributes:
- **unique**: Boolean value to determine if the value of the column
should be unique across all rows of the underlying entities table.
- **nullable**: Determines if NULL values allowed for this column. If not specified, default value is false. When using typed properties on entity class defaults to true when property is nullable.
- **nullable**: Determines if NULL values allowed for this column. If not specified, default value is false.
- **options**: Array of additional options:
@@ -350,7 +350,7 @@ in order to specify that it is an embedded class.
Required attributes:
- **class**: The embeddable class
- **class**: The embeddable class. You can omit this value if you use a PHP property type instead.
.. code-block:: php
@@ -398,11 +398,11 @@ Example:
<?php
/**
* @Entity(repositoryClass="MyProject\UserRepository")
* @Entity(repositoryClass="MyProject\UserRepository", readOnly=true)
*/
class User
{
//...
// ...
}
.. _annref_entity_result:
@@ -455,7 +455,8 @@ Optional attributes:
- **strategy**: Set the name of the identifier generation strategy.
Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE.
Valid values are ``AUTO``, ``SEQUENCE``, ``TABLE``, ``IDENTITY``, ``UUID``, ``CUSTOM`` and ``NONE``, explained
in the :ref:`Identifier Generation Strategies <identifier-generation-strategies>` section.
If not specified, default value is AUTO.
Example:
@@ -649,8 +650,6 @@ Optional attributes:
constraint level. Defaults to false.
- **nullable**: Determine whether the related entity is required, or if
null is an allowed state for the relation. Defaults to true.
When using typed properties on entity class defaults to false when
property is not nullable.
- **onDelete**: Cascade Action (Database-level)
- **columnDefinition**: DDL SQL snippet that starts after the column
name and specifies the complete (non-portable!) column definition.

View File

@@ -83,7 +83,7 @@ be any regular PHP class observing the following restrictions:
- An entity class must not implement ``__wakeup`` or
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
Also consider implementing
`Serializable <http://php.net/manual/en/class.serializable.php>`_
`Serializable <https://php.net/manual/en/class.serializable.php>`_
instead.
- Any two entity classes in a class hierarchy that inherit
directly or indirectly from one another must not have a mapped
@@ -189,7 +189,7 @@ The Unit of Work
Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a
typical implementation of the
`Unit of Work pattern <http://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
`Unit of Work pattern <https://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
to keep track of all the things that need to be done the next time
``flush`` is invoked. You usually do not directly interact with a
``UnitOfWork`` but with the ``EntityManager`` instead.

View File

@@ -182,7 +182,7 @@ Here is a one-to-one relationship between a ``Customer`` and a
``Cart``. The ``Cart`` has a reference back to the ``Customer`` so
it is bidirectional.
Here we see the ``mappedBy`` and ``inversedBy`` annotations for the first time.
Here we see the ``mappedBy`` and ``inversedBy`` attributes for the first time.
They are used to tell Doctrine which property on the other side refers to the
object.
@@ -259,6 +259,7 @@ Generated MySQL Schema:
CREATE TABLE Cart (
id INT AUTO_INCREMENT NOT NULL,
customer_id INT DEFAULT NULL,
UNIQUE INDEX UNIQ_BA388B79395C3F3 (customer_id),
PRIMARY KEY(id)
) ENGINE = InnoDB;
CREATE TABLE Customer (
@@ -977,10 +978,10 @@ similar defaults. As an example, consider this mapping:
<?php
class User
{
//...
// ...
/** @ManyToMany(targetEntity="Group") */
private $groups;
//...
// ...
}
.. code-block:: xml
@@ -1008,7 +1009,7 @@ This is essentially the same as the following, more verbose, mapping:
<?php
class User
{
//...
// ...
/**
* Many Users have Many Groups.
* @ManyToMany(targetEntity="Group")
@@ -1018,7 +1019,7 @@ This is essentially the same as the following, more verbose, mapping:
* )
*/
private $groups;
//...
// ...
}
.. code-block:: xml

View File

@@ -352,10 +352,10 @@ Example:
#[Entity(repositoryClass: UserRepository::class, readOnly: false)]
class User
{
//...
// ...
}
.. _attrref_entity_result:
.. _attrref_generatedvalue:
#[GeneratedValue]
~~~~~~~~~~~~~~~~~
@@ -384,7 +384,7 @@ Example:
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
#[Id, Column(type: "integer"), GeneratedValue(strategy="IDENTITY")]
#[Id, Column(type: "integer"), GeneratedValue(strategy: "IDENTITY")]
protected $id = null;
.. _attrref_haslifecyclecallbacks:
@@ -427,7 +427,9 @@ has meaning in the ``SchemaTool`` schema generation context.
Required attributes:
- **name**: Name of the Index
- **columns**: Array of columns.
- **fields**: Array of fields. Exactly one of **fields, columns** is required.
- **columns**: Array of columns. Exactly one of **fields, columns** is required.
Optional attributes:
@@ -446,6 +448,7 @@ Basic example:
#[Entity]
#[Index(name: "category_idx", columns: ["category"])]
#[Index(name: "brand_idx", fields: ["brand"])]
class ECommerceProduct
{
}
@@ -485,7 +488,7 @@ Example:
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Id;
#[Id, Column(type="integer")]
#[Id, Column(type: "integer")]
protected $id = null;
.. _attrref_inheritancetype:
@@ -514,7 +517,7 @@ Examples:
#[Entity]
#[InheritanceType("SINGLE_TABLE")]
#[DiscriminatorColumn(name="discr", type="string")]
#[DiscriminatorColumn(name: "discr", type: "string")]
#[DiscriminatorMap({"person" = "Person", "employee" = "Employee"})]
class Person
{
@@ -523,7 +526,7 @@ Examples:
#[Entity]
#[InheritanceType("JOINED")]
#[DiscriminatorColumn(name="discr", type="string")]
#[DiscriminatorColumn(name: "discr", type: "string")]
#[DiscriminatorMap({"person" = "Person", "employee" = "Employee"})]
class Person
{
@@ -736,7 +739,7 @@ Example:
// ... fields and methods
}
#[Entiy]
#[Entity]
class EntitySubClassFoo extends BaseEntity
{
// ... fields and methods
@@ -1003,7 +1006,7 @@ Basic example:
use Doctrine\ORM\Mapping\UniqueConstraint;
#[Entity]
#[UniqueConstraint(name: "ean", columns=["ean"])]
#[UniqueConstraint(name: "ean", columns: ["ean"])]
class ECommerceProduct
{
}

View File

@@ -51,6 +51,7 @@ Doctrine provides several different ways to specify object-relational
mapping metadata:
- :doc:`Docblock Annotations <annotations-reference>`
- :doc:`Attributes <attributes-reference>`
- :doc:`XML <xml-mapping>`
- :doc:`YAML <yaml-mapping>`
- :doc:`PHP code <php-mapping>`
@@ -76,7 +77,7 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
/** @Entity */
class Message
{
//...
// ...
}
.. code-block:: xml
@@ -108,7 +109,7 @@ You can change this by configuring information about the table:
*/
class Message
{
//...
// ...
}
.. code-block:: xml
@@ -217,9 +218,11 @@ PHP Types Mapping
_________________
Since version 2.9 Doctrine can determine usable defaults from property types
on entity classes. When property type is nullable the default for ``nullable``
Column attribute is set to TRUE. Additionally, Doctrine will map PHP types
to ``type`` attribute as follows:
on entity classes. When property type is nullable this has no effect on
``nullable`` Column attribute at the moment for backwards compatibility
reasons.
Additionally, Doctrine will map PHP types to ``type`` attribute as follows:
- ``DateInterval``: ``dateinterval``
- ``DateTime``: ``datetime``
@@ -289,7 +292,7 @@ A cookbook article shows how to define :doc:`your own custom mapping types
.. warning::
All Date types assume that you are exclusively using the default timezone
set by `date_default_timezone_set() <http://php.net/manual/en/function.date-default-timezone-set.php>`_
set by `date_default_timezone_set() <https://php.net/manual/en/function.date-default-timezone-set.php>`_
or by the php.ini configuration ``date.timezone``. Working with
different timezones will cause troubles and unexpected behavior.
@@ -319,7 +322,7 @@ annotation.
* @GeneratedValue
*/
private $id;
//...
// ...
}
.. code-block:: xml
@@ -350,6 +353,8 @@ what you want. It defaults to the identifier generation mechanism your current
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
and Oracle and so on.
.. _identifier-generation-strategies:
Identifier Generation Strategies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -406,7 +411,7 @@ besides specifying the sequence's name:
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
*/
protected $id = null;
//...
// ...
}
.. code-block:: xml

View File

@@ -51,7 +51,7 @@ internally but also mean more work during ``flush``.
$em->clear(); // Detaches all objects from Doctrine!
}
}
$em->flush(); //Persist objects that did not make up an entire batch
$em->flush(); // Persist objects that did not make up an entire batch
$em->clear();
Bulk Updates

View File

@@ -74,7 +74,7 @@ Memcache
In order to use the Memcache cache driver you must have it compiled
and enabled in your php.ini. You can read about Memcache
`on the PHP website <http://php.net/memcache>`_. It will
`on the PHP website <https://php.net/memcache>`_. It will
give you a little background information about what it is and how
you can use it as well as how to install it.
@@ -99,7 +99,7 @@ Memcache.
In order to use the Memcached cache driver you must have it compiled
and enabled in your php.ini. You can read about Memcached
`on the PHP website <http://php.net/memcached>`_. It will
`on the PHP website <https://php.net/memcached>`_. It will
give you a little background information about what it is and how
you can use it as well as how to install it.
@@ -121,7 +121,7 @@ Redis
In order to use the Redis cache driver you must have it compiled
and enabled in your php.ini. You can read about what Redis is
`from here <http://redis.io/>`_. Also check
`from here <https://redis.io/>`_. Also check
`A PHP extension for Redis <https://github.com/nicolasff/phpredis/>`_ for how you can use
and install the Redis PHP extension.
@@ -286,9 +286,8 @@ Result Cache
~~~~~~~~~~~~
The result cache can be used to cache the results of your queries
so that we don't have to query the database or hydrate the data
again after the first time. You just need to configure the result
cache implementation.
so that we don't have to query the database again after the first time.
You just need to configure the result cache implementation.
.. code-block:: php

View File

@@ -61,7 +61,7 @@ This policy can be configured as follows:
Notify
~~~~~~
.. warning::
.. note::
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
(`Details <https://github.com/doctrine/orm/issues/8383>`_)

View File

@@ -95,7 +95,7 @@ If you want to configure Doctrine in more detail, take a look at the :doc:`Advan
.. note::
You can learn more about the database connection configuration in the
`Doctrine DBAL connection configuration reference <http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
`Doctrine DBAL connection configuration reference <https://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
Setting up the Commandline Tool
-------------------------------

View File

@@ -1,5 +1,5 @@
Doctrine Query Language
===========================
=======================
DQL stands for Doctrine Query Language and is an Object
Query Language derivative that is very similar to the Hibernate
@@ -487,6 +487,26 @@ where you can generate an arbitrary join with the following syntax:
<?php
$query = $em->createQuery('SELECT u FROM User u JOIN Banlist b WITH u.email = b.email');
With an arbitrary join the result differs from the joins using a mapped property.
The result of an arbitrary join is an one dimensional array with a mix of the entity from the ``SELECT``
and the joined entity fitting to the filtering of the query. In case of the example with ``User``
and ``Blacklist``, it can look like this:
- User
- Blacklist
- Blacklist
- User
- Blacklist
- User
- Blacklist
- Blacklist
- Blacklist
In this form of join, the ``Blacklist`` entities found by the filtering in the ``WITH`` part are not fetched by an accessor
method on ``User``, but are already part of the result. In case the accessor method for Blacklists is invoked on a User instance,
it loads all the related ``Blacklist`` objects corresponding to this ``User``. This change of behaviour needs to be considered
when the DQL is switched to an arbitrary join.
.. note::
The differences between WHERE, WITH and HAVING clauses may be
confusing.
@@ -680,29 +700,35 @@ The following functions are supported in SELECT, WHERE and HAVING
clauses:
- IDENTITY(single\_association\_path\_expression [, fieldMapping]) - Retrieve the foreign key column of association of the owning side
- ABS(arithmetic\_expression)
- CONCAT(str1, str2)
- CURRENT\_DATE() - Return the current date
- CURRENT\_TIME() - Returns the current time
- CURRENT\_TIMESTAMP() - Returns a timestamp of the current date
- ``IDENTITY(single_association_path_expression [, fieldMapping])`` -
Retrieve the foreign key column of association of the owning side
- ``ABS(arithmetic_expression)``
- ``CONCAT(str1, str2)``
- ``CURRENT_DATE()`` - Return the current date
- ``CURRENT_TIME()`` - Returns the current time
- ``CURRENT_TIMESTAMP()`` - Returns a timestamp of the current date
and time.
- LENGTH(str) - Returns the length of the given string
- LOCATE(needle, haystack [, offset]) - Locate the first
- ``LENGTH(str)`` - Returns the length of the given string
- ``LOCATE(needle, haystack [, offset])`` - Locate the first
occurrence of the substring in the string.
- LOWER(str) - returns the string lowercased.
- MOD(a, b) - Return a MOD b.
- SIZE(collection) - Return the number of elements in the
- ``LOWER(str)`` - returns the string lowercased.
- ``MOD(a, b)`` - Return a MOD b.
- ``SIZE(collection)`` - Return the number of elements in the
specified collection
- SQRT(q) - Return the square-root of q.
- SUBSTRING(str, start [, length]) - Return substring of given
- ``SQRT(q)`` - Return the square-root of q.
- ``SUBSTRING(str, start [, length])`` - Return substring of given
string.
- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim
- ``TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str)`` - Trim
the string by the given trim char, defaults to whitespaces.
- UPPER(str) - Return the upper-case of the given string.
- DATE_ADD(date, value, unit) - Add the given time to a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
- DATE_SUB(date, value, unit) - Subtract the given time from a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2.
- ``UPPER(str)`` - Return the upper-case of the given string.
- ``DATE_ADD(date, value, unit)`` - Add the given time to a given date.
(Supported units are ``SECOND``, ``MINUTE``, ``HOUR``, ``DAY``,
``WEEK``, ``MONTH``, ``YEAR``)
- ``DATE_SUB(date, value, unit)`` - Subtract the given time from a
given date. (Supported units are ``SECOND``, ``MINUTE``, ``HOUR``,
``DAY``, ``WEEK``, ``MONTH``, ``YEAR``)
- ``DATE_DIFF(date1, date2)`` - Calculate the difference in days
between date1-date2.
Arithmetic operators
~~~~~~~~~~~~~~~~~~~~
@@ -812,7 +838,7 @@ what type of results to expect.
Single Table
~~~~~~~~~~~~
`Single Table Inheritance <http://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
is an inheritance mapping strategy where all classes of a hierarchy
are mapped to a single database table. In order to distinguish
which row represents which type in the hierarchy a so-called
@@ -905,7 +931,7 @@ entities:
Class Table Inheritance
~~~~~~~~~~~~~~~~~~~~~~~
`Class Table Inheritance <http://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
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
@@ -1154,10 +1180,10 @@ make best use of the different result formats:
The constants for the different hydration modes are:
- Query::HYDRATE\_OBJECT
- Query::HYDRATE\_ARRAY
- Query::HYDRATE\_SCALAR
- Query::HYDRATE\_SINGLE\_SCALAR
- ``Query::HYDRATE_OBJECT``
- ``Query::HYDRATE_ARRAY``
- ``Query::HYDRATE_SCALAR``
- ``Query::HYDRATE_SINGLE_SCALAR``
Object Hydration
^^^^^^^^^^^^^^^^
@@ -1223,7 +1249,7 @@ Scalar Hydration:
1. Fields from classes are prefixed by the DQL alias in the result.
A query of the kind 'SELECT u.name ..' returns a key 'u\_name' in
A query of the kind 'SELECT u.name ..' returns a key 'u_name' in
the result rows.
Single Scalar Hydration
@@ -1371,7 +1397,7 @@ userland. However the following few hints are to be used in
userland:
- Query::HINT\_FORCE\_PARTIAL\_LOAD - Allows to hydrate objects
- ``Query::HINT_FORCE_PARTIAL_LOAD`` - Allows to hydrate objects
although not all their columns are fetched. This query hint can be
used to handle memory consumption problems with large result-sets
that contain char or binary data. Doctrine has no way of implicitly
@@ -1379,14 +1405,14 @@ userland:
``EntityManager::refresh()`` if they are to be reloaded fully from
the database. This query hint is deprecated and will be removed
in the future (`Details <https://github.com/doctrine/orm/issues/8471>`_)
- Query::HINT\_REFRESH - This query is used internally by
- ``Query::HINT_REFRESH`` - This query is used internally by
``EntityManager::refresh()`` and can be used in userland as well.
If you specify this hint and a query returns the data for an entity
that is already managed by the UnitOfWork, the fields of the
existing entity will be refreshed. In normal operation a result-set
that loads data of an already existing entity is discarded in favor
of the already existing entity.
- Query::HINT\_CUSTOM\_TREE\_WALKERS - An array of additional
- ``Query::HINT_CUSTOM_TREE_WALKERS`` - An array of additional
``Doctrine\ORM\Query\TreeWalker`` instances that are attached to
the DQL query parsing process.
@@ -1741,7 +1767,7 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS
QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
InExpression ::= ArithmeticExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
InstanceOfParameter ::= AbstractSchemaName | InputParameter
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]

View File

@@ -329,9 +329,9 @@ XML would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="User">

View File

@@ -52,7 +52,7 @@ or adding entities to a collection twice. You have to check for both conditions
in the code before calling ``$em->flush()`` if you know that unique constraint failures
can occur.
In `Symfony2 <http://www.symfony.com>`_ for example there is a Unique Entity Validator
In `Symfony2 <https://www.symfony.com>`_ for example there is a Unique Entity Validator
to achieve this task.
For collections you can check with ``$collection->contains($entity)`` if an entity is already
@@ -112,8 +112,8 @@ over this collection using a LIMIT statement (or vendor equivalent).
Doctrine does not offer a solution for this out of the box but there are several extensions
that do:
* `DoctrineExtensions <http://github.com/beberlei/DoctrineExtensions>`_
* `Pagerfanta <http://github.com/whiteoctober/pagerfanta>`_
* `DoctrineExtensions <https://github.com/beberlei/DoctrineExtensions>`_
* `Pagerfanta <https://github.com/whiteoctober/pagerfanta>`_
Why does pagination not work correctly with fetch joins?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -97,4 +97,4 @@ See :doc:`Best Practices <reference/best-practices>`
Change Tracking policies
------------------------
See: :doc:`Change Tracking Policies <reference/change-tracking-policies>`
See: :doc:`Change Tracking Policies <change-tracking-policies>`

View File

@@ -31,24 +31,31 @@ Example:
.. code-block:: php
<?php
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\OneToOne;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\MappedSuperclass;
use Doctrine\ORM\Mapping\Entity;
/** @MappedSuperclass */
class MappedSuperclassBase
class Person
{
/** @Column(type="integer") */
protected $mapped1;
/** @Column(type="string") */
protected $mapped2;
/**
* @OneToOne(targetEntity="MappedSuperclassRelated1")
* @JoinColumn(name="related1_id", referencedColumnName="id")
* @OneToOne(targetEntity="Toothbrush")
* @JoinColumn(name="toothbrush_id", referencedColumnName="id")
*/
protected $mappedRelated1;
protected $toothbrush;
// ... more fields and methods
}
/** @Entity */
class EntitySubClass extends MappedSuperclassBase
class Employee extends Person
{
/** @Id @Column(type="integer") */
private $id;
@@ -58,6 +65,15 @@ Example:
// ... more fields and methods
}
/** @Entity */
class Toothbrush
{
/** @Id @Column(type="integer") */
private $id;
// ... more fields and methods
}
The DDL for the corresponding database schema would look something
like this (this is for SQLite):
@@ -73,7 +89,7 @@ defined on that class directly.
Single Table Inheritance
------------------------
`Single Table Inheritance <http://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
is an inheritance mapping strategy where all classes of a hierarchy
are mapped to a single database table. In order to distinguish
which row represents which type in the hierarchy a so-called
@@ -181,7 +197,7 @@ the root entity of the single-table inheritance hierarchy.
Class Table Inheritance
-----------------------
`Class Table Inheritance <http://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
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
@@ -274,6 +290,9 @@ be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
Otherwise Doctrine *CANNOT* create proxy instances
of this entity and will *ALWAYS* load the entity eagerly.
There is also another important performance consideration that it is *NOT POSSIBLE*
to query for the base entity without any LEFT JOINs to the sub-types.
SQL Schema considerations
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -321,7 +340,7 @@ Example:
*/
class User
{
//other fields mapping
// other fields mapping
/**
* @ManyToMany(targetEntity="Group", inversedBy="users")
@@ -460,7 +479,7 @@ Things to note:
- This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany)
- The association type *CANNOT* be changed.
- The override could redefine the joinTables or joinColumns depending on the association type.
- The override could redefine inversedBy to reference more than one extended entity.
- The override could redefine ``inversedBy`` to reference more than one extended entity.
- The override could redefine fetch to modify the fetch strategy of the extended entity.
Attribute Override

View File

@@ -112,10 +112,10 @@ 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:
- `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 ORM Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
- `Doctrine2 "Behaviors" in a Nutshell <https://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_
- `A re-usable Versionable behavior for Doctrine2 <https://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_
- `Write your own ORM on top of Doctrine2 <https://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_
- `Doctrine ORM Behavioral Extensions <https://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
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
@@ -131,8 +131,8 @@ extensions out there that offer support for Nested Set with
ORM:
- `Doctrine2 Hierarchical-Structural Behavior <http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
- `Doctrine2 NestedSet <http://github.com/blt04/doctrine2-nestedset>`_
- `Doctrine2 Hierarchical-Structural Behavior <https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
- `Doctrine2 NestedSet <https://github.com/blt04/doctrine2-nestedset>`_
Known Issues
------------

View File

@@ -2,7 +2,7 @@ Partial Objects
===============
.. warning::
.. note::
Creating Partial Objects through DQL is deprecated and
will be removed in the future, use data transfer object

View File

@@ -543,7 +543,7 @@ using ``addCriteria``:
// ...
$criteria = Criteria::create()
->orderBy(['firstName', 'ASC']);
->orderBy(['firstName' => Criteria::ASC]);
// $qb instanceof QueryBuilder
$qb->addCriteria($criteria);

View File

@@ -114,7 +114,7 @@ Timestamp region
Tracks the timestamps of the most recent updates to particular entity.
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
.. _reference-second-level-cache-mode:
@@ -177,10 +177,11 @@ To enable the second-level-cache, you should provide a cache factory.
.. code-block:: php
<?php
/* @var $config \Doctrine\ORM\Cache\RegionsConfiguration */
/* @var $cache \Doctrine\Common\Cache\Cache */
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $cacheConfig */
/** @var \Doctrine\Common\Cache\Cache $cache */
/** @var \Doctrine\ORM\Configuration $config */
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($config, $cache);
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($cacheConfig, $cache);
// Enable second-level-cache
$config->setSecondLevelCacheEnabled();
@@ -208,7 +209,7 @@ It allows you to provide a specific implementation of the following components :
``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>`_.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
Region Lifetime
~~~~~~~~~~~~~~~
@@ -218,8 +219,9 @@ To specify a default lifetime for all regions or specify a different lifetime fo
.. code-block:: php
<?php
/* @var $config \Doctrine\ORM\Configuration */
/* @var $cacheConfig \Doctrine\ORM\Cache\CacheConfiguration */
/** @var \Doctrine\ORM\Configuration $config */
/** @var \Doctrine\ORM\Cache\CacheConfiguration $cacheConfig */
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $regionConfig */
$cacheConfig = $config->getSecondLevelCacheConfiguration();
$regionConfig = $cacheConfig->getRegionsConfiguration();
@@ -270,7 +272,7 @@ 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>`_.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
Entity cache definition
@@ -313,7 +315,7 @@ level cache region.
.. code-block:: xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Country">
<cache usage="READ_ONLY" region="my_entity_region" />
<id name="id" type="integer" column="id">
@@ -389,7 +391,7 @@ It caches the primary keys of association and cache each element will be cached
.. code-block:: xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="State">
<cache usage="NONSTRICT_READ_WRITE" />
@@ -464,8 +466,8 @@ Basic entity cache
$country1 = $em->find('Country', 1); // Retrieve item from cache
$country->setName("New Name");
$em->persist($country);
$country1->setName("New Name");
$em->flush(); // Hit database to update the row and update cache
$em->clear(); // Clear entity manager

View File

@@ -10,7 +10,7 @@ we cannot protect you from SQL injection.
Please also read the documentation chapter on Security in Doctrine DBAL. This
page only handles Security issues in the ORM.
- `DBAL Security Page <http://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
- `DBAL Security Page <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
If you find a Security bug in Doctrine, please report it on Jira and change the
Security Level to "Security Issues". It will be visible to Doctrine Core

View File

@@ -68,7 +68,7 @@ looks like this:
// $em instanceof EntityManager
$em->getConnection()->beginTransaction(); // suspend auto-commit
try {
//... do some work
// ... do some work
$user = new User;
$user->setName('George');
$em->persist($user);
@@ -98,7 +98,7 @@ functionally equivalent to the previously shown code looks as follows:
<?php
// $em instanceof EntityManager
$em->transactional(function($em) {
//... do some work
// ... do some work
$user = new User;
$user->setName('George');
$em->persist($user);

View File

@@ -16,11 +16,11 @@ Bidirectional Associations
The following rules apply to **bidirectional** associations:
- The inverse side has to have the ``mappedBy`` attribute of the OneToOne,
OneToMany, or ManyToMany mapping declaration. The mappedBy
OneToMany, or ManyToMany mapping declaration. The ``mappedBy``
attribute contains the name of the association-field on the owning side.
- The owning side has to have the ``inversedBy`` attribute of the
OneToOne, ManyToOne, or ManyToMany mapping declaration.
The inversedBy attribute contains the name of the association-field
OneToOne, ManyToOne, or ManyToMany mapping declaration.
The ``inversedBy`` attribute contains the name of the association-field
on the inverse-side.
- ManyToOne is always the owning side of a bidirectional association.
- OneToMany is always the inverse side of a bidirectional association.

View File

@@ -134,6 +134,10 @@ optimize the performance of the Flush Operation:
explicit strategies of notifying the UnitOfWork what objects/properties
changed.
.. note::
Flush only a single entity with ``$entityManager->flush($entity)`` is deprecated and will be removed in ORM 3.0.
(`Details <https://github.com/doctrine/orm/issues/8459>`_)
Query Internals
---------------

View File

@@ -291,7 +291,7 @@ example that encapsulate much of the association management code:
<?php
class User
{
//...
// ...
public function markCommentRead(Comment $comment) {
// Collections implement ArrayAccess
$this->commentsRead[] = $comment;
@@ -463,14 +463,14 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
<?php
class User
{
//...
// ...
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* @OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"})
*/
private $commentsAuthored;
//...
// ...
}
...you can now create a user and an associated comment like this:

View File

@@ -338,7 +338,7 @@ in multiple ways with very different performance impacts.
.. note::
Calling ``remove`` on an entity will remove the object from the identiy
Calling ``remove`` on an entity will remove the object from the identity
map and therefore detach it. Querying the same entity again, for example
via a lazy loaded relation, will return a new object.
@@ -388,8 +388,7 @@ automatically without invoking the ``detach`` method:
currently managed by the EntityManager instance become detached.
- When serializing an entity. The entity retrieved upon subsequent
unserialization will be detached (This is the case for all entities
that are serialized and stored in some cache, i.e. when using the
Query Result Cache).
that are serialized and stored in some cache).
The ``detach`` operation is usually not as frequently needed and
used as ``persist`` and ``remove``.

View File

@@ -16,9 +16,9 @@ setup for the latest code in trunk.
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
...
@@ -102,9 +102,9 @@ of several common elements:
// Doctrine.Tests.ORM.Mapping.User.dcm.xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\ORM\Mapping\User" table="cms_users">
@@ -763,9 +763,9 @@ entity relationship. You can define this in XML with the "association-key" attri
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Application\Model\ArticleAttribute">

View File

@@ -41,9 +41,10 @@
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/annotations-reference
reference/attributes-reference
reference/xml-mapping
reference/yaml-mapping
reference/annotations-reference
reference/php-mapping
reference/caching
reference/improving-performance

View File

@@ -43,9 +43,10 @@ Reference Guide
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/annotations-reference
reference/attributes-reference
reference/xml-mapping
reference/yaml-mapping
reference/annotations-reference
reference/php-mapping
reference/caching
reference/improving-performance

View File

@@ -58,9 +58,9 @@ and year of production as primary keys:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="VehicleCatalogue\Model\Car">
@@ -194,9 +194,9 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Application\Model\ArticleAttribute">
@@ -302,7 +302,7 @@ of products purchased and maybe even the current price.
private $items;
/** @Column(type="boolean") */
private $payed = false;
private $paid = false;
/** @Column(type="boolean") */
private $shipped = false;
/** @Column(type="datetime") */

View File

@@ -71,9 +71,9 @@ switch to extra lazy as shown in these examples:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\CMS\CmsGroup">

View File

@@ -82,7 +82,8 @@ that directory with the following contents:
{
"require": {
"doctrine/orm": "^2.6.2",
"symfony/yaml": "2.*"
"symfony/yaml": "2.*",
"symfony/cache": "^5.3"
},
"autoload": {
"psr-0": {"": "src/"}
@@ -138,8 +139,8 @@ step:
$useSimpleAnnotationReader = false;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode, $proxyDir, $cache, $useSimpleAnnotationReader);
// or if you prefer yaml or XML
//$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
//$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
// $config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
// $config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
// database configuration parameters
$conn = array(
@@ -235,44 +236,246 @@ entity definition:
/**
* @var int
*/
protected $id;
private $id;
/**
* @var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
private $name;
}
When creating entity classes, all of the fields should be ``protected`` or ``private``
(not ``public``), with getter and setter methods for each one (except ``$id``).
The use of mutators allows Doctrine to hook into calls which
manipulate the entities in ways that it could not if you just
directly set the values with ``entity#field = foo;``
When creating entity classes, all of the fields should be ``private``.
Use ``protected`` when strictly needed and very rarely if not ever ``public``.
Adding behavior to Entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are two options to define methods in entities:
**getters/setters**, or **mutators and DTOs**,
respectively for **anemic entities** or **rich entities**.
**Anemic entities: Getters and setters**
The most popular method is to create two kinds of methods to
**read** (getter) and **update** (setter) the object's properties.
The id field has no setter since, generally speaking, your code
should not set this value since it represents a database id value.
(Note that Doctrine itself can still set the value using the
Reflection API instead of a defined setter function.)
The next step for persistence with Doctrine is to describe the
structure of the ``Product`` entity to Doctrine using a metadata
language. The metadata language describes how entities, their
properties and references should be persisted and what constraints
should be applied to them.
.. note::
Doctrine ORM does not use any of the methods you defined: it uses
reflection to read and write values to your objects, and will never
call methods, not even ``__construct``.
This approach is mostly used when you want to focus on behavior-less
entities, and when you want to have all your business logic in your
services rather than in the objects themselves.
Getters and setters are a common convention which makes it possible to
expose each field of your entity to the external world, while allowing
you to keep some type safety in place.
Such an approach is a good choice for RAD (rapid application development),
but may lead to problems later down the road, because providing such an
easy way to modify any field in your entity means that the entity itself
cannot guarantee validity of its internal state. Having any object in
invalid state is dangerous:
- An invalid state can bring bugs in your business logic.
- The state can be implicitly saved in the database: any forgotten ``flush``
can persist the broken state.
- If persisted, the corrupted data will be retrieved later in your application
when the data is loaded again, thereby leading to bugs in your business logic.
- When bugs occur after corrupted data is persisted, troubleshooting will
become much harder, and you might be aware of the bug too late to fix it in a
proper manner.
implicitly saved in database, thereby leading to corrupted or inconsistent
data in your storage, and later in your application when the data is loaded again.
.. note::
This method, although very common, is inappropriate for Domain Driven
Design (`DDD <https://en.wikipedia.org/wiki/Domain-driven_design>`)
where methods should represent real business operations and not simple
property change, And business invariants should be maintained both in the
application state (entities in this case) and in the database, with no
space for data corruption.
Here is an example of a simple **anemic entity**:
.. configuration-block::
.. code-block:: php
<?php
class User
{
private $username;
private $passwordHash;
private $bans;
public function getUsername(): string
{
return $this->username;
}
public function setUsername(string $username): void
{
$this->username = $username;
}
public function getPasswordHash(): string
{
return $this->passwordHash;
}
public function setPasswordHash(string $passwordHash): void
{
$this->passwordHash = $passwordHash;
}
public function getBans(): array
{
return $this->bans;
}
public function addBan(Ban $ban): void
{
$this->bans[] = $ban;
}
}
In the example above, we avoid all possible logic in the entity and only care
about putting and retrieving data into it without validation (except the one
provided by type-hints) nor consideration about the object's state.
As Doctrine ORM is a persistence tool for your domain, the state of an object is
really important. This is why we strongly recommend using rich entities.
**Rich entities: Mutators and DTOs**
We recommend using a rich entity design and rely on more complex mutators,
and if needed based on DTOs.
In this design, you should **not** use getters nor setters, and instead,
implement methods that represent the **behavior** of your domain.
For example, when having a ``User`` entity, we could foresee
the following kind of optimization.
Example of a rich entity with proper accessors and mutators:
.. configuration-block::
.. code-block:: php
<?php
class User
{
private $banned;
private $username;
private $passwordHash;
private $bans;
public function toNickname(): string
{
return $this->username;
}
public function authenticate(string $password, callable $checkHash): bool
{
return $checkHash($password, $this->passwordHash) && ! $this->hasActiveBans();
}
public function changePassword(string $password, callable $hash): void
{
$this->passwordHash = $hash($password);
}
public function ban(\DateInterval $duration): void
{
assert($duration->invert !== 1);
$this->bans[] = new Ban($this);
}
}
.. note::
Please note that this example is only a stub. When going further in the
documentation, we will update this object with more behavior and maybe
update some methods.
The entities should only mutate state after checking that all business logic
invariants are being respected.
Additionally, our entities should never see their state change without
validation. For example, creating a ``new Product()`` object without any data
makes it an **invalid object**.
Rich entities should represent **behavior**, not **data**, therefore
they should be valid even after a ``__construct()`` call.
To help creating such objects, we can rely on ``DTOs``, and/or make
our entities always up-to-date. This can be performed with static constructors,
or rich mutators that accept ``DTOs`` as parameters.
The role of the ``DTO`` is to maintain the entity's state and to help us rely
upon objects that correctly represent the data that is used to mutate the
entity.
.. note::
A `DTO <https://en.wikipedia.org/wiki/Data_transfer_object>` is an object
that only carries data without any logic. Its only goal is to be transferred
from one service to another.
A ``DTO`` often represents data sent by a client and that has to be validated,
but can also be used as simple data carrier for other cases.
By using ``DTOs``, if we take our previous ``User`` example, we could create
a ``ProfileEditingForm`` DTO that will be a plain model, totally unrelated to
our database, that will be populated via a form and validated.
Then we can add a new mutator to our ``User``:
.. configuration-block::
.. code-block:: php
<?php
class User
{
public function updateFromProfile(ProfileEditingDTO $profileFormDTO): void
{
// ...
}
public static function createFromRegistration(UserRegistrationDTO $registrationDTO): self
{
// ...
}
}
There are several advantages to using such a model:
* **Entity state is always valid.** Since no setters exist, this means that we
only update portions of the entity that should already be valid.
* Instead of having plain getters and setters, our entity now has
**real behavior**: it is much easier to determine the logic in the domain.
* DTOs can be reused in other components, for example deserializing mixed
content, using forms...
* Classic and static constructors can be used to manage different ways to
create our objects, and they can also use DTOs.
* Anemic entities tend to isolate the entity from logic, whereas rich
entities allow putting the logic in the object itself, including data
validation.
The next step for persistence with Doctrine is to describe the structure of
the ``Product`` entity to Doctrine using a metadata language. The metadata
language describes how entities, their properties and references should be
persisted and what constraints should be applied to them.
Metadata for an Entity can be configured using DocBlock annotations directly
in the Entity class itself, or in an external XML or YAML file. This Getting
@@ -299,11 +502,11 @@ but you only need to choose one.
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
*/
protected $name;
private $name;
// .. (other code)
}
@@ -311,9 +514,9 @@ but you only need to choose one.
.. code-block:: xml
<!-- config/xml/Product.dcm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Product" table="products">
@@ -494,25 +697,25 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
* @ORM\GeneratedValue
* @var int
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $description;
private $description;
/**
* @ORM\Column(type="datetime")
* @var DateTime
*/
protected $created;
private $created;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $status;
private $status;
public function getId()
{
@@ -569,13 +772,13 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
* @ORM\Column(type="integer")
* @var int
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $name;
private $name;
public function getId()
{
@@ -622,7 +825,7 @@ domain model to match the requirements:
{
// ... (previous code)
protected $products;
private $products;
public function __construct()
{
@@ -640,8 +843,8 @@ domain model to match the requirements:
{
// ... (previous code)
protected $reportedBugs;
protected $assignedBugs;
private $reportedBugs;
private $assignedBugs;
public function __construct()
{
@@ -716,8 +919,8 @@ the bi-directional reference:
{
// ... (previous code)
protected $engineer;
protected $reporter;
private $engineer;
private $reporter;
public function setEngineer(User $engineer)
{
@@ -750,8 +953,8 @@ the bi-directional reference:
{
// ... (previous code)
protected $reportedBugs;
protected $assignedBugs;
private $reportedBugs;
private $assignedBugs;
public function addReportedBug(Bug $bug)
{
@@ -802,7 +1005,7 @@ the database that points from Bugs to Products.
{
// ... (previous code)
protected $products;
private $products;
public function assignToProduct(Product $product)
{
@@ -838,37 +1041,37 @@ the ``Product`` before:
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
*/
protected $description;
private $description;
/**
* @ORM\Column(type="datetime")
*/
protected $created;
private $created;
/**
* @ORM\Column(type="string")
*/
protected $status;
private $status;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="assignedBugs")
*/
protected $engineer;
private $engineer;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="reportedBugs")
*/
protected $reporter;
private $reporter;
/**
* @ORM\ManyToMany(targetEntity="Product")
*/
protected $products;
private $products;
// ... (other code)
}
@@ -876,9 +1079,9 @@ the ``Product`` before:
.. code-block:: xml
<!-- config/xml/Bug.dcm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Bug" table="bugs">
@@ -976,25 +1179,25 @@ Finally, we'll add metadata mappings for the ``User`` entity.
* @ORM\Column(type="integer")
* @var int
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $name;
private $name;
/**
* @ORM\OneToMany(targetEntity="Bug", mappedBy="reporter")
* @var Bug[] An ArrayCollection of Bug objects.
*/
protected $reportedBugs;
private $reportedBugs;
/**
* @ORM\OneToMany(targetEntity="Bug", mappedBy="engineer")
* @var Bug[] An ArrayCollection of Bug objects.
*/
protected $assignedBugs;
private $assignedBugs;
// .. (other code)
}
@@ -1002,9 +1205,9 @@ Finally, we'll add metadata mappings for the ``User`` entity.
.. code-block:: xml
<!-- config/xml/User.dcm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="User" table="users">
@@ -1188,9 +1391,9 @@ The console output of this script is then:
use-case. Don't we use an ORM to get rid of all the endless
hand-writing of SQL? Doctrine introduces DQL which is best
described as **object-query-language** and is a dialect of
`OQL <http://en.wikipedia.org/wiki/Object_Query_Language>`_ and
`OQL <https://en.wikipedia.org/wiki/Object_Query_Language>`_ and
similar to `HQL <http://www.hibernate.org>`_ or
`JPQL <http://en.wikipedia.org/wiki/Java_Persistence_Query_Language>`_.
`JPQL <https://en.wikipedia.org/wiki/Java_Persistence_Query_Language>`_.
It does not know the concept of columns and tables, but only those
of Entity-Class and property. Using the Metadata we defined before
it allows for very short distinctive and powerful queries.
@@ -1444,7 +1647,7 @@ Entity Repositories
For now we have not discussed how to separate the Doctrine query logic from your model.
In Doctrine 1 there was the concept of ``Doctrine_Table`` instances for this
separation. The similar concept in Doctrine2 is called Entity Repositories, integrating
the `repository pattern <http://martinfowler.com/eaaCatalog/repository.html>`_ at the heart of Doctrine.
the `repository pattern <https://martinfowler.com/eaaCatalog/repository.html>`_ at the heart of Doctrine.
Every Entity uses a default repository by default and offers a bunch of convenience
methods that you can use to query for instances of that Entity. Take for example
@@ -1540,14 +1743,14 @@ we have to adjust the metadata slightly.
**/
class Bug
{
//...
// ...
}
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Bug" table="bugs" repository-class="BugRepository">

View File

@@ -39,3 +39,7 @@ collection. You can disable this behavior by setting the
``$fetchJoinCollection`` flag to ``false``; in that case only 2 instead of the 3 queries
described are executed. We hope to automate the detection for this in
the future.
.. note::
``$fetchJoinCollection`` flag set to ``true`` might affect results if you use aggregations in your query.

View File

@@ -100,9 +100,9 @@ The code and mappings for the Market entity looks like this:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Market">
@@ -186,9 +186,9 @@ here are the code and mappings for it:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Stock">

View File

@@ -309,7 +309,7 @@
<xs:complexType name="embedded">
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="class" type="orm:fqcn" use="required" />
<xs:attribute name="class" type="orm:fqcn" use="optional" />
<xs:attribute name="column-prefix" type="xs:string" use="optional" />
<xs:attribute name="use-column-prefix" type="xs:boolean" default="true" use="optional" />
</xs:complexType>

View File

@@ -622,9 +622,9 @@ abstract class AbstractQuery
*
* @deprecated 2.7 Use {@see enableResultCache} and {@see disableResultCache} instead.
*
* @param bool $useCache
* @param int $lifetime
* @param string $resultCacheId
* @param bool $useCache Whether or not to cache the results of this query.
* @param int $lifetime How long the cache entry is valid, in seconds.
* @param string $resultCacheId ID to use for the cache entry.
*
* @return static This query instance.
*/
@@ -667,7 +667,7 @@ abstract class AbstractQuery
/**
* Defines how long the result cache will be active before expire.
*
* @param int|null $lifetime How long the cache entry is valid.
* @param int|null $lifetime How long the cache entry is valid, in seconds.
*
* @return static This query instance.
*/
@@ -856,7 +856,7 @@ abstract class AbstractQuery
* @return mixed
*
* @throws NonUniqueResultException If the query result is not unique.
* @throws NoResultException If the query returned no result and hydration mode is not HYDRATE_SINGLE_SCALAR.
* @throws NoResultException If the query returned no result.
*/
public function getSingleResult($hydrationMode = null)
{
@@ -886,6 +886,7 @@ abstract class AbstractQuery
*
* @throws NoResultException If the query returned no result.
* @throws NonUniqueResultException If the query result is not unique.
* @throws NoResultException If the query returned no result.
*/
public function getSingleScalarResult()
{

View File

@@ -57,7 +57,7 @@ interface Region extends MultiGetRegion
*
* @param CacheKey $key The key under which to cache the item.
* @param CacheEntry $entry The entry to cache.
* @param Lock $lock The lock previously obtained.
* @param Lock|null $lock The lock previously obtained.
*
* @throws CacheException Indicates a problem accessing the region.
*/

View File

@@ -26,6 +26,7 @@ use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\Cache as CacheDriver;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Doctrine\Common\Proxy\AbstractProxyFactory;
use Doctrine\Deprecations\Deprecation;
@@ -154,7 +155,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Adds a new default annotation driver with a correctly configured annotation reader. If $useSimpleAnnotationReader
* is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported.
*
* @param bool $useSimpleAnnotationReader
* @param string|string[] $paths
* @param bool $useSimpleAnnotationReader
* @psalm-param string|list<string> $paths
*
* @return AnnotationDriver
@@ -326,6 +328,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
);
$this->_attributes['metadataCacheImpl'] = $cacheImpl;
$this->_attributes['metadataCache'] = CacheAdapter::wrap($cacheImpl);
}
public function getMetadataCache(): ?CacheItemPoolInterface
@@ -335,7 +338,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
public function setMetadataCache(CacheItemPoolInterface $cache): void
{
$this->_attributes['metadataCache'] = $cache;
$this->_attributes['metadataCache'] = $cache;
$this->_attributes['metadataCacheImpl'] = DoctrineProvider::wrap($cache);
}
/**
@@ -388,11 +392,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $name The name of the query.
*
* @psalm-return array{string, ResultSetMapping} A tuple with the first
* element being the SQL
* string and the second
* element being the
* ResultSetMapping.
* @return mixed[]
* @psalm-return array{string, ResultSetMapping} A tuple with the first element being the SQL string and the second
* element being the ResultSetMapping.
*
* @throws ORMException
*/
@@ -430,16 +432,12 @@ class Configuration extends \Doctrine\DBAL\Configuration
throw ORMException::proxyClassesAlwaysRegenerating();
}
if ($this->getMetadataCache()) {
return;
if (! $this->getMetadataCache()) {
throw ORMException::metadataCacheNotConfigured();
}
$metadataCacheImpl = $this->getMetadataCacheImpl();
if (! $metadataCacheImpl) {
throw ORMException::metadataCacheNotConfigured();
}
if ($metadataCacheImpl instanceof ArrayCache) {
throw ORMException::metadataCacheUsesNonPersistentCache($metadataCacheImpl);
}
@@ -635,7 +633,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Adds a custom hydration mode.
*
* @param string $modeName The hydration mode name.
* @psalm-param class-string $hydrator The hydrator class name.
* @param string $hydrator The hydrator class name.
* @psalm-param class-string $hydrator
*
* @return void
*/

View File

@@ -885,9 +885,10 @@ use function sprintf;
/**
* Factory method to create EntityManager instances.
*
* @param array<string, mixed>|Connection $connection An array with the connection parameters or an existing Connection instance.
* @param Configuration $config The Configuration instance to use.
* @param EventManager $eventManager The EventManager instance to use.
* @param mixed[]|Connection $connection An array with the connection parameters or an existing Connection instance.
* @param Configuration $config The Configuration instance to use.
* @param EventManager|null $eventManager The EventManager instance to use.
* @psalm-param array<string, mixed>|Connection $connection
*
* @return EntityManager The created EntityManager.
*
@@ -908,9 +909,10 @@ use function sprintf;
/**
* Factory method to create Connection instances.
*
* @param array<string, mixed>|Connection $connection An array with the connection parameters or an existing Connection instance.
* @param Configuration $config The Configuration instance to use.
* @param EventManager $eventManager The EventManager instance to use.
* @param mixed[]|Connection $connection An array with the connection parameters or an existing Connection instance.
* @param Configuration $config The Configuration instance to use.
* @param EventManager|null $eventManager The EventManager instance to use.
* @psalm-param array<string, mixed>|Connection $connection
*
* @return Connection
*

View File

@@ -321,11 +321,13 @@ interface EntityManagerInterface extends ObjectManager
* {@inheritDoc}
*
* @psalm-param string|class-string<T> $className
* @phpstan-param string $className
*
* @return Mapping\ClassMetadata
* @psalm-return Mapping\ClassMetadata<T>
* @phpstan-return Mapping\ClassMetadata<object>
*
* @template T of object
* @psalm-template T of object
*/
public function getClassMetadata($className);
}

View File

@@ -64,8 +64,6 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Initializes a new <tt>EntityRepository</tt>.
*
* @psalm-param Mapping\ClassMetadata $class
*/
public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
{
@@ -208,7 +206,8 @@ class EntityRepository implements ObjectRepository, Selectable
* @psalm-param array<string, mixed> $criteria
* @psalm-param array<string, string>|null $orderBy
*
* @psalm-return list<T> The objects.
* @return object[] The objects.
* @psalm-return list<T>
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
{
@@ -250,7 +249,8 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Adds support for magic method calls.
*
* @param string $method
* @param string $method
* @param mixed[] $arguments
* @psalm-param list<mixed> $arguments
*
* @return mixed The returned value from the resolved method.

View File

@@ -412,9 +412,11 @@ abstract class AbstractHydrator
* values according to their types. The resulting row has the same number
* of elements as before.
*
* @param mixed[] $data
* @psalm-param array<string, mixed> $data
*
* @psalm-return array<string, mixed> The processed row.
* @return mixed[] The processed row.
* @psalm-return array<string, mixed>
*/
protected function gatherScalarRowData(&$data)
{
@@ -448,6 +450,7 @@ abstract class AbstractHydrator
*
* @param string $key Column name
*
* @return mixed[]|null
* @psalm-return array<string, mixed>|null
*/
protected function hydrateColumnInfo($key)

View File

@@ -99,7 +99,8 @@ class HydrationException extends ORMException
}
/**
* @param string $discrValue
* @param string $discrValue
* @param string[] $discrMap
* @psalm-param array<string, string> $discrMap
*
* @return HydrationException

View File

@@ -21,6 +21,7 @@
namespace Doctrine\ORM\Internal\Hydration;
use Iterator;
use ReturnTypeWillChange;
/**
* Represents a result structure that can be iterated over, hydrating row-by-row
@@ -55,6 +56,7 @@ class IterableResult implements Iterator
*
* @throws HydrationException
*/
#[ReturnTypeWillChange]
public function rewind()
{
if ($this->_rewinded === true) {
@@ -70,6 +72,7 @@ class IterableResult implements Iterator
*
* @return mixed[]|false
*/
#[ReturnTypeWillChange]
public function next()
{
$this->_current = $this->_hydrator->hydrateRow();
@@ -81,6 +84,7 @@ class IterableResult implements Iterator
/**
* @return mixed
*/
#[ReturnTypeWillChange]
public function current()
{
return $this->_current;
@@ -89,6 +93,7 @@ class IterableResult implements Iterator
/**
* @return int
*/
#[ReturnTypeWillChange]
public function key()
{
return $this->_key;
@@ -97,6 +102,7 @@ class IterableResult implements Iterator
/**
* @return bool
*/
#[ReturnTypeWillChange]
public function valid()
{
return $this->_current !== false;

View File

@@ -27,7 +27,7 @@ use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
/**
* A lazy collection that allow a fast count when using criteria object
* A lazy collection that allows a fast count when using criteria object
* Once count gets executed once without collection being initialized, result
* is cached and returned on subsequent calls until collection gets loaded,
* then returning the number of loaded results.

View File

@@ -24,9 +24,21 @@ namespace Doctrine\ORM\Mapping;
* {@inheritDoc}
*
* @todo remove or rename ClassMetadataInfo to ClassMetadata
* @template T of object
* @template-covariant T of object
* @template-extends ClassMetadataInfo<T>
*/
class ClassMetadata extends ClassMetadataInfo
{
/**
* Repeating the ClassMetadataInfo constructor to infer correctly the template with PHPStan
*
* @see https://github.com/doctrine/orm/issues/8709
*
* @param string $entityName The name of the entity class the new instance is used for.
* @psalm-param class-string<T> $entityName
*/
public function __construct($entityName, ?NamingStrategy $namingStrategy = null)
{
parent::__construct($entityName, $namingStrategy);
}
}

View File

@@ -41,12 +41,12 @@ use Doctrine\Persistence\Mapping\ReflectionService;
use ReflectionClass;
use ReflectionException;
use function array_map;
use function assert;
use function class_exists;
use function count;
use function end;
use function explode;
use function in_array;
use function is_subclass_of;
use function strpos;
use function strtolower;
@@ -77,18 +77,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
/** @var mixed[] */
private $embeddablesActiveNesting = [];
/**
* {@inheritDoc}
*/
protected function loadMetadata($name)
{
$loaded = parent::loadMetadata($name);
array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
return $loaded;
}
/**
* @return void
*/
@@ -295,6 +283,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
throw MappingException::invalidClassInDiscriminatorMap($subClass, $class->name);
}
}
} else {
assert($parent instanceof ClassMetadataInfo); // https://github.com/doctrine/orm/issues/8746
if ((! $class->reflClass || ! $class->reflClass->isAbstract()) && ! in_array($class->name, $parent->discriminatorMap)) {
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
}
} elseif ($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
@@ -310,45 +303,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy());
}
/**
* Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
* map classes and looking for a fitting one.
*
* @throws MappingException
*/
private function resolveDiscriminatorValue(ClassMetadata $metadata): void
{
if (
$metadata->discriminatorValue
|| ! $metadata->discriminatorMap
|| $metadata->isMappedSuperclass
|| ! $metadata->reflClass
|| $metadata->reflClass->isAbstract()
) {
return;
}
// minor optimization: avoid loading related metadata when not needed
foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
if ($discriminatorClass === $metadata->name) {
$metadata->discriminatorValue = $discriminatorValue;
return;
}
}
// iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) {
$metadata->discriminatorValue = $discriminatorValue;
return;
}
}
throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName);
}
/**
* Adds a default discriminator map if no one is given
*

View File

@@ -81,7 +81,7 @@ use const PHP_VERSION_ID;
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* @template T of object
* @template-covariant T of object
* @template-implements ClassMetadata<T>
*/
class ClassMetadataInfo implements ClassMetadata
@@ -710,6 +710,7 @@ class ClassMetadataInfo implements ClassMetadata
* metadata of the class with the given name.
*
* @param string $entityName The name of the entity class the new instance is used for.
* @psalm-param class-string<T> $entityName
*/
public function __construct($entityName, ?NamingStrategy $namingStrategy = null)
{
@@ -1313,7 +1314,8 @@ class ClassMetadataInfo implements ClassMetadata
* @param string $fieldName The field name that represents the association in
* the object model.
*
* @psalm-return array<string, mixed> The mapping.
* @return mixed[] The mapping.
* @psalm-return array<string, mixed>
*
* @throws MappingException
*/
@@ -1387,6 +1389,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $queryName The query name.
*
* @return mixed[]
* @psalm-return array<string, mixed>
*
* @throws MappingException
@@ -1467,10 +1470,6 @@ class ClassMetadataInfo implements ClassMetadata
$type = $this->reflClass->getProperty($mapping['fieldName'])->getType();
if ($type) {
if (! isset($mapping['nullable'])) {
$mapping['nullable'] = $type->allowsNull();
}
if (
! isset($mapping['type'])
&& ($type instanceof ReflectionNamedType)
@@ -1526,14 +1525,6 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['targetEntity'] = $type->getName();
}
if (isset($mapping['joinColumns'])) {
foreach ($mapping['joinColumns'] as &$joinColumn) {
if ($type->allowsNull() === false) {
$joinColumn['nullable'] = false;
}
}
}
return $mapping;
}
@@ -2962,6 +2953,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param string $event
*
* @return string[]
* @psalm-return list<string>
*/
public function getLifecycleCallbacks($event)
@@ -3049,7 +3041,8 @@ class ClassMetadataInfo implements ClassMetadata
*
* @see getDiscriminatorColumn()
*
* @psalm-param array<string, mixed> $columnDef
* @param mixed[]|null $columnDef
* @psalm-param array<string, mixed>|null $columnDef
*
* @return void
*
@@ -3101,6 +3094,7 @@ class ClassMetadataInfo implements ClassMetadata
* Adds one entry of the discriminator map with a new class and corresponding name.
*
* @param string $name
* @param string $className
* @psalm-param class-string $className
*
* @return void
@@ -3415,6 +3409,11 @@ class ClassMetadataInfo implements ClassMetadata
/**
* {@inheritDoc}
*
* @param string $assocName
*
* @return string
* @psalm-return class-string
*
* @throws InvalidArgumentException
*/
public function getAssociationTargetClass($assocName)
@@ -3441,6 +3440,7 @@ class ClassMetadataInfo implements ClassMetadata
*
* @param AbstractPlatform $platform
*
* @return string[]
* @psalm-return list<string>
*/
public function getQuotedIdentifierColumnNames($platform)
@@ -3543,6 +3543,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* @param string $targetClass
*
* @return mixed[][]
* @psalm-return array<string, array<string, mixed>>
*/
public function getAssociationsByTargetClass($targetClass)
@@ -3604,9 +3605,16 @@ class ClassMetadataInfo implements ClassMetadata
{
$this->assertFieldNotMapped($mapping['fieldName']);
if (! isset($mapping['class']) && $this->isTypedProperty($mapping['fieldName'])) {
$type = $this->reflClass->getProperty($mapping['fieldName'])->getType();
if ($type instanceof ReflectionNamedType) {
$mapping['class'] = $type->getName();
}
}
$this->embeddedClasses[$mapping['fieldName']] = [
'class' => $this->fullyQualifiedClassName($mapping['class']),
'columnPrefix' => $mapping['columnPrefix'],
'columnPrefix' => $mapping['columnPrefix'] ?? null,
'declaredField' => $mapping['declaredField'] ?? null,
'originalField' => $mapping['originalField'] ?? null,
];

View File

@@ -57,8 +57,8 @@ final class Column implements Annotation
/** @var bool */
public $unique = false;
/** @var bool|null */
public $nullable;
/** @var bool */
public $nullable = false;
/** @var array<string,mixed> */
public $options = [];
@@ -76,7 +76,7 @@ final class Column implements Annotation
?int $precision = null,
?int $scale = null,
bool $unique = false,
?bool $nullable = null,
bool $nullable = false,
array $options = [],
?string $columnDefinition = null
) {

View File

@@ -18,8 +18,8 @@ use ReflectionProperty;
use function assert;
use function class_exists;
use function constant;
use function count;
use function defined;
use function get_class;
class AttributeDriver extends AnnotationDriver
{
@@ -38,6 +38,23 @@ class AttributeDriver extends AnnotationDriver
parent::__construct(new AttributeReader(), $paths);
}
/**
* {@inheritDoc}
*/
public function isTransient($className)
{
$classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
foreach ($classAnnotations as $a) {
$annot = $a instanceof RepeatableAttributeCollection ? $a[0] : $a;
if (isset($this->entityAnnotationClasses[get_class($annot)])) {
return false;
}
}
return true;
}
public function loadMetadataForClass($className, ClassMetadata $metadata): void
{
assert($metadata instanceof ClassMetadataInfo);

View File

@@ -11,7 +11,8 @@ use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use function count;
use function assert;
use function is_string;
use function is_subclass_of;
/**
@@ -22,63 +23,75 @@ final class AttributeReader
/** @var array<string,bool> */
private array $isRepeatableAttribute = [];
/** @return array<object> */
/** @return array<Annotation|RepeatableAttributeCollection> */
public function getClassAnnotations(ReflectionClass $class): array
{
return $this->convertToAttributeInstances($class->getAttributes());
}
/** @return array<object>|object|null */
/** @return Annotation|RepeatableAttributeCollection|null */
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
return $this->getClassAnnotations($class)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
return $this->getClassAnnotations($class)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}
/** @return array<object> */
/** @return array<Annotation|RepeatableAttributeCollection> */
public function getMethodAnnotations(ReflectionMethod $method): array
{
return $this->convertToAttributeInstances($method->getAttributes());
}
/** @return array<object>|object|null */
/** @return Annotation|RepeatableAttributeCollection|null */
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
return $this->getMethodAnnotations($method)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
return $this->getMethodAnnotations($method)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}
/** @return array<object> */
/** @return array<Annotation|RepeatableAttributeCollection> */
public function getPropertyAnnotations(ReflectionProperty $property): array
{
return $this->convertToAttributeInstances($property->getAttributes());
}
/** @return array<object>|object|null */
/** @return Annotation|RepeatableAttributeCollection|null */
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
return $this->getPropertyAnnotations($property)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
return $this->getPropertyAnnotations($property)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}
/**
* @param array<object> $attributes
* @param array<ReflectionAttribute> $attributes
*
* @return array<Annotation>
* @return array<Annotation|RepeatableAttributeCollection>
*/
private function convertToAttributeInstances(array $attributes): array
{
$instances = [];
foreach ($attributes as $attribute) {
$attributeName = $attribute->getName();
assert(is_string($attributeName));
// Make sure we only get Doctrine Annotations
if (! is_subclass_of($attribute->getName(), Annotation::class)) {
if (! is_subclass_of($attributeName, Annotation::class)) {
continue;
}
$instance = $attribute->newInstance();
assert($instance instanceof Annotation);
if ($this->isRepeatable($attribute->getName())) {
$instances[$attribute->getName()][] = $instance;
if ($this->isRepeatable($attributeName)) {
if (! isset($instances[$attributeName])) {
$instances[$attributeName] = new RepeatableAttributeCollection();
}
$collection = $instances[$attributeName];
assert($collection instanceof RepeatableAttributeCollection);
$collection[] = $instance;
} else {
$instances[$attribute->getName()] = $instance;
$instances[$attributeName] = $instance;
}
}

View File

@@ -145,6 +145,8 @@ class DatabaseDriver implements MappingDriver
/**
* Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.
*
* @param Table[] $entityTables
* @param Table[] $manyToManyTables
* @psalm-param list<Table> $entityTables
* @psalm-param list<Table> $manyToManyTables
*
@@ -380,21 +382,22 @@ class DatabaseDriver implements MappingDriver
/**
* Build field mapping from a schema column definition
*
* @return mixed[]
* @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
* }
* 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(string $tableName, Column $column): array
{

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Driver;
use ArrayObject;
use Doctrine\ORM\Mapping\Annotation;
/**
* @template-extends ArrayObject<int,Annotation>
*/
final class RepeatableAttributeCollection extends ArrayObject
{
}

View File

@@ -324,7 +324,7 @@ class XmlDriver extends FileDriver
$mapping = [
'fieldName' => (string) $embeddedMapping['name'],
'class' => (string) $embeddedMapping['class'],
'class' => isset($embeddedMapping['class']) ? (string) $embeddedMapping['class'] : null,
'columnPrefix' => $useColumnPrefix ? $columnPrefix : false,
];

View File

@@ -408,7 +408,7 @@ class YamlDriver extends FileDriver
foreach ($element['embedded'] as $name => $embeddedMapping) {
$mapping = [
'fieldName' => $name,
'class' => $embeddedMapping['class'],
'class' => $embeddedMapping['class'] ?? null,
'columnPrefix' => $embeddedMapping['columnPrefix'] ?? null,
];
$metadata->mapEmbedded($mapping);

View File

@@ -31,16 +31,13 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class Embedded implements Annotation
{
/**
* @Required
* @var string
*/
/** @var string|null */
public $class;
/** @var string|bool|null */
public $columnPrefix;
public function __construct(string $class, $columnPrefix = null)
public function __construct(?string $class = null, $columnPrefix = null)
{
$this->class = $class;
$this->columnPrefix = $columnPrefix;

View File

@@ -22,6 +22,7 @@ namespace Doctrine\ORM\Mapping;
use Attribute;
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Doctrine\Deprecations\Deprecation;
/**
* @Annotation
@@ -31,7 +32,7 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class ManyToMany implements Annotation
{
/** @var string */
/** @var string|null */
public $targetEntity;
/** @var string */
@@ -61,7 +62,7 @@ final class ManyToMany implements Annotation
* @param array<string> $cascade
*/
public function __construct(
string $targetEntity,
?string $targetEntity = null,
?string $mappedBy = null,
?string $inversedBy = null,
?array $cascade = null,
@@ -69,6 +70,14 @@ final class ManyToMany implements Annotation
bool $orphanRemoval = false,
?string $indexBy = null
) {
if ($targetEntity === null) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/issues/8753',
'Passing no target entity is deprecated.'
);
}
$this->targetEntity = $targetEntity;
$this->mappedBy = $mappedBy;
$this->inversedBy = $inversedBy;

View File

@@ -922,6 +922,9 @@ class MappingException extends ORMException
}
/**
* @param string $className
* @param string $propertyName
*
* @return self
*/
public static function illegalOverrideOfInheritedProperty($className, $propertyName)

View File

@@ -22,6 +22,7 @@ namespace Doctrine\ORM\Mapping;
use Doctrine\Instantiator\Instantiator;
use ReflectionProperty;
use ReturnTypeWillChange;
/**
* Acts as a proxy to a nested Property structure, making it look like
@@ -60,7 +61,10 @@ class ReflectionEmbeddedProperty extends ReflectionProperty
/**
* {@inheritDoc}
*
* @return mixed
*/
#[ReturnTypeWillChange]
public function getValue($object = null)
{
$embeddedObject = $this->parentProperty->getValue($object);
@@ -74,7 +78,10 @@ class ReflectionEmbeddedProperty extends ReflectionProperty
/**
* {@inheritDoc}
*
* @return void
*/
#[ReturnTypeWillChange]
public function setValue($object, $value = null)
{
$embeddedObject = $this->parentProperty->getValue($object);

View File

@@ -46,7 +46,6 @@ use function spl_object_hash;
* Similarly, if you remove entities from a collection that is part of a one-many
* mapping this will only result in the nulling out of the foreign keys on flush.
*
* @phpstan-template TKey
* @psalm-template TKey of array-key
* @psalm-template T
* @template-implements Collection<TKey,T>
@@ -595,6 +594,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* @param int $offset
* @param int|null $length
*
* @return mixed[]
* @psalm-return array<TKey,T>
*/
public function slice($offset, $length = null): array
@@ -637,7 +637,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
* Selects all elements from a selectable that match the expression and
* return a new collection containing these elements.
*
* @return Collection<TKey, T>
* @psalm-return Collection<TKey, T>
*
* @throws RuntimeException
*/

View File

@@ -298,7 +298,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
* have to join in the actual entities table leading to additional
* JOIN.
*
* @psalm-param array<string, mixed> $mapping Array containing mapping information.
* @param mixed[] $mapping Array containing mapping information.
* @psalm-param array<string, mixed> $mapping
*
* @return string[] ordered tuple:
* - JOIN condition to add to the SQL
@@ -350,6 +351,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
/**
* Generate ON condition
*
* @param mixed[] $mapping
* @psalm-param array<string, mixed> $mapping
*
* @return string[]
@@ -464,6 +466,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
*
* @param mixed $element
*
* @return mixed[]
* @psalm-return list<mixed>
*/
protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element)
@@ -513,6 +516,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
*
* @param mixed $element
*
* @return mixed[]
* @psalm-return list<mixed>
*/
protected function getInsertRowSQLParameters(PersistentCollection $collection, $element)

View File

@@ -1722,6 +1722,7 @@ class BasicEntityPersister implements EntityPersister
* Subclasses are supposed to override this method if they intend to change
* or alter the criteria by which entities are selected.
*
* @param mixed[]|null $assoc
* @psalm-param array<string, mixed> $criteria
* @psalm-param array<string, mixed>|null $assoc
*
@@ -2009,7 +2010,8 @@ class BasicEntityPersister implements EntityPersister
/**
* Generates the appropriate join SQL for the given join column.
*
* @psalm-param array<array<string, mixed>> $joinColumns The join columns definition of an association.
* @param array[] $joinColumns The join columns definition of an association.
* @psalm-param array<array<string, mixed>> $joinColumns
*
* @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise.
*/

View File

@@ -103,9 +103,10 @@ interface EntityPersister
/**
* Gets the SQL WHERE condition for matching a field with a given value.
*
* @param string $field
* @param mixed $value
* @param string|null $comparison
* @param string $field
* @param mixed $value
* @param mixed[]|null $assoc
* @param string|null $comparison
* @psalm-param array<string, mixed>|null $assoc
*
* @return string
@@ -186,19 +187,21 @@ interface EntityPersister
/**
* Loads an entity by a list of field criteria.
*
* @param object|null $entity The entity to load the data into. If not specified, a new entity is created.
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* for loading the entity.
* @param int|null $limit Limit number of results.
* @psalm-param array<string, mixed> $hints Hints for entity creation.
* @psalm-param array<string, mixed> $criteria The criteria by which
* to load the entity.
* @psalm-param array<string, mixed>|null $assoc The association that
* connects the entity to
* load to another entity,
* if any.
* @psalm-param array<string, string>|null $orderBy Criteria to order by.
* @param mixed[] $criteria The criteria by which to load the entity.
* @param object|null $entity The entity to load the data into. If not specified,
* a new entity is created.
* @param mixed[]|null $assoc The association that connects the entity
* to load to another entity, if any.
* @param mixed[] $hints Hints for entity creation.
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* for loading the entity.
* @param int|null $limit Limit number of results.
* @param string[]|null $orderBy Criteria to order by.
* @psalm-param array<string, mixed> $criteria
* @psalm-param array<string, mixed>|null $assoc
* @psalm-param array<string, mixed> $hints
* @psalm-param array<string, string>|null $orderBy
*
* @return object|null The loaded and managed entity instance or NULL if the entity can not be found.
*

View File

@@ -34,7 +34,7 @@ use function is_array;
* The joined subclass persister maps a single entity instance to several tables in the
* database as it is defined by the <tt>Class Table Inheritance</tt> strategy.
*
* @see http://martinfowler.com/eaaCatalog/classTableInheritance.html
* @see https://martinfowler.com/eaaCatalog/classTableInheritance.html
*/
class JoinedSubclassPersister extends AbstractEntityInheritancePersister
{
@@ -192,14 +192,14 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$paramIndex = 1;
$data = $insertData[$tableName] ?? [];
foreach ((array) $id as $idName => $idVal) {
foreach ($id as $idName => $idVal) {
$type = $this->columnTypes[$idName] ?? Type::STRING;
$stmt->bindValue($paramIndex++, $idVal, $type);
}
foreach ($data as $columnName => $value) {
if (! is_array($id) || ! isset($id[$columnName])) {
if (! isset($id[$columnName])) {
$stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]);
}
}

View File

@@ -31,7 +31,7 @@ use function implode;
* Persister for entities that participate in a hierarchy mapped with the
* SINGLE_TABLE strategy.
*
* @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html
* @link https://martinfowler.com/eaaCatalog/singleTableInheritance.html
*/
class SingleTablePersister extends AbstractEntityInheritancePersister
{

View File

@@ -21,7 +21,7 @@
namespace Doctrine\ORM\Query\AST;
/**
* InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")"
* InExpression ::= ArithmeticExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")"
*
* @link www.doctrine-project.org
*/

View File

@@ -60,7 +60,8 @@ abstract class Base
}
/**
* @psalm-param list<string|object> $args
* @param string[]|object[]|string|object $args
* @psalm-param list<string|object>|string|object $args
*
* @return static
*/

View File

@@ -38,8 +38,9 @@ class Func
/**
* Creates a function, with the given argument.
*
* @param string $name
* @psalm-param list<mixed> $arguments
* @param string $name
* @param mixed[]|mixed $arguments
* @psalm-param list<mixed>|mixed $arguments
*/
public function __construct($name, $arguments)
{

View File

@@ -262,6 +262,7 @@ class Parser
/**
* Adds a custom tree walker for modifying the AST.
*
* @param string $className
* @psalm-param class-string $className
*
* @return void
@@ -489,8 +490,9 @@ class Parser
/**
* Generates a new syntax error.
*
* @param string $expected Expected string.
* @psalm-param array<string, mixed>|null $token Got token.
* @param string $expected Expected string.
* @param mixed[]|null $token Got token.
* @psalm-param array<string, mixed>|null $token
*
* @return void
* @psalm-return no-return
@@ -509,14 +511,15 @@ class Parser
$message .= $expected !== '' ? sprintf('Expected %s, got ', $expected) : 'Unexpected ';
$message .= $this->lexer->lookahead === null ? 'end of string.' : sprintf("'%s'", $token['value']);
throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL()));
throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL() ?? ''));
}
/**
* Generates a new semantical error.
*
* @param string $message Optional message.
* @psalm-param array<string, mixed>|null $token Optional token.
* @param string $message Optional message.
* @param mixed[]|null $token Optional token.
* @psalm-param array<string, mixed>|null $token
*
* @return void
*
@@ -525,7 +528,7 @@ class Parser
public function semanticalError($message = '', $token = null)
{
if ($token === null) {
$token = $this->lexer->lookahead ?? ['position' => null];
$token = $this->lexer->lookahead ?? ['position' => 0];
}
// Minimum exposed chars ahead of token
@@ -538,7 +541,7 @@ class Parser
$pos = strpos($dql, ' ', $length > $pos ? $pos : $length);
$length = $pos !== false ? $pos - $token['position'] : $distance;
$tokenPos = isset($token['position']) && $token['position'] > 0 ? $token['position'] : '-1';
$tokenPos = $token['position'] > 0 ? $token['position'] : '-1';
$tokenStr = substr($dql, $token['position'], $length);
// Building informative message

View File

@@ -131,7 +131,8 @@ class ParserResult
*
* @param string|int $dqlPosition The name or position of the DQL parameter.
*
* @psalm-return list<int> The positions of the corresponding SQL parameters.
* @return int[] The positions of the corresponding SQL parameters.
* @psalm-return list<int>
*/
public function getSqlParameterPositions($dqlPosition)
{

View File

@@ -165,6 +165,7 @@ class QueryException extends ORMException
}
/**
* @param string[] $assoc
* @psalm-param array<string, string> $assoc
*
* @return QueryException
@@ -190,6 +191,7 @@ class QueryException extends ORMException
}
/**
* @param string[] $assoc
* @psalm-param array<string, string> $assoc
*
* @return QueryException
@@ -215,6 +217,7 @@ class QueryException extends ORMException
}
/**
* @param string[] $assoc
* @psalm-param array<string, string> $assoc
*
* @return QueryException

View File

@@ -84,10 +84,11 @@ class ResultSetMappingBuilder extends ResultSetMapping
/**
* Adds a root entity and all of its fields to the result set.
*
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param string[] $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param array<string, string> $renamedColumns
*
* @return void
*/
@@ -103,13 +104,14 @@ class ResultSetMappingBuilder extends ResultSetMapping
/**
* Adds a joined entity and all of its fields to the result set.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param array<string, string> $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @param string[] $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
* @psalm-param array<string, string> $renamedColumns
*
* @return void
*/
@@ -125,8 +127,9 @@ class ResultSetMappingBuilder extends ResultSetMapping
/**
* Adds all fields of the given class to the result set mapping (columns and meta fields).
*
* @param string $class
* @param string $alias
* @param string $class
* @param string $alias
* @param string[] $columnAliasMap
* @psalm-param array<string, string> $columnAliasMap
*
* @return void
@@ -430,6 +433,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
* Works only for all the entity results. The select parts for scalar
* expressions have to be written manually.
*
* @param string[] $tableAliases
* @psalm-param array<string, string> $tableAliases
*
* @return string

View File

@@ -240,14 +240,15 @@ class SqlWalker implements TreeWalker
*
* @param string $dqlAlias The DQL alias.
*
* @return mixed[]
* @psalm-return array{
* metadata: ClassMetadata,
* parent: string,
* relation: mixed[],
* map: mixed,
* nestingLevel: int,
* token: array
* }
* metadata: ClassMetadata,
* parent: string,
* relation: mixed[],
* map: mixed,
* nestingLevel: int,
* token: array
* }
*/
public function getQueryComponent($dqlAlias)
{
@@ -1108,7 +1109,7 @@ class SqlWalker implements TreeWalker
if ($targetClass->isInheritanceTypeJoined()) {
$ctiJoins = $this->generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
// If we have WITH condition, we need to build nested joins for target class table and cti joins
if ($withCondition) {
if ($withCondition && $ctiJoins) {
$sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition'];
} else {
$sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins;

View File

@@ -23,6 +23,7 @@ namespace Doctrine\ORM\Query;
use ArrayAccess;
use Doctrine\ORM\AbstractQuery;
use Iterator;
use ReturnTypeWillChange;
use function key;
use function next;
@@ -58,6 +59,7 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
* @return string|false
* @psalm-return class-string<TreeWalker>|false
*/
#[ReturnTypeWillChange]
public function rewind()
{
return reset($this->walkers);
@@ -66,6 +68,7 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
/**
* @return TreeWalker|null
*/
#[ReturnTypeWillChange]
public function current()
{
return $this->offsetGet(key($this->walkers));
@@ -74,6 +77,7 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
/**
* @return int
*/
#[ReturnTypeWillChange]
public function key()
{
return key($this->walkers);
@@ -82,6 +86,7 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
/**
* @return TreeWalker|null
*/
#[ReturnTypeWillChange]
public function next()
{
next($this->walkers);
@@ -91,25 +96,34 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
/**
* {@inheritdoc}
*
* @return bool
*/
#[ReturnTypeWillChange]
public function valid()
{
return key($this->walkers) !== null;
}
/**
* {@inheritdoc}
* @param mixed $offset
* @psalm-param array-key|null $offset
*
* @return bool
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->walkers[$offset]);
return isset($this->walkers[$offset ?? '']);
}
/**
* @param mixed $offset
* @psalm-param array-key|null $offset
*
* @return TreeWalker|null
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
if ($this->offsetExists($offset)) {
@@ -128,7 +142,10 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
*
* @param string $value
* @psalm-param array-key|null $offset
*
* @return void
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if ($offset === null) {
@@ -139,12 +156,16 @@ class TreeWalkerChainIterator implements Iterator, ArrayAccess
}
/**
* {@inheritdoc}
* @param mixed $offset
* @psalm-param array-key|null $offset
*
* @return void
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->walkers[$offset]);
unset($this->walkers[$offset ?? '']);
}
}
}

View File

@@ -692,9 +692,10 @@ class QueryBuilder
* The available parts are: 'select', 'from', 'join', 'set', 'where',
* 'groupBy', 'having' and 'orderBy'.
*
* @param string $dqlPartName The DQL part name.
* @param bool $append Whether to append (true) or replace (false).
* @psalm-param string|object|list<string>|array{join: array<int|string, object>} $dqlPart An Expr object.
* @param string $dqlPartName The DQL part name.
* @param string|object|array $dqlPart An Expr object.
* @param bool $append Whether to append (true) or replace (false).
* @psalm-param string|object|list<string>|array{join: array<int|string, object>} $dqlPart
*
* @return static
*/
@@ -1458,6 +1459,7 @@ class QueryBuilder
/**
* Resets DQL parts.
*
* @param string[]|null $parts
* @psalm-param list<string>|null $parts
*
* @return static

View File

@@ -82,6 +82,8 @@ EOT
/**
* {@inheritdoc}
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{

View File

@@ -81,6 +81,8 @@ EOT
/**
* {@inheritdoc}
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{

View File

@@ -56,6 +56,8 @@ EOT
/**
* {@inheritdoc}
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{

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