Compare commits

..

572 Commits

Author SHA1 Message Date
Grégoire Paris ebae57eb96 Merge pull request #8480 from greg0ire/cs-20210216
CS batch 11/an estimated 26
2021-02-16 23:10:18 +01:00
Benjamin Eberlei 30a7c2aa67 [GH-8410] Fix memory leak in new toIterable and state bug. (#8467)
* [GH-8410] Fix memory leak in new toIterable and state bug.

The new AbstractQuery::toIterable() had a memory leak that
AbstractQuery::iterable() did not have. This leak is now fixed.

After fixing the leak, one test failed where the identity map in
ObjectHydrator triggered and lead to a notice. Introduced a new
AbstractHydrator::cleanupAfterRowIteration() that the ObjectHydrator
uses to cleanup the state.

* [GH-8413] Bugfix: Iterating with multiple, mixed results

When multiple entity results are part of a row, the result handling
must be different. In addition mixed results with scalars are broken
and now throw an exception as illegal operation.

* Housekeeping: phpcs

* [GH-8413] Add assertions for entity alias iteration.

* [GH-8387] Missing @deprecated on Query::iterate
2021-02-16 17:52:20 +01:00
Benjamin Eberlei 3a9b8fde9b Housekeeping: Fix cs 2021-02-16 16:24:19 +01:00
Yosh 4f864bc178 Identifier type is not set when many2many relations are deleted (#8401)
* Ensure identifier type is set on deleteJoinTableRecords

* Housekeeping: phpcs

* Housekeeping: phpcs

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-02-16 16:12:39 +01:00
Grégoire Paris d76cbd755f Manually fix cs 2021-02-16 09:07:26 +01:00
Grégoire Paris 4aece04ae7 Automatically fix cs 2021-02-16 08:48:48 +01:00
Grégoire Paris f31dbf8d4e Merge pull request #8479 from greg0ire/cs-20210215
CS batch 10/an estimated 27
2021-02-15 23:44:27 +01:00
Grégoire Paris 416f35dba9 Manually fix cs 2021-02-14 21:00:58 +01:00
Grégoire Paris c29370e061 Automatically fix cs 2021-02-14 14:39:35 +01:00
Grégoire Paris 4e0f6837d0 Merge pull request #8478 from beberlei/CsFixes2
CS Fixes 2 string interpolation to sprintf
2021-02-14 14:39:00 +01:00
Benjamin Eberlei e45d212f02 Housekeeping: CS fixes Doctrine\ORM\Mapping\MappingException 2021-02-14 14:28:42 +01:00
Benjamin Eberlei 8f62bd39b5 Housekeeping: CS fixes Doctrine\ORM\ORMException 2021-02-14 09:53:19 +01:00
Benjamin Eberlei 5e11afcdf1 Housekeeping: CS fixes Doctrine\ORM\Mapping\MappingException 2021-02-14 09:49:39 +01:00
Benjamin Eberlei f833222017 Cs fixes1 (#8475)
* Housekeeping: CS Query, AbstractQuery, NativeQuery.

* Housekeeping: phpcs TreeWalker

* Housekeeping: CS Doctrine\ORM\EntityManager

* Housekeeping: CS Doctrine\ORM\Cache

* Upgrade git-phpcs

* Drop unused method parameter

* Describe types more precisely

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-02-14 09:23:15 +01:00
Grégoire Paris c7f39ebbde Merge pull request #8477 from greg0ire/cs-20210214
CS batch 9/an estimated 28
2021-02-14 09:20:11 +01:00
Grégoire Paris 15f08ed006 Manually fix cs 2021-02-14 00:04:18 +01:00
Grégoire Paris b6fd4b5ef3 Automatically fix cs 2021-02-14 00:04:11 +01:00
Grégoire Paris 5e4dae88f3 Merge pull request #8476 from greg0ire/cs-20210213
CS batch 8/an estimated 30
2021-02-13 17:26:58 +01:00
Grégoire Paris 7312ddeda7 Manually fix cs 2021-02-13 13:46:01 +01:00
Grégoire Paris 9a67b6f699 Merge pull request #8474 from greg0ire/cs-20210212
CS batch 7/an estimated 30
2021-02-12 23:03:28 +01:00
Grégoire Paris f59a0c349b Manually fix cs 2021-02-12 20:43:17 +01:00
Grégoire Paris 4c8831f716 Remove unused helper method 2021-02-12 20:04:56 +01:00
Grégoire Paris 01ca442be7 Ignore error about no assignment
These files must be require'd
2021-02-12 08:45:07 +01:00
Grégoire Paris 01374ca2ab Ignore rule about lone comment
That comment is mistaken for a method comment because it precedes one.
The issue was reported but will most likely not be fixed.
2021-02-12 07:39:56 +01:00
Grégoire Paris 94e8b1d43c Merge pull request #8470 from greg0ire/cs-20210211
CS batch 6/an estimated 30
2021-02-12 07:25:43 +01:00
Grégoire Paris 61d0f96c17 Manually fix cs 2021-02-11 21:02:10 +01:00
Grégoire Paris 41729be80a Spell "first" properly 2021-02-11 18:24:56 +01:00
Grégoire Paris 6dbaa39016 Merge pull request #8469 from greg0ire/cs-20210209
CS batch 5/an estimated 30
2021-02-10 21:11:11 +01:00
Grégoire Paris 58c95a92d1 Manually fix cs 2021-02-09 22:58:05 +01:00
Grégoire Paris 4958180b02 Merge pull request #8468 from greg0ire/cs-20210208
CS batch 4/an estimated 30
2021-02-09 22:22:41 +01:00
Grégoire Paris 8312ff0cb5 Merge pull request #8353 from fezfez/patch-1
Add docs to export-ignore
2021-02-08 21:23:35 +01:00
Grégoire Paris fb9b9b276e Manually fix cs 2021-02-08 13:53:04 +01:00
Grégoire Paris 792a9a9149 Merge pull request #8464 from greg0ire/cs-20210207
CS batch 3/many
2021-02-07 14:21:09 +01:00
Grégoire Paris 0f655f9fb6 Manually fix cs 2021-02-07 12:21:40 +01:00
Grégoire Paris 2d7acbd07f Merge pull request #8457 from greg0ire/cs-20210206
CS Batch 2/many
2021-02-06 12:22:10 +01:00
Grégoire Paris b06679cc14 Manually fix cs 2021-02-06 11:44:32 +01:00
Julian Ullrich 2693a93aed fixed entity generation for numeric values (#8434)
* fixed entity generation for numeric values

* fixed entity generation for numeric values

Co-authored-by: julian <julian@ullrichmail.net>
2021-02-06 00:35:45 +01:00
Benjamin Eberlei 8724589c6e Housekeeping: Fix wrong typehint - Closes #8421 2021-02-06 00:23:45 +01:00
Grégoire Paris 424305ef38 Merge pull request #8455 from greg0ire/cs
Cs
2021-02-05 23:40:01 +01:00
Grégoire Paris 9d01f6a45c Run phpcbf after running git-phpcs
git-phpcs can publish comments to Github, that will be helpful.
phpcbf will still be helpful to ensure no one upgrades the coding
standard without also fixing issues that can be autofixed.
2021-02-05 21:39:13 +01:00
Grégoire Paris 7ed487b534 Manually fix CS 2021-02-05 21:38:33 +01:00
Grégoire Paris 40f3925589 Merge pull request #8144 from greg0ire/cs
Automated fixes with phpcbf + manual fixes
2021-02-05 08:44:58 +01:00
Alexander Schranz f92c3dba32 Fix --complete flag in orm:ensure-production-settings command (#8426) 2021-02-01 22:08:04 +01:00
Grégoire Paris bcbd4401b8 Ignore export directory 2021-01-30 23:50:15 +01:00
Grégoire Paris d6aca8e146 Fix proxy file exclude pattern
__CG__ is a file prefix, not a directory
2021-01-30 23:45:46 +01:00
Grégoire Paris 8f1911a4fe Fix cs by hand 2021-01-30 23:29:40 +01:00
Grégoire Paris 7f30cd3102 Require doctrine/common ^3.0.3
That release comes with a fix for a bug that affects us since we are
using return type declarations for wakeUp() in proxyfied classes in on
of our tests.
2021-01-30 18:55:32 +01:00
Grégoire Paris f01fe3e050 Fix or remove wrong assertions 2021-01-30 18:55:32 +01:00
Grégoire Paris 210c2ee6a4 Remove strict types 2021-01-30 18:55:32 +01:00
Grégoire Paris 497dfd1a84 Avoid covariant return types
It's not supported by PHP 7.2/7.3
2021-01-30 18:55:31 +01:00
Grégoire Paris d9f0e2a27f Add exclude rules for tricky cases 2021-01-30 18:55:31 +01:00
Grégoire Paris 9a40ac6e2a Restore weird phpdoc
Tests do not pass when I format that phpdoc nicely.
2021-01-30 18:55:31 +01:00
Grégoire Paris 1687d9c479 Restore version annotation, but capitalized
@version is commonly used for svn ids and is forbidden
2021-01-30 18:55:31 +01:00
Gabriel Ostrolucký 1a46ed8901 Relax contract of EntityListenerResolver so it doesn't require class name (#8448)
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-01-30 13:37:26 +01:00
Grégoire Paris 36d0352c01 Add missing use statements 2021-01-30 11:21:44 +01:00
Grégoire Paris 15eacd787b Remove weird extra argument 2021-01-30 11:21:44 +01:00
Grégoire Paris 5b3f9bdd7b Fix type declarations 2021-01-30 11:21:44 +01:00
Grégoire Paris e00dba94f4 Remove strict types 2021-01-30 11:21:44 +01:00
Grégoire Paris ab0e4007a5 Use interface instead of concretion 2021-01-30 11:17:40 +01:00
Grégoire Paris 32266c54f9 Fix compatibility with parent signature 2021-01-30 11:17:39 +01:00
Grégoire Paris dd2120cd41 Make default value compatible with phpdoc 2021-01-30 11:17:39 +01:00
Grégoire Paris 8991df0785 Add missing return types 2021-01-30 11:17:39 +01:00
Grégoire Paris ca31923a39 Run phpcbf in the CI
There are still many CS issues to fix, but with this, we now know people
can run vendor/bin/phpcbf to fix their issues and only their issues.
2021-01-30 11:17:39 +01:00
Grégoire Paris 68bc00b6c6 Automatically fix CS 2021-01-30 10:58:42 +01:00
Grégoire Paris 5b55b8c6cf Disable rules that would result in BC-breaks 2021-01-29 23:21:12 +01:00
Grégoire Paris 10f381bc95 Restrict fixes to be compatible with PHP 7.2 2021-01-29 21:16:18 +01:00
Nikolay Gagarinov 40aa8fe5db update getting-started fix type, improve doc (#8440)
* update getting-started fix type, improve doc

* Update getting-started.rst
2021-01-26 21:40:41 +01:00
Simon Podlipsky 5801474ba3 Catch doctrine/persistence MappingException (#8264)
When driver chain is used doctrine/persistence MappingException is thrown instead of doctrine/orm MappingException
2021-01-11 09:07:06 +01:00
Claudio Zizza 9dbd960631 Update website config to current repository branches (#8420) 2021-01-11 09:06:06 +01:00
Grégoire Paris 544df89055 Use proper workflow name (#8418)
This must have been a copy/paste/adapt mistake.
2021-01-11 09:05:18 +01:00
azjezz 378944dd27 remove T from class metadata (#8398) 2020-12-19 20:48:16 +01:00
Benjamin Eberlei 8b749642cd [GH-8231] Bugfix: Missed dirty check synchronization check. (#8392)
When an entity with change tracking policy "deferred explicit" gets
removed, then persisted again, it is not schedulded for a dirty check
synchronization. This is not the case for entities that are persisted
and are already in the managed state.
2020-12-14 11:00:07 +01:00
Nic Wortel 277b53a970 Use the ramsey/composer-install action to install dependencies (#8388)
Follow-up of https://github.com/doctrine/.github/pull/16.
2020-12-10 20:03:47 +01:00
Benjamin Morel 2febb4509a Fix Psalm param (#8386) 2020-12-09 11:03:01 +01:00
Benjamin Eberlei 21d2c88013 [GH-7486] Bugfix: failing the command when clearing empty cache is wrong semantics. (#8376) 2020-12-06 22:00:00 +01:00
Vašek Henzl e7d33eb1a9 Infer datetime_immutable DBAL type for \DateTimeImmutable instance parameters (#8328)
The support for passing \DateTimeImmutable instance as a query parameter has
been added to ORM in #1333 (the year 2015), a long time before immutable date
types (datetime_immutable etc) were introduced to DBAL in doctrine/dbal#2450
(2017).

Back then, it made sense to treat \DateTimeImmutable (or any
\DateTimeInterface) in the same way as \DateTime and infer parameter type as
datetime. However, when immutable date types were later added to DBAL, it
wasn't reflected anyhow in type inference in ORM and \DateTimeImmmutable
instances are still inferred as datetime DBAL type.

This PR fixes this IMO incorrect behaviour of
ParameterTypeInferer::inferType(): for a \DateTimeImmmutable parameter, it now
returns datetime_immutable DBAL type; for \DateTime or any other types
implementing \DateTimeInterface, it returns datetime DBAL type as it did
before.

This behaviour is in line with DateTimeImmutableType handling only
\DateTimeImmutable and DateTimeType handling any \DateTimeInterface.

Why? In most cases, it doesn't matter and datetime works for \DateTimeImmutable
parameters just fine. But it does matter if using custom implementation of
datetime_immutable type like UTCDateTimeImmutableType from
simpod/doctrine-utcdatetime. Then the broken type inference is revealed.

This is partially related to #6443, however, this PR isn't about custom DBAL
types but about correct type inference for build-in types.
2020-12-05 23:36:33 +01:00
Michel Hunziker cab7a4558d Fix invalid psalm annotation (#8374) 2020-12-05 14:47:49 +01:00
Benjamin Eberlei 242cf1a33d Fix ambiguous case where an entity is also a Traversable (#8371)
* Fix ambiguous case where an entity is also a Traversable

* Address phpcs violations.

* Address phpcs violations.

* Address phpcs violations.

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

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

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

This reverts commit f4ebded63c.

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

* [GH-8229] Revert ccae8f7176 PR #8234

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

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

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

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

* Make phpcs happy
2020-11-25 23:04:56 +01:00
Claudio Zizza 011d3c21eb Update functionality of code examples (#8336) 2020-11-24 22:39:42 +01:00
Benjamin Eberlei 3d46e07887 PHP8 Support (#8303)
* Update doctrine/dbal to 2.12 for PHP 8 support.

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

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

* Fix PHPUnit API related changes.

* Add PHP 8 support for EntityGenerator namespace detection.

* Use new assertEqualsWithDetla for QueryDqlFunctionTest with date comparisons

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

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

This reverts commit 279070491d50deaa4d41e17b28bb5a68f5a22796.

* More matchToken => match reverts

* Housekeeping: phpcs

* Housekeeping: phpcs

* Housekeeping: phpcs

* Housekeeping: phpcs

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

* Update doctrine/coding-standard to 8.x

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

* Update to Psalm 4.

* Not failOnWarning anymore.

* Fix phpcs

* fix phpcs

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

* Relax doctrine/dbal requirement and add 7.2 CI support again.
2020-11-15 13:24:31 +01:00
Roma b1ac293a50 Add missing backtick in rst markup (#8335) 2020-11-10 20:37:36 +01:00
Romain Grégoire f4ebded63c Fix SQL alias generation regression for simple inheritance (#8329)
This fixes a regression from 099c5b42e1.
Without the fix, "where part" in SQL is generated with incorrect aliases.
See https://github.com/doctrine/orm/issues/8229#issuecomment-722942180.
2020-11-10 10:53:01 +01:00
Benjamin Eberlei 51bc596502 Update to Psalm 4. (#8332) 2020-11-08 10:38:21 +01:00
Grégoire Paris 95f1b48422 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-11-07 20:19:45 +01:00
Grégoire Paris 385b5a2f80 Phpunit 8 (#8330)
* Revert to whitelist

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

* Upgrade to PHPUnit 8

This unlocks PCOV usage for coverage

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

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

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

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

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

* Fix wrong argument

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

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

* Upgrade doctrine/coding-standard

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

* Account for doctrine/reflection deprecation
2020-10-28 11:14:10 +01:00
Simon Podlipsky 4bfc84f035 Rename getIterable() to toIterable() (part 2) (#8293) 2020-10-17 23:55:39 +02:00
Michael Voříšek ca27cc3f72 Fix EOL of text files (#8310) 2020-10-17 19:04:42 +02:00
Mateusz Sip 53dc5b2ac3 Detect associations inside embeddables (#8291) 2020-10-17 18:49:16 +02:00
orklah f1219f1418 Add psalm template support to several types (#8289) 2020-10-17 12:01:52 +02:00
Grégoire Paris 072066f746 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-10-17 11:07:05 +02:00
Laurent VOULLEMIER 5fde5801c1 Fix many typos (#8299)
- Some uppercase letters were used in the middle of sentence
- Some dots were missing
- There was two sentences with wrong or missing words
2020-10-13 21:52:04 +02:00
Thomas Landauer 18d96fcc02 Update working-with-indexed-associations.rst (#8298)
* Update working-with-indexed-associations.rst

Fixing broken link

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

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

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

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

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

Improved code formatting

* Update docs/en/reference/events.rst

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

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

* Update events.rst

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

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

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

* remove inexistant SimpleEntityExpression

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

(partially cherry picked from commit 17e7c2a42e)

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

Fixes #8106.

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

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

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

* Controls growing rate of the abstract test class

* Fixes incorrect test case

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

* Removes non architecture compliant code

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

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

* Use CodeCov instead of Scrutinizer
2020-06-17 22:47:25 +02:00
Grégoire Paris 5ced62bf83 Run benchmark on PHP 7.4 (#8186)
phpbench recently dropped compatibilty with PHP 7.1
When this job was introduced, 7.1 was the latest version of PHP we
supported, so it makes sense to bump to 7.4 now.
See https://github.com/phpbench/phpbench/releases/tag/0.17.0
See e07c90df44
2020-06-17 22:06:42 +02:00
Grégoire Paris bee8decd18 Require persistence 2 and common 3 (#8166)
This allows us to drop the autoload-calls-based BC layer.
2020-06-17 14:56:17 +02:00
Alexander Berl 93867f8d77 TASK: Replace "Blacklist" example with "Banlist" (#8174) 2020-06-08 23:06:53 +02:00
Laurent VOULLEMIER 6bce7e9cab Add iterable support for IN criterias (#8162) 2020-06-03 18:40:53 +02:00
Grégoire Paris 4d8418fe6f Merge remote-tracking branch 'origin/2.7' into drop-persistence-bc-layer 2020-06-02 18:40:48 +02:00
Grégoire Paris d95e03ba66 Allow doctrine/common 3 and doctrine/persistence 2 (#8158) 2020-05-26 18:03:49 +02:00
Robert Basic 825ceb6b7a Fix inline code example (#8153)
* Fix inline code example

* Remove extra backslash
2020-05-22 22:45:24 +02:00
orklah de2e2a1d74 Add psalm types (#7989) 2020-05-18 21:48:28 +02:00
Benjamin Eberlei 850d57e791 [GH-8137] Update doctrine/inflector dependency to 1.4|2.0 (#8147)
* [GH-8137] Update doctrine/inflector dependency to 1.4|2.0 and resolve deprecations.

* [GH-8137] Address review comments

* Address phpcs issues.

* More CS

* Codingstyle
2020-05-17 19:58:02 +02:00
Grégoire Paris c3dd71704b Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-05-16 15:14:51 +02:00
Grégoire Paris 6780a963f7 Migrate git-phpcs to Github actions (#8146) 2020-05-16 14:01:10 +02:00
Philippe Le Van (@plv) 0b305e5bd3 Add possibility to use sql comments in the DQL syntax (#8140) 2020-05-15 08:04:58 +02:00
Benjamin Eberlei 4d172e2591 Revert changes to embeddable mapping in 2.7 (#8138)
* Revert "Fix inherited embeddables and nesting after AnnotationDriver change #8006 (#8036)"

This reverts commit a9b6b72017.

* Revert "Make Embeddable not transient"

This reverts commit 58677c29b4.

* Housekeeping: CS fixes
2020-05-12 20:10:26 +02:00
Grégoire Paris 21a98234d0 Static analysis with Psalm (#8116)
* Remove useless ternaries

If these expressions are truish inside the condition, they will still be
truish inside the if.

* Describe properties more accurately

These are not objects, they are strings holding class names for classes
that implement TreeWalker.

* Remove duplicate key

Comparison::IS and Comparison::EQ are the same. I chose to remove IS
because it does not seem to exist anymore on master

* Remove unwanted . before = operator

This worked, but makes no sense.

* Setup static analysis with Psalm

* Move PHPStan to Github actions
2020-05-11 23:06:46 +02:00
Gabriel Birke 061207861b Improve CLI config documentation (#8130)
Change code examples for `cli-config.php` to use
`ConsoleRunner::createHelperSet` instead of constructing the helper set
array yourself.
Remove DBAL-specific documentation

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>
2020-05-11 21:43:26 +02:00
Grégoire Paris 8a9954e46c Address deprecations from persistence (#7953)
A backwards-compatibility layer has been added to persistence to help
consumers move to the new namespacing. It is based on class aliases,
which means the type declaration changes should not be a BC-break: types
are the same.
See https://github.com/doctrine/persistence/pull/71

This means:
- using the new namespaces
- adding autoload calls for new types to types that may be extended and
use persistence types in type declarations of non-constructor methods,
so that signature compatibility is recognized by old versions of php.
More details on this at
https://dev.to/greg0ire/how-to-deprecate-a-type-in-php-48cf
2020-05-07 08:35:39 +02:00
Grzesiek 527fff53cc removed repository class from metadata (#8125)
The annotation is required only in the next chapter of the tutorial, specifically the "Entity Repositories"
2020-05-03 21:58:20 +02:00
Maks Rafalko 70fb1ecd78 Warn users about performance issues with DateTime objects in setParameter() (#8114) 2020-04-25 22:16:17 +02:00
Jeroen van den Heuvel 73ec483e9d Convert PHP to SQL for new object expression (#8062) 2020-04-16 23:01:43 +02:00
Mathieu 8d67eec812 Fix JoinColumn documentation (#7966) 2020-03-30 21:33:13 +02:00
Vincent Langlet a418cf6418 Remove TODO (#8078) 2020-03-30 20:06:04 +02:00
Arne 6138afdca9 [Docs] Make clear that calling remove() detaches the object (#8081)
I changed a relationship from eager to lazy loading which broker the behaviour of my application in regards to object removal. It was not clear for me that removing an object detaches it and subsequent calls like contains() in a OneToMany relationship with the object scheduled for removal will return false afterwards.
2020-03-27 19:42:39 +01:00
Vincent Langlet dafe298ce5 Fix phpdoc (#8074) 2020-03-19 07:41:02 +01:00
Matthias Pigulla 58b8130ea1 Fix regression in 2.7.1 when mysqli is used with discriminator column that is not a string (#8055)
* Add a test case showing the regression

* Cast the discriminator value to string

* Fix CS
2020-03-16 11:19:12 +01:00
Grégoire Paris 3c91792dd8 Address deprecation about PHPUnit annotations (#8070)
These changes have been done using Rector, this might not result in the
best placement for method calls, but it is not worse than the current
situation.

I used the configuration documented at
https://github.com/rectorphp/rector/blob/b398e8740c3619525061350d4d4e1bca20dabea9/docs/HowItWorks.md#221-order-of-rectors

Fixes #8069
2020-03-16 08:56:59 +01:00
Benjamin Eberlei a705f526fb [GH-7633] disallow cache partial objects (#8050)
* [GH-7633] Bugfix: Partial queries were stored in 2LC.

There was a check in DefaultQueryCache that prevented partial queries,
because they are not supported. However the checked hint
Query::HINT_FORCE_PARTIAL_LOAD is optional, so cant be used to prevent
caching partial DQL queries.

Introduce a new hint that the SqlWalker sets on detecing a PARTIAL
query and throw an exception in the DefaultQueryCache if thats found.

* Housekeeping: CS

* [GH-7633] HINT_FORCE_PARTIAL_LOAD still needs to be checked.

* Housekeeping: Fix CS
2020-03-15 01:11:34 +01:00
Maciej Malarz a9b6b72017 Fix inherited embeddables and nesting after AnnotationDriver change #8006 (#8036)
* Add test case

* Treat parent embeddables as mapped superclasses

* [GH-8031] Bugfix: Get working again on nested embeddables in inherited embeddables.

* Housekeeping: CS

* Update note on limitations

* [GH-8031] Verify assocations still do not work with Embeddables.

* Housekeeping: CS

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2020-03-15 01:00:58 +01:00
Jorrit Schippers cd905fff77 Fix documentation of default generated value behavior (#8068) 2020-03-13 20:40:31 +01:00
Claudio Zizza 431d0a3c5e Remove ORM 2.0 version constraints from docs (#8002) 2020-03-06 21:47:40 +01:00
Andreas Möller eb700405be Fix: Use neutral pronouns (#8059) 2020-03-06 16:08:53 +01:00
Rosemary Orchard 9273057649 Annotations override naming strategy (#8041)
Add a note/warning that annotations override the naming strategy.
2020-03-01 14:01:26 +01:00
Soliman 1da002ca2f Throw OptimisticLockException when connection::commit() returns… (#7946)
* Throw OptimisticLockException when connection::commit() returns false

* Update unit tests

* Fix doctrine persistence version to avoid deprecations changes

* Apply changes from 2.8.x

* Update from 2.8.x
2020-02-29 23:06:47 +01:00
Grégoire Paris e04a79526e Merge pull request #7230 from holtkamp/patch-2
Mention that lifecycle callbacks do not support Embeddables
2020-02-17 23:00:44 +01:00
Menno Holtkamp d157a6cbeb Mention that lifecycle callbacks do not support Embeddables
As discussed in https://github.com/doctrine/doctrine2/issues/6855
2020-02-17 22:25:00 +01:00
Benjamin Eberlei ca57222010 Merge pull request #8023 from peterkeatingie/query-cache-fix
Put into cache using root entity name
2020-02-16 10:50:24 +01:00
Peter Keating 9bb2bf0cce Put into cache using root entity name 2020-02-15 15:53:47 +00:00
Benjamin Eberlei 445796af0e Travis: Use 7.4 instead of 7.4snapshot 2020-02-15 15:35:56 +01:00
Benjamin Eberlei ab93285284 Remove nightly builds from .travis.yml 2020-02-15 15:34:36 +01:00
Benjamin Eberlei ef639d4de6 Remove nightly builds from .travis.yml 2020-02-15 15:34:05 +01:00
Benjamin Eberlei 31f4dd671a Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-02-13 21:31:29 +01:00
Benjamin Eberlei a692670469 Merge pull request #8006 from doctrine/malarzm-patch-1
Make Embeddable not transient
2020-02-13 21:31:02 +01:00
Maciej Malarz 58677c29b4 Make Embeddable not transient 2020-02-13 21:05:52 +01:00
Benjamin Eberlei 60c4867ed3 Merge branch 2.7 into 2.8.x 2020-02-12 23:42:36 +01:00
Benjamin Eberlei 9a0fcb5a86 Merge pull request #7940 from doctrine/GH-7864-ExtraLazyRemoveElement
[GH-7864] Bugfix in PersistentCollection::removeElement for EXTRA_LAZY.
2020-02-12 23:42:06 +01:00
Benjamin Eberlei 0ee1716b26 Merge branch 2.7 into 2.8.x 2020-02-12 23:38:27 +01:00
Benjamin Eberlei 8104c65d6c Merge pull request #7987 from beberlei/GH-7982-NoSqlExecutor
[GH-7982] no sql executor leads to parse error
2020-02-12 23:37:39 +01:00
Benjamin Eberlei a64d254d07 [GH-7982] Bugfix: Passing string|null DQL to Lexer(string $input) leads to downstream notice. 2020-02-12 23:23:12 +01:00
Claudio Zizza a236a83fa8 Merge pull request #7993 from SenseException/readme2-8
Update version and links in readme
2020-01-20 11:14:33 +01:00
Claudio Zizza 37f1bd7606 Update version and links in readme 2020-01-17 21:42:59 +01:00
Grégoire Paris af4cb282ba Merge pull request #7978 from alexeyshockov/patch-2
Stable PHP 7.4 in Travis
2020-01-17 07:49:48 +01:00
Benjamin Eberlei ce4914ba0e Merge 2.7 to 2.8.x 2020-01-17 00:08:23 +01:00
Benjamin Eberlei fdad48278b Merge pull request #7991 from greg0ire/7.4-sa
Try running phpstan on php 7.4
2020-01-17 00:06:40 +01:00
Benjamin Eberlei fc94127d7f Make ocramius/package-versions 1.2 the lowest version for phpstan 2020-01-16 23:51:17 +01:00
Grégoire Paris dea3e5df44 Try running phpstan on php 7.4
It might be easier to find packages compatible with both our locked deps
and phpstan with that version of php.
2020-01-16 23:49:15 +01:00
Alexey Shokov bdfd6c1677 Stable PHP 7.4 in Travis 2020-01-16 23:27:20 +01:00
Benjamin Eberlei 5d7d3e99a0 Downgrade ocramius/package-versions to lowest in composer.lock to support all PHP versions. 2020-01-16 23:18:00 +01:00
Benjamin Eberlei 3bc1096fd0 [GH-7982] Default Query state to dirty to fix execution of empty query. 2020-01-15 23:30:59 +01:00
Benjamin Eberlei 8e0157d97d Merge branch '2.7' into 2.8.x 2020-01-15 22:57:06 +01:00
Benjamin Eberlei a2f01f7ccc Allow everything from ocramius/package-versions ^1.0. 2020-01-15 22:56:08 +01:00
Benjamin Eberlei 1767f4b8e7 Merge branch '2.7' into 2.8.x 2020-01-15 22:02:55 +01:00
Benjamin Eberlei 401db453a2 Merge pull request #7974 from beberlei/gh-7505
[GH-7505] Bug in SimpleObjectHydrator when using inheritance with same field
2020-01-15 22:02:25 +01:00
Benjamin Eberlei 6e59ec8f16 [GH-7505] Fix cs 2020-01-15 21:52:11 +01:00
Benjamin Eberlei 87e491465a Add @group 2020-01-15 21:13:25 +01:00
Luís Cobucci 8b588eceb2 Merge pull request #7973 from DocFX/patch-1
Just a micro grammar update.
2020-01-14 11:26:21 +01:00
Benjamin Eberlei edce36598f Adjust tests back for 2.x. 2020-01-09 00:41:47 +01:00
Benjamin Eberlei ca95b0ee13 Merge branch '2.7' into 2.8.x 2020-01-08 19:39:17 +01:00
Woody Gilk 20c46035d1 [Docs] Prefer PhpFileCache for caching and remove APC/XCache. 2020-01-08 19:36:08 +01:00
William Pinaud 324aacfb54 Just a micro grammar update. 2020-01-08 18:52:11 +01:00
Benjamin Eberlei 1edfcabead Merge pull request #7894 from TomckySan/convert-default-value-to-boolean
Fix boolean properties default value when generating entities.
2020-01-05 16:11:33 +01:00
Luís Cobucci 2785cde792 Merge pull request #7957 from lcobucci/fix-version-information
Fix version information
2019-12-17 19:19:05 +01:00
Luís Cobucci d67e3e8b1b Rely on ocramius/package-versions to render the version
Since `Doctrine\ORM\Version` is now deprecated it shall not be updated
on future releases.

This ensures that our CLI tool will present the correct version number.
2019-12-17 15:47:55 +01:00
Luís Cobucci d629c4e487 Remove build.xml and related files
We aren't using ant/phing to handle the releases any more.
2019-12-17 01:03:34 +01:00
Luís Cobucci 4a4226213f Merge pull request #7875 from nicolas-grekas/schema-tool
Whilelist existing assets we know about from metadata in SchemaTool::getUpdateSchemaSql()
2019-12-16 23:59:31 +01:00
Andreas Braun 0ce1440884 Add upgrade note about schema_filter change 2019-12-16 23:45:49 +01:00
Laurent VOULLEMIER 9aa28b4e33 Test asset whitelisting on SchemaTool#getUpdateSchemasSql() 2019-12-16 23:45:49 +01:00
Nicolas Grekas 5c2b6870bf Whitelist existing assets we know about from metadata in SchemaTool::getUpdateSchemaSql() 2019-12-16 23:22:50 +01:00
Luís Cobucci 4389b2c188 Merge pull request #7956 from lcobucci/fix-test-suite
Ignore Doctrine\Common\Persistence\ObjectManagerDecorator deprecation
2019-12-16 21:49:25 +01:00
Luís Cobucci e481d9880b Ignore Doctrine\Common\Persistence\ObjectManagerDecorator deprecation
Since applying the fixes requires bumping up the dependency, which isn't
done in a patch release.

This should be removed in v2.8.0.
2019-12-16 21:22:28 +01:00
Luís Cobucci 85528f28e2 Fix CS errors 2019-12-16 21:22:23 +01:00
Luís Cobucci 5873242fb5 Merge pull request #7937 from doctrine/GH-7930-SqliteForeignKeys
Revert SchemaTool change to check for foreign key support
2019-12-16 10:45:07 +01:00
Luís Cobucci 4aa09861dd Merge pull request #7941 from Grafikart/feat-typed-functions
Allow DQL functions to specify return type
2019-12-12 19:50:23 +01:00
Grafikart 24e9a7caaf Allow defining return types for DQL functions 2019-12-12 14:19:09 +01:00
Luís Cobucci d90df59118 Merge pull request #7948 from beberlei/TravisSmokeTesting
Add stage that runs before Test to allow fast failures on Sqlite/phpcs
2019-12-12 11:54:41 +01:00
Benjamin Eberlei f9103a7b41 Add another stage that runs before Test to allow fast failures on Sqlite, Quality + PHPCS-differ 2019-12-12 11:43:24 +01:00
Luís Cobucci 9891477094 Merge pull request #7928 from kokspflanze/2.7-patch-deprecated
Deprecate EntityRepository#clear()
2019-12-09 21:56:41 +01:00
kokspflanze 59e3a55110 Deprecate EntityRepository#clear() 2019-12-09 21:42:11 +01:00
Luís Cobucci c9e41d0aa7 Merge pull request #7934 from BenMorel/php74
Fix Trying to access array offset on value of type null
2019-12-09 21:32:58 +01:00
Benjamin Morel f37c12834d Fix Trying to access array offset on value of type null 2019-12-09 21:24:29 +01:00
Benjamin Eberlei 041404e8b3 [GH-7864] Revert removeElement EXTRA_LAZY support. 2019-12-07 00:03:09 +01:00
Benjamin Eberlei bfc68b3aba Add warning about removeElement on extra lazy 2019-12-03 20:34:44 +01:00
Benjamin Eberlei 1e628370c4 [GH-7864] Address review comments. 2019-12-03 19:35:49 +01:00
Benjamin Eberlei ae2b9b1921 Housekeeping: phpcbf to fix issues. 2019-12-01 21:11:09 +01:00
Benjamin Eberlei 419df77a09 [GH-7864] ExtraLazyCollectionTest is not cacahble and should not fail SLC suite. 2019-12-01 20:28:30 +01:00
Benjamin Eberlei d6f6b2e97c [GH-7864] Remove tests that checked invalid behavior. 2019-12-01 19:47:58 +01:00
Benjamin Eberlei 75d5adf599 [GH-7864] Bugfix in PersistentCollection::removeElement for EXTRA_LAZY. 2019-12-01 19:27:45 +01:00
Benjamin Eberlei cfd6fadf9c Revert "#7841 SchemaTool generates extra diff for platforms without FK support"
This reverts commit 3707c39124.
2019-12-01 11:23:45 +01:00
Tomoka Baba 2bf7916c52 Fix to pass code quality check. 2019-11-20 17:03:34 +09:00
Tomoka Baba 253fd10cc0 Modified test to use assertTrue. 2019-11-20 17:02:25 +09:00
Tomoka Baba 2c956d55f2 Fix to pass code quality check. 2019-11-20 17:02:25 +09:00
Tomoka Baba 3db992e953 Add test code. 2019-11-20 17:01:52 +09:00
Tomoka Baba 6fc9b3ab16 Fix to pass code quality check. 2019-11-20 17:01:52 +09:00
Tomoka Baba 2d833a5e86 Fix boolean properties default value when generating entities. 2019-11-20 17:01:19 +09:00
Luís Cobucci a416a9a8b2 Bump up version 2019-11-19 09:43:57 +01:00
Luís Cobucci 4d763ca4c9 Bump up version 2019-11-19 09:38:05 +01:00
Luís Cobucci 398d74deaa Merge pull request #7911 from lcobucci/be-more-explicit-on-deprecation-messages
Be explicit about which Doctrine package in message
2019-11-19 09:36:14 +01:00
Luís Cobucci 3314322929 Be explicit about which Doctrine package in message
Avoiding possible confusion while reading the deprecation messages.
2019-11-19 09:03:36 +01:00
Luís Cobucci ce93817bf7 Merge pull request #7909 from lcobucci/add-deprecation-messages
Add deprecation messages
2019-11-19 08:21:44 +01:00
Luís Cobucci 50992eafa2 Deprecated the usage of number unaware underscore naming strategy 2019-11-19 02:15:11 +01:00
Luís Cobucci 9ccb8837e7 Add deprecation message for EM#clear($entityName) 2019-11-19 01:34:50 +01:00
Luís Cobucci d959744c0a Merge pull request #7079 from mairo744/hotfix/sqlite-join-table-name
fix getJoinTableName for sqlite with schema attribute
2019-11-18 23:37:33 +01:00
mairo744 0264ba1759 Fix creation of join table names with schemas in SQLite
Join table name doesnt depending on the platform.
Table name was "schema.table" instead of "schema__table".

(cherry picked from commit 4878cd3f4ef30ffc6047c18e0f7b16aafeabc3b4)
2019-11-18 23:25:28 +01:00
Luís Cobucci 8332fa1855 Merge remote-tracking branch 'upstream/2.6' into 2.7 2019-11-18 23:06:28 +01:00
Luís Cobucci 4fae126459 Bump up version 2019-11-18 23:05:16 +01:00
Luís Cobucci 2d9b935183 Bump up version 2019-11-18 23:01:21 +01:00
Luís Cobucci 4804f602f8 Merge pull request #7908 from lcobucci/fix-bc-break-on-naming-strategy
Fix BC-break on underscore naming strategy
2019-11-18 22:57:27 +01:00
Luís Cobucci 3d17290eb5 Fix BC-break on underscore naming strategy
We broke our BC promises on the last patch release by changing how the
underscore naming strategy parses values with numbers.

This commit makes it possible to configure whether or not to make the
underscore naming strategy aware of numbers, keeping the old
configuration as default value.
2019-11-18 22:38:14 +01:00
Luís Cobucci 8420d24f90 Merge remote-tracking branch 'upstream/2.6' into 2.7 2019-11-18 19:59:58 +01:00
Luís Cobucci 52f2b37921 Bump up version 2019-11-18 12:17:41 +01:00
Luís Cobucci 16751d210f Bump up version 2019-11-18 12:06:51 +01:00
Luís Cobucci 686f508576 Merge pull request #7905 from lcobucci/7890-paginator-objecti
[Paginator] Fix type conversion during hydration of pagination limit subquery
2019-11-18 10:50:54 +01:00
Luís Cobucci 00ef1eba90 Add paginator query hint to force type conversion
We're keeping a BC layer in the hydrator, which prevents type conversion
in scalar results.

This makes bypasses such layer in order to always convert the identifier
types when limiting the result set during a pagination.

The main goal here is to keep the conversion DB->PHP inside of the
hydrator components.
2019-11-18 10:27:10 +01:00
Gabriel Ostrolucký 3843eee5cb [Paginator] Add test case for regression with custom id
Co-authored-by: Alexei Korolev <alexei.korolev@gmail.com>
2019-11-18 10:27:10 +01:00
Luís Cobucci f576e6c41f Merge pull request #7904 from greg0ire/validate-composer-json
Make sure composer files are valid
2019-11-16 11:47:02 +01:00
Grégoire Paris c79d2e0dc2 Make sure composer files are valid
The composer.lock is put under version control and it often happens to
be out of sync with the composer.json, which could lead to
hard-to-understand issues.
Using the --strict option here because we might as well aim for a
perfectly valid composer.json
2019-11-16 10:59:39 +01:00
Grégoire Paris 33b8d020a7 Synchronize lock file and json manifest 2019-11-16 10:59:39 +01:00
Luís Cobucci 1b2daac25d Merge pull request #7710 from rtek/pretty-tool-describe
Prettified arrays in tool command orm:mapping:describe
2019-11-16 02:15:31 +01:00
Luís Cobucci 977985f756 Merge pull request #7701 from someniatko/deprecate-use-result-cache
Split and deprecate AbstractQuery#useResultCache()
2019-11-16 02:12:29 +01:00
rtek 0c36f87935 Prettify arrays in orm:mapping:describe command
This will prevent excessive column width and wrapping in the output which uses Symfony\Component\Console\Style\SymfonyStyle::table().
2019-11-16 02:03:03 +01:00
someniatko e8f265d480 Make ResultCacheTest tests slightly more logical 2019-11-16 01:59:57 +01:00
Illia Somov 7bcbad076d Split and deprecate AbstractQuery#useResultCache() 2019-11-16 01:59:57 +01:00
Luís Cobucci 57496e32fd Add minor BC-break notes on output walkers in paginator
As explained in
https://github.com/doctrine/orm/pull/7863#issuecomment-554578313.
2019-11-16 01:48:37 +01:00
Luís Cobucci 797bfc53c4 Fix deprecation messages version 2019-11-16 01:32:15 +01:00
Luís Cobucci 8c47dcb6fc Merge pull request #7863 from Seb33300/skip-limit-subquery
Paginator: Skip limit subquery if not required
2019-11-16 01:21:43 +01:00
Sébastien ALFAIATE 6347190886 Skip limit subquery if not required 2019-11-16 01:03:22 +01:00
Luís Cobucci 9162f3519d Merge pull request #7900 from doctrine/2.6.x-merge-up-into-2.7
Merge up 2.6 to 2.7
2019-11-16 00:27:50 +01:00
Grégoire Paris fc9314d9f5 Merge remote-tracking branch 'origin/2.7' into 7900--2.6.x-merge-up-into-2.7 2019-11-15 23:50:05 +01:00
Luís Cobucci 26806d08eb Require more updated doctrine packages 2019-11-15 23:46:22 +01:00
Luís Cobucci 6a827d5b61 Merge pull request #7861 from ferrastas/bug_removing_collection
Delete statements will not be created using `clear`
2019-11-15 22:58:31 +01:00
Gabriel Ostrolucký 7d77984306 Restore ability to clear deferred explicit tracked collections
This was regression from #7862 which tried to respect tracking config
when clearing collections, but this logic can happen in UOW only,
PersistentCollection::clear is triggered too early to know what
is (going to be) persisted.

Fixes #7862
2019-11-15 22:49:06 +01:00
Ferran Vidal ec93014713 Delete statements will not be created using clear. 2019-11-15 22:43:53 +01:00
Luís Cobucci c83094bde0 Merge pull request #7684 from rharink/2.6
only replace '_id' at end of columnName
2019-11-15 16:50:16 +01:00
Robert den Harink 982d1519db only replace '_id' at end of columnName 2019-11-15 16:36:48 +01:00
Marco Pivetta f7c04ae537 Merge pull request #7901 from lcobucci/add-deprecation-notices
Add deprecation warnings for 2.7.x
2019-11-15 16:15:31 +01:00
Michael Moravec f9a4258ded Upgrading notes for 2.7 2019-11-15 14:43:33 +01:00
Michael Moravec eb9f11bf96 Added deprecation warnings for 2.x 2019-11-15 14:43:15 +01:00
Luís Cobucci 2b8cb9de79 Add basic tool to verify deprecation messages 2019-11-15 14:42:48 +01:00
Luís Cobucci 570abb5bad Fix PHP warnings in test suite 2019-11-15 14:42:47 +01:00
Luís Cobucci 855244fd10 Merge pull request #7865 from Ocramius/fix/#7837-paginate-with-custom-identifier-types-even-with-cached-dql-parsing
#7837 paginate with custom identifier types even with enabled DQL query cache
2019-11-15 11:08:22 +01:00
Guilherme Blanco c62977412c Merge pull request #7869 from BenMorel/patch-4
UnitOfWork::clear() misses $eagerLoadingEntities
2019-11-15 00:27:08 -05:00
Gabriel Ostrolucký 98e557b68e Improve assertion failure message for testWillFindSongsInPaginatorEvenWithCachedQueryParsing 2019-11-14 23:37:13 +01:00
Mickaël RAYBAUD-ROIG 3a32c00dcf Add a failing test for issue #7505 2019-11-14 23:28:42 +01:00
Gabriel Ostrolucký 1dde2c9e8e Add test case verifying eager loads are clear
Otherwise, getClassMetadata would be triggered more times
2019-11-14 22:17:06 +01:00
Marco Pivetta adfd010a78 Merge pull request #7889 from ajgarlag/hotfix/fix-tests-with-dbal-2.10
Use quoted collation declaration when available.
2019-11-05 15:52:18 +01:00
Antonio J. García Lagar 1bc4e1f594 Use quoted collation declaration when available. 2019-11-05 14:58:24 +01:00
Marco Pivetta 21680df9bd Merge pull request #7884 from rogeriolino/patch-1
[Documentation] Advanced field value... - missing entity alias
2019-11-05 01:23:52 +01:00
Rogério Alencar Lino Filho 19aa3c125c missing entity alias 2019-10-31 18:20:58 -03:00
Marco Pivetta e9e012a037 Merge pull request #7880 from kuraobi/update-doc-dql-qb
Update documentation to recommend DQL over QueryBuilder when possible
2019-10-29 19:04:03 +01:00
Mathieu Lemoine d1db0655ac Update documentation to recommend DQL over QueryBuilder when possible 2019-10-29 16:26:17 +01:00
Luís Cobucci 2d643e6b7b Merge pull request #7876 from nicolas-grekas/sf5-cmd
Fix compat of commands with Symfony 5
2019-10-23 16:12:18 +02:00
Nicolas Grekas 4d6b1f3e63 Fix compat of commands with Symfony 5 2019-10-23 16:00:19 +02:00
Jonathan H. Wage d9c30e34c4 Merge pull request #7723 from nicolas-grekas/sf5
Allow Symfony 5.0
2019-10-23 15:57:25 +02:00
Nicolas Grekas 90c1ee0bd0 Allow Symfony 5.0 2019-10-23 15:57:25 +02:00
Marco Pivetta cfcca3a63c Merge pull request #7600 from Majkl578/travis-php7.4-2.7
[2.7] CI: Test against PHP 7.4snapshot instead of nightly (8.0)
2019-10-23 15:57:24 +02:00
Michael Moravec af0949adab Merge pull request #7382 from Majkl578/homepage-2.7
Update homepage
2019-10-23 15:57:24 +02:00
Michael Moravec cdb652ad87 CI: Test against PHP 7.4snapshot instead of nightly (8.0) 2019-10-23 15:57:24 +02:00
Claudio Zizza 4fb1ebfc10 Create 2.7 upgrade headline for deprecation changes 2019-10-23 15:57:24 +02:00
Michael Moravec 46c1b57560 Update homepage 2019-10-23 15:57:24 +02:00
Claudio Zizza fdbbf7edd1 Add deprecation of EntityManagerInterface::copy() to upgrade information 2019-10-23 15:57:23 +02:00
Claudio Zizza 2fed8204c1 Set copy-method as deprecated 2019-10-23 15:57:23 +02:00
Michael Moravec 76f03b5db0 Bump version to 2.7-dev 2019-10-23 15:57:19 +02:00
Guilherme Blanco 9fef4e86e4 Merge pull request #7871 from BenMorel/2.6
AbstractQuery::getSingleScalarResult() throws exception when no result
2019-10-18 10:37:53 -04:00
Benjamin Morel 4781dc03e9 AbstractQuery::getSingleScalarResult() throws exception when no result 2019-10-16 20:41:00 +02:00
Benjamin Morel cc5f84ac22 UnitOfWork::clear() misses $eagerLoadingEntities 2019-10-16 10:11:55 +02:00
Marco Pivetta 023e94661a #7837 force expiry of query cache when WhereInWalker is being used
In order to figure out the paginated query identifier type, we would
have to parse the DQL query into an AST+SQL anyway, so we'd have
to re-parse it manually: instead of doing that, we can force the
`WhereInWalker` to be reached at all times by forcing the
`$whereInQuery` to use no query cache.

While it is a sad performance regression, it is also not a
noticeable one, since we'll be performing an `O(1)` operation
around an I/O one (query execution, in this case).
2019-10-10 18:23:31 +02:00
Marco Pivetta b59fc23f86 #7837 reproduced issue: DQL caching prevents WhereInWalker run
Since `WhereInWalker` does not run, query parameters are not translated
from their in-memory type to the expected SQL type when the paginator
is run again with the same DQL string. This is an architectural
issue, since (for the sake of simplicity) we moved parameter
translation into the SQL walker, we didn't consider that SQL
walkers only act when no cache is in place. The translatio
needs to be moved into the paginator logic again.
2019-10-10 17:30:43 +02:00
Luís Cobucci d71dd5d94f Bump up version 2019-10-08 20:04:50 +02:00
Luís Cobucci 63513e9a05 Merge pull request #7856 from lcobucci/fix/underscore-strategy-dont-work-with-numbers
Fix underscore naming strategy behaviour with numbers
2019-10-08 12:06:24 +02:00
Luís Cobucci c802bc46a5 Format NamingStrategyTest according to our CS 2019-10-08 11:56:11 +02:00
Luís Cobucci 506bf0ee12 Allow numbers in property names on underscore naming strategy 2019-10-08 11:56:11 +02:00
Luís Cobucci a36809db72 Merge pull request #7851 from peter-gribanov/reflFieldValue2.6
Remove not used variable $reflFieldValue in ObjectHydrator
2019-10-04 07:50:26 +02:00
Peter Gribanov 5b00d7ba5e remove not used variable $reflFieldValue in ObjectHydrator 2019-10-03 11:14:24 +03:00
Luís Cobucci b22604352d Merge pull request #7849 from axi/patch-1
Mention SQL logger impact on batch processing
2019-10-02 14:14:48 +02:00
axi 00c6b1bc60 Update batch-processing.rst
Clarify note
2019-10-02 14:00:06 +02:00
Luís Cobucci 4b0d86ee92 Merge pull request #7842 from vpArth-php/gh-7841
#7841 SchemaTool generates extra diff for platforms without FK support
2019-10-02 10:50:42 +02:00
Alexander Deider 3707c39124 #7841 SchemaTool generates extra diff for platforms without FK support 2019-10-02 15:35:59 +07:00
Luís Cobucci fe72b00df2 Merge pull request #7850 from nlx-lars/nlx-lars/bugfix/7836-dont-merge-criteria
Don't merge PersistentCollection orderBy with criteria in matching()
2019-10-02 10:02:38 +02:00
Lars Lauger 79a7ecc92f Don't merge PersistentCollection orderBy with criteria in matching()
If no orderings are given to PersistentCollection::matching(), the
orderBy annotation will be used if present. If the criteria contains
orderings, those will be used without merging them with the orderBy.

See #7836
2019-10-02 09:23:38 +02:00
Luís Cobucci 16df8bfe0d Merge pull request #7298 from dunglas/patch-2
Add a missing type in Query::getFirstResult PHPDoc
2019-10-02 04:27:19 +02:00
Kévin Dunglas b37ceaa9f7 Add a missing type in Query::getFirstResult and Query::getDQL 2019-10-02 04:13:42 +02:00
Luís Cobucci c41fdbce8a Merge pull request #7727 from madand/patch-1
[doc] Finish incomplete definition of class UTCDateTimeType
2019-10-02 04:11:20 +02:00
Luís Cobucci 7526adc80a Merge pull request #7443 from naitsirch/fix/issue6793
Added doc about exception in Query#getOneOrNullResult()
2019-10-02 04:07:25 +02:00
Andriy Kmit 766eb693fb Finish incomplete definition of class UTCDateTimeType 2019-10-02 03:57:06 +02:00
Luís Cobucci f9e2ae3488 Merge pull request #7667 from jschaedl/patch-1
Fixes example One-To-One, Self-referencing
2019-10-02 03:56:01 +02:00
Luís Cobucci 6bf2ff5d10 Merge pull request #7671 from jschaedl/patch-4
Added missing "the"
2019-10-02 03:45:50 +02:00
Jan Schädlich 27fcc01d81 Fixes example One-To-One, Self-referencing 2019-10-02 03:37:23 +02:00
Jan Schädlich 3ac1f8e680 Added missing "the" 2019-10-02 03:36:06 +02:00
Luís Cobucci b63db53552 Merge pull request #7764 from guillaume-a/7763
#7763 escape quotes in field comments
2019-10-02 02:56:05 +02:00
Guillaume Aveline bed8186573 Fix comment quoting in the EntityGenerator
Fixes: https://github.com/doctrine/orm/issues/7763
2019-10-02 02:42:09 +02:00
Luís Cobucci f08ff83d0a Merge pull request #7768 from mickaelandrieu/patch-1
EntityManagerHelper can't accept an array of paths
2019-10-01 22:51:13 +02:00
axi 7c8c0906be Update batch-processing.rst
Looking for a way to improve one of our bulk update treatment, I went back to this page then found elsewhere that setting logger to null was a really effective way to improve time and memory consumption. Might be a right place to state it ? Don't know if my edit style is ok
2019-10-01 17:46:09 +02:00
Grégoire Paris 167cb44ea1 Merge pull request #7742 from bocharsky-bw/patch-1
Start i var from 1 instead of 0
2019-09-28 18:54:56 +02:00
Mickaël Andrieu 5d74bdb240 Remove misleading documentation
EntityManagerHelper does not have a second argument, see
https://github.com/doctrine/orm/blob/ca38249f6c71bc1d1c355822040a1788e3eeebd6/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php#L49
2019-09-28 12:32:09 +02:00
Luís Cobucci ca38249f6c Merge pull request #7838 from samnela/fix/name-classmetadata
Fix the name of ClassMetadata in documentation
2019-09-27 22:31:49 +02:00
Samuel NELA 6a74f373b9 Fix the name of ClassMetadata in documentation 2019-09-27 21:59:32 +02:00
Luís Cobucci b52ef5a100 Merge pull request #7322 from dennisenderink/fix/joinedsubclasspersister-pass-identifier-types-on-delete
JoinedSubclassPersister pass identifier types on delete
2019-09-20 16:30:26 +02:00
Luís Cobucci ef783f7049 Make use of the PersisterHelper to get field type
Removing the unnecessary code duplication.
2019-09-20 16:17:43 +02:00
Luís Cobucci 435d624d33 Centralise functional test classes
Which provides better isolation and makes things a bit more
understandable.
2019-09-20 16:17:43 +02:00
Dennis Enderink 53775fe086 Added correct return types to docblock 2019-09-20 16:17:43 +02:00
Dennis Enderink 59f1679fed Patched spacing 2019-09-20 16:17:43 +02:00
Dennis Enderink 390d081fca Created new DBAL type and updated/reverted tests; also clarified new method return type 2019-09-20 16:17:43 +02:00
Dennis Enderink 37d1d57900 Added unit tests 2019-09-20 16:17:42 +02:00
Dennis Enderink d7a537c941 Pass ClassMetadata object instead of string in parent classes loop 2019-09-20 16:17:42 +02:00
Dennis Enderink cfe73cd74f Separated class identifiers types retrieval in a separate method and implemented in JoinedSubclassPersister delete method 2019-09-20 15:40:03 +02:00
Luís Cobucci d0e1da8c51 Merge pull request #7490 from vladyslavstartsev/patch-2
Fix broken link
2019-09-20 15:32:13 +02:00
vladyslavstartsev 7fbe663ea0 Fix broken link
closing #7489
2019-09-20 15:31:12 +02:00
Luís Cobucci 409f2f5d82 Merge pull request #7672 from jschaedl/patch-5
Added cross-links to relevant documentation
2019-09-20 15:30:14 +02:00
Luís Cobucci 3d8b672771 Merge pull request #7610 from smtchahal/patch-1
Change APC to OPcache in improving-performance.rst
2019-09-20 15:29:07 +02:00
Jan Schädlich 17650a6100 Added cross-links to relevant documentation 2019-09-20 15:19:49 +02:00
Luís Cobucci 1588ca7e1f Merge pull request #7821 from Ocramius/bug/#7820-paginator-ignores-dbal-type-conversions-in-identifiers
Bug: #7820 paginator ignores dbal type conversions in identifiers
2019-09-20 15:13:58 +02:00
Marco Pivetta 0de17319d3 #7820 use PersisterHelper to figure out identifier types
This logic was pre-existing, but I forgot about it while writing
#7820, therefore it was re-implemented inside this unit of
code. Now we just use the `PersisterHelper`, which does all
the nice and shiny identifier type discovery operations we need.
2019-09-20 15:03:22 +02:00
Marco Pivetta 681ff32e76 #7820 documented PersisterHelper#getTypeOfField() array return type
Array values are `string`, and the array is a packed array.
2019-09-20 14:57:44 +02:00
Marco Pivetta caee6c8685 #7820 restricted return type of Doctrine\ORM\Mapping\ClassMetadataInfo#getTypeOfColumn()
This method will always return `string|null`, so we can safely
remove DBAL types from its possible return types.
2019-09-20 14:51:56 +02:00
Marco Pivetta c67a515cc2 As per discussion with @lcobucci, it is better to keep dragons where
there be dragons, and this change does indeed rewrite the previous
approach by moving the responsibility of type conversion on a query
object from the `Paginator` to the `WhereInWalker`, which already
has access to class metadata for the root of the selection (and can
reliably detect the root of the selection too)
2019-09-19 20:05:34 +02:00
Luís Cobucci 24892779f7 Merge pull request #7818 from SenseException/simple-annotation-docs
Add note into docs about not using SimpleAnnotationReader
2019-09-18 10:49:43 +02:00
Marco Pivetta 39d2113549 Fixed #7820 - convert identifiers for WHERE IN(?) queries before binding parameters
This patch introduces new internal API on the `ResultSetMapping` class, which is responsible
for finding the type of the single column identifier of a DQL query selection root.
2019-09-17 16:54:25 +02:00
Marco Pivetta 65522d9775 Failing test case for #7820 - paginator doesn't use custom ID types
When using a `Doctrine\ORM\Tools\Pagination\Paginator` to iterate over a query that has entities with a custom DBAL type used in the identifier, then `$id->__toString()` is used implicitly by PDO, instead of being converted by the `Doctrine\DBAL\Types` system.

In order to reproduce this, you must have identifiers implementing `#__toString()` (to allow the `UnitOfWork` to hash them) and other accessors that are used by the custom DBAL type during DB/PHP conversions. If `#__toString()` and the DBAL type conversions are asymmetric, then the paginator will fail to find records.

Tricky situation, but this very much affects `ramsey/uuid-doctrine` and anyone relying on the `uuid_binary`.
2019-09-17 11:37:50 +02:00
Claudio Zizza 50eecf698c Add note into docs about not using SimpleAnnotationReader 2019-09-15 22:50:46 +02:00
Luís Cobucci 20ab78e3c1 Merge pull request #7753 from SenseException/getting-started-annotation
Add ORM annotations in getting-started docs
2019-09-12 17:01:53 +02:00
Luís Cobucci 613ffe9bbd Backport documentation sidebar 2019-09-10 16:31:41 +02:00
Luís Cobucci 61ff45f98e Merge pull request #7785 from mlocati/php74-fixes
Fix "access array offset on value of type null" PHP 7.4 notices
2019-09-10 16:08:49 +02:00
Luís Cobucci a8aa475d09 Add PHP 7.4 to test matrix 2019-09-10 15:48:05 +02:00
Luís Cobucci a4215cfa59 Update locked dependencies 2019-09-10 15:48:04 +02:00
Luís Cobucci a4ac9a721f Upgrade PHPUnit to 7.5 2019-09-10 15:48:03 +02:00
Michele Locati 447183e235 Fix "access array offset on value of type null" PHP 7.4 notices 2019-09-10 15:47:57 +02:00
Luís Cobucci 642e543b4b Merge pull request #7778 from umpirsky/fix/issue-7266
Guard L2C regions against corrupted data
2019-08-14 18:07:58 +02:00
Luís Cobucci 80503c4837 Guard cache regions against corrupted data
For some bizarre reason the underlying cache drivers are returning
unexpected values, which are leaking to the cache objects and causing
them to error.

This makes our cache regions much more strict about the types that are
fetched from the cache provider, ensuring that no invalid information is
ever sent to the hydrators.
2019-08-14 17:42:56 +02:00
Luís Cobucci 3577064f8c Make closure static
To adhere to our coding standard.
2019-08-14 17:42:56 +02:00
Luís Cobucci b6663733c0 Add type assertion to be more strict about persister type 2019-08-14 17:42:56 +02:00
Luís Cobucci b9d6834213 Remove unnecessary function calls 2019-08-14 17:42:56 +02:00
Luís Cobucci eafc4c5a0c Remove unnecessary parentheses 2019-08-14 17:42:56 +02:00
Saša Stamenković ecf80b47a0 Call to a member function resolveAssociationEntries() on boolean
The following mistakes occur occasionally:

```
Call to a member function resolveAssociationEntries() on boolean {"detail":"[object] (Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Call to a member function resolveAssociationEntries() on boolean at /www/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php:140)"}
```

On cache miss the parameter `$entityEntry` sometimes will be false. This fixes issue #7266.
2019-08-14 17:42:56 +02:00
Luís Cobucci 5499555862 Merge pull request #7791 from Steveb-p/fix-docs
Fix preFlush event documentation stating incorrectly that flush can be called safely
2019-08-14 14:03:13 +02:00
Paweł Niedzielski 70df74f65f Fix preFlush event documentation stating incorrectly that flush can be called safely
Original author: egonolieux
Supersedes #6858
2019-08-14 13:41:31 +02:00
Luís Cobucci 74415becce Merge pull request #7737 from Smartel1/smartel1/patch1
Fix MEMBER_OF comparison when using criteria in query builder
2019-08-14 10:07:11 +02:00
drews 3a56cf8ad9 Add MEMBER_OF comparison to queryExpressionVisitor 2019-08-14 09:47:20 +02:00
Luís Cobucci 6b7f53f0f3 Merge pull request #7766 from stephanschuler/respect-collection-ordering-in-selectable-matching
Respect collection orderBy meta when matching()

Fixes https://github.com/doctrine/orm/issues/7767
2019-08-13 23:42:32 +02:00
Stephan Schuler e51666e8be Fix PersistentCollection::matching() not respecting collection ordering
The ordering of a Criteria is prefered over the collections default
ordering.

The default collection ordering used as additional sorting attributes.
2019-08-13 23:34:54 +02:00
Luís Cobucci 6e56bcd75f Merge pull request #7750 from AlexSmerw/issue_7735_null_values_in_entities_cache_for_2.6
Fix incorrect return of null values in L2C
2019-08-12 01:28:20 +02:00
Luís Cobucci 48bfef1f7a Merge pull request #7761 from paxal/persistent_collection/deferred_explicit_2.6
Do not modify UOW on PersistentCollection::clear() when owner has DEFFERED_EXPLICIT change tracking policy
2019-08-12 01:21:21 +02:00
A.Kuterev e8f91434a7 Avoid reusing variable name
The same variable name is used below, and that causes a bug etc.
Fixes https://github.com/doctrine/orm/issues/7735
2019-08-12 01:18:48 +02:00
Luís Cobucci 7e26d82790 Merge pull request #7794 from lcobucci/fix-compatibility-with-dev-dependencies
Fix test compatibility with DBAL 2.10.x-dev
2019-08-12 00:00:45 +02:00
Luís Cobucci 869b70e4db Use Ubuntu Xenial for MySQL 5.7 build
Since July 21st 2019 it's no longer possible to install MySQL 5.7 in
Ubuntu Trusty.

More info: https://docs.travis-ci.com/user/database-setup/#mysql-57
2019-08-11 23:41:32 +02:00
Luís Cobucci 33904cb9c1 Fix test compatibility with DBAL 2.10.x-dev 2019-08-11 23:41:32 +02:00
Cyril PASCAL a42191eecf Add functional test for ArrayCollection::clear() bug 2019-07-19 16:21:12 +02:00
Cyril PASCAL 3fbf163d34 Do not modify UOW on PersistentCollection::clear() when owner has DEFFERED_EXPLICIT change tracking policy 2019-06-26 16:07:15 +02:00
naitsirch 1c45e1b744 Fixed grammatical mistake in doc
Co-Authored-By: Grégoire Paris <postmaster@greg0ire.fr>
2019-06-24 22:07:56 +02:00
Claudio Zizza c777aa62b6 Fix of ORM annotation in examples 2019-06-23 23:39:41 +02:00
Luís Cobucci 6296bd4e1d Merge pull request #7744 from noobshow/patch-1
Fixed a typo-error
2019-06-18 08:30:35 +02:00
Luís Cobucci 5a236c19f5 Merge pull request #7731 from greg0ire/try-mysql-addon
Replace custom install script with add-on
2019-06-17 23:33:04 +02:00
Olumide Samson 4f8a1f92a3 Fixed a typo-error
exploitet changed to exploited
2019-06-17 09:00:45 +01:00
Victor Bocharsky 5612790307 Start i var from 1 instead of 0
Because (0 % $batchSize) === 0 but we don't want to execute flush() and clear() on the first iteration.
2019-06-11 13:19:56 +03:00
Jonathan H. Wage 0b5be00374 Merge pull request #7732 from lchrusciel/patch-1
[Documentation] Missing comma fix
2019-06-04 11:11:46 -05:00
Łukasz Chruściel 145cc782ff [Documentation] Missing comma fix 2019-06-04 17:38:26 +02:00
Jonathan H. Wage 9712506be8 Merge pull request #7729 from JoppeDC/patch-1
Update DATE_ADD and DATE_SUB docs
2019-06-04 10:23:15 -05:00
Grégoire Paris bd9ead11c5 Replace custom install script with add-on
Following this documentation:
https://docs.travis-ci.com/user/database-setup/#mysql-57
found via https://stackoverflow.com/a/49542847/353612
2019-06-03 22:12:54 +02:00
Joppe de Cuyper a98ebf7344 Whitespace fixes 2019-06-03 20:21:19 +02:00
Joppe de Cuyper c721ab63ee Update DATE_ADD and DATE_SUB docs 2019-06-03 20:20:03 +02:00
Jonathan H. Wage 2820438afc Merge pull request #7694 from darrylhein/patch-1
Change variable name in docs
2019-04-29 11:00:10 -05:00
Darryl Hein 180cfcc3e3 change variable name
to make it consistent throughout document
2019-04-28 21:07:22 -06:00
Marco Pivetta 52d806a34a Merge pull request #7612 from spirlici/patch-1
Update ordered-associations.rst
2019-03-12 00:16:09 +01:00
Marco Pivetta 49a8f2ec96 Merge pull request #7630 from yethee/gh-7629
Fix #7629 - `scheduledForSynchronization` leaks memory when using `@ORM\ChangeTrackingPolicy("DEFERRED_EXPLICIT")`
2019-03-01 21:24:57 +01:00
yethee 7f5f4629e5 Ensure state is cleanup after empty commit 2019-03-01 23:00:01 +03:00
yethee d91e0b3867 Failing tests 2019-02-28 17:12:54 +03:00
Sergiu Pirlici b537758b32 Update ordered-associations.rst
Fixed some typos
2019-02-16 15:04:45 +02:00
Sumit Chahal 2ba6e473de Change APC to OPcache in improving-performance.rst
OPcache is a lot better supported now than APC,
which is apparently not maintained anymore.
2019-02-16 16:44:21 +05:30
Marco Pivetta de97061d65 Merge pull request #7596 from mbessolov/patch-1
Correct method names and broken link in docs
2019-02-09 15:47:17 +01:00
Michael Bessolov 624ee78081 Correct method names and broken link in docs
This basically applying the same documentation fix as in #7335 (master) to 2.6 branch
2019-02-08 22:29:10 -08:00
Marco Pivetta e003bb2bb4 Merge pull request #7577 from SenseException/advanced-config-link
Fix of single link to dbal docs in advanced-configuration.rst
2019-01-23 07:48:12 +01:00
Claudio Zizza 5c5f310646 Fix of link to dbal docs 2019-01-22 22:42:13 +01:00
Marco Pivetta c10433e512 Merge pull request #7572 from SenseException/remove-codeigniter
Remove codeigniter Framework example
2019-01-17 22:35:58 +01:00
Claudio Zizza 580c530041 Remove codeigniter example from docs 2019-01-17 21:54:33 +01:00
Marco Pivetta 4d461afbd6 Merge pull request #7571 from batwolf/patch-1
Fix typo in inheritance mappings docs
2019-01-17 21:42:45 +01:00
Marco Pivetta 536e31f343 Merge pull request #7385 from SenseException/links-and-typos
Update information and links of documentation
2019-01-17 21:11:50 +01:00
Marc Plotz c6eb4df25e fix typo
`has to allow null values` vs `has to allows null values`
2019-01-17 10:14:56 +01:00
Claudio Zizza aae00e3987 Fix of links and php version after review 2019-01-07 14:40:26 +01:00
Claudio Zizza b56800b15c Fix of links and anchors 2019-01-07 14:40:17 +01:00
Claudio Zizza be461be36b Update getting help section 2019-01-07 14:40:10 +01:00
Claudio Zizza 85171a9490 Fix of reStructuredText format 2019-01-07 14:40:02 +01:00
Claudio Zizza f5b9f2052a Update MySQL links to current version 2019-01-07 14:39:54 +01:00
Claudio Zizza 3d652997d1 Remove changelog from documentation
This was removed in favour of the UPGRADE.md file, which
contains the changelog information needed.
2019-01-07 14:39:42 +01:00
Marco Pivetta 10393dca68 Merge pull request #7557 from doctrine/malarzm-patch-1
Change Stackoverflow tag to doctrine-orm
2019-01-05 17:48:41 +01:00
Maciej Malarz 597bfaea03 Change Stackoverflow tag to doctrine-orm 2019-01-04 22:20:24 +01:00
Jonathan H. Wage 98b8ced814 Merge pull request #7551 from Majkl578/repo-rename/2.6
[2.6] Migrate repository name doctrine/doctrine2 -> doctrine/orm
2019-01-03 17:18:59 -06:00
Michael Moravec efaee8ce85 Migrate repository name doctrine/doctrine2 -> doctrine/orm 2019-01-03 09:07:03 +01:00
Luís Cobucci 6e93f5bb72 Merge pull request #7528 from Ocramius/fix/#7527-prevent-unit-of-work-lookup-for-known-value-types
Fix #7527:  prevent `UnitOfWork` lookup for DBAL types specified in `Doctrine\ORM\Query#setParameter()`
2018-12-21 21:54:20 +01:00
Marco Pivetta a41f5673bc #7527 automated CS checks 2018-12-20 22:59:46 +01:00
Marco Pivetta ca436f0bae #7527 performance benchmark - verifying performance impact of inferred query parameter types
As an example result:

```
./phpbench.phar run tests/Doctrine/Performance/Query --iterations=50 --revs=50 --report=aggregate
PhpBench 0.15-dev (dcbe193). Running benchmarks.
Using configuration file: /home/ocramius/Documents/doctrine/doctrine2/phpbench.json

\Doctrine\Performance\Query\QueryBoundParameterProcessingBench

    benchExecuteParsedQueryWithInferredParameterTypeI49 P0 	[μ Mo]/r: 643.684 634.664 (μs) 	[μSD μRSD]/r: 17.700μs 2.75%
    benchExecuteParsedQueryWithDeclaredParameterTypeI49 P0 	[μ Mo]/r: 97.673 94.251 (μs) 	[μSD μRSD]/r: 8.259μs 8.46%

2 subjects, 100 iterations, 100 revs, 0 rejects, 0 failures, 0 warnings
(best [mean mode] worst) = 88.460 [370.679 364.458] 127.400 (μs)
⅀T: 37,067.880μs μSD/r 12.980μs μRSD/r: 5.603%
suite: 133f0e30090f815142331ebec6af18241694e7c0, date: 2018-12-19, stime: 10:47:10
+------------------------------------+--------------------------------------------------+--------+--------+------+-----+------------+-----------+-----------+-----------+-----------+----------+--------+-------+
| benchmark                          | subject                                          | groups | params | revs | its | mem_peak   | best      | mean      | mode      | worst     | stdev    | rstdev | diff  |
+------------------------------------+--------------------------------------------------+--------+--------+------+-----+------------+-----------+-----------+-----------+-----------+----------+--------+-------+
| QueryBoundParameterProcessingBench | benchExecuteParsedQueryWithInferredParameterType |        | []     | 50   | 50  | 5,970,568b | 604.680μs | 643.684μs | 634.664μs | 677.640μs | 17.700μs | 2.75%  | 6.59x |
| QueryBoundParameterProcessingBench | benchExecuteParsedQueryWithDeclaredParameterType |        | []     | 50   | 50  | 5,922,424b | 88.460μs  | 97.673μs  | 94.251μs  | 127.400μs | 8.259μs  | 8.46%  | 1.00x |
+------------------------------------+--------------------------------------------------+--------+--------+------+-----+------------+-----------+-----------+-----------+-----------+----------+--------+-------+
```

This indicates that the performance impact for NOT declaring parameter types
explicitly is *MASSIVE*.
2018-12-19 10:52:11 +01:00
Marco Pivetta d8212e8dd6 Merge pull request #7530 from vladyslavstartsev/patch-3
Documentation error fix
2018-12-17 16:00:44 +01:00
vladyslavstartsev 12eb9f42dc Documentation error fix 2018-12-16 20:33:21 +02:00
Marco Pivetta 23af164d7a Note: this will still lead to the UnitOfWork#getSingleIdentifierValue() still being
called when not specifying the type of a DQL parameter being bound via
`Doctrine\ORM\Query#setParameter()`:

```php
$query->setParameter('foo', $theValue, $theType);
```

A full parameter bind is required in order to gain back performance:

```php
$query->setParameter('foo', $theValue, $theType);
```

This is up for discussion with patch reviewers.
2018-12-16 18:05:02 +01:00
Marco Pivetta 960a437d46 #7527 failing test case: UnitOfWork#getSingleIdentifierValue() should not be called for a well specified parameter type
As previously reported by @flaushi in https://github.com/doctrine/doctrine2/pull/7471#discussion_r241949045, we discovered
that binding a parameter causes a `ClassMetadataFactory#getClassMetadata()` call, which in turn leads to large performance
regression when using any `object` type as parameter.

Following two snippets lead to an internal `ClassMetadataFactory#getClassMetadata()` call, which in turn leads to an
exception being thrown and garbage collected, plus multiple associated performance implications:

```php
$query->setParameter('foo', new DateTime());
$query->getResult();
```

```php
$query->setParameter('foo', new DateTime(), DateTimeType::NAME);
$query->getResult();
```

This is due to following portion of code:

https://github.com/doctrine/doctrine2/blob/434820973cadf2da2d66e7184be370084cc32ca8/lib/Doctrine/ORM/Query.php#L406-L409

Notice how `$value = $this->processParameterValue($value);` happens before attempting to infer the type for the parameter value.

That call leads to this segment being reached, which leads to the regression:

https://github.com/doctrine/doctrine2/blob/434820973cadf2da2d66e7184be370084cc32ca8/lib/Doctrine/ORM/AbstractQuery.php#L423-L433

Assuming the bound parameter type is provided, we can completely skip attempting to introspect the given object:

```php
$query->setParameter('foo', new DateTime(), DateTimeType::NAME);
$query->getResult();
```

Processing the parameter value is not needed in this case, so we can safely skip that logic for all known parameters.
In order to not introduce a BC break or change the `AbstractQuery#processParameterValue()` implementation, we could filter
out all parameters for which the type is given upfront, and later on merge them back in instead.

The test expectation to be set is for `UnitOfWork#getSingleIdentifierValue()` to never be called.
2018-12-16 15:37:45 +01:00
Marco Pivetta 237bebe2ed Merge pull request #7519 from koftikes/fix/#7518-phpdoc-error
#7518 Fixed type mismatch between `EntityRepository#__construct()` and its documented constructor arguments
2018-12-13 08:14:30 +01:00
Jonathan H. Wage fc3dca772e Merge pull request #7521 from doctrine/update-chat-link
Update chat link from Gitter to Slack.
2018-12-12 20:07:31 +00:00
Konstantin Litvinov ee64d31f48 7518 Fixed PHPDoc Error. 2018-12-12 17:08:35 +03:00
Michael Moravec 493ff74a0d Merge pull request #7473 from Majkl578/incremental-cs-2.x
Incremental CS checks in 2.x branches
2018-12-10 14:43:55 +01:00
Michael Moravec 78c7000962 Lock dependencies for Code Quality stage 2018-12-10 13:58:51 +01:00
Michael Moravec 6a05e01298 Perform incremental coding standard checks for pull requests 2018-12-10 13:58:51 +01:00
Gabriel Ostrolucký 7de3434733 Update doctrine/coding-standard in 2.x branch
Co-Authored-By: Michael Moravec <me@majkl.me>
2018-12-10 13:58:51 +01:00
Luís Cobucci 74e6189f3e Merge pull request #7483 from javiereguiluz/patch-9
Fixed a minor syntax issue
2018-11-21 10:48:33 +01:00
Javier Eguiluz 2e7a3affba Fixed a minor syntax issue 2018-11-21 09:06:54 +01:00
Luís Cobucci 505ec21f97 Bump up development version 2018-11-21 01:24:06 +01:00
Luís Cobucci 434820973c Bump up version 2018-11-21 00:46:46 +01:00
Luís Cobucci 41ff526921 Merge pull request #6830 from Tobion/fix-collation-foreign-key
fix applying column options on foreign key columns
2018-11-21 00:41:17 +01:00
Luís Cobucci 0be52b0087 Isolate entities used by the new test
To ensure we don't have any unintended side-effect.
2018-11-21 00:20:20 +01:00
Tobias Schultze ee8dc496d9 Fix applying collation on foreign key columns 2018-11-21 00:20:15 +01:00
Luís Cobucci f80656cddf Merge pull request #7317 from protecinnovations/fix/7316-xml-order-by-dir-many-to-many
[XML] Fix default value of many-to-many order-by to ASC
2018-11-20 13:11:22 +01:00
Alex Denvir 72121c01ec [XML] Fix default value of many-to-many order-by to ASC 2018-11-20 12:33:29 +01:00
Luís Cobucci ac505390dd Merge pull request #7472 from seferov/patch-2
fix incorrect phpdoc typehint
2018-11-20 09:41:01 +01:00
Luís Cobucci 728e6e15c5 Merge pull request #7441 from asgrim/fix-getResult-type
$hydrationMode throughout can be a string as well as int (for custom modes)
2018-11-20 09:40:04 +01:00
Luís Cobucci d21305378c Merge pull request #7471 from alcaeus/fix-unloaded-metadata-parameter-processing
Fix parameter value processing for objects with unloaded metadata
2018-11-15 11:34:31 +01:00
Andreas Braun 0552749059 Fix parameter value processing for objects with unloaded metadata 2018-11-15 11:21:05 +01:00
Farhad Safarov fbd3fe95e4 fix incorrect phpdoc typehint 2018-11-13 13:01:10 +03:00
James Titcumb c6d02daee0 $hydrationMode throughout can be a string as well as int (for custom modes) 2018-11-12 13:58:42 +00:00
Luís Cobucci 5208035003 Merge pull request #7444 from naitsirch/fix/issue6968
Fixed URLs of doctrine-mapping.xsd in docs
2018-11-12 11:40:01 +01:00
Luís Cobucci d93956eff0 Use HTTPS endpoint for XML schema location 2018-11-12 11:29:32 +01:00
naitsirch b3b06d3e7d Fixed URLs of doctrine-mapping.xsd in docs
Until now the references to the `doctrine-mapping.xsd` consisted of different URLs.

A grep of docs showed:
* /Users/robo/dev/php/Doctrine/doctrine-mapping.xsd
* http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd
* http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd
* https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd

Now it is used http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd everywhere.
2018-11-12 11:09:15 +01:00
Michael Moravec 427f815975 Merge pull request #7465 from unguul/patch-1
Fixes tiny typo in the 'Working with DateTime instances' documentation
2018-11-11 23:24:29 +01:00
Michael Moravec bf601ce268 Merge pull request #7421 from seferov/patch-1
JIRA to Github issues on Limitations and Known Issues
2018-11-11 23:24:00 +01:00
Michael Moravec 8bfb363fcc Merge pull request #7434 from naitsirch/fix/doc-faq-public-property
Removed FAQ paragraph stating public variables are disallowed
2018-11-11 23:22:58 +01:00
Michael Moravec ebf2630a66 Merge pull request #7435 from oguzdumanoglu/patch-2
Fix a typo on Documentation
2018-11-11 23:22:25 +01:00
Michael Moravec 9018955e1f Merge pull request #7412 from ThomasLandauer/patch-1
Some formatting improvements
2018-11-10 21:05:50 +01:00
Thomas Landauer 88d58ae0a3 Some formatting improvements 2018-11-10 20:45:03 +01:00
Michael Moravec 2fc99afd44 Merge pull request #7423 from ThomasLandauer/patch-2
Update association-mapping.rst
2018-11-10 20:40:44 +01:00
Michael Moravec fa0885e25d Merge pull request #7374 from SenseException/deprecate-yaml-docs
Deprecation message in documentation for YAML
2018-11-10 20:33:51 +01:00
Alexandru Ungureanu 0e4a0108d2 Fixes small typo 2018-11-08 13:59:21 +02:00
naitsirch 17bc627bf2 Added hint about exception in Query#getOneOrNullResult()
When calling `Query#getOneOrNullResult()` and there are more than one
objects in the result an `NonUniqueResultException` is thrown.
This information was missing in the documentation about the query result
formats.

This commit addresses #6793.
2018-10-29 21:26:02 +01:00
Oguz Dumanoglu 58370256c0 Fix a typo
There was a typo in Working with Associations page.
2018-10-19 16:32:27 +02:00
naitsirch d5364231c2 Removed FAQ paragraph stating public variables are disallowed
In #7427 @flaushi mentioned the outdated paragraph. This commit removes
this one.
2018-10-18 22:36:29 +02:00
Luís Cobucci 4df3a4d436 Merge pull request #7428 from Majkl578/php7.3
CI: Test against PHP 7.3
2018-10-14 09:39:26 +02:00
Michael Moravec 812989490c CI: Test against PHP 7.3 2018-10-13 20:33:26 +02:00
Thomas Landauer 892ef9edb7 Update association-mapping.rst
Added info about owning and inverse side.
2018-10-09 21:33:42 +02:00
Farhad Safarov 982782f8c9 JIRA to Github issues 2018-10-09 11:52:22 +03:00
Michael Moravec 7319f524a3 Merge pull request #7397 from eibt/patch-1
Update getting-started.rst
2018-09-23 07:04:34 +02:00
Michael Moravec 1d71fbf77b Merge pull request #7367 from timdev/fix/entitymanager-find-with-optimistic-lock-no-need-tx
Fix for BC break in 2.6.2 when calling EM::find() with LockMode::OPTIMISTIC outside of a TX
2018-09-23 06:43:26 +02:00
Ivan 7eacfec2c3 Fix typo in getting-started.rst 2018-09-23 06:39:11 +02:00
Michael Moravec 46f2a41cf7 Merge pull request #7377 from sserbin/fix-query-andx-doctype
Fix query andX doctype
2018-09-23 06:37:42 +02:00
Michael Moravec fd2baf6f65 Merge pull request #7260 from stof/regression_commit_order
Fix the handling of circular references in the commit order calculator
2018-09-23 05:44:14 +02:00
Michael Moravec c8bf06d549 Merge pull request #7401 from bobdenotter/patch-1
[docs] Fix docblock in `inheritance-mapping.rst`
2018-09-23 05:34:59 +02:00
Tim Lieberman 3acfa50214 Fix for BC break #7366 when calling EM::find() with LockMode::OPTIMISTIC outside of a TX 2018-09-23 05:33:05 +02:00
sserbin 3dbe205498 Query\Expr::andX(): added string as allowed parameter type 2018-09-23 05:23:49 +02:00
Michael Moravec 899cce8094 Merge pull request #7363 from philippe-unitiz/2.6
Fix compatibility with phan
2018-09-23 05:16:52 +02:00
Bob den Otter 7400d51444 Fix docblock in inheritance-mapping.rst 2018-09-23 05:12:55 +02:00
Michael Moravec 96c344d22b Merge pull request #7345 from guilliamxavier/improve-DOMDocument-construct
Correct DOMDocument constructor in test
2018-09-23 05:11:21 +02:00
Michael Moravec f48d71ecd0 Merge pull request #7378 from BenMorel/patch-2
Typo fix
2018-09-23 05:05:58 +02:00
philippe-unitiz d3acbbf79b Fix constructor argument type in Query\Base 2018-09-23 05:02:30 +02:00
philippe-unitiz cb9ec8234b Fix multiline parameter phpDoc in Query\Expr
See https://github.com/phan/phan/issues/1897 (parser won't accept `@param` spanning over several lines)
2018-09-23 05:02:01 +02:00
Guilliam Xavier 47c72e583e correct load-only DOMDocument constructor in test 2018-09-23 04:52:52 +02:00
Christophe Coevoet 568c2d308c Fix the computation of commit order for circular dependencies
When finding a circular dependencies, we must ensure that all dependencies
of a node have been visited before adding it to the sorted list.
2018-09-20 12:13:25 +02:00
Christophe Coevoet 11a7f359d1 Add a unit test reproducing the commit order regression 2018-09-20 12:11:44 +02:00
Christophe Coevoet 145f1f5198 Add a test reproducing GH7259 2018-09-20 11:53:08 +02:00
Claudio Zizza ff1df41485 Add deprecation note to getting-started chapter 2018-09-07 21:06:08 +02:00
Claudio Zizza d36aec8fb7 Add deprecation message for YAML into docs 2018-08-30 23:24:30 +02:00
Benjamin Morel 2779b5ee91 Typo fix 2018-08-28 14:56:01 +02:00
Pierre-Louis FORT 32efbd3edd Handle removed parameters by tree walker in Paginator 2018-08-27 11:23:21 +02:00
Luís Cobucci 68718eac1b Merge pull request #7360 from lcobucci/fix-partial-reference-docblock
Document getPartialReference() properly
2018-08-19 16:25:51 +02:00
Luís Cobucci 7b64b4a207 Document getPartialReference() properly
According to the current implementation that method also returns `null`,
however the interface's documentation was incorrect.

Ref: https://github.com/doctrine/doctrine2/blob/v2.6.2/lib/Doctrine/ORM/EntityManager.php#L514-L516
2018-08-18 14:52:17 +02:00
Michael Moravec f1143f591f Merge pull request #7325 from paxal/php73_compatible
Make code php 7.3 lint-compatible
2018-08-02 14:23:28 +02:00
Cyril PASCAL 07fc401d25 Make code php 7.3 lint-compatible 2018-07-26 14:32:52 +02:00
Luís Cobucci 96f166a7e9 Merge pull request #7307 from Majkl578/remaining-deprecations
Fix remaining usages of deprecated ClassLoader and Inflector from doctrine/common
2018-07-13 07:27:10 +02:00
Michael Moravec f4b775323d Fix remaining usages of deprecated ClassLoader and Inflector from doctrine/common 2018-07-13 05:31:39 +02:00
Michael Moravec 43d308116d Bump version to 2.6.3-DEV 2018-07-12 23:24:26 +02:00
Michael Moravec d2b4dd71d2 Preparing v2.6.2 release 2018-07-12 22:47:13 +02:00
Luís Cobucci 36e6a73d5b Merge pull request #7296 from Majkl578/fix/2.6/#7286
Fix #7286: StringPrimary no longer accepts aggregate functions as argument
2018-07-10 00:05:51 +02:00
Michael Moravec e26158a45e Fix #7286: StringPrimary no longer accepts aggregate functions as argument 2018-07-09 19:12:39 +02:00
Marco Pivetta 3cfcd6a856 Merge pull request #7291 from Majkl578/fix/2.6/#7068
[2.6] Fix for #7068: EntityManager::find() with pessimistic lock should check for transaction
2018-07-03 09:40:48 +02:00
Michael Kühn ff68806bfa Fix for #7068: EntityManager::find() with pessimistic lock should check for transaction 2018-07-03 03:00:58 +02:00
Michael Moravec 4192c3abf4 Merge pull request #7290 from Majkl578/dbal-2.8-tests-compat
Fix compatibility with DBAL 2.8 (doctrine/dbal#3157)
2018-07-03 02:58:02 +02:00
Michael Moravec ac1e1c7d23 Fix compatibility with DBAL 2.8 where OFFSET 0 is no longer generated (doctrine/dbal#3157) 2018-07-03 02:14:23 +02:00
Luís Cobucci 9ab999618c Merge pull request #7276 from Majkl578/entityrepository-count-upgrade
Add UPGRADE note for EntityRepository::count()
2018-07-03 02:05:46 +02:00
Michael Moravec f2666a472f Add UPGRADE note for EntityRepository::count() 2018-06-27 20:41:59 +02:00
Luís Cobucci ceda5d3bc7 Merge pull request #7274 from Majkl578/non-deprecated-lexer-and-inflector
Use non-deprecated version of Lexer and Inflector
2018-06-25 23:56:32 +02:00
Michael Moravec 6d81d519b6 Use non-deprecated version of Lexer and Inflector 2018-06-25 14:20:52 +02:00
Marco Pivetta 88d1d79516 Merge pull request #7253 from JarJak/patch-2
Mention that Doctrine does not use Entities public API
2018-06-09 07:28:20 +02:00
Jarek Jakubowski cfc6cfd1a3 Unnecessary newline removed, small improvements in text 2018-06-09 00:29:59 +02:00
Jarek Jakubowski 6b7d67b427 Add info about Doctrine not using constructor 2018-06-08 20:29:37 +02:00
Jarek Jakubowski b6d08b15c0 Mention that Doctrine does not use Entities public API 2018-06-08 18:47:39 +02:00
Marco Pivetta 01f89a8cdc Merge pull request #7190 from Tobion/patch-1
Fix wrong type in phpdoc of AbstractIdGenerator
2018-04-13 16:29:25 +01:00
mikeSimonson efd7a5dca6 Merge pull request #7146 from Awkan/fix/7141-xml-order-by-default-asc
[XML] Fix default value of one-to-many order-by to ASC
2018-04-12 22:29:41 +02:00
Tobias Schultze 7ba0290643 entity should be nullable as in master 2018-04-10 19:15:48 +02:00
Tobias Schultze 8ceb47178b Fix wrong type in phpdoc of AbstractIdGenerator
\Doctrine\ORM\Mapping\Entity is the annotation class which is not correct. The entity object itself is meant here as tests also assume see https://github.com/doctrine/doctrine2/blob/2.6/tests/Doctrine/Tests/ORM/Id/AssignedGeneratorTest.php#L28

Found this when running phpstan on our code that used a custom generator.
2018-04-10 18:31:36 +02:00
Donovan Bourlard 2560d4f419 Fix default value of one-to-many order-by to ASC, #7141 2018-03-22 14:51:02 +01:00
Marco Pivetta 87ee409783 Merge pull request #7082 from mariusklocke/issue-7062
Add failing test for issue #7062
2018-02-27 08:30:56 +01:00
Luís Cobucci d47c1f3e9b Fix basic entity persister type resolver
Which was using the wrong way to fetch the field type and using the
association type instead of the column type.
2018-02-26 14:39:06 +01:00
Marius Klocke b952dac339 Add a failing test for issue 7062 2018-02-26 14:39:05 +01:00
Luís Cobucci ffb7d4c79c Merge pull request #7093 from lcobucci/patch-association-identifier-not-quoted
Fix updating entities with quoted identifier association
2018-02-25 20:28:33 +01:00
Jan Langer e68717b725 Fix updating entities with quoted identifier association 2018-02-25 20:10:18 +01:00
Luís Cobucci 30a063ef9d Merge pull request #6701 from vhenzl/pr/issue-6531-test
Add failing tests for #6531 

Fixes https://github.com/doctrine/doctrine2/issues/6043
Fixes https://github.com/doctrine/doctrine2/issues/6531
Fixes https://github.com/doctrine/doctrine2/issues/7002
Fixes https://github.com/doctrine/doctrine2/pull/7003
2018-02-19 23:17:19 +01:00
Nicolas FRANÇOIS 35c3669ebc Fix handling entities with post generated IDs as FK
This prevents a throw in UnitOfWork#addToIdentityMap because some fields
are null.
2018-02-19 23:05:13 +01:00
Vašek Henzl 23f4f03575 Add failing tests for #6531
Tests are based on examples from "Composite and Foreign Keys as Primary Key" tutorial:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html
2018-02-19 22:14:57 +01:00
Luís Cobucci a912fc09be Add @group to delete query test 2018-02-19 22:04:28 +01:00
Marco Pivetta a736a3713b Merge pull request #6988 from kbond/inheritance-issue
Inheritance middle-layer doesn't get hydrated
2018-02-19 12:13:08 +01:00
Luís Cobucci f2da5bc93e Extract private method to retrieve discriminator values 2018-02-19 12:07:44 +01:00
Luís Cobucci 2905b435db Remove loose comparison on discriminator values
According to mapping drivers the discriminator values can always be
converted to strings so it's safe to assume that we can actually do a
strict comparison during hydration.
2018-02-19 12:07:43 +01:00
Toni Cornelissen 48ca6dbcec Use partial discriminator map on multi-inheritance
Hydrator was ignoring data from subclasses when using multiple
inheritance levels. With this patch it will now use the discriminator
values from all subclasses of the class being hydrated.
2018-02-19 12:07:42 +01:00
Kevin Bond 15a4302902 Inheritance middle-layer doesn't get hydrated with HYDRATE_OBJECT 2018-02-19 12:07:41 +01:00
Marco Pivetta 1f82a20312 Merge pull request #7077 from lcobucci/fix-delete-bc-break
Fix BC-break on delete without alias DQL
2018-02-19 11:32:46 +01:00
Luís Cobucci fc943b70f6 Use early-returns to improve readability of the Parser 2018-02-19 00:53:42 +01:00
Luís Cobucci f36470941c Fix BC-break on delete queries with nasty workaround
The `v2.5.x` series of the ORM allowed to have DELETE DQLs without using
an alias, even though it didn't follow the grammar rules of the parser.
We fixed that issue on `v2.6.0` however that was a BC-breaking change
and lots of people were relying on this faulty behaviour.

This workaround fixes the BC-break, without even trying to be elegant.
In `v2.7.0.` we should raise a deprecation notice to notify people that
we'll drop that "feature" in `v3.0`.
2018-02-19 00:53:36 +01:00
Carnage ae6d80daab Adds sql generation test 2018-02-19 00:50:27 +01:00
Luís Cobucci 44e82e2720 Remove unused functions 2018-02-17 19:49:16 +01:00
Luís Cobucci e94467d6da Fix incorrect value in L2C+lock test
Which was causing the optimistic lock to fail in MySQL since it was
trying to update the data with exact same value.
2018-02-17 19:46:22 +01:00
Luís Cobucci 794c7708e8 Merge branch 'backport/fix/l2c-version' into 2.6
Backporting https://github.com/doctrine/doctrine2/pull/7069
2018-02-17 18:09:39 +01:00
‘Andrey Lukin’ 8e73926359 Add version fields into L2C data 2018-02-17 18:05:32 +01:00
‘Andrey Lukin’ 8fc1d74820 Add test for L2C using optimistic locks
As explained in #7067, fields with `@ORM\Version` annotation were not
being added to L2C cached data.
2018-02-17 18:05:27 +01:00
Luís Cobucci 496c6a9f03 Merge branch 'backport/fix-date-issues-once-and-for-all' into 2.6
Backporting https://github.com/doctrine/doctrine2/pull/7055
2018-02-09 17:21:29 +01:00
Luís Cobucci 7873f700b0 Add missing tests for day calculation
For the DATE_SUB() and DATE_ADD() functions.
2018-02-09 17:21:06 +01:00
Luís Cobucci 46c0861f45 Fix date calculation in tests (again)
Now using PHP to calculate the expected date manipulation, keeping a day
as delta since PHP resets the hour when performing operations with
days/weeks/months/years.

February is a wonderful month, isn't it?
2018-02-09 17:21:06 +01:00
Luís Cobucci 5149c0ff25 Merge branch 'backport/fix/7031-tests-february' into 2.6
Backporting: https://github.com/doctrine/doctrine2/pull/7032
2018-02-02 09:07:25 +01:00
Michael Moravec cf99d62472 QueryDqlFunctionTest: Increase delta for testDateAdd() to work in February 2018-02-02 08:59:35 +01:00
Luís Cobucci 5878797eae Merge pull request #6971 from rolando-caldas/master
Exception Call to undefined method Doctrine\Common\Cache\MemcachedCache::setMemcache()
2018-01-30 01:53:35 +01:00
Rolando Caldas 8c2d090dc8 Exception Call to undefined method Doctrine\Common\Cache\MemcachedCache::setMemcache()
When memcached extension is loaded Doctrine\ORM\Tools\Setup.php  calls to setMemcache method. The MemcachedCache class has the setMemcached method instead. Changed this call in Setup to setMemcached and $memcache to $memcached to keep the name like the extension
2018-01-30 01:38:56 +01:00
Luís Cobucci 3f772eac32 Merge pull request #7021 from lcobucci/fix-phpstan-check
Fix incorrect variable reference
2018-01-30 01:38:21 +01:00
Luís Cobucci 62c952d258 Fix wrong variable reference 2018-01-30 01:21:34 +01:00
Luís Cobucci c2f698e56e Merge pull request #6997 from NicolaF/fix/fix-6991-2.6
ManyToManyPersister fails to remove join table entry if there is multiple join columns
2018-01-30 01:19:12 +01:00
Nicolas FRANÇOIS 40f2a3efba Add test case for many-to-many collection deletion, when owning side has a composite PK 2018-01-30 01:04:28 +01:00
Nicolas FRANÇOIS 333b9c0b99 Fix #6991: correctly resolve identifer values in ManyToManyPersister 2018-01-19 12:19:02 +01:00
Luís Cobucci 90d19b4131 Bumping development version to v2.6.1-DEV 2017-12-20 02:01:05 +01:00
1587 changed files with 80310 additions and 56797 deletions
+12 -6
View File
@@ -12,21 +12,27 @@
"upcoming": true
},
{
"name": "2.8",
"branchName": "2.8.x",
"slug": "2.8",
"name": "2.9",
"branchName": "2.9.x",
"slug": "2.9",
"upcoming": true
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"name": "2.8",
"branchName": "2.8.x",
"slug": "2.8",
"current": true,
"aliases": [
"current",
"stable"
]
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"maintained": false
},
{
"name": "2.6",
"branchName": "2.6",
+5 -1
View File
@@ -1,5 +1,7 @@
/tests export-ignore
/tools export-ignore
/docs export-ignore
/.github export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
@@ -11,4 +13,6 @@ CONTRIBUTING.md export-ignore
phpunit.xml.dist export-ignore
run-all.sh export-ignore
phpcs.xml.dist export-ignore
composer.lock export-ignore
phpbench.json export-ignore
phpstan.neon export-ignore
psalm.xml export-ignore
-3
View File
@@ -1,3 +0,0 @@
patreon: phpdoctrine
tidelift: packagist/doctrine/orm
custom: https://www.doctrine-project.org/sponsorship.html
-37
View File
@@ -1,37 +0,0 @@
---
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/master/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
View File
@@ -1,34 +0,0 @@
---
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? -->
-18
View File
@@ -1,18 +0,0 @@
---
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. -->
@@ -1,20 +0,0 @@
---
name: ❓ Support Question
about: Have a problem that you can't figure out? 🤔
---
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | -----
| Version | x.y.z
<!--
Before asking question here, please try asking on Gitter or Slack first.
Find out more about Doctrine support channels here: https://www.doctrine-project.org/community/
Keep in mind that GitHub is primarily an issue tracker.
-->
### Support Question
<!-- Describe the issue you are facing here. -->
@@ -1,19 +0,0 @@
---
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. -->
@@ -1,18 +0,0 @@
---
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. -->
@@ -1,26 +0,0 @@
---
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.7
* submitting a new feature: target the next minor branch: 2.8.x
* submitting a BC-breaking change: target the master branch
-->
### 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. -->
-47
View File
@@ -1,47 +0,0 @@
name: CI
on:
pull_request:
push:
branches:
- master
jobs:
coding-standards:
name: "Coding Standards"
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring
tools: composer, cs2pr
- name: composer install
run: "composer install --no-progress --no-suggest --no-interaction --prefer-dist --optimize-autoloader"
- name: phpcs
run: "php vendor/bin/phpcs -q --report=checkstyle --no-colors | cs2pr"
static-analysis:
name: "Static Analysis"
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring
tools: composer, cs2pr
- name: composer install
run: "composer install --no-progress --no-suggest --no-interaction --prefer-dist --optimize-autoloader"
- name: phpstan
run: "php vendor/bin/phpstan analyse --error-format=checkstyle --no-progress | cs2pr"
+43
View File
@@ -0,0 +1,43 @@
name: "Coding Standards"
on:
pull_request:
jobs:
coding-standards:
name: "Coding Standards"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 10
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: "cs2pr"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Install diff-sniffer"
run: "wget https://github.com/diff-sniffer/diff-sniffer/releases/download/0.5.1/diff-sniffer.phar"
- name: "Fetch head branch"
run: "git remote set-branches --add origin $GITHUB_BASE_REF && git fetch origin $GITHUB_BASE_REF"
- name: "Run diff-sniffer"
run: "php diff-sniffer.phar origin/$GITHUB_BASE_REF...$GITHUB_SHA --report=checkstyle | cs2pr"
- name: "Run phpcbf"
run: "vendor/bin/phpcbf"
@@ -0,0 +1,254 @@
name: "Continuous Integration"
on:
pull_request:
push:
env:
fail-fast: true
jobs:
phpunit-smoke-check:
name: "PHPUnit with SQLite"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
deps:
- "highest"
include:
- deps: "lowest"
php-version: "7.3"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
extensions: "pdo, pdo_sqlite"
coverage: "pcov"
ini-values: "zend.assertions=1"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
dependency-versions: "${{ matrix.deps }}"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --coverage-clover=coverage-no-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 0
- name: "Run PHPUnit with Second Level Cache"
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 1
- name: "Upload coverage file"
uses: "actions/upload-artifact@v2"
with:
name: "phpunit-sqlite-${{ matrix.deps }}-${{ matrix.php-version }}-coverage"
path: "coverage*.xml"
phpunit-postgres:
name: "PHPUnit with PostgreSQL"
runs-on: "ubuntu-20.04"
needs: "phpunit-smoke-check"
strategy:
matrix:
php-version:
- "7.4"
postgres-version:
- "9.6"
- "13"
services:
postgres:
image: "postgres:${{ matrix.postgres-version }}"
env:
POSTGRES_PASSWORD: "postgres"
options: >-
--health-cmd "pg_isready"
ports:
- "5432:5432"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
ini-values: "zend.assertions=1"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v2"
with:
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-coverage"
path: "coverage.xml"
phpunit-mariadb:
name: "PHPUnit with MariaDB"
runs-on: "ubuntu-20.04"
needs: "phpunit-smoke-check"
strategy:
matrix:
php-version:
- "7.4"
mariadb-version:
- "10.5"
extension:
- "mysqli"
- "pdo_mysql"
services:
mariadb:
image: "mariadb:${{ matrix.mariadb-version }}"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: "doctrine_tests"
options: >-
--health-cmd "mysqladmin ping --silent"
ports:
- "3306:3306"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
ini-values: "zend.assertions=1"
extensions: "${{ matrix.extension }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v2"
with:
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
path: "coverage.xml"
phpunit-mysql:
name: "PHPUnit with MySQL"
runs-on: "ubuntu-20.04"
needs: "phpunit-smoke-check"
strategy:
matrix:
php-version:
- "7.4"
mysql-version:
- "5.7"
- "8.0"
extension:
- "mysqli"
- "pdo_mysql"
services:
mysql:
image: "mysql:${{ matrix.mysql-version }}"
options: >-
--health-cmd "mysqladmin ping --silent"
-e MYSQL_ALLOW_EMPTY_PASSWORD=yes
-e MYSQL_DATABASE=doctrine_tests
ports:
- "3306:3306"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
ini-values: "zend.assertions=1"
extensions: "${{ matrix.extension }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 0
- name: "Run PHPUnit with Second Level Cache"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-no-cache.xml"
env:
ENABLE_SECOND_LEVEL_CACHE: 1
- name: "Upload coverage files"
uses: "actions/upload-artifact@v2"
with:
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
path: "coverage*.xml"
upload_coverage:
name: "Upload coverage to Codecov"
runs-on: "ubuntu-20.04"
needs:
- "phpunit-smoke-check"
- "phpunit-postgres"
- "phpunit-mariadb"
- "phpunit-mysql"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Download coverage files"
uses: "actions/download-artifact@v2"
with:
path: "reports"
- name: "Upload to Codecov"
uses: "codecov/codecov-action@v1"
with:
directory: reports
@@ -0,0 +1,46 @@
name: "Automatic Releases"
on:
milestone:
types:
- "closed"
jobs:
release:
name: "Git tag, release & create merge-up PR"
runs-on: "ubuntu-20.04"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Release"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:release"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"SHELL_VERBOSITY": "3"
- name: "Create Merge-Up Pull Request"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:create-merge-up-pull-request"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create new milestones"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:create-milestones"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
+56
View File
@@ -0,0 +1,56 @@
name: Static Analysis
on:
pull_request:
jobs:
static-analysis-phpstan:
name: "PHPStan"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout code"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: cs2pr
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run a static analysis with phpstan/phpstan"
run: "php vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
static-analysis-psalm:
name: "Psalm"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout code"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run a static analysis with vimeo/psalm"
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
+3 -4
View File
@@ -7,13 +7,12 @@ lib/api/
lib/Doctrine/Common
lib/Doctrine/DBAL
/.settings/
*.iml
.buildpath
.project
.idea
*.iml
vendor/
composer.phar
/tests/Doctrine/Performance/history.db
/.phpcs-cache
phpbench.phar
phpbench.phar.pubkey
composer.lock
/.phpunit.result.cache
-32
View File
@@ -1,32 +0,0 @@
build:
nodes:
analysis:
environment:
php:
version: 7.4
cache:
disabled: false
directories:
- ~/.composer/cache
project_setup:
override: true
tests:
override:
- php-scrutinizer-run
- phpcs-run
dependencies:
override:
- composer install --no-interaction --prefer-dist
tools:
external_code_coverage:
timeout: 3600
filter:
excluded_paths:
- docs
build_failure_conditions:
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
- 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
- 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection
-82
View File
@@ -1,82 +0,0 @@
dist: trusty
sudo: false
language: php
php:
- 7.3
- 7.4
env:
- DB=mariadb
- DB=mysql
- DB=pgsql
- DB=sqlite
before_install:
- |
if [[ "$COVERAGE" != "1" ]]; then
phpenv config-rm ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini || echo "xdebug is not installed"
fi
- echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- travis_retry composer self-update
install:
- rm composer.lock
- travis_retry composer update --no-interaction --prefer-dist --no-suggest --no-progress
script:
- |
if [[ "$DB" == "mysql" || "$DB" == "mariadb" ]]; then
mysql -e "CREATE SCHEMA doctrine_tests; GRANT ALL PRIVILEGES ON doctrine_tests.* to travis@'%'";
fi
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml
# temporarily disabled
#- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
jobs:
include:
- stage: Test
env: DB=mariadb
addons:
mariadb: "10.4"
- stage: Test
env: DB=sqlite DEPENDENCIES=low
install:
- travis_retry composer update --no-interaction --prefer-dist --no-suggest --no-progress --prefer-lowest
- stage: Test
if: type = cron
php: 7.3
env: DB=sqlite DEV_DEPENDENCIES
install:
- rm composer.lock
- composer config minimum-stability dev
- travis_retry composer update --no-interaction --prefer-dist --no-suggest --no-progress
- stage: Test
env: DB=sqlite COVERAGE
before_script:
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
script:
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --coverage-clover ./build/logs/clover.xml
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml
- stage: Code Quality
env: DB=none BENCHMARK
before_script:
- wget https://phpbench.github.io/phpbench/phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar.pubkey
script:
- php phpbench.phar run --bootstrap=tests/Doctrine/Tests/TestInit.php -l dots --report=default
allow_failures:
# temporarily disabled
- env: DB=mysql
- env: DB=mariadb
- env: DB=pgsql
cache:
directories:
- $HOME/.composer/cache
+49 -58
View File
@@ -1,81 +1,79 @@
# Contributing to Doctrine ORM
# Contribute to Doctrine
Thank you for contributing to Doctrine ORM!
Thank you for contributing to Doctrine!
Before we can merge your pull request here are some guidelines that you need to follow.
Before we can merge your Pull-Request here are some guidelines that you need to follow.
These guidelines exist not to annoy you, but to keep the code base clean,
unified and future proof.
## Obtaining a copy
## We only accept PRs to "master"
In order to submit a pull request, you will need to [fork the project][Fork] and obtain a
fresh copy of the source code:
Our branching strategy is "everything to master first", even
bugfixes and we then merge them into the stable branches. You should only
open pull requests against the master branch. Otherwise we cannot accept the PR.
```sh
git clone git@github.com:<your-github-name>/orm.git
cd orm
```
Then you will have to run a Composer installation in the project:
```sh
curl -sS https://getcomposer.org/installer | php
./composer.phar install
```
## Choosing the branch
* I am submitting a bugfix for a stable release
* Your PR should target the [lowest active stable branch (2.7)][2.7].
* I am submitting a new feature
* Your PR should target the [master branch (3.0)][Master].
* I am submitting a BC-breaking change
* Your PR must target the [master branch (3.0)][Master].
* Please also try to provide a deprecation path in a PR targeting the [2.8 branch][2.8].
Please always create a new branch for your changes (i.e. do not commit directly into `master`
in your fork), otherwise you would run into troubles with creating multiple pull requests.
There is one exception to the rule, when we merged a bug into some stable branches
we do occasionally accept pull requests that merge the same bug fix into earlier
branches.
## Coding Standard
We follow the [Doctrine Coding Standard][CS].
Please refer to this repository to learn about the rules your code should follow.
You can also use `vendor/bin/phpcs` to validate your changes locally.
We use PSR-1 and PSR-2:
## Tests
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
Please try to add a test for your pull request.
with some exceptions/differences:
* Keep the nesting of control structures per method as small as possible
* Align equals (=) signs
* Add spaces between assignment, control and return statements
* Prefer early exit over nesting conditions
* Add spaces around a negation if condition ``if ( ! $cond)``
## Unit-Tests
Please try to add a test for your pull-request.
* If you want to fix a bug or provide a reproduce case, create a test file in
``tests/Doctrine/Tests/ORM/Functional/Ticket`` with the identifier of the issue,
i.e. ``GH1234Test.php`` for an issue with id `#1234`.
* If you want to contribute new functionality, add unit or functional tests
``tests/Doctrine/Tests/ORM/Functional/Ticket`` with the name of the ticket,
``DDC1234Test.php`` for example.
* If you want to contribute new functionality add unit- or functional tests
depending on the scope of the feature.
You can run the tests by calling ``vendor/bin/phpunit`` from the root of the project.
It will run all the tests with an in-memory SQLite database.
You can run the unit-tests by calling ``vendor/bin/phpunit`` from the root of the project.
It will run all the tests with an in memory SQLite database.
In order to do that, you will need a fresh copy of the ORM, and you
will have to run a composer installation in the project:
```sh
git clone git@github.com:doctrine/orm.git
cd orm
curl -sS https://getcomposer.org/installer | php --
./composer.phar install
```
To run the testsuite against another database, copy the ``phpunit.xml.dist``
to for example ``mysql.phpunit.xml`` and edit the parameters. You can
take a look at the ``tests/travis`` folder for some examples. Then run:
vendor/bin/phpunit -c mysql.phpunit.xml
If you do not provide these parameters, the test suite will use an in-memory
sqlite database.
Tips for creating unit tests:
1. If you put a test into the `Ticket` namespace as described above, put the testcase
and all entities into the same file.
See [DDC2306Test][Test Example] for an example.
1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class.
See `https://github.com/doctrine/orm/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
example.
## CI
## Travis
We automatically run all pull requests through [Travis CI][Travis].
* The test suite is ran against SQLite, MySQL, MariaDB and PostgreSQL on all supported PHP versions.
* The code is validated against our [Coding Standard](#coding-standard).
* The code is checked by a static analysis tool.
If you break the tests, we cannot merge your code,
so please make sure that your code is working before opening a pull request.
We automatically run your pull request through [Travis CI](http://www.travis-ci.org)
against SQLite, MySQL and PostgreSQL. If you break the tests, we cannot merge your code,
so please make sure that your code is working before opening up a Pull-Request.
## Getting merged
@@ -84,10 +82,3 @@ everything as fast as possible, but cannot always live up to our own expectation
Thank you very much again for your contribution!
[Master]: https://github.com/doctrine/orm/tree/master
[2.8]: https://github.com/doctrine/orm/tree/2.8.x
[2.7]: https://github.com/doctrine/orm/tree/2.7
[CS]: https://github.com/doctrine/coding-standard
[Fork]: https://guides.github.com/activities/forking/
[Travis]: https://www.travis-ci.org
[Test Example]: https://github.com/doctrine/orm/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) Doctrine Project
Copyright (c) 2006-2015 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
+12 -38
View File
@@ -1,52 +1,26 @@
[![Tidelift](https://tidelift.com/badges/github/doctrine/orm)](https://tidelift.com/subscription/pkg/packagist-doctrine-orm?utm_source=packagist-doctrine-orm&utm_medium=referral&utm_campaign=readme)
| [Master][Master] | [2.8.x][2.8] |
|:----------------:|:----------:|
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
| [Master][Master] | [2.8][2.8] | [2.7][2.7] |
|:----------------:|:----------:|:----------:|
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] | [![Build status][2.7 image]][2.7] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] | [![Coverage Status][2.7 coverage image]][2.7 coverage] |
##### :warning: You are browsing the code of upcoming Doctrine 3.0.
##### Things changed a lot here and major code changes should be expected. If you are rather looking for a stable version, refer to the [2.7 branch][2.7] for the current stable release or [2.8 branch][2.8] for the upcoming release. If you are submitting a pull request, please see the _[Which branch should I choose?](#which-branch-should-i-choose)_ section below.
-----
Doctrine 3 is an object-relational mapper (ORM) for PHP 7.2+ that provides transparent persistence
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
without requiring unnecessary code duplication.
-----
### Which branch should I choose?
Please see [Choosing the branch](CONTRIBUTING.md#choosing-the-branch) to get more information about which branch
you should target your pull request at.
## Doctrine ORM for enterprise
Available as part of the Tidelift Subscription.
The maintainers of Doctrine ORM and thousands of other packages are working with Tidelift to deliver commercial support
and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve
code health, while paying the maintainers of the exact dependencies you use.
[Learn more.](https://tidelift.com/subscription/pkg/packagist-doctrine-orm?utm_source=packagist-doctrine-orm&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
## More resources:
* [Website](http://www.doctrine-project.org)
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
[Master image]: https://img.shields.io/travis/doctrine/orm/master.svg?style=flat-square
[Master]: https://travis-ci.org/doctrine/orm
[Master coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/master.svg?style=flat-square
[Master coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=master
[2.8 image]: https://img.shields.io/travis/doctrine/orm/2.8.x.svg?style=flat-square
[2.8]: https://github.com/doctrine/orm/tree/2.8.x
[2.8 coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/2.8.x.svg?style=flat-square
[2.8 coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=2.8.x
[2.7 image]: https://img.shields.io/travis/doctrine/orm/2.7.svg?style=flat-square
[2.7]: https://github.com/doctrine/orm/tree/2.7
[2.7 coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/2.7.svg?style=flat-square
[2.7 coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=2.7
[Master coverage image]: https://codecov.io/gh/doctrine/orm/branch/master/graph/badge.svg
[Master coverage]: https://codecov.io/gh/doctrine/orm/branch/master
[2.8 image]: https://img.shields.io/travis/doctrine/orm/2.8.svg?style=flat-square
[2.8]: https://github.com/doctrine/orm/tree/2.8
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8/graph/badge.svg
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8
+76 -268
View File
@@ -1,191 +1,86 @@
# Upgrade to 3.0
# Upgrade to 2.8
## BC Break: Removed ability to clear cache via console with some cache drivers
## Minor BC BREAK: Failed commit now throw OptimisticLockException
The console commands `orm:clear-cache:metadata`, `orm:clear-cache:result`,
and `orm:clear-cache:query` cannot be used with the `ApcCache`, `ApcuCache`,
or `XcacheCache` because the memory is only available to the webserver process.
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
## BC Break: `orm:run-dql` command's `$depth` parameter removed
# Upgrade to 2.7
The `$depth` parameter has been removed, the dumping functionality
is now provided by [`symfony/var-dumper`](https://github.com/symfony/var-dumper).
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods
## BC Break: Dropped `Doctrine\ORM\Tools\Setup::registerAutoloadDirectory()`
Method `Doctrine\ORM\AbstractQuery#useResultCache()` which could be used for both enabling and disabling the cache
(depending on passed flag) was split into two.
This method used deprecated Doctrine Autoloader and has been removed. Please rely on Composer autoloading instead.
## Minor BC BREAK: paginator output walkers aren't be called anymore on sub-queries for queries without max results
## BC Break: Dropped automatic discriminator map discovery
To optimize DB interaction, `Doctrine\ORM\Tools\Pagination\Paginator` no longer fetches identifiers to be able to
perform the pagination with join collections when max results isn't set in the query.
Automatic discriminator map discovery exhibited multiple flaws
that can't be reliably addressed and supported:
## Minor BC BREAK: tables filtered with `schema_filter` are no longer created
* discovered entries are not namespaced which leads to collisions,
* the class name is part of the discriminator map, therefore the class
must never be renamed.
When generating schema diffs, if a source table is filtered out by a `schema_filter` expression, then a `CREATE TABLE` was
always generated, even if the table already existed. This has been changed in this release and the table will no longer
be created.
As a consequence this feature has been dropped.
## Deprecated number unaware `Doctrine\ORM\Mapping\UnderscoreNamingStrategy`
If your code relied on this feature, please build the discriminator map for
your inheritance tree manually where each entry is an unqualified lowercase
name of the member entities.
In the last patch of the `v2.6.x` series, we fixed a bug that was not converting names properly when they had numbers
(e.g.: `base64Encoded` was wrongly converted to `base64encoded` instead of `base64_encoded`).
## BC Break: Missing type declaration added for identifier generators
In order to not break BC we've introduced a way to enable the fixed behavior using a boolean constructor argument. This
argument will be removed in 3.0 and the default behavior will be the fixed one.
The interfaces `Doctrine\ORM\Sequencing\Generator` and
`Doctrine\ORM\Sequencing\Planning\ValueGenerationPlan` now uses explicit type
declaration for parameters and return (as much as possible).
## Deprecated: `Doctrine\ORM\AbstractQuery#useResultCache()`
## BC Break: Removed possibility to extend the doctrine mapping xml schema with anything
Method `Doctrine\ORM\AbstractQuery#useResultCache()` is deprecated because it is split into `enableResultCache()`
and `disableResultCache()`. It will be removed in 3.0.
If you want to extend it now you have to provide your own validation schema.
## Deprecated code generators and related console commands
## BC Break: Entity Listeners no long support naming convention methods
If you want their behavior to be kept, please add the necessary Annotation methods (in case XML driver is used,
no changes are necessary).
## BC Break: Removed `Doctrine\ORM\Mapping\Exporter\VariableExporter` constants
This constant has been removed
* `Doctrine\ORM\Mapping\Exporter\VariableExporter::INDENTATION`
## BC Break: Removed support for named queries and named native queries
These classes have been removed:
* `Doctrine/ORM/Annotation/NamedQueries`
* `Doctrine/ORM/Annotation/NamedQuery`
* `Doctrine/ORM/Annotation/NamedNativeQueries`
* `Doctrine/ORM/Annotation/NamedNativeQuery`
* `Doctrine/ORM/Annotation/ColumnResult`
* `Doctrine/ORM/Annotation/FieldResult`
* `Doctrine/ORM/Annotation/EntityResult`
* `Doctrine/ORM/Annotation/SqlResultSetMapping`
* `Doctrine/ORM/Annotation/SqlResultSetMappings`
These methods have been removed:
* `Doctrine/ORM/Configuration::addNamedQuery()`
* `Doctrine/ORM/Configuration::getNamedQuery()`
* `Doctrine/ORM/Configuration::addNamedNativeQuery()`
* `Doctrine/ORM/Configuration::getNamedNativeQuery()`
* `Doctrine/ORM/Decorator/EntityManagerDecorator::createNamedQuery()`
* `Doctrine/ORM/Decorator/EntityManagerDecorator::createNamedNativeQuery()`
* `Doctrine/ORM/EntityManager::createNamedQuery()`
* `Doctrine/ORM/EntityManager::createNamedNativeQuery()`
* `Doctrine/ORM/EntityManagerInterface::createNamedQuery()`
* `Doctrine/ORM/EntityManagerInterface::createNamedNativeQuery()`
* `Doctrine/ORM/EntityRepository::createNamedQuery()`
* `Doctrine/ORM/EntityRepository::createNamedNativeQuery()`
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedQuery()`
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedQueries()`
* `Doctrine/ORM/Mapping/ClassMetadata::addNamedQuery()`
* `Doctrine/ORM/Mapping/ClassMetadata::hasNamedQuery()`
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedNativeQuery()`
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedNativeQueries()`
* `Doctrine/ORM/Mapping/ClassMetadata::addNamedNativeQuery()`
* `Doctrine/ORM/Mapping/ClassMetadata::hasNamedNativeQuery()`
* `Doctrine\ORM\Mapping\ClassMetadata::addSqlResultSetMapping()`
* `Doctrine\ORM\Mapping\ClassMetadata::getSqlResultSetMapping()`
* `Doctrine\ORM\Mapping\ClassMetadata::getSqlResultSetMappings()`
* `Doctrine\ORM\Mapping\ClassMetadata::hasSqlResultSetMapping()`
## BC Break: Removed support for entity namespace aliases
The support for namespace aliases has been removed.
Please migrate to using `::class` for referencing classes.
These methods have been removed:
* `Doctrine\ORM\Configuration::addEntityNamespace()`
* `Doctrine\ORM\Configuration::getEntityNamespace()`
* `Doctrine\ORM\Configuration::setEntityNamespaces()`
* `Doctrine\ORM\Configuration::getEntityNamespaces()`
* `Doctrine\ORM\Mapping\AbstractClassMetadataFactory::getFqcnFromAlias()`
* `Doctrine\ORM\ORMException::unknownEntityNamespace()`
## BC Break: Removed same-namespace class name resolution
Support for same-namespace class name resolution in mappings has been removed.
If you're using annotation driver, please migrate to references using `::class`.
If you're using XML driver, please migrate to fully qualified references.
These methods have been removed:
* Doctrine\ORM\Mapping\ClassMetadata::fullyQualifiedClassName()
## BC Break: Removed code generators and related console commands
These console commands have been removed:
These console commands have been deprecated:
* `orm:convert-mapping`
* `orm:generate:entities`
* `orm:generate-repositories`
These classes have been removed:
These classes have been deprecated:
* `Doctrine\ORM\Tools\EntityGenerator`
* `Doctrine\ORM\Tools\EntityRepositoryGenerator`
The whole Doctrine\ORM\Tools\Export namespace with all its members has been removed as well.
Whole Doctrine\ORM\Tools\Export namespace with all its members have been deprecated as well.
## BC Break: proxies no longer implement `Doctrine\ORM\Proxy\Proxy`
## Deprecated `Doctrine\ORM\Proxy\Proxy` marker interface
Proxy objects no longer implement `Doctrine\ORM\Proxy\Proxy` nor
`Doctrine\Common\Persistence\Proxy`: instead, they implement
Proxy objects in Doctrine ORM 3.0 will no longer implement `Doctrine\ORM\Proxy\Proxy` nor
`Doctrine\Persistence\Proxy`: instead, they implement
`ProxyManager\Proxy\GhostObjectInterface`.
These related classes have been removed:
These related classes have been deprecated:
* `Doctrine\ORM\Proxy\ProxyFactory` - replaced by `Doctrine\ORM\Proxy\Factory\StaticProxyFactory`
and `Doctrine\ORM\Proxy\Factory\ProxyFactory`
* `Doctrine\ORM\Proxy\Proxy`
* `Doctrine\ORM\Proxy\ProxyFactory`
* `Doctrine\ORM\Proxy\Autoloader` - we suggest using the composer autoloader instead
* `Doctrine\ORM\Reflection\RuntimePublicReflectionProperty`
These methods have been deprecated:
These methods have been removed:
* `Doctrine\ORM\Configuration#getProxyDir()`
* `Doctrine\ORM\Configuration#getAutoGenerateProxyClasses()`
* `Doctrine\ORM\Configuration#getProxyDir()`
* `Doctrine\ORM\Configuration#getProxyNamespace()`
Proxy class names change: the generated proxies now follow
the [`ClassNameInflector`](https://github.com/Ocramius/ProxyManager/blob/2.1.1/src/ProxyManager/Inflector/ClassNameInflector.php)
naming.
## Deprecated `Doctrine\ORM\Version`
Proxies are also always generated if not found: fatal errors due to missing
proxy classes should no longer occur with ORM default settings.
The `Doctrine\ORM\Version` class is now deprecated and will be removed in Doctrine ORM 3.0:
please refrain from checking the ORM version at runtime or use
[ocramius/package-versions](https://github.com/Ocramius/PackageVersions/).
In addition to that, the following changes affect entity lazy-loading semantics:
* `final` methods are now allowed
* `__clone` is no longer called by the ORM
* `__wakeup` is no longer called by the ORM
* `serialize($proxy)` will lead to full recursive proxy initialization: please mitigate
the recursive initialization by implementing
the [`Serializable`](https://secure.php.net/manual/en/class.serializable.php) interface
* `clone $proxy` will lead to full initialization of the cloned instance, not the
original instance
* lazy-loading a detached proxy no longer causes the proxy identifiers to be reset
to `null`
* identifier properties are always set when the ORM produces a proxy instance
* calling a method on a proxy no longer causes proxy lazy-loading if the method does
not access any un-initialized proxy state
* accessing entity private state, even with reflection, will trigger lazy-loading
## BC Break: Removed `Doctrine\ORM\Version`
The `Doctrine\ORM\Version` class is no longer available: please refrain from checking the ORM version at runtime.
## BC Break: Removed `EntityManager#merge()` and `EntityManager#detach()` methods
## Deprecated `EntityManager#merge()` and `EntityManager#detach()` methods
Merge and detach semantics were a poor fit for the PHP "share-nothing" architecture.
In addition to that, merging/detaching caused multiple issues with data integrity
in the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.
The following API methods were therefore removed:
The following API methods were therefore deprecated:
* `EntityManager#merge()`
* `EntityManager#detach()`
@@ -198,20 +93,25 @@ In order to maintain performance on batch processing jobs, it is endorsed to ena
the second level cache (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
on entities that are frequently reused across multiple `EntityManager#clear()` calls.
An alternative to `EntityManager#merge()` is not provided by ORM 3.0, since the merging
An alternative to `EntityManager#merge()` will not be provided by ORM 3.0, since the merging
semantics should be part of the business domain rather than the persistence domain of an
application. If your application relies heavily on CRUD-alike interactions and/or `PATCH`
restful operations, you should look at alternatives such as [JMSSerializer](https://github.com/schmittjoh/serializer).
## BC Break: Added the final keyword for `EntityManager`
## Extending `EntityManager` is deprecated
Final keyword has been added to the ``EntityManager::class`` in order to ensure that EntityManager is not used as valid extension point. Valid extension point should be EntityManagerInterface.
Final keyword will be added to the `EntityManager::class` in Doctrine ORM 3.0 in order to ensure that EntityManager
is not used as valid extension point. Valid extension point should be EntityManagerInterface.
## BC Break: ``EntityManagerInterface`` is now used instead of ``EntityManager`` in typehints
## Deprecated `EntityManager#clear($entityName)`
`Sequencing\Generator#generate()` now takes ``EntityManagerInterface`` as its first argument instead of ``EntityManager``. If you have any custom generators, please update your code accordingly.
If your code relies on clearing a single entity type via `EntityManager#clear($entityName)`,
the signature has been changed to `EntityManager#clear()`.
## BC Break: Removed `EntityManager#flush($entity)` and `EntityManager#flush($entities)`
The main reason is that partial clears caused multiple issues with data integrity
in the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.
## Deprecated `EntityManager#flush($entity)` and `EntityManager#flush($entities)`
If your code relies on single entity flushing optimisations via
`EntityManager#flush($entity)`, the signature has been changed to
@@ -219,15 +119,15 @@ If your code relies on single entity flushing optimisations via
Said API was affected by multiple data integrity bugs due to the fact
that change tracking was being restricted upon a subset of the managed
entities. The ORM cannot support committing subsets of the managed
entities. The ORM cannot support committing subsets of the managed
entities while also guaranteeing data integrity, therefore this
utility was removed.
The `flush()` semantics remain the same, but the change tracking will be performed
The `flush()` semantics will remain the same, but the change tracking will be performed
on all entities managed by the unit of work, and not just on the provided
`$entity` or `$entities`, as the parameter is now completely ignored.
The same applies to `UnitOfWork#commit($entity)`, which now is simply
The same applies to `UnitOfWork#commit($entity)`, which will simply be
`UnitOfWork#commit()`.
If you would still like to perform batching operations over small `UnitOfWork`
@@ -237,97 +137,15 @@ instances, it is suggested to follow these paths instead:
cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
* use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html)
## BC Break: Removed ``YAML`` mapping drivers.
## Deprecated `YAML` mapping drivers.
If your code relies on ``YamlDriver`` or ``SimpleYamlDriver``, you **MUST** change to
If your code relies on `YamlDriver` or `SimpleYamlDriver`, you **MUST** change to
annotation or XML drivers instead.
## BC Break: Changed methods in ``ClassMetadata``
* ``ClassMetadata::addInheritedProperty``
* ``ClassMetadata::setDiscriminatorColumn``
## BC Break: Removed methods in ``ClassMetadata``
* ``ClassMetadata::getTypeOfField`` (to be removed, part of Common API)
## BC Break: Removed methods in ``ClassMetadata``
* ``ClassMetadata::setTableName`` => Use ``ClassMetadata::setPrimaryTable(['name' => ...])``
* ``ClassMetadata::getFieldMapping`` => Use ``ClassMetadata::getProperty()`` and its methods
* ``ClassMetadata::getQuotedColumnName`` => Use ``ClassMetadata::getProperty()::getQuotedColumnName()``
* ``ClassMetadata::getQuotedTableName``
* ``ClassMetadata::getQuotedJoinTableName``
* ``ClassMetadata::getQuotedIdentifierColumnNames``
* ``ClassMetadata::getIdentifierColumnNames`` => Use ``ClassMetadata::getIdentifierColumns($entityManager)``
* ``ClassMetadata::setVersionMetadata``
* ``ClassMetadata::setVersioned``
* ``ClassMetadata::invokeLifecycleCallbacks``
* ``ClassMetadata::isInheritedField`` => Use ``ClassMetadata::getProperty()::isInherited()``
* ``ClassMetadata::isUniqueField`` => Use ``ClassMetadata::getProperty()::isUnique()``
* ``ClassMetadata::isNullable`` => Use ``ClassMetadata::getProperty()::isNullable()``
* ``ClassMetadata::getTypeOfColumn()`` => Use ``PersisterHelper::getTypeOfColumn()``
## BC Break: Removed ``quoted`` index from table, field and sequence mappings
Quoting is now always called. Implement your own ``Doctrine\ORM\Mapping\NamingStrategy`` to manipulate
your schema, tables and column names to your custom desired naming convention.
## BC Break: Removed ``ClassMetadata::$fieldMappings[$fieldName]['requireSQLConversion']``
ORM Type SQL conversion is now always being applied, minimizing the risks of error prone code in ORM internals
## BC Break: Removed ``ClassMetadata::$columnNames``
If your code relies on this property, you should search/replace from this:
$metadata->columnNames[$fieldName]
To this:
$metadata->getProperty($fieldName)->getColumnName()
## BC Break: Renamed ``ClassMetadata::setIdentifierValues()`` to ``ClassMetadata::assignIdentifier()``
Provides a more meaningful name to method.
## BC Break: Removed ``ClassMetadata::$namespace``
The namespace property in ClassMetadata was only used when using association
classes in the same namespace and it was used to speedup ClassMetadata
creation purposes. Namespace could be easily inferred by asking ``\ReflectionClass``
which was already stored internally.
### BC Break: Removed ``ClassMetadata::$isVersioned``
Switched to a method alternative: ``ClassMetadata::isVersioned()``
## BC Break: Removed ``Doctrine\ORM\Mapping\ClassMetadataInfo``
There was no reason to keep a blank class. All references are now pointing
to ``Doctrine\ORM\Mapping\ClassMetadata``.
## BC Break: Annotations classes namespace change
All Annotations classes got moved from ``Doctrine\ORM\Mapping`` into a more
pertinent namespace ``Doctrine\ORM\Annotation``. This change was done to add
room for Metadata namespace refactoring.
## Minor BC break: Mappings now store ``DBAL\Type`` instances instead of strings
This leads to manual ``ResultSetMapping`` building instances to also hold Types in meta results.
Example:
$rsm->addMetaResult('e ', 'e_discr', 'discr', false, Type::getType('string'));
## Enhancement: Mappings now store their declaring ``ClassMetadata``
Every field, association or embedded now contains a pointer to its declaring ``ClassMetadata``.
## Enhancement: Mappings now store their corresponding table name
Every field, association join column or inline embedded field/association holds a reference to its owning table name.
## Deprecated: `Doctrine\ORM\EntityManagerInterface#copy()`
Method `Doctrine\ORM\EntityManagerInterface#copy()` never got its implementation and is deprecated.
It will be removed in 3.0.
# Upgrade to 2.6
@@ -359,10 +177,6 @@ Method `Doctrine\ORM\Query\Parser#overwriteInternalDQLFunctionNotAllowed()` was
removed because of the choice to allow users to overwrite internal functions, ie
`AVG`, `SUM`, `COUNT`, `MIN` and `MAX`. [#6500](https://github.com/doctrine/orm/pull/6500)
## Minor BC BREAK: removed $className parameter on `AbstractEntityInheritancePersister#getSelectJoinColumnSQL()`
As `$className` parameter was not used in the method, it was safely removed.
## PHP 7.1 is now required
Doctrine 2.6 now requires PHP 7.1 or newer.
@@ -379,6 +193,10 @@ As a consequence, automatic cache setup in Doctrine\ORM\Tools\Setup::create*Conf
Method `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()` was unused and part
of the internal API of the ORM, so it was removed. [#5600](https://github.com/doctrine/orm/pull/5600).
## Minor BC BREAK: removed $className parameter on `AbstractEntityInheritancePersister#getSelectJoinColumnSQL()`
As `$className` parameter was not used in the method, it was safely removed.
## Minor BC BREAK: query cache key time is now a float
As of 2.5.5, the `QueryCacheEntry#time` property will contain a float value
@@ -402,8 +220,8 @@ either:
- map those classes as `MappedSuperclass`
## Minor BC BREAK: ``EntityManagerInterface`` instead of ``EntityManager`` in type-hints
As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require
As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require
an ``EntityManagerInterface`` instead.
If you are extending any of the following classes, then you need to check following
signatures:
@@ -496,7 +314,7 @@ the `Doctrine\ORM\Repository\DefaultRepositoryFactory`.
When executing DQL queries with new object expressions, instead of returning DTOs numerically indexes, it will now respect user provided aliases. Consider the following query:
SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true
Previously, your result would be similar to this:
array(
@@ -527,7 +345,6 @@ From now on, the resultset will look like this:
Added way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder()
# Upgrade to 2.4
## BC BREAK: Compatibility Bugfix in PersistentCollection#matching()
@@ -572,7 +389,6 @@ Now parenthesis are considered, the previous DQL will generate:
SELECT 100 / (2 * 2) FROM my_entity
# Upgrade to 2.3
## Auto Discriminator Map breaks userland implementations with Listener
@@ -630,18 +446,17 @@ above you must implement these new methods.
## Metadata Drivers
Metadata drivers have been rewritten to reuse code from Doctrine\Common. Anyone who is using the
Metadata drivers have been rewritten to reuse code from `Doctrine\Persistence`. Anyone who is using the
`Doctrine\ORM\Mapping\Driver\Driver` interface should instead refer to
`Doctrine\Common\Persistence\Mapping\Driver\MappingDriver`. Same applies to
`Doctrine\Persistence\Mapping\Driver\MappingDriver`. Same applies to
`Doctrine\ORM\Mapping\Driver\AbstractFileDriver`: you should now refer to
`Doctrine\Common\Persistence\Mapping\Driver\FileDriver`.
`Doctrine\Persistence\Mapping\Driver\FileDriver`.
Also, following mapping drivers have been deprecated, please use their replacements in Doctrine\Common as listed:
* `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain`
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\PHPDriver`
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver`
* `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Persistence\Mapping\Driver\MappingDriverChain`
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Persistence\Mapping\Driver\PHPDriver`
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Persistence\Mapping\Driver\StaticPHPDriver`
# Upgrade to 2.2
@@ -726,12 +541,11 @@ Also, Doctrine 2.2 now is around 10-15% faster than 2.1.
Previously EntityManager#find(null) returned null. It now throws an exception.
# Upgrade to 2.1
## Interface for EntityRepository
The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
The EntityRepository now has an interface Doctrine\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
## AnnotationReader changes
@@ -751,7 +565,6 @@ The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory
This is already done inside the ``$config->newDefaultAnnotationDriver``, so everything should automatically work if you are using this method. You can verify if everything still works by executing a console command such as schema-validate that loads all metadata into memory.
# Update from 2.0-BETA3 to 2.0-BETA4
## XML Driver <change-tracking-policy /> element demoted to attribute
@@ -760,7 +573,6 @@ We changed how the XML Driver allows to define the change-tracking-policy. The w
<entity change-tracking-policy="DEFERRED_IMPLICT" />
# Update from 2.0-BETA2 to 2.0-BETA3
## Serialization of Uninitialized Proxies
@@ -823,12 +635,10 @@ don't loose anything through this.
The default allocation size for sequences has been changed from 10 to 1. This step was made
to not cause confusion with users and also because it is partly some kind of premature optimization.
# Update from 2.0-BETA1 to 2.0-BETA2
There are no backwards incompatible changes in this release.
# Upgrade from 2.0-ALPHA4 to 2.0-BETA1
## EntityRepository deprecates access to protected variables
@@ -899,6 +709,7 @@ access all entities.
Xml and Yaml Drivers work as before!
## New inversedBy attribute
It is now *mandatory* that the owning side of a bidirectional association specifies the
@@ -962,7 +773,7 @@ you need to use the following, explicit syntax:
## XML Mapping Driver
The 'inheritance-type' attribute changed to take last bit of ClassMetadata constant names, i.e.
NONE, SINGLE_TABLE, JOINED
NONE, SINGLE_TABLE, INHERITANCE_TYPE_JOINED
## YAML Mapping Driver
@@ -994,7 +805,6 @@ The Collection interface in the Common package has been updated with some missin
that were present only on the default implementation, ArrayCollection. Custom collection
implementations need to be updated to adhere to the updated interface.
# Upgrade from 2.0-ALPHA3 to 2.0-ALPHA4
## CLI Controller changes
@@ -1031,8 +841,6 @@ With new required method AbstractTask::buildDocumentation, its implementation de
database schema without deleting any unused tables, sequences or foreign keys.
* Use "doctrine schema-tool --complete-update" to do a full incremental update of
your schema.
# Upgrade from 2.0-ALPHA2 to 2.0-ALPHA3
This section details the changes made to Doctrine 2.0-ALPHA3 to make it easier for you
-2
View File
@@ -1,6 +1,4 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
include('doctrine.php');
+17 -3
View File
@@ -1,7 +1,21 @@
<?php
declare(strict_types=1);
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
require_once 'Doctrine/Common/ClassLoader.php';
+9 -9
View File
@@ -1,9 +1,9 @@
@echo off
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
GOTO RUN
:USE_PEAR_PATH
set PHPBIN=%PHP_PEAR_PHP_BIN%
:RUN
"%PHPBIN%" "@bin_dir@\doctrine" %*
@echo off
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
GOTO RUN
:USE_PEAR_PATH
set PHPBIN=%PHP_PEAR_PHP_BIN%
:RUN
"%PHPBIN%" "@bin_dir@\doctrine" %*
+17 -2
View File
@@ -1,6 +1,21 @@
<?php
declare(strict_types=1);
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
use Symfony\Component\Console\Helper\HelperSet;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
-16
View File
@@ -1,16 +0,0 @@
version=2.0.0BETA2
dependencies.common=2.0.0BETA4
dependencies.dbal=2.0.0BETA4
stability=beta
build.dir=build
dist.dir=dist
report.dir=reports
log.archive.dir=logs
project.pirum_dir=
project.download_dir=
project.xsd_dir=
test.phpunit_configuration_file=
test.phpunit_generate_coverage=0
test.pmd_reports=0
test.pdepend_exec=
test.phpmd_exec=
-78
View File
@@ -1,78 +0,0 @@
<?xml version="1.0"?>
<project name="DoctrineORM" default="build" basedir=".">
<property file="build.properties" />
<target name="php">
<exec executable="which" outputproperty="php_executable">
<arg value="php" />
</exec>
</target>
<target name="prepare">
<mkdir dir="build" />
</target>
<target name="build" depends="check-git-checkout-clean,prepare,php,composer">
<exec executable="${php_executable}">
<arg value="build/composer.phar" />
<arg value="archive" />
<arg value="--dir=build" />
</exec>
</target>
<target name="composer" depends="php,composer-check,composer-download">
<exec executable="${php_executable}">
<arg value="build/composer.phar" />
<arg value="install" />
</exec>
</target>
<target name="composer-check" depends="prepare">
<available file="build/composer.phar" property="composer.present"/>
</target>
<target name="composer-download" unless="composer.present">
<exec executable="wget">
<arg value="-Obuild/composer.phar" />
<arg value="http://getcomposer.org/composer.phar" />
</exec>
</target>
<target name="check-git-checkout-clean">
<exec executable="git" failonerror="true">
<arg value="diff-index" />
<arg value="--quiet" />
<arg value="HEAD" />
</exec>
</target>
<macrodef name="git-commit">
<attribute name="file" default="NOT SET"/>
<attribute name="message" default="NOT SET"/>
<sequential>
<exec executable="git">
<arg value="add" />
<arg value="@{file}" />
</exec>
<exec executable="git">
<arg value="commit" />
<arg value="-m" />
<arg value="@{message}" />
</exec>
</sequential>
</macrodef>
<macrodef name="git-tag">
<attribute name="version" default="NOT SET" />
<sequential>
<exec executable="git">
<arg value="tag" />
<arg value="-m" />
<arg value="v@{version}" />
<arg value="v@{version}" />
</exec>
</sequential>
</macrodef>
</project>
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<var name="db_driver" value="mysqli"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_port" value="3306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<var name="db_driver" value="pdo_mysql"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_port" value="3306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<var name="db_driver" value="pdo_pgsql"/>
<var name="db_host" value="localhost" />
<var name="db_user" value="postgres" />
<var name="db_password" value="postgres" />
<var name="db_dbname" value="doctrine_tests" />
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
>
<php>
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
+29 -38
View File
@@ -1,18 +1,8 @@
{
"name": "doctrine/orm",
"type": "library",
"description": "PHP object relational mapper (ORM) that sits on top of a powerful database abstraction layer (DBAL). One of its key features is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL). This provides developers with a powerful alternative to SQL that maintains flexibility without requiring unnecessary code duplication.",
"keywords": [
"php",
"orm",
"mysql",
"object",
"data",
"mapper",
"mapping",
"query",
"dql"
],
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "https://www.doctrine-project.org/projects/orm.html",
"license": "MIT",
"authors": [
@@ -22,38 +12,34 @@
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
],
"support": {
"chat": "https://www.doctrine-project.org/slack",
"docs": "https://www.doctrine-project.org/projects/orm.html",
"email": "doctrine-user@googlegroups.com",
"issues": "https://github.com/doctrine/orm/issues",
"rss": "https://github.com/doctrine/orm/releases.atom",
"source": "https://github.com/doctrine/orm"
},
"config": {
"sort-packages": true
},
"require": {
"php": "^7.3",
"ext-ctype": "*",
"doctrine/annotations": "~1.7",
"doctrine/cache": "~1.6",
"doctrine/collections": "^1.4",
"doctrine/dbal": "dev-missed-commits",
"doctrine/event-manager": "^1.0",
"doctrine/inflector": "~1.0",
"doctrine/instantiator": "~1.1",
"doctrine/persistence": "^1.1",
"doctrine/reflection": "^1.0",
"ocramius/package-versions": "^1.1.2",
"ocramius/proxy-manager": "^2.1.1",
"symfony/console": "~4.0|~5.0",
"symfony/var-dumper": "^4.1"
"php": "^7.2|^8.0",
"ext-pdo": "*",
"composer/package-versions-deprecated": "^1.8",
"doctrine/annotations": "^1.11.1",
"doctrine/cache": "^1.9.1",
"doctrine/collections": "^1.5",
"doctrine/common": "^3.0.3",
"doctrine/dbal": "^2.10.0",
"doctrine/event-manager": "^1.1",
"doctrine/inflector": "^1.4|^2.0",
"doctrine/instantiator": "^1.3",
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^2.0",
"symfony/console": "^3.0|^4.0|^5.0"
},
"require-dev": {
"doctrine/coding-standard": "^6.0",
"phpstan/phpstan": "^0.11",
"phpunit/phpunit": "^7.0"
"doctrine/coding-standard": "^8.0",
"phpstan/phpstan": "^0.12.18",
"phpunit/phpunit": "^8.5|^9.4",
"symfony/yaml": "^3.4|^4.0|^5.0",
"vimeo/psalm": "4.1.1"
},
"suggest": {
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
},
"autoload": {
"psr-4": { "Doctrine\\ORM\\": "lib/Doctrine/ORM" }
@@ -65,6 +51,11 @@
}
},
"bin": ["bin/doctrine"],
"extra": {
"branch-alias": {
"dev-master": "2.7.x-dev"
}
},
"archive": {
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
}
Generated
-4214
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
en/_exts/configurationblock.pyc
build
en/_build
.idea
+3
View File
@@ -0,0 +1,3 @@
[submodule "en/_theme"]
path = en/_theme
url = https://github.com/doctrine/doctrine-sphinx-theme.git
+4 -2
View File
@@ -1,4 +1,4 @@
The Doctrine2 documentation is licensed under [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
The Doctrine ORM documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
Creative Commons Legal Code
@@ -337,6 +337,7 @@ BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
License is not intended to restrict the license of any rights under
applicable law.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no warranty
@@ -358,4 +359,5 @@ 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 https://creativecommons.org/.
Creative Commons may be contacted at http://creativecommons.org/.
+18
View File
@@ -0,0 +1,18 @@
# Doctrine ORM Documentation
## How to Generate:
Using Ubuntu 14.04 LTS:
1. Run ./bin/install-dependencies.sh
2. Run ./bin/generate-docs.sh
It will generate the documentation into the build directory of the checkout.
## Theme issues
If you get a "Theme error", check if the `en/_theme` subdirectory is empty,
in which case you will need to run:
1. git submodule init
2. git submodule update
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
EXECPATH=`dirname $0`
cd $EXECPATH
cd ..
rm build -Rf
sphinx-build en build
sphinx-build -b latex en build/pdf
rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
sudo apt-get update && sudo apt-get install -y python2.7 python-sphinx python-pygments
+89
View File
@@ -0,0 +1,89 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Doctrine2ORM.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Doctrine2ORM.qhc"
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
+93
View File
@@ -0,0 +1,93 @@
#Copyright (c) 2010 Fabien Potencier
#
#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 the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is furnished
#to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
from docutils.parsers.rst import Directive, directives
from docutils import nodes
from string import upper
class configurationblock(nodes.General, nodes.Element):
pass
class ConfigurationBlock(Directive):
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
formats = {
'html': 'HTML',
'xml': 'XML',
'php': 'PHP',
'yaml': 'YAML',
'jinja': 'Twig',
'html+jinja': 'Twig',
'jinja+html': 'Twig',
'php+html': 'PHP',
'html+php': 'PHP',
'ini': 'INI',
'php-annotations': 'Annotations',
}
def run(self):
env = self.state.document.settings.env
node = nodes.Element()
node.document = self.state.document
self.state.nested_parse(self.content, self.content_offset, node)
entries = []
for i, child in enumerate(node):
if isinstance(child, nodes.literal_block):
# add a title (the language name) before each block
#targetid = "configuration-block-%d" % env.new_serialno('configuration-block')
#targetnode = nodes.target('', '', ids=[targetid])
#targetnode.append(child)
innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']])
para = nodes.paragraph()
para += [innernode, child]
entry = nodes.list_item('')
entry.append(para)
entries.append(entry)
resultnode = configurationblock()
resultnode.append(nodes.bullet_list('', *entries))
return [resultnode]
def visit_configurationblock_html(self, node):
self.body.append(self.starttag(node, 'div', CLASS='configuration-block'))
def depart_configurationblock_html(self, node):
self.body.append('</div>\n')
def visit_configurationblock_latex(self, node):
pass
def depart_configurationblock_latex(self, node):
pass
def setup(app):
app.add_node(configurationblock,
html=(visit_configurationblock_html, depart_configurationblock_html),
latex=(visit_configurationblock_latex, depart_configurationblock_latex))
app.add_directive('configuration-block', ConfigurationBlock)
+1
Submodule docs/en/_theme added at 6f1bc8bead
+201
View File
@@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
#
# Doctrine 2 ORM documentation build configuration file, created by
# sphinx-quickstart on Fri Dec 3 18:10:24 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os, datetime
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath('_exts'))
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['configurationblock']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Doctrine 2 ORM'
copyright = u'2010-%y, Doctrine Project Team'.format(datetime.date.today)
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '2'
# The full version, including alpha/beta/rc tags.
release = '2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'doctrine'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_theme']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'Doctrine2ORMdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Doctrine2ORM.tex', u'Doctrine 2 ORM Documentation',
u'Doctrine Project Team', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
primary_domain = "dcorm"
def linkcode_resolve(domain, info):
if domain == 'dcorm':
return 'http://'
return None
@@ -4,7 +4,7 @@ Advanced field value conversion using custom mapping types
.. sectionauthor:: Jan Sorgalla <jsorgalla@googlemail.com>
When creating entities, you sometimes have the need to transform field values
before they are saved to the database. In Doctrine you can use Custom Mapping
before they are saved to the database. In Doctrine you can use Custom Mapping
Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`).
There are several ways to achieve this: converting the value inside the Type
@@ -15,7 +15,7 @@ type `Point <https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html>`_.
The ``Point`` type is part of the `Spatial extension <https://dev.mysql.com/doc/refman/8.0/en/spatial-extensions.html>`_
of MySQL and enables you to store a single location in a coordinate space by
using x and y coordinates. You can use the Point type to store a
using x and y coordinates. You can use the Point type to store a
longitude/latitude pair to represent a geographic location.
The entity
@@ -29,25 +29,23 @@ The entity class:
.. code-block:: php
<?php
namespace Geo\Entity;
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @Entity
*/
class Location
{
/**
* @ORM\Column(type="point")
* @Column(type="point")
*
* @var \Geo\ValueObject\Point
*/
private $point;
/**
* @ORM\Column(type="string")
* @Column(type="string")
*
* @var string
*/
@@ -86,7 +84,7 @@ The entity class:
}
}
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
``$point`` field. We will create this custom mapping type in the next chapter.
The point class:
@@ -94,7 +92,7 @@ The point class:
.. code-block:: php
<?php
namespace Geo\ValueObject;
class Point
@@ -195,10 +193,10 @@ 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) <https://en.wikipedia.org/wiki/Well-known_text>`_.
`Well-known text (WKT) <http://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
Internally, MySQL stores geometry values in a binary format that is not
identical to the WKT format. So, we need to let MySQL transform the WKT
representation into its internal format.
@@ -212,13 +210,13 @@ which convert WKT strings to and from the internal format of MySQL.
.. note::
When using DQL queries, the ``convertToPHPValueSQL`` and
When using DQL queries, the ``convertToPHPValueSQL`` and
``convertToDatabaseValueSQL`` methods only apply to identification variables
and path expressions in SELECT clauses. Expressions in WHERE clauses are
and path expressions in SELECT clauses. Expressions in WHERE clauses are
**not** wrapped!
If you want to use Point values in WHERE clauses, you have to implement a
:doc:`user defined function <dql-user-defined-functions>` for
:doc:`user defined function <dql-user-defined-functions>` for
``PointFromText``.
Example usage
@@ -251,8 +249,8 @@ Example usage
$em->clear();
// Fetch the Location object
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location l WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
$location = $query->getSingleResult();
/** @var Geo\ValueObject\Point $point */
/* @var Geo\ValueObject\Point */
$point = $location->getPoint();
+89 -57
View File
@@ -6,7 +6,7 @@ Aggregate Fields
You will often come across the requirement to display aggregate
values of data that can be computed by using the MIN, MAX, COUNT or
SUM SQL functions. For any ORM this is a tricky issue
traditionally. Doctrine 2 offers several ways to get access to
traditionally. Doctrine ORM offers several ways to get access to
these values and this article will describe all of them from
different perspectives.
@@ -22,7 +22,7 @@ into the account can either be of positive or negative money
values. Each account has a credit limit and the account is never
allowed to have a balance below that value.
For simplicity we live in a world were money is composed of
For simplicity we live in a world where money is composed of
integers only. Also we omit the receiver/sender name, stated reason
for transfer and the execution date. These all would have to be
added on the ``Entry`` object.
@@ -35,55 +35,73 @@ Our entities look like:
namespace Bank\Entities;
use Doctrine\ORM\Annotation as ORM;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Account
{
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
private $id;
/** @ORM\Column(type="string", unique=true) */
private $no;
/** @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"}) */
private $entries;
/** @ORM\Column(type="integer") */
private $maxCredit = 0;
public function __construct($no, $maxCredit = 0)
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id;
/**
* @ORM\Column(type="string", unique=true)
*/
private string $no;
/**
* @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
*/
private array $entries;
/**
* @ORM\Column(type="integer")
*/
private int $maxCredit = 0;
public function __construct(string $no, int $maxCredit = 0)
{
$this->no = $no;
$this->maxCredit = $maxCredit;
$this->entries = new \Doctrine\Common\Collections\ArrayCollection();
}
}
/**
* @ORM\Entity
*/
class Entry
{
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
private $id;
/** @ORM\ManyToOne(targetEntity="Account", inversedBy="entries") */
private $account;
/** @ORM\Column(type="integer") */
private $amount;
public function __construct($account, $amount)
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id;
/**
* @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
*/
private Account $account;
/**
* @ORM\Column(type="integer")
*/
private int $amount;
public function __construct(Account $account, int $amount)
{
$this->account = $account;
$this->amount = $amount;
// more stuff here, from/to whom, stated reason, execution date and such
}
public function getAmount()
public function getAmount(): Amount
{
return $this->amount;
}
@@ -141,12 +159,14 @@ collection, which means we can compute this value at runtime:
class Account
{
// .. previous code
public function getBalance()
public function getBalance(): int
{
$balance = 0;
foreach ($this->entries as $entry) {
$balance += $entry->getAmount();
}
return $balance;
}
}
@@ -170,13 +190,12 @@ relation with this method:
<?php
class Account
{
public function addEntry($amount)
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
$e = new Entry($this, $amount);
$this->entries[] = $e;
return $e;
}
}
@@ -185,25 +204,28 @@ Now look at the following test-code for our entities:
.. code-block:: php
<?php
class AccountTest extends \PHPUnit_Framework_TestCase
use PHPUnit\Framework\TestCase;
class AccountTest extends TestCase
{
public function testAddEntry()
{
$account = new Account("123456", $maxCredit = 200);
$this->assertEquals(0, $account->getBalance());
$account->addEntry(500);
$this->assertEquals(500, $account->getBalance());
$account->addEntry(-700);
$this->assertEquals(-200, $account->getBalance());
}
public function testExceedMaxLimit()
{
$account = new Account("123456", $maxCredit = 200);
$this->setExpectedException("Exception");
$this->expectException(Exception::class);
$account->addEntry(-1000);
}
}
@@ -214,9 +236,12 @@ To enforce our rule we can now implement the assertion in
.. code-block:: php
<?php
class Account
{
private function assertAcceptEntryAllowed($amount)
// .. previous code
private function assertAcceptEntryAllowed(int $amount): void
{
$futureBalance = $this->getBalance() + $amount;
$allowedMinimalBalance = ($this->maxCredit * -1);
@@ -263,21 +288,20 @@ entries collection) we want to add an aggregate field called
/**
* @ORM\Column(type="integer")
*/
private $balance = 0;
public function getBalance()
private int $balance = 0;
public function getBalance(): int
{
return $this->balance;
}
public function addEntry($amount)
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
$e = new Entry($this, $amount);
$this->entries[] = $e;
$this->balance += $amount;
return $e;
}
}
@@ -301,16 +325,19 @@ potentially lead to inconsistent state. See this example:
.. code-block:: php
<?php
use Bank\Entities\Account;
// The Account $accId has a balance of 0 and a max credit limit of 200:
// request 1 account
$account1 = $em->find('Bank\Entities\Account', $accId);
$account1 = $em->find(Account::class, $accId);
// request 2 account
$account2 = $em->find('Bank\Entities\Account', $accId);
$account2 = $em->find(Account::class, $accId);
$account1->addEntry(-200);
$account2->addEntry(-200);
// now request 1 and 2 both flush the changes.
The aggregate field ``Account::$balance`` is now -200, however the
@@ -327,10 +354,14 @@ Optimistic locking is as easy as adding a version column:
.. code-block:: php
<?php
class Account
{
/** @ORM\Column(type="integer") @ORM\Version */
private $version;
/**
* @ORM\Column(type="integer")
* @ORM\Version
*/
private int $version;
}
The previous example would then throw an exception in the face of
@@ -344,9 +375,11 @@ the database using a FOR UPDATE.
.. code-block:: php
<?php
use Bank\Entities\Account;
use Doctrine\DBAL\LockMode;
$account = $em->find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ);
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ);
Keeping Updates and Deletes in Sync
-----------------------------------
@@ -367,4 +400,3 @@ field that offers serious performance benefits over iterating all
the related objects that make up an aggregate value. Finally I
showed how you can ensure that your aggregate fields do not get out
of sync due to race-conditions and concurrent access.
+1 -1
View File
@@ -95,7 +95,7 @@ class name. Now the new type can be used when mapping columns:
<?php
class MyPersistentClass
{
/** @ORM\Column(type="mytype") */
/** @Column(type="mytype") */
private $field;
}
+18 -25
View File
@@ -4,8 +4,8 @@ Persisting the Decorator Pattern
.. sectionauthor:: Chris Woodford <chris.woodford@gmail.com>
This recipe will show you a simple example of how you can use
Doctrine 2 to persist an implementation of the
`Decorator Pattern <https://en.wikipedia.org/wiki/Decorator_pattern>`_
Doctrine ORM to persist an implementation of the
`Decorator Pattern <http://en.wikipedia.org/wiki/Decorator_pattern>`_
Component
---------
@@ -23,26 +23,23 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
namespace Test;
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="discr", type="string")
* @ORM\DiscriminatorMap({
* "cc" = "Test\Component\ConcreteComponent",
* "cd" = "Test\Decorator\ConcreteDecorator"
* })
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"cc" = "Test\Component\ConcreteComponent",
"cd" = "Test\Decorator\ConcreteDecorator"})
*/
abstract class Component
{
/**
* @ORM\Id @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/** @ORM\Column(type="string", nullable=true) */
/** @Column(type="string", nullable=true) */
protected $name;
/**
@@ -87,10 +84,9 @@ purpose of keeping this example simple).
namespace Test\Component;
use Doctrine\ORM\Annotation as ORM;
use Test\Component;
/** @ORM\Entity */
/** @Entity */
class ConcreteComponent extends Component
{}
@@ -107,15 +103,13 @@ use a ``MappedSuperclass`` for this.
namespace Test;
use Doctrine\ORM\Annotation as ORM;
/** @ORM\MappedSuperclass */
/** @MappedSuperclass */
abstract class Decorator extends Component
{
/**
* @ORM\OneToOne(targetEntity="Test\Component", cascade={"all"})
* @ORM\JoinColumn(name="decorates", referencedColumnName="id")
* @OneToOne(targetEntity="Test\Component", cascade={"all"})
* @JoinColumn(name="decorates", referencedColumnName="id")
*/
protected $decorates;
@@ -191,14 +185,13 @@ of the getSpecial() method to its return value.
namespace Test\Decorator;
use Doctrine\ORM\Annotation as ORM;
use Test\Decorator;
/** @ORM\Entity */
/** @Entity */
class ConcreteDecorator extends Decorator
{
/** @ORM\Column(type="string", nullable=true) */
/** @Column(type="string", nullable=true) */
protected $special;
/**
@@ -244,7 +237,7 @@ objects
use Test\Component\ConcreteComponent,
Test\Decorator\ConcreteDecorator;
// assumes Doctrine 2 is configured and an instance of
// assumes Doctrine ORM is configured and an instance of
// an EntityManager is available as $em
// create a new concrete component
+17 -14
View File
@@ -1,4 +1,4 @@
Extending DQL in Doctrine 2: Custom AST Walkers
Extending DQL in Doctrine ORM: Custom AST Walkers
===============================================
.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
@@ -12,9 +12,9 @@ the Doctrine ORM.
In Doctrine 1 the DQL language was not implemented using a real
parser. This made modifications of the DQL by the user impossible.
Doctrine 2 in contrast has a real parser for the DQL language,
Doctrine ORM in contrast has a real parser for the DQL language,
which transforms the DQL statement into an
`Abstract Syntax Tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
`Abstract Syntax Tree <http://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
@@ -28,6 +28,7 @@ generating the SQL statement.
There are two types of custom tree walkers that you can hook into
the DQL parser:
- An output walker. This one actually generates the SQL, and there
is only ever one of them. We implemented the default SqlWalker
implementation for it.
@@ -39,6 +40,7 @@ Now this is all awfully technical, so let me come to some use-cases
fast to keep you motivated. Using walker implementation you can for
example:
- Modify the AST to generate a Count Query to be used with a
paginator for any given DQL query.
- Modify the Output Walker to generate vendor-specific SQL
@@ -86,7 +88,7 @@ API would look for this use-case:
$pageNum = 1;
$query = $em->createQuery($dql);
$query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20);
$totalResults = Paginate::count($query);
$results = $query->getResult();
@@ -99,12 +101,12 @@ The ``Paginate::count(Query $query)`` looks like:
{
static public function count(Query $query)
{
/** @var Query $countQuery */
/* @var $countQuery Query */
$countQuery = clone $query;
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker'));
$countQuery->setFirstResult(null)->setMaxResults(null);
return $countQuery->getSingleScalarResult();
}
}
@@ -128,20 +130,20 @@ implementation is:
{
$parent = null;
$parentName = null;
foreach ($this->getQueryComponents() as $dqlAlias => $qComp) {
foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
$parent = $qComp;
$parentName = $dqlAlias;
break;
}
}
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
$parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$AST->selectClause->selectExpressions = array(
new SelectExpression(
new AggregateExpression('count', $pathExpression, true), null
@@ -165,7 +167,7 @@ can be set via ``Query::setHint($name, $value)`` as shown in the
previous example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint.
We will implement a custom Output Walker that allows to specify the
SQL\_NO\_CACHE query hint.
``SQL_NO_CACHE`` query hint.
.. code-block:: php
@@ -178,7 +180,7 @@ SQL\_NO\_CACHE query hint.
Our ``MysqlWalker`` will extend the default ``SqlWalker``. We will
modify the generation of the SELECT clause, adding the
SQL\_NO\_CACHE on those queries that need it:
``SQL_NO_CACHE`` on those queries that need it:
.. code-block:: php
@@ -194,7 +196,7 @@ SQL\_NO\_CACHE on those queries that need it:
public function walkSelectClause($selectClause)
{
$sql = parent::walkSelectClause($selectClause);
if ($this->getQuery()->getHint('mysqlWalker.sqlNoCache') === true) {
if ($selectClause->isDistinct) {
$sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql);
@@ -202,7 +204,7 @@ SQL\_NO\_CACHE on those queries that need it:
$sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql);
}
}
return $sql;
}
}
@@ -212,3 +214,4 @@ understanding of the DQL Parser and Walkers, but may offer your
huge benefits with using vendor specific features. This would still
allow you write DQL queries instead of NativeQueries to make use of
vendor specific features.
+18 -15
View File
@@ -10,13 +10,14 @@ change it during the life of your project. This decision for a
specific vendor potentially allows you to make use of powerful SQL
features that are unique to the vendor.
It is worth to mention that Doctrine 2 also allows you to handwrite
It is worth to mention that Doctrine ORM also allows you to handwrite
your SQL instead of extending the DQL parser. Extending DQL is sort of an
advanced extension point. You can map arbitrary SQL to your objects
and gain access to vendor specific functionalities using the
``EntityManager#createNativeQuery()`` API as described in
the :doc:`Native Query <../reference/native-sql>` chapter.
The DQL Parser has hooks to register functions that can then be
used in your DQL queries and transformed into SQL, allowing to
extend Doctrines Query capabilities to the vendors strength. This
@@ -44,7 +45,7 @@ configuration:
$config->addCustomStringFunction($name, $class);
$config->addCustomNumericFunction($name, $class);
$config->addCustomDatetimeFunction($name, $class);
$em = EntityManager::create($dbParams, $config);
The ``$name`` is the name the function will be referred to in the
@@ -95,7 +96,7 @@ discuss it step by step:
// (1)
public $firstDateExpression = null;
public $secondDateExpression = null;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER); // (2)
@@ -105,7 +106,7 @@ discuss it step by step:
$this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)
$parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'DATEDIFF(' .
@@ -179,28 +180,28 @@ I'll skip the blah and show the code for this function:
public $firstDateExpression = null;
public $intervalExpression = null;
public $unit = null;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_IDENTIFIER);
$this->intervalExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_IDENTIFIER);
/** @var Lexer $lexer */
/* @var $lexer Lexer */
$lexer = $parser->getLexer();
$this->unit = $lexer->token['value'];
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'DATE_ADD(' .
@@ -239,10 +240,12 @@ functionalities in DQL, we would be excited to see user extensions
that add vendor specific function packages, for example more math
functions, XML + GIS Support, Hashing functions and so on.
For 2.0 we will come with the current set of functions, however for
For ORM we will come with the current set of functions, however for
a future version we will re-evaluate if we can abstract even more
vendor sql functions and extend the DQL languages scope.
Code for this Extension to DQL and other Doctrine Extensions can be
found
`in the GitHub DoctrineExtensions repository <https://github.com/beberlei/DoctrineExtensions>`_.
`in the GitHub DoctrineExtensions repository <http://github.com/beberlei/DoctrineExtensions>`_.
+38 -25
View File
@@ -12,14 +12,12 @@ this working.
Merging entity into an EntityManager
------------------------------------
In Doctrine, an entity objects has to be "managed" by an EntityManager to be
updated. Entities saved into the session are not managed in the next request
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.
It is a good idea to avoid storing entities in serialized formats such as
``$_SESSION``: instead, store the entity identifiers or raw data.
references between other entities. You can achieve this by calling
``EntityManager#merge()``.
For a representative User object the code to get turn an instance from
the session into a managed Doctrine object looks like this:
@@ -28,28 +26,43 @@ the session into a managed Doctrine object looks like this:
<?php
require_once 'bootstrap.php';
$em = GetEntityManager(); // creates an EntityManager
$em = GetEntityManager(); // creates an EntityManager
session_start();
if (isset($_SESSION['user'])) {
$user = $em->find(User::class, $_SESSION['user']);
if (! $user instanceof User) {
// user not found in the database
$_SESSION['user'] = null;
}
if (isset($_SESSION['user']) && $_SESSION['user'] instanceof User) {
$user = $_SESSION['user'];
$user = $em->merge($user);
}
Serializing entities into the session
-------------------------------------
.. note::
Serializing entities in the session means serializing also all associated
entities and collections. While this might look like a quick solution in
simple applications, you will encounter problems due to the fact that the
data in the session is stale.
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.
Serializing entity into the session
-----------------------------------
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.
.. code-block:: php
<?php
require_once 'bootstrap.php';
$em = GetEntityManager(); // creates an EntityManager
$user = $em->find("User", 1);
$em->detach($user);
$_SESSION['user'] = $user;
.. note::
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.
In order to prevent working with stale data, try saving only minimal
information about your entities in your session, without storing entire
entity objects. Should you need the full information of an object, so it
is suggested to re-query the database, which is usually the most
authoritative source of information in typical PHP applications.
@@ -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 <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
for all our domain objects.
Option 1
@@ -16,6 +16,7 @@ In this implementation we will make use of PHPs highly dynamic
nature to dynamically access properties of a subtype in a supertype
at runtime. Note that this implementation has 2 main caveats:
- It will not work with private fields
- It will not go through any getters/setters
@@ -27,15 +28,15 @@ at runtime. Note that this implementation has 2 main caveats:
public function offsetExists($offset) {
return isset($this->$offset);
}
public function offsetSet($offset, $value) {
$this->$offset = $value;
}
public function offsetGet($offset) {
return $this->$offset;
}
public function offsetUnset($offset) {
$this->$offset = null;
}
@@ -49,6 +50,7 @@ Again we use PHPs dynamic nature to invoke methods on a subtype
from a supertype at runtime. This implementation has the following
caveats:
- It relies on a naming convention
- The semantics of offsetExists can differ
- offsetUnset will not work with typehinted setters
@@ -63,15 +65,15 @@ caveats:
$value = $this->{"get$offset"}();
return $value !== null;
}
public function offsetSet($offset, $value) {
$this->{"set$offset"}($value);
}
public function offsetGet($offset) {
return $this->{"get$offset"}();
}
public function offsetUnset($offset) {
$this->{"set$offset"}(null);
}
@@ -93,17 +95,18 @@ exception (i.e. BadMethodCallException).
public function offsetExists($offset) {
// option 1 or option 2
}
public function offsetSet($offset, $value) {
throw new BadMethodCallException("Array access of class " . get_class($this) . " is read-only!");
}
public function offsetGet($offset) {
// option 1 or option 2
}
public function offsetUnset($offset) {
throw new BadMethodCallException("Array access of class " . get_class($this) . " is read-only!");
}
}
@@ -7,7 +7,7 @@ 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 <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
for all our domain objects.
Implementing NotifyPropertyChanged
@@ -22,17 +22,17 @@ implement the ``NotifyPropertyChanged`` interface from the
.. code-block:: php
<?php
use Doctrine\Common\NotifyPropertyChanged;
use Doctrine\Common\PropertyChangedListener;
use Doctrine\Persistence\NotifyPropertyChanged;
use Doctrine\Persistence\PropertyChangedListener;
abstract class DomainObject implements NotifyPropertyChanged
{
private $listeners = array();
public function addPropertyChangedListener(PropertyChangedListener $listener) {
$this->listeners[] = $listener;
}
/** Notifies listeners of a change. */
protected function onPropertyChanged($propName, $oldValue, $newValue) {
if ($this->listeners) {
@@ -50,12 +50,12 @@ listeners:
.. code-block:: php
<?php
// Mapping not shown, either in annotations or xml as usual
// Mapping not shown, either in annotations, xml or yaml as usual
class MyEntity extends DomainObject
{
private $data;
// ... other fields as usual
public function setData($data) {
if ($data != $this->data) { // check: is it actually modified?
$this->onPropertyChanged('data', $this->data, $data);
@@ -69,3 +69,4 @@ not mandatory but recommended. That way you can avoid unnecessary
updates and also have full control over when you consider a
property changed.
@@ -0,0 +1,78 @@
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>`_,
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
way by guarding the custom wakeup or clone code with an entity
identity check, as demonstrated in the following sections.
Safely implementing __wakeup
----------------------------
To safely implement ``__wakeup``, simply enclose your
implementation code in an identity check as follows:
.. code-block:: php
<?php
class MyEntity
{
private $id; // This is the identifier of the entity.
//...
public function __wakeup()
{
// If the entity has an identity, proceed as normal.
if ($this->id) {
// ... Your code here as normal ...
}
// otherwise do nothing, do NOT throw an exception!
}
//...
}
Safely implementing __clone
---------------------------
Safely implementing ``__clone`` is pretty much the same:
.. code-block:: php
<?php
class MyEntity
{
private $id; // This is the identifier of the entity.
//...
public function __clone()
{
// If the entity has an identity, proceed as normal.
if ($this->id) {
// ... Your code here as normal ...
}
// otherwise do nothing, do NOT throw an exception!
}
//...
}
Summary
-------
As you have seen, it is quite easy to safely make use of
``__wakeup`` and ``__clone`` in your entities without adding any
really Doctrine-specific or Doctrine-dependant code.
These implementations are possible and safe because when Doctrine
invokes these methods, the entities never have an identity (yet).
Furthermore, it is possibly a good idea to check for the identity
in your code anyway, since it's rarely the case that you want to
unserialize or clone an entity with no identity.
+8 -17
View File
@@ -1,12 +1,12 @@
Mysql Enums
===========
The type system of Doctrine 2 consists of flyweights, which means there is only
The type system of Doctrine ORM consists of flyweights, which means there is only
one instance of any given type. Additionally types do not contain state. Both
assumptions make it rather complicated to work with the Enum Type of MySQL that
is used quite a lot by developers.
When using Enums with a non-tweaked Doctrine 2 application you will get
When using Enums with a non-tweaked Doctrine ORM application you will get
errors from the Schema-Tool commands due to the unknown database type "enum".
By default Doctrine does not map the MySQL enum type to a Doctrine type.
This is because Enums contain state (their allowed values) and Doctrine
@@ -43,16 +43,13 @@ entities:
.. code-block:: php
<?php
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class Article
{
const STATUS_VISIBLE = 'visible';
const STATUS_INVISIBLE = 'invisible';
/** @ORM\Column(type="string") */
/** @Column(type="string") */
private $status;
public function setStatus($status)
@@ -70,13 +67,10 @@ the **columnDefinition** attribute.
.. code-block:: php
<?php
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class Article
{
/** @ORM\Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
/** @Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
private $status;
}
@@ -137,13 +131,10 @@ Then in your entity you can just use this type:
.. code-block:: php
<?php
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class Article
{
/** @ORM\Column(type="enumvisibility") */
/** @Column(type="enumvisibility") */
private $status;
}
@@ -1,13 +1,11 @@
Keeping your Modules independent
=================================
.. versionadded:: 2.2
One of the goals of using modules is to create discrete units of functionality
that do not have many (if any) dependencies, allowing you to use that
functionality in other applications without including unnecessary items.
Doctrine 2.2 includes a new utility called the ``ResolveTargetEntityListener``,
Doctrine ORM includes a new utility called the ``ResolveTargetEntityListener``,
that functions by intercepting certain calls inside Doctrine and rewrite
targetEntity parameters in your metadata mapping at runtime. It means that
in your bundle you are able to use an interface or abstract class in your
@@ -68,7 +66,7 @@ An Invoice entity
namespace Acme\InvoiceModule\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping AS ORM;
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
/**
@@ -139,3 +137,4 @@ bundles, keeping them usable by themselves, but still being able to
define relationships between different objects. By using this method,
I've found my bundles end up being easier to maintain independently.
+17 -16
View File
@@ -23,32 +23,31 @@ appropriate autoloaders.
.. code-block:: php
<?php
namespace DoctrineExtensions;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Mapping;
use \Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class TablePrefix
{
protected $prefix = '';
public function __construct($prefix)
{
$this->prefix = (string) $prefix;
}
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
if ($classMetadata->inheritanceType !== Mapping\InheritanceType::SINGLE_TABLE ||
$classMetadata->getName() === $classMetadata->rootEntityName) {
$classMetadata->setTableName($this->prefix . $classMetadata->getTableName());
if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {
$classMetadata->setPrimaryTable([
'name' => $this->prefix . $classMetadata->getTableName()
]);
}
foreach ($classMetadata->associationMappings as $fieldName => $mapping) {
if ($mapping['type'] == Mapping\ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) {
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
$mappedTableName = $mapping['joinTable']['name'];
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
}
@@ -69,17 +68,19 @@ before the prefix has been set.
If you set this listener up, be aware that you will need
to clear your caches and drop then recreate your database schema.
.. code-block:: php
<?php
// $connectionOptions and $config set earlier
$evm = new \Doctrine\Common\EventManager;
// Table Prefix
$tablePrefix = new \DoctrineExtensions\TablePrefix('prefix_');
$evm->addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $tablePrefix);
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm);
@@ -12,6 +12,7 @@ Scenario / Problem
Given a Content-Management-System, we probably want to add / edit
some so-called "blocks" and "panels". What are they for?
- A block might be a registration form, some text content, a table
with information. A good example might also be a small calendar.
- A panel is by definition a block that can itself contain blocks.
@@ -22,13 +23,14 @@ So, in this scenario, when building your CMS, you will surely add
lots of blocks and panels to your pages and you will find yourself
highly uncomfortable because of the following:
- Every existing page needs to know about the panels it contains -
therefore, you'll have an association to your panels. But if you've
got several types of panels - what do you do? Add an association to
every panel-type? This wouldn't be flexible. You might be tempted
to add an AbstractPanelEntity and an AbstractBlockEntity that use
class inheritance. Your page could then only confer to the
AbstractPanelType and Doctrine 2 would do the rest for you, i.e.
AbstractPanelType and Doctrine ORM would do the rest for you, i.e.
load the right entities. But - you'll for sure have lots of panels
and blocks, and even worse, you'd have to edit the discriminator
map *manually* every time you or another developer implements a new
@@ -56,6 +58,7 @@ the middle of your page, for example).
Such an interface could look like this:
.. code-block:: php
<?php
@@ -84,12 +87,12 @@ Such an interface could look like this:
* @return \Zend_View_Helper_Interface
*/
public function setView(\Zend_View_Interface $view);
/**
* @return \Zend_View_Interface
*/
public function getView();
/**
* Renders this strategy. This method will be called when the user
* displays the site.
@@ -97,7 +100,7 @@ Such an interface could look like this:
* @return string
*/
public function renderFrontend();
/**
* Renders the backend of this block. This method will be called when
* a user tries to reconfigure this block instance.
@@ -115,21 +118,21 @@ Such an interface could look like this:
* @return array
*/
public function getRequiredPanelTypes();
/**
* Determines whether a Block is able to use a given type or not
* @param string $typeName The typename
* @return boolean
*/
public function canUsePanelType($typeName);
public function setBlockEntity(AbstractBlock $block);
public function getBlockEntity();
}
As you can see, we have a method "setBlockEntity" which ties a potential strategy to an object of type AbstractBlock. This type will simply define the basic behaviour of our blocks and could potentially look something like this:
.. code-block:: php
<?php
@@ -149,16 +152,16 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
/**
* This var contains the classname of the strategy
* that is used for this blockitem. (This string (!) value will be persisted by Doctrine 2)
* that is used for this blockitem. (This string (!) value will be persisted by Doctrine ORM)
*
* This is a doctrine field, so make sure that you use an @column annotation or setup your
* xml files correctly
* yaml or xml files correctly
* @var string
*/
protected $strategyClassName;
/**
* This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine 2.
* This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine ORM.
*
* @var BlockStrategyInterface
*/
@@ -174,7 +177,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
public function getStrategyClassName() {
return $this->strategyClassName;
}
/**
* Returns the instantiated strategy
*
@@ -183,7 +186,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
public function getStrategyInstance() {
return $this->strategyInstance;
}
/**
* Sets the strategy this block / panel should work as. Make sure that you've used
* this method before persisting the block!
@@ -196,7 +199,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
$strategy->setBlockEntity($this);
}
Now, the important point is that $strategyClassName is a Doctrine 2
Now, the important point is that $strategyClassName is a Doctrine ORM
field, i.e. Doctrine will persist this value. This is only the
class name of your strategy and not an instance!
@@ -210,28 +213,28 @@ This might look like this:
.. code-block:: php
<?php
use Doctrine\ORM,
Doctrine\Common;
use \Doctrine\ORM,
\Doctrine\Common;
/**
* The BlockStrategyEventListener will initialize a strategy after the
* block itself was loaded.
*/
class BlockStrategyEventListener implements Common\EventSubscriber {
protected $view;
public function __construct(\Zend_View_Interface $view) {
$this->view = $view;
}
public function getSubscribedEvents() {
return array(ORM\Events::postLoad);
}
public function postLoad(ORM\Event\LifecycleEventArgs $args) {
$blockItem = $args->getEntity();
// Both blocks and panels are instances of Block\AbstractBlock
if ($blockItem instanceof Block\AbstractBlock) {
$strategy = $blockItem->getStrategyClassName();
@@ -248,3 +251,4 @@ This might look like this:
In this example, even some variables are set - like a view object
or a specific configuration object.
+17 -19
View File
@@ -3,7 +3,7 @@ Validation of Entities
.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
Doctrine 2 does not ship with any internal validators, the reason
Doctrine ORM does not ship with any internal validators, the reason
being that we think all the frameworks out there already ship with
quite decent ones that can be integrated into your Domain easily.
What we offer are hooks to execute any kind of validation.
@@ -15,6 +15,7 @@ What we offer are hooks to execute any kind of validation.
perform validations in value setters or any other method of your
entities that are used in your code.
Entities can register lifecycle event methods with Doctrine that
are called on different occasions. For validation we would need to
hook into the events called before persisting and updating. Even
@@ -24,8 +25,8 @@ the additional benefit of being able to re-use your validation in
any other part of your domain.
Say we have an ``Order`` with several ``OrderLine`` instances. We
never want to allow any customer to order for a larger sum than he
is allowed to:
never want to allow any customer to order for a larger sum than they
are allowed to:
.. code-block:: php
@@ -35,12 +36,12 @@ is allowed to:
public function assertCustomerAllowedBuying()
{
$orderLimit = $this->customer->getOrderLimit();
$amount = 0;
foreach ($this->orderLines as $line) {
$amount += $line->getAmount();
}
if ($amount > $orderLimit) {
throw new CustomerOrderLimitExceededException();
}
@@ -57,17 +58,14 @@ First Annotations:
.. code-block:: php
<?php
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
* @Entity
* @HasLifecycleCallbacks
*/
class Order
{
/**
* @ORM\PrePersist @ORM\PreUpdate
* @PrePersist @PreUpdate
*/
public function assertCustomerAllowedBuying() {}
}
@@ -79,12 +77,15 @@ In XML Mappings:
<doctrine-mapping>
<entity name="Order">
<lifecycle-callbacks>
<lifecycle-callback type="prePersist" method="assertCustomerAllowedBuying" />
<lifecycle-callback type="preUpdate" method="assertCustomerAllowedBuying" />
<lifecycle-callback type="prePersist" method="assertCustomerallowedBuying" />
<lifecycle-callback type="preUpdate" method="assertCustomerallowedBuying" />
</lifecycle-callbacks>
</entity>
</doctrine-mapping>
YAML needs some little change yet, to allow multiple lifecycle
events for one method, this will happen before Beta 1 though.
Now validation is performed whenever you call
``EntityManager#persist($order)`` or when you call
``EntityManager#flush()`` and an order is about to be updated. Any
@@ -98,24 +99,21 @@ validation callbacks.
.. code-block:: php
<?php
use Doctrine\ORM\Annotation as ORM;
class Order
{
/**
* @ORM\PrePersist @ORM\PreUpdate
* @PrePersist @PreUpdate
*/
public function validate()
{
if (!($this->plannedShipDate instanceof DateTime)) {
throw new ValidateException();
}
if ($this->plannedShipDate->format('U') < time()) {
throw new ValidateException();
}
if ($this->customer == null) {
throw new OrderRequiresCustomerException();
}
+16 -18
View File
@@ -3,7 +3,7 @@ Working with DateTime Instances
There are many nitty gritty details when working with PHPs DateTime instances. You have to know their inner
workings pretty well not to make mistakes with date handling. This cookbook entry holds several
interesting pieces of information on how to work with PHP DateTime instances in Doctrine 2.
interesting pieces of information on how to work with PHP DateTime instances in ORM.
DateTime changes are detected by Reference
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -15,13 +15,10 @@ these comparisons are always made **BY REFERENCE**. That means the following cha
.. code-block:: php
<?php
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class Article
{
/** @ORM\Column(type="datetime") */
/** @Column(type="datetime") */
private $updated;
public function setUpdated()
@@ -61,16 +58,16 @@ Handling different Timezones with the DateTime Type
If you first come across the requirement to save different timezones you may be still optimistic about how
to manage this mess,
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2)
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine ORM)
that supports timezones correctly. Correctly here means that you can cover all the use-cases that
can come up with timezones. If you don't believe me you should read up on `Storing DateTime
in Databases <https://derickrethans.nl/storing-date-time-in-database.html>`_.
in Databases <http://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
in different offset directions depending on the real location.
The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine 2. However there is a workaround
The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine ORM. However there is a workaround
that even allows correct date-time handling with timezones:
1. Always convert any DateTime instance to UTC.
@@ -93,7 +90,10 @@ the UTC time at the time of the booking and the timezone the event happened in.
class UTCDateTimeType extends DateTimeType
{
static private $utc;
/**
* @var \DateTimeZone
*/
private static $utc;
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
@@ -127,9 +127,9 @@ the UTC time at the time of the booking and the timezone the event happened in.
return $converted;
}
private static function getUtc()
private static function getUtc(): \DateTimeZone
{
return self::$utc ? self::$utc : self::$utc = new \DateTimeZone('UTC');
return self::$utc ?: self::$utc = new \DateTimeZone('UTC');
}
}
@@ -149,6 +149,7 @@ code before bootstrapping the ORM:
Type::overrideType('datetime', UTCDateTimeType::class);
Type::overrideType('datetimetz', UTCDateTimeType::class);
To be able to transform these values
back into their real timezone you have to save the timezone in a separate field of the entity
requiring timezoned datetimes:
@@ -156,20 +157,17 @@ requiring timezoned datetimes:
.. code-block:: php
<?php
namespace Shipping;
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @Entity
*/
class Event
{
/** @ORM\Column(type="datetime") */
/** @Column(type="datetime") */
private $created;
/** @ORM\Column(type="string") */
/** @Column(type="string") */
private $timezone;
/**
+110 -6
View File
@@ -1,8 +1,10 @@
ORM Documentation
=================
Welcome to Doctrine 2 ORM's documentation!
==========================================
The Doctrine ORM documentation is comprised of tutorials, a reference section and
cookbook articles that explain different parts of the Object Relational Mapper.
The Doctrine documentation is comprised of tutorials, a reference section and
cookbook articles that explain different parts of the Object Relational mapper.
Doctrine DBAL and Doctrine Common both have their own documentation.
Getting Help
------------
@@ -17,8 +19,110 @@ Doctrine ORM don't panic. You can get help from different sources:
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
If you need more structure over the different topics you can browse the :doc:`table
of contents <toc>`.
Getting Started
---------------
The best way to get started is with the :doc:`Getting Started with Doctrine <tutorials/getting-started>` tutorial.
Use the sidebar to browse other tutorials and documentation for the Doctrine PHP ORM.
* **Tutorial**:
:doc:`Getting Started with Doctrine <tutorials/getting-started>`
* **Setup**:
:doc:`Installation & Configuration <reference/configuration>`
Mapping Objects onto a Database
-------------------------------
* **Mapping**:
:doc:`Objects <reference/basic-mapping>` |
:doc:`Associations <reference/association-mapping>` |
:doc:`Inheritance <reference/inheritance-mapping>`
* **Drivers**:
:doc:`Docblock Annotations <reference/annotations-reference>` |
:doc:`XML <reference/xml-mapping>` |
:doc:`YAML <reference/yaml-mapping>` |
:doc:`PHP <reference/php-mapping>`
Working with Objects
--------------------
* **Basic Reference**:
:doc:`Entities <reference/working-with-objects>` |
:doc:`Associations <reference/working-with-associations>` |
:doc:`Events <reference/events>`
* **Query Reference**:
:doc:`DQL <reference/dql-doctrine-query-language>` |
:doc:`QueryBuilder <reference/query-builder>` |
:doc:`Native SQL <reference/native-sql>`
* **Internals**:
:doc:`Internals explained <reference/unitofwork>` |
:doc:`Associations <reference/unitofwork-associations>`
Advanced Topics
---------------
* :doc:`Architecture <reference/architecture>`
* :doc:`Advanced Configuration <reference/advanced-configuration>`
* :doc:`Limitations and known issues <reference/limitations-and-known-issues>`
* :doc:`Commandline Tools <reference/tools>`
* :doc:`Transactions and Concurrency <reference/transactions-and-concurrency>`
* :doc:`Filters <reference/filters>`
* :doc:`NamingStrategy <reference/namingstrategy>`
* :doc:`Improving Performance <reference/improving-performance>`
* :doc:`Caching <reference/caching>`
* :doc:`Partial Objects <reference/partial-objects>`
* :doc:`Change Tracking Policies <reference/change-tracking-policies>`
* :doc:`Best Practices <reference/best-practices>`
* :doc:`Metadata Drivers <reference/metadata-drivers>`
* :doc:`Batch Processing <reference/batch-processing>`
* :doc:`Second Level Cache <reference/second-level-cache>`
Tutorials
---------
* :doc:`Indexed associations <tutorials/working-with-indexed-associations>`
* :doc:`Extra Lazy Associations <tutorials/extra-lazy-associations>`
* :doc:`Composite Primary Keys <tutorials/composite-primary-keys>`
* :doc:`Ordered associations <tutorials/ordered-associations>`
* :doc:`Pagination <tutorials/pagination>`
* :doc:`Override Field/Association Mappings In Subclasses <tutorials/override-field-association-mappings-in-subclasses>`
* :doc:`Embeddables <tutorials/embeddables>`
Changelogs
----------
* `Upgrade <https://github.com/doctrine/doctrine2/blob/master/UPGRADE.md>`_
Cookbook
--------
* **Patterns**:
:doc:`Aggregate Fields <cookbook/aggregate-fields>` |
:doc:`Decorator Pattern <cookbook/decorator-pattern>` |
:doc:`Strategy Pattern <cookbook/strategy-cookbook-introduction>`
* **DQL Extension Points**:
:doc:`DQL Custom Walkers <cookbook/dql-custom-walkers>` |
:doc:`DQL User-Defined-Functions <cookbook/dql-user-defined-functions>`
* **Implementation**:
:doc:`Array Access <cookbook/implementing-arrayaccess-for-domain-objects>` |
:doc:`Notify ChangeTracking Example <cookbook/implementing-the-notify-changetracking-policy>` |
:doc:`Using Wakeup Or Clone <cookbook/implementing-wakeup-or-clone>` |
:doc:`Working with DateTime <cookbook/working-with-datetime>` |
:doc:`Validation <cookbook/validation-of-entities>` |
:doc:`Entities in the Session <cookbook/entities-in-session>` |
:doc:`Keeping your Modules independent <cookbook/resolve-target-entity-listener>`
* **Hidden Gems**
:doc:`Prefixing Table Name <cookbook/sql-table-prefixes>`
* **Custom Datatypes**
:doc:`MySQL Enums <cookbook/mysql-enums>`
:doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`
.. include:: toc.rst
+113
View File
@@ -0,0 +1,113 @@
@ECHO OFF
REM Command file for Sphinx documentation
set SPHINXBUILD=sphinx-build
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end
+137 -106
View File
@@ -9,16 +9,15 @@ steps of configuration.
.. code-block:: php
<?php
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Configuration;
use Doctrine\Common\Proxy\ProxyFactory;
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Configuration;
// ...
if ($applicationMode == "development") {
$cache = new \Doctrine\Common\Cache\ArrayCache;
} else {
$cache = new \Doctrine\Common\Cache\ApcuCache;
$cache = new \Doctrine\Common\Cache\ApcCache;
}
$config = new Configuration;
@@ -28,16 +27,17 @@ steps of configuration.
$config->setQueryCacheImpl($cache);
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
$config->setProxyNamespace('MyProject\Proxies');
$config->setAutoGenerateProxyClasses($applicationMode === 'development')
if ('development' === $applicationMode) {
$config->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL);
if ($applicationMode == "development") {
$config->setAutoGenerateProxyClasses(true);
} else {
$config->setAutoGenerateProxyClasses(false);
}
$connectionOptions = [
$connectionOptions = array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite'
];
);
$em = EntityManager::create($connectionOptions, $config);
@@ -50,8 +50,9 @@ 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 `APCu <https://php.net/apcu>`_. APCu provides you with
a very fast in-memory cache storage that you can use for the metadata and
Doctrine is `APC <http://www.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.
Configuration Options
@@ -60,32 +61,30 @@ Configuration Options
The following sections describe all the configuration options
available on a ``Doctrine\ORM\Configuration`` instance.
Proxy Directory
~~~~~~~~~~~~~~~
Proxy Directory (***REQUIRED***)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: php
<?php
$config->setProxyDir($dir);
$config->getProxyDir();
Sets the directory where Doctrine generates any proxy
Gets or sets the directory where Doctrine generates any proxy
classes. For a detailed explanation on proxy classes and how they
are used in Doctrine, refer to the "Proxy Objects" section further
down.
Setting the proxy target directory will also implicitly cause a
call to ``Doctrine\ORM\Configuration#setAutoGenerateProxyClasses()``
with a value of ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``.
Proxy Namespace
~~~~~~~~~~~~~~~
Proxy Namespace (***REQUIRED***)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: php
<?php
$config->setProxyNamespace($namespace);
$config->getProxyNamespace();
Sets the namespace to use for generated proxy classes. For
Gets or sets the namespace to use for generated proxy classes. For
a detailed explanation on proxy classes and how they are used in
Doctrine, refer to the "Proxy Objects" section further down.
@@ -104,13 +103,16 @@ classes.
There are currently 4 available implementations:
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
- ``Doctrine\ORM\Mapping\Driver\YamlDriver``
- ``Doctrine\ORM\Mapping\Driver\DriverChain``
Throughout the most part of this manual the AnnotationDriver is
used in the examples. For information on the usage of the XmlDriver
please refer to the dedicated chapters ``XML Mapping``.
or YamlDriver please refer to the dedicated chapters
``XML Mapping`` and ``YAML Mapping``.
The annotation driver can be configured with a factory method on
the ``Doctrine\ORM\Configuration``:
@@ -139,7 +141,7 @@ Metadata Cache (***RECOMMENDED***)
Gets or sets the cache implementation to use for caching metadata
information, that is, all the information you supply via
annotations or xml, so that they do not need to be parsed and
annotations, xml or yaml, so that they do not need to be parsed and
loaded from scratch on every single request which is a waste of
resources. The cache implementation must implement the
``Doctrine\Common\Cache\Cache`` interface.
@@ -148,6 +150,8 @@ Usage of a metadata cache is highly recommended.
The recommended implementations for production are:
- ``Doctrine\Common\Cache\ApcCache``
- ``Doctrine\Common\Cache\ApcuCache``
- ``Doctrine\Common\Cache\MemcacheCache``
- ``Doctrine\Common\Cache\XcacheCache``
@@ -178,6 +182,8 @@ Usage of a query cache is highly recommended.
The recommended implementations for production are:
- ``Doctrine\Common\Cache\ApcCache``
- ``Doctrine\Common\Cache\ApcuCache``
- ``Doctrine\Common\Cache\MemcacheCache``
- ``Doctrine\Common\Cache\XcacheCache``
@@ -203,8 +209,8 @@ implementation that logs to the standard output using ``echo`` and
``var_dump`` can be found at
``Doctrine\DBAL\Logging\EchoSQLLogger``.
Auto-generating Proxy Classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Auto-generating Proxy Classes (***OPTIONAL***)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Proxy classes can either be generated manually through the Doctrine
Console or automatically at runtime by Doctrine. The configuration
@@ -217,89 +223,65 @@ option that controls this behavior is:
Possible values for ``$mode`` are:
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER``
Generate the proxy class when the proxy file does not exist.
This strategy can potentially cause disk access.
Note that autoloading will be attempted before falling back
to generating a proxy class: if an already existing proxy class
is found, then no file write operations will be performed.
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_EVAL``
Generate the proxy classes and evaluate them on the fly via ``eval()``,
avoiding writing the proxies to disk.
This strategy is only sane for development and long running
processes.
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_NEVER``
This flag is deprecated, and is an alias
of ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_EVAL``
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_ALWAYS``
This flag is deprecated, and is an alias
of ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean
value. This is still possible, ``FALSE`` being equivalent to
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
Manually generating Proxy Classes for performance
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While the ORM can generate proxy classes when required, it is suggested
to not let this happen for production environments, as it has a major
impact on your application's performance.
In a production environment, it is highly recommended to use
``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
in combination with a well-configured
`composer class autoloader<https://getcomposer.org/doc/01-basic-usage.md#autoloading>`_.
Here is an example of such setup:
.. code-block:: json
{
"autoload": {
"psr-4": {
"MyProject\\": "path/to/project/sources/",
"GeneratedProxies\\": "path/to/generated/proxies/"
}
}
}
You would then configure the ORM to use the ``"GeneratedProxies"``
and the ``"path/to/generated/proxies/"`` for the proxy classes:
Never autogenerate a proxy. You will need to generate the proxies
manually, for this use the Doctrine Console like so:
.. code-block:: php
<?php
$config->setProxyDir('path/to/generated/proxies/');
$config->setProxyNamespace('GeneratedProxies');
$ ./doctrine orm:generate-proxies
To make sure proxies are never generated by Doctrine, you'd forcefully
generate them during deployment operations:
When you do this in a development environment,
be aware that you may get class/file not found errors if certain proxies
are not yet generated. You may also get failing lazy-loads if new
methods were added to the entity class that are not yet in the proxy class.
In such a case, simply use the Doctrine Console to (re)generate the
proxy classes.
.. code-block:: sh
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS``
$ ./vendor/bin/doctrine orm:generate-proxies
$ composer dump-autoload
Always generates a new proxy in every request and writes it to disk.
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
Generate the proxy class when the proxy file does not exist.
This strategy causes a file exists call whenever any proxy is
used the first time in a request.
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL``
Generate the proxy classes and evaluate them on the fly via eval(),
avoiding writing the proxies to disk.
This strategy is only sane for development.
In a production environment, it is highly recommended to use
AUTOGENERATE_NEVER to allow for optimal performances. The other
options are interesting in development environment.
``setAutoGenerateProxyClasses`` can accept a boolean
value. This is still possible, ``FALSE`` being equivalent to
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
Development vs Production Configuration
---------------------------------------
You should code your Doctrine2 bootstrapping with two different
runtime models in mind. There are some serious benefits of using
APCu or Memcache in production. In development however this will
APC or Memcache in production. In development however this will
frequently give you fatal errors, when you change your entities and
the cache still keeps the outdated metadata. That is why we
recommend the ``ArrayCache`` for development.
Furthermore you should disable the Auto-generating Proxy Classes
option in production.
Furthermore you should have the Auto-generating Proxy Classes
option to true in development and to false in production. If this
option is set to ``TRUE`` it can seriously hurt your script
performance if several proxy classes are re-generated during script
execution. Filesystem calls of that magnitude can even slower than
all the database queries Doctrine issues. Additionally writing a
proxy sets an exclusive file lock which can cause serious
performance bottlenecks in systems with regular concurrent
requests.
Connection Options
------------------
@@ -317,7 +299,7 @@ Proxy Objects
A proxy object is an object that is put in place or used instead of
the "real" object. A proxy object can add behavior to the object
being proxied without that object being aware of it. In Doctrine 2,
being proxied without that object being aware of it. In ORM,
proxy objects are used to realize several features but mainly for
transparent lazy-loading.
@@ -327,7 +309,7 @@ of the objects. This is an essential property as without it there
would always be fragile partial objects at the outer edges of your
object graph.
Doctrine 2 implements a variant of the proxy pattern where it
Doctrine ORM implements a variant of the proxy pattern where it
generates classes that extend your entity classes and adds
lazy-loading capabilities to them. Doctrine can then give you an
instance of such a proxy class whenever you request an object of
@@ -348,17 +330,16 @@ identifier. You could simply do this:
<?php
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
// $itemId comes from somewhere, probably a request parameter
$item = $em->getReference(\MyProject\Model\Item::class, $itemId);
$item = $em->getReference('MyProject\Model\Item', $itemId);
$cart->addItem($item);
Here, we added an ``Item`` to a ``Cart`` without loading the Item from the
database.
If you access any persistent state that isn't yet available in the ``Item``
instance, the proxying mechanism would fully initialize the object's state
transparently from the database.
Here ``$item`` is actually an instance of the proxy class that was generated
for the ``Item`` class but your code does not need to care. In fact it
**should not care**. Proxy objects should be transparent to your code.
Here, we added an Item to a Cart without loading the Item from the
database. If you invoke any method on the Item instance, it would
fully initialize its state transparently from the database. Here
$item is actually an instance of the proxy class that was generated
for the Item class but your code does not need to care. In fact it
**should not care**. Proxy objects should be transparent to your
code.
Association proxies
~~~~~~~~~~~~~~~~~~~
@@ -366,7 +347,7 @@ Association proxies
The second most important situation where Doctrine uses proxy
objects is when querying for objects. Whenever you query for an
object that has a single-valued association to another object that
is configured ``LAZY``, without joining that association in the same
is configured LAZY, without joining that association in the same
query, Doctrine puts proxy objects in place where normally the
associated object would be. Just like other proxies it will
transparently initialize itself on first access.
@@ -378,12 +359,61 @@ transparently initialize itself on first access.
This will override the 'fetch' option specified in the mapping for
that association, but only for that query.
Generating Proxy classes
~~~~~~~~~~~~~~~~~~~~~~~~
In a production environment, it is highly recommended to use
``AUTOGENERATE_NEVER`` to allow for optimal performances.
However you will be required to generate the proxies manually
using the Doctrine Console:
.. code-block:: php
$ ./doctrine orm:generate-proxies
The other options are interesting in development environment:
- ``AUTOGENERATE_ALWAYS`` will require you to create and configure
a proxy directory. Proxies will be generated and written to file
on each request, so any modification to your code will be acknowledged.
- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing
proxy file. If your code changes, you will need to regenerate the
proxies manually.
- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request,
but without writing them to disk.
Autoloading Proxies
-------------------
When you deserialize proxy objects from the session or any other storage
it is necessary to have an autoloading mechanism in place for these classes.
For implementation reasons Proxy class names are not PSR-0 compliant. This
means that you have to register a special autoloader for these classes:
.. code-block:: php
<?php
use Doctrine\Common\Proxy\Autoloader;
$proxyDir = "/path/to/proxies";
$proxyNamespace = "MyProxies";
Autoloader::register($proxyDir, $proxyNamespace);
If you want to execute additional logic to intercept the proxy file not found
state you can pass a closure as the third argument. It will be called with
the arguments proxydir, namespace and className when the proxy file could not
be found.
Multiple Metadata Sources
-------------------------
When using different components using Doctrine 2 you may end up
When using different components using Doctrine ORM you may end up
with them using two different metadata drivers, for example XML and
annotationsL. You can use the DriverChain Metadata implementations to
YAML. You can use the DriverChain Metadata implementations to
aggregate these drivers based on namespaces:
.. code-block:: php
@@ -393,7 +423,7 @@ aggregate these drivers based on namespaces:
$chain = new DriverChain();
$chain->addDriver($xmlDriver, 'Doctrine\Tests\Models\Company');
$chain->addDriver($annotationDriver, 'Doctrine\Tests\ORM\Mapping');
$chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping');
Based on the namespace of the entity the loading of entities is
delegated to the appropriate driver. The chain semantics come from
@@ -403,6 +433,7 @@ the entity class name against the namespace using a
correctly if sub-namespaces use different metadata driver
implementations.
Default Repository (***OPTIONAL***)
-----------------------------------
+49 -25
View File
@@ -5,29 +5,29 @@ You've probably used docblock annotations in some form already,
most likely to provide documentation metadata for a tool like
``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a
tool to embed metadata inside the documentation section which can
then be processed by some tool. Doctrine 2 generalizes the concept
then be processed by some tool. Doctrine ORM generalizes the concept
of docblock annotations so that they can be used for any kind of
metadata and so that it is easy to define new docblock annotations.
In order to allow more involved annotation values and to reduce the
chances of clashes with other docblock annotations, the Doctrine 2
chances of clashes with other docblock annotations, the Doctrine ORM
docblock annotations feature an alternative syntax that is heavily
inspired by the Annotation syntax introduced in Java 5.
The implementation of these enhanced docblock annotations is
located in the ``Doctrine\Common\Annotations`` namespace and
therefore part of the Common package. Doctrine 2 docblock
therefore part of the Common package. Doctrine ORM docblock
annotations support namespaces and nested annotations among other
things. The Doctrine 2 ORM defines its own set of docblock
things. The Doctrine ORM ORM defines its own set of docblock
annotations for supplying object-relational mapping metadata.
.. note::
If you're not comfortable with the concept of docblock
annotations, don't worry, as mentioned earlier Doctrine 2 provides
the XML alternative and you could easily implement your own
annotations, don't worry, as mentioned earlier Doctrine ORM provides
XML and YAML alternatives and you could easily implement your own
favourite mechanism for defining ORM metadata.
In this chapter a reference of every Doctrine 2 Annotation is given
In this chapter a reference of every Doctrine ORM Annotation is given
with short explanations on their context and usage.
Index
@@ -99,7 +99,7 @@ Optional attributes:
- **length**: Used by the "string" type to determine its maximum
length in the database. Doctrine does not validate the length of a
string values for you.
string value for you.
- **precision**: The precision for a decimal (exact numeric) column
(applies only for decimal column), which is the maximum number of
@@ -213,7 +213,7 @@ Optional attributes:
~~~~~~~~~~~~~~~~~~~~~
The Change Tracking Policy annotation allows to specify how the
Doctrine 2 UnitOfWork should detect changes in properties of
Doctrine ORM UnitOfWork should detect changes in properties of
entities during flush. By default each entity is checked according
to a deferred implicit strategy, which means upon flush UnitOfWork
compares all the properties of an entity to a previously stored
@@ -276,11 +276,13 @@ to a string column of length 255 called ``dtype``.
Required attributes:
- **name**: The column name of the discriminator. This name is also
used during Array hydration as key to specify the class-name.
Optional attributes:
- **type**: By default this is string.
- **length**: By default this is 255.
@@ -310,6 +312,7 @@ depending on whether the classes are in the namespace or not.
// ...
}
.. _annref_embeddable:
@Embeddable
@@ -336,6 +339,7 @@ annotation to establish the relationship between the two classes.
*/
private $address;
.. _annref_embedded:
@Embedded
@@ -348,6 +352,7 @@ Required attributes:
- **class**: The embeddable class
.. code-block:: php
<?php
@@ -367,6 +372,7 @@ Required attributes:
{
// ...
.. _annref_entity:
@Entity
@@ -377,11 +383,12 @@ the persistence of all classes marked as entities.
Optional attributes:
- **repositoryClass**: Specifies the FQCN of a subclass of the
EntityRepository. Use of repositories for entities is encouraged to keep
specialized DQL and SQL operations separated from the Model/Domain
Layer.
- **readOnly**: (>= 2.1) Specifies that this entity is marked as read only and not
- **readOnly**: Specifies that this entity is marked as read only and not
considered for change-tracking. Entities of this type can be persisted
and removed though.
@@ -391,11 +398,11 @@ Example:
<?php
/**
* @Entity(repositoryClass="MyProject\UserRepository", readOnly=true)
* @Entity(repositoryClass="MyProject\UserRepository")
*/
class User
{
// ...
//...
}
.. _annref_entity_result:
@@ -426,6 +433,7 @@ Required attributes:
- **name**: Name of the persistent field or property of the class.
Optional attributes:
- **column**: Name of the column in the SELECT clause.
@@ -445,9 +453,9 @@ used as default.
Optional attributes:
- **strategy**: Set the name of the identifier generation strategy.
Valid values are ``AUTO``, ``SEQUENCE``, ``TABLE``, ``IDENTITY``, ``CUSTOM`` and ``NONE``, explained
in the :ref:`Identifier Generation Strategies <identifier-generation-strategies>` section.
Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE.
If not specified, default value is AUTO.
Example:
@@ -503,6 +511,7 @@ has meaning in the SchemaTool schema generation context.
Required attributes:
- **name**: Name of the Index
- **columns**: Array of columns.
@@ -610,20 +619,17 @@ Examples:
This annotation is used in the context of relations in
:ref:`@ManyToOne <annref_manytoone>`, :ref:`@OneToOne <annref_onetoone>` fields
and in the Context of :ref:`@JoinTable <annref_jointable>` nested inside
a @ManyToMany. This annotation is not required. If it is not
specified the attributes *name* and *referencedColumnName* are
inferred from the table and primary key names.
a @ManyToMany. If this annotation or both *name* and *referencedColumnName*
are missing they will be computed considering the field's name and the current
:doc:`naming strategy <namingstrategy>`.
Required attributes:
Optional attributes:
- **name**: Column name that holds the foreign key identifier for
this relation. In the context of @JoinTable it specifies the column
name in the join table.
- **referencedColumnName**: Name of the primary key identifier that
is used for joining of this relation.
Optional attributes:
is used for joining of this relation. Defaults to *id*.
- **unique**: Determines whether this relation is exclusive between the
affected entities and should be enforced as such on the database
constraint level. Defaults to false.
@@ -674,6 +680,7 @@ using the affected table and the column names.
Optional attributes:
- **name**: Database name of the join-table
- **joinColumns**: An array of @JoinColumn annotations describing the
join-relation between the owning entities table and the join table.
@@ -705,12 +712,14 @@ describes a many-to-one relationship between two entities.
Required attributes:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
- **cascade**: Cascade Option
- **fetch**: One of LAZY or EAGER
- inversedBy - The inversedBy attribute designates the field in
@@ -739,12 +748,14 @@ entities.
Required attributes:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
- **mappedBy**: This option specifies the property name on the
targetEntity that is the owning side of this relation. It is a
required attribute for the inverse side of a relationship.
@@ -800,7 +811,8 @@ The @MappedSuperclass annotation cannot be used in conjunction with
Optional attributes:
- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository.
- **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository.
That will be inherited for all subclasses of that Mapped Superclass.
Example:
@@ -836,11 +848,13 @@ Required attributes:
- **name**: The name used to refer to the query with the EntityManager methods that create query objects.
- **query**: The SQL query string.
Optional attributes:
- **resultClass**: The class of the result.
- **resultSetMapping**: The name of a SqlResultSetMapping, as defined in metadata.
Example:
.. code-block:: php
@@ -906,12 +920,14 @@ primary key column names apply here too.
Required attributes:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
- **cascade**: Cascade Option
- **fetch**: One of LAZY or EAGER
- **orphanRemoval**: Boolean that specifies if orphans, inverse
@@ -938,12 +954,14 @@ Example:
Required attributes:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
- **cascade**: Cascade Option
- **orphanRemoval**: Boolean that specifies if orphans, inverse
OneToOne entities that are not connected to any owning instance,
@@ -960,7 +978,7 @@ Example:
<?php
/**
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
*/
public $phonenumbers;
@@ -1068,10 +1086,12 @@ the increment size and initial values of the sequence.
Required attributes:
- **sequenceName**: Name of the sequence
Optional attributes:
- **allocationSize**: Increment the sequence by the allocation size
when its fetched. A value larger than 1 allows optimization for
scenarios where you create more than one new entity per request.
@@ -1102,6 +1122,7 @@ Required attributes:
- **name**: The name given to the result set mapping, and used to refer to it in the methods of the Query API.
Optional attributes:
- **entities**: Array of @EntityResult, Specifies the result set mapping to entities.
@@ -1202,13 +1223,15 @@ unqualified classname.
Required attributes:
- **name**: Name of the table
Optional attributes:
- **indexes**: Array of @Index annotations
- **uniqueConstraints**: Array of @UniqueConstraint annotations.
- **schema**: (>= 2.5) Name of the schema the table lies in.
- **schema**: Name of the schema the table lies in.
Example:
@@ -1238,6 +1261,7 @@ context.
Required attributes:
- **name**: Name of the Index
- **columns**: Array of columns.
+35 -20
View File
@@ -2,29 +2,29 @@ Architecture
============
This chapter gives an overview of the overall architecture,
terminology and constraints of Doctrine 2. It is recommended to
terminology and constraints of Doctrine ORM. It is recommended to
read this chapter carefully.
Using an Object-Relational Mapper
---------------------------------
As the term ORM already hints at, Doctrine 2 aims to simplify the
As the term ORM already hints at, Doctrine ORM aims to simplify the
translation between database rows and the PHP object model. The
primary use case for Doctrine are therefore applications that
utilize the Object-Oriented Programming Paradigm. For applications
that do not primarily work with objects Doctrine 2 is not suited very
that do not primarily work with objects Doctrine ORM is not suited very
well.
Requirements
------------
Doctrine 2 requires a minimum of PHP 7.1. For greatly improved
Doctrine ORM requires a minimum of PHP 7.1. For greatly improved
performance it is also recommended that you use APC with PHP.
Doctrine 2 Packages
Doctrine ORM Packages
-------------------
Doctrine 2 is divided into three main packages.
Doctrine ORM is divided into three main packages.
- Common
- DBAL (includes Common)
@@ -34,6 +34,7 @@ This manual mainly covers the ORM package, sometimes touching parts
of the underlying DBAL and Common packages. The Doctrine code base
is split in to these packages for a few reasons and they are to...
- ...make things more maintainable and decoupled
- ...allow you to use the code in Doctrine Common without the ORM
or DBAL
@@ -71,16 +72,27 @@ Entities
An entity is a lightweight, persistent domain object. An entity can
be any regular PHP class observing the following restrictions:
- An entity class must not be final or contain final methods.
- All persistent properties/field of any entity class should
always be private or protected, otherwise lazy-loading might not
work as expected. In case you serialize entities (for example Session)
properties should be protected (See Serialize section below).
- An entity class must not implement ``__clone`` or
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
- 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>`_
instead.
- Any two entity classes in a class hierarchy that inherit
directly or indirectly from one another must not have a mapped
property with the same name. That is, if B inherits from A then B
must not have a mapped field with the same name as an already
mapped field that is inherited from A.
- An entity cannot make use of func_get_args() to implement variable parameters.
Generated proxies do not support this for performance reasons and your code might
actually fail to work when violating this restriction.
Entities support inheritance, polymorphic associations, and
polymorphic queries. Both abstract and concrete classes can be
@@ -94,12 +106,14 @@ classes, and non-entity classes may extend entity classes.
never calls entity constructors, thus you are free to use them as
you wish and even have it require arguments of any type.
Entity states
~~~~~~~~~~~~~
An entity instance can be characterized as being NEW, MANAGED,
DETACHED or REMOVED.
- A NEW entity instance has no persistent identity, and is not yet
associated with an EntityManager and a UnitOfWork (i.e. those just
created with the "new" operator).
@@ -136,24 +150,24 @@ subsequent access must be through the interface type.
Serializing entities
~~~~~~~~~~~~~~~~~~~~
Serializing entities is generally to be avoided.
If you intend to serialize (and unserialize) entity
Serializing entities can be problematic and is not really
recommended, at least not as long as an entity instance still holds
references to proxy objects or is still managed by an
EntityManager. If you intend to serialize (and unserialize) entity
instances that still hold references to proxy objects you may run
into problems, because all proxy properties will be initialized
recursively, leading to large serialized object graphs, especially
for circular associations.
If you really must serialize entities, regardless if proxies are
involved or not, then consider implementing the ``Serializable``
interface and manually checking for cyclic dependencies in your
object graph.
into problems with private properties because of technical
limitations. Proxy objects implement ``__sleep`` and it is not
possible for ``__sleep`` to return names of private properties in
parent classes. On the other hand it is not a solution for proxy
objects to implement ``Serializable`` because Serializable does not
work well with any potential cyclic object references (at least we
did not find a way yet, if you did, please contact us).
The EntityManager
~~~~~~~~~~~~~~~~~
The ``EntityManager`` class is a central access point to the ORM
functionality provided by Doctrine 2. The ``EntityManager`` API is
The ``EntityManager`` class is a central access point to the
functionality provided by Doctrine ORM. The ``EntityManager`` API is
used to manage the persistence of your objects and to query for
persistent objects.
@@ -175,8 +189,9 @@ The Unit of Work
Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a
typical implementation of the
`Unit of Work pattern <https://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
`Unit of Work pattern <http://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.
+182 -12
View File
@@ -18,13 +18,13 @@ This chapter is split into three different sections.
One tip for working with relations is to read the relation from left to right, where the left word refers to the current Entity. For example:
- OneToMany - One instance of the current Entity has Many instances (references) to the refered Entity.
- ManyToOne - Many instances of the current Entity refer to One instance of the refered Entity.
- OneToOne - One instance of the current Entity refers to One instance of the refered Entity.
- OneToMany - One instance of the current Entity has Many instances (references) to the referred Entity.
- ManyToOne - Many instances of the current Entity refer to One instance of the referred Entity.
- OneToOne - One instance of the current Entity refers to One instance of the referred Entity.
See below for all the possible relations.
See below for all the possible relations.
An association is considered to be unidirectional if only one side of the association has
An association is considered to be unidirectional if only one side of the association has
a property referring to the other side.
To gain a full understanding of associations you should also read about :doc:`owning and
@@ -68,6 +68,18 @@ A many-to-one association is the most common association between objects. Exampl
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
manyToOne:
address:
targetEntity: Address
joinColumn:
name: address_id
referencedColumnName: id
.. note::
The above ``@JoinColumn`` is optional as it would default
@@ -133,6 +145,17 @@ references one ``Shipment`` entity.
</entity>
</doctrine-mapping>
.. code-block:: yaml
Product:
type: entity
oneToOne:
shipment:
targetEntity: Shipment
joinColumn:
name: shipment_id
referencedColumnName: id
Note that the @JoinColumn is not really necessary in this example,
as the defaults would be the same.
@@ -159,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`` attributes for the first time.
Here we see the ``mappedBy`` and ``inversedBy`` annotations for the first time.
They are used to tell Doctrine which property on the other side refers to the
object.
@@ -210,6 +233,22 @@ object.
</entity>
</doctrine-mapping>
.. code-block:: yaml
Customer:
oneToOne:
cart:
targetEntity: Cart
mappedBy: customer
Cart:
oneToOne:
customer:
targetEntity: Customer
inversedBy: cart
joinColumn:
name: customer_id
referencedColumnName: id
Note that the @JoinColumn is not really necessary in this example,
as the defaults would be the same.
@@ -220,7 +259,6 @@ 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 (
@@ -248,7 +286,7 @@ below.
// ...
/**
* One Student has One Student.
* One Student has One Mentor.
* @OneToOne(targetEntity="Student")
* @JoinColumn(name="mentor_id", referencedColumnName="id")
*/
@@ -334,6 +372,24 @@ bidirectional many-to-one.
</entity>
</doctrine-mapping>
.. code-block:: yaml
Product:
type: entity
oneToMany:
features:
targetEntity: Feature
mappedBy: product
Feature:
type: entity
manyToOne:
product:
targetEntity: Product
inversedBy: features
joinColumn:
name: product_id
referencedColumnName: id
Note that the @JoinColumn is not really necessary in this example,
as the defaults would be the same.
@@ -413,6 +469,24 @@ The following example sets up such a unidirectional one-to-many association:
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
manyToMany:
phonenumbers:
targetEntity: Phonenumber
joinTable:
name: users_phonenumbers
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
phonenumber_id:
referencedColumnName: id
unique: true
Generates the following MySQL Schema:
.. code-block:: sql
@@ -483,6 +557,19 @@ database perspective is known as an adjacency list approach.
</entity>
</doctrine-mapping>
.. code-block:: yaml
Category:
type: entity
oneToMany:
children:
targetEntity: Category
mappedBy: parent
manyToOne:
parent:
targetEntity: Category
inversedBy: children
Note that the @JoinColumn is not really necessary in this example,
as the defaults would be the same.
@@ -554,6 +641,22 @@ entities:
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
manyToMany:
groups:
targetEntity: Group
joinTable:
name: users_groups
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
group_id:
referencedColumnName: id
Generated MySQL Schema:
.. code-block:: sql
@@ -651,6 +754,30 @@ one is bidirectional.
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
manyToMany:
groups:
targetEntity: Group
inversedBy: users
joinTable:
name: users_groups
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
group_id:
referencedColumnName: id
Group:
type: entity
manyToMany:
users:
targetEntity: User
mappedBy: groups
The MySQL schema is exactly the same as for the Many-To-Many
uni-directional case above.
@@ -796,6 +923,14 @@ As an example, consider this mapping:
</entity>
</doctrine-mapping>
.. code-block:: yaml
Product:
type: entity
oneToOne:
shipment:
targetEntity: Shipment
This is essentially the same as the following, more verbose,
mapping:
@@ -821,6 +956,17 @@ mapping:
</entity>
</doctrine-mapping>
.. code-block:: yaml
Product:
type: entity
oneToOne:
shipment:
targetEntity: Shipment
joinColumn:
name: shipment_id
referencedColumnName: id
The @JoinTable definition used for many-to-many mappings has
similar defaults. As an example, consider this mapping:
@@ -831,10 +977,10 @@ similar defaults. As an example, consider this mapping:
<?php
class User
{
// ...
//...
/** @ManyToMany(targetEntity="Group") */
private $groups;
// ...
//...
}
.. code-block:: xml
@@ -845,6 +991,14 @@ similar defaults. As an example, consider this mapping:
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
manyToMany:
groups:
targetEntity: Group
This is essentially the same as the following, more verbose, mapping:
.. configuration-block::
@@ -854,7 +1008,7 @@ This is essentially the same as the following, more verbose, mapping:
<?php
class User
{
// ...
//...
/**
* Many Users have Many Groups.
* @ManyToMany(targetEntity="Group")
@@ -864,7 +1018,7 @@ This is essentially the same as the following, more verbose, mapping:
* )
*/
private $groups;
// ...
//...
}
.. code-block:: xml
@@ -884,6 +1038,22 @@ This is essentially the same as the following, more verbose, mapping:
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
manyToMany:
groups:
targetEntity: Group
joinTable:
name: User_Group
joinColumns:
User_id:
referencedColumnName: id
inverseJoinColumns:
Group_id:
referencedColumnName: id
In that case, the name of the join table defaults to a combination
of the simple, unqualified class names of the participating
classes, separated by an underscore character. The names of the
+87 -48
View File
@@ -52,15 +52,16 @@ mapping metadata:
- :doc:`Docblock Annotations <annotations-reference>`
- :doc:`XML <xml-mapping>`
- :doc:`YAML <yaml-mapping>`
- :doc:`PHP code <php-mapping>`
This manual will usually show mapping metadata via docblock annotations, though
many examples also show the equivalent configuration in XML.
many examples also show the equivalent configuration in YAML and XML.
.. note::
All metadata drivers perform equally. Once the metadata of a class has been
read from the source (annotations or xml) it is stored in an instance
read from the source (annotations, xml or yaml) it is stored in an instance
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are
stored in the metadata cache. If you're not using a metadata cache (not
recommended!) then the XML driver is the fastest.
@@ -75,7 +76,7 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
/** @Entity */
class Message
{
// ...
//...
}
.. code-block:: xml
@@ -86,6 +87,12 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
</entity>
</doctrine-mapping>
.. code-block:: yaml
Message:
type: entity
# ...
With no additional information, Doctrine expects the entity to be saved
into a table with the same name as the class in our case ``Message``.
You can change this by configuring information about the table:
@@ -101,7 +108,7 @@ You can change this by configuring information about the table:
*/
class Message
{
// ...
//...
}
.. code-block:: xml
@@ -112,6 +119,13 @@ You can change this by configuring information about the table:
</entity>
</doctrine-mapping>
.. code-block:: yaml
Message:
type: entity
table: message
# ...
Now the class ``Message`` will be saved and fetched from the table ``message``.
Property Mapping
@@ -151,6 +165,19 @@ default.
</entity>
</doctrine-mapping>
.. code-block:: yaml
Message:
type: entity
fields:
id:
type: integer
text:
length: 140
postedAt:
type: datetime
column: posted_at
When we don't explicitly specify a column name via the ``name`` option, Doctrine
assumes the field name is also the column name. This means that:
@@ -200,48 +227,36 @@ mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc.
depending on the RDBMS brand). Here is a quick overview of the
built-in mapping types:
- ``string``: Type that maps an SQL VARCHAR to a PHP string.
- ``integer``: Type that maps an SQL INT to a PHP integer.
- ``string``: Type that maps a SQL VARCHAR to a PHP string.
- ``integer``: Type that maps a SQL INT to a PHP integer.
- ``smallint``: Type that maps a database SMALLINT to a PHP
integer.
- ``bigint``: Type that maps a database BIGINT to a PHP string.
- ``boolean``: Type that maps an SQL boolean or equivalent (TINYINT) to a PHP boolean.
- ``decimal``: Type that maps an SQL DECIMAL to a PHP string.
- ``date``: Type that maps an SQL DATETIME to a PHP DateTime
- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
- ``decimal``: Type that maps a SQL DECIMAL to a PHP string.
- ``date``: Type that maps a SQL DATETIME to a PHP DateTime
object.
- ``date_immutable``: Type that maps an SQL DATETIME to a PHP DateTimeImmutable
object.
- ``time``: Type that maps an SQL TIME to a PHP DateTime object.
- ``time_immutable``: Type that maps an SQL TIME to a PHP DateTimeImmutable object.
- ``datetime``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime
object with the current timezone.
- ``datetimetz``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime
object with the timezone specified in the value from the database.
- ``datetime_immutable``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTimeImmutable
object with the current timezone.
- ``datetimetz_immutable``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTimeImmutable
object with the timezone specified in the value from the database.
- ``dateinterval``: Type that maps an interval to a PHP DateInterval object
- ``text``: Type that maps an SQL CLOB to a PHP string.
- ``object``: Type that maps an SQL CLOB to a PHP object using
- ``time``: Type that maps a SQL TIME to a PHP DateTime object.
- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
DateTime object.
- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
DateTime object with timezone.
- ``text``: Type that maps a SQL CLOB to a PHP string.
- ``object``: Type that maps a SQL CLOB to a PHP object using
``serialize()`` and ``unserialize()``
- ``array``: Type that maps an SQL CLOB to a PHP array using
- ``array``: Type that maps a SQL CLOB to a PHP array using
``serialize()`` and ``unserialize()``
- ``simple_array``: Type that maps an SQL CLOB to a one-dimensional PHP array using
- ``simple_array``: Type that maps a SQL CLOB to a PHP array using
``implode()`` and ``explode()``, with a comma as delimiter. *IMPORTANT*
Only use this type if you are sure that your values cannot contain a ",".
- ``json_array``: Type that maps an SQL CLOB to a PHP array using
``json_encode()`` and ``json_decode()``. This one has been deprecated in favor
of ``json`` type.
- ``json``: Type that maps an SQL CLOB to a PHP array using
``json_encode()`` and ``json_decode()``. An empty value is correctly represented as ``null``
- ``float``: Type that maps an SQL Float (Double Precision) to a
- ``json_array``: Type that maps a SQL CLOB to a PHP array using
``json_encode()`` and ``json_decode()``
- ``float``: Type that maps a SQL Float (Double Precision) to a
PHP double. *IMPORTANT*: Works only with locale settings that use
decimal points as separator.
- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to
varchar but uses a specific type if the platform supports it.
- ``blob``: Type that maps an SQL BLOB to a PHP resource stream
- ``binary``: Type that maps an SQL binary to a PHP resource stream
- ``blob``: Type that maps a SQL BLOB to a PHP resource stream
A cookbook article shows how to define :doc:`your own custom mapping types
<../cookbook/custom-mapping-types>`.
@@ -255,7 +270,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() <https://php.net/manual/en/function.date-default-timezone-set.php>`_
set by `date_default_timezone_set() <http://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.
@@ -285,7 +300,7 @@ annotation.
* @GeneratedValue
*/
private $id;
// ...
//...
}
.. code-block:: xml
@@ -299,12 +314,22 @@ annotation.
</entity>
</doctrine-mapping>
.. code-block:: yaml
Message:
type: entity
id:
id:
type: integer
generator:
strategy: AUTO
fields:
# fields here
In most cases using the automatic generator strategy (``@GeneratedValue``) is
what you want. It defaults to the identifier generation mechanism your current
database vendor prefers: AUTO_INCREMENT with MySQL, SERIAL with PostgreSQL,
Sequences with Oracle and so on.
.. _identifier-generation-strategies:
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
and Oracle and so on.
Identifier Generation Strategies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -330,6 +355,8 @@ Here is the list of possible generation strategies:
strategy does currently not provide full portability and is
supported by the following platforms: MySQL/SQLite/SQL Anywhere
(AUTO\_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL).
- ``UUID``: Tells Doctrine to use the built-in Universally Unique Identifier
generator. This strategy provides full portability.
- ``TABLE``: Tells Doctrine to use a separate table for ID
generation. This strategy provides full portability.
***This strategy is not yet implemented!***
@@ -360,7 +387,7 @@ besides specifying the sequence's name:
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
*/
protected $id = null;
// ...
//...
}
.. code-block:: xml
@@ -374,6 +401,20 @@ besides specifying the sequence's name:
</entity>
</doctrine-mapping>
.. code-block:: yaml
Message:
type: entity
id:
id:
type: integer
generator:
strategy: SEQUENCE
sequenceGenerator:
sequenceName: message_seq
allocationSize: 100
initialValue: 1
The initial value specifies at which value the sequence should
start.
@@ -382,7 +423,7 @@ performance of Doctrine. The allocationSize specifies by how much
values the sequence is incremented whenever the next value is
retrieved. If this is larger than 1 (one) Doctrine can generate
identifier values for the allocationSizes amount of entities. In
the above example with ``allocationSize=100`` Doctrine 2 would only
the above example with ``allocationSize=100`` Doctrine ORM would only
need to access the sequence once to generate the identifiers for
100 new entities.
@@ -397,6 +438,7 @@ need to access the sequence once to generate the identifiers for
configuration option is never larger than the actual sequences
INCREMENT BY value, otherwise you may get duplicate keys.
.. note::
It is possible to use strategy="AUTO" and at the same time
@@ -405,10 +447,11 @@ need to access the sequence once to generate the identifiers for
of the underlying platform is SEQUENCE, such as for Oracle and
PostgreSQL.
Composite Keys
~~~~~~~~~~~~~~
With Doctrine 2 you can use composite primary keys, using ``@Id`` on more then
With Doctrine ORM you can use composite primary keys, using ``@Id`` on more then
one column. Some restrictions exist opposed to using a single identifier in
this case: The use of the ``@GeneratedValue`` annotation is not supported,
which means you can only use composite keys if you generate the primary key
@@ -441,15 +484,11 @@ according to the used database platform.
.. _reference-basic-mapping-custom-mapping-types:
.. versionadded: 2.3
For more control over column quoting the ``Doctrine\ORM\Mapping\QuoteStrategy`` interface
was introduced in 2.3. It is invoked for every column, table, alias and other
was introduced in ORM. It is invoked for every column, table, alias and other
SQL names. You can implement the QuoteStrategy and set it by calling
``Doctrine\ORM\Configuration#setQuoteStrategy()``.
.. versionadded: 2.4
The ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name.
You can use it with the following code:
+27 -24
View File
@@ -15,6 +15,16 @@ especially what the strategies presented here provide help with.
you use the tools for your particular RDBMS for these bulk
operations.
.. note::
Having an SQL logger enabled when processing batches can have a serious impact on performance and resource usage.
To avoid that you should disable it in the DBAL configuration:
.. code-block:: php
<?php
$em->getConnection()->getConfiguration()->setSQLLogger(null);
Bulk Inserts
------------
@@ -41,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
@@ -65,7 +75,7 @@ Iterating results
~~~~~~~~~~~~~~~~~
An alternative solution for bulk updates is to use the
``Query#iterate()`` facility to iterate over the query results step
``Query#toIterable()`` facility to iterate over the query results step
by step instead of loading the whole result into memory at once.
The following example shows how to do this, combining the iteration
with the batching strategy that was already used for bulk inserts:
@@ -76,9 +86,7 @@ with the batching strategy that was already used for bulk inserts:
$batchSize = 20;
$i = 1;
$q = $em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
foreach ($iterableResult as $row) {
$user = $row[0];
foreach ($q->toIterable() as $user) {
$user->increaseCredit();
$user->calculateNewBonuses();
if (($i % $batchSize) === 0) {
@@ -101,6 +109,7 @@ with the batching strategy that was already used for bulk inserts:
additional memory not visible to the PHP process. For large sets this
may easily kill the process for no apparent reason.
Bulk Deletes
------------
@@ -126,7 +135,7 @@ Iterating results
~~~~~~~~~~~~~~~~~
An alternative solution for bulk deletes is to use the
``Query#iterate()`` facility to iterate over the query results step
``Query#toIterable()`` facility to iterate over the query results step
by step instead of loading the whole result into memory at once.
The following example shows how to do this:
@@ -136,9 +145,8 @@ The following example shows how to do this:
$batchSize = 20;
$i = 1;
$q = $em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
while (($row = $iterableResult->next()) !== false) {
$em->remove($row[0]);
foreach($q->toIterable() as $row) {
$em->remove($row);
if (($i % $batchSize) === 0) {
$em->flush(); // Executes all deletions.
$em->clear(); // Detaches all objects from Doctrine!
@@ -153,25 +161,24 @@ The following example shows how to do this:
fetch-join a collection-valued association. The nature of such SQL
result sets is not suitable for incremental hydration.
Iterating Large Results for Data-Processing
-------------------------------------------
You can use the ``iterate()`` method just to iterate over a large
result and no UPDATE or DELETE intention. The ``IterableResult``
instance returned from ``$query->iterate()`` implements the
Iterator interface so you can process a large result without memory
You can use the ``toIterable()`` method just to iterate over a large
result and no UPDATE or DELETE intention. ``$query->toIterable()`` returns ``iterable``
so you can process a large result without memory
problems using the following approach:
.. code-block:: php
<?php
$q = $this->em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
foreach ($iterableResult as $row) {
// do stuff with the data in the row, $row[0] is always the object
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
foreach ($q->toIterable() as $row) {
// do stuff with the data in the row
// detach all entities from Doctrine, so that Garbage-Collection can kick in immediately
$this->em->clear();
// detach from Doctrine, so that it can be Garbage-Collected immediately
$this->_em->detach($row[0]);
}
.. note::
@@ -180,9 +187,5 @@ problems using the following approach:
fetch-join a collection-valued association. The nature of such SQL
result sets is not suitable for incremental hydration.
Packages for easing Batch Processing
------------------------------------
You can implement batch processing yourself, or use an existing
package such as `DoctrineBatchUtils <https://github.com/Ocramius/DoctrineBatchUtils>`_,
which already provides the logic described above in an encapsulated format.
+6 -3
View File
@@ -12,12 +12,14 @@ Constrain relationships as much as possible
It is important to constrain relationships as much as possible.
This means:
- Impose a traversal direction (avoid bidirectional associations
if possible)
- Eliminate nonessential associations
This has several benefits:
- Reduced coupling in your domain model
- Simpler code in your domain model (no need to maintain
bidirectionality properly)
@@ -41,7 +43,7 @@ should use events judiciously.
Use cascades judiciously
------------------------
Automatic cascades of the persist/remove/refresh/etc. operations are
Automatic cascades of the persist/remove/merge/etc. operations are
very handy but should be used wisely. Do NOT simply add all
cascades to all associations. Think about which cascades actually
do make sense for you for a particular association, given the
@@ -72,11 +74,11 @@ collections in entities in the constructor. Example:
<?php
namespace MyProject\Model;
use Doctrine\Common\Collections\ArrayCollection;
class User {
private $addresses;
private $articles;
public function __construct() {
$this->addresses = new ArrayCollection;
$this->articles = new ArrayCollection;
@@ -106,3 +108,4 @@ queries generally don't have any noticeable performance impact, it
is still preferable to use fewer, well-defined transactions that
are established through explicit transaction boundaries.
+66 -68
View File
@@ -1,11 +1,11 @@
Caching
=======
Doctrine provides cache drivers in the ``Common`` package for some
of the most popular caching implementations such as APC, Memcache
Doctrine provides cache drivers in the ``doctrine/cache`` package for some
of the most popular caching implementations such as APCu, Memcache
and Xcache. We also provide an ``ArrayCache`` driver which stores
the data in a PHP array. Obviously, when using ``ArrayCache``, the
cache does not persist between requests, but this is useful for
the data in a PHP array. Obviously, when using ``ArrayCache``, the
cache does not persist between requests, but this is useful for
testing in a development environment.
Cache Drivers
@@ -18,6 +18,7 @@ this interface.
The interface defines the following public methods for you to implement:
- fetch($id) - Fetches an entry from the cache
- contains($id) - Test if an entry exists in the cache
- save($id, $data, $lifeTime = false) - Puts data into the cache for x seconds. 0 = infinite time
@@ -27,6 +28,7 @@ Each driver extends the ``CacheProvider`` class which defines a few
abstract protected methods that each of the drivers must
implement:
- doFetch($id)
- doContains($id)
- doSave($id, $data, $lifeTime = false)
@@ -43,48 +45,36 @@ This documentation does not cover every single cache driver included
with Doctrine. For an up-to-date-list, see the
`cache directory on GitHub <https://github.com/doctrine/cache/tree/master/lib/Doctrine/Common/Cache>`_.
APC
~~~
PhpFileCache
~~~~~~~~~~~~
In order to use the APC cache driver you must have it compiled and
enabled in your php.ini. You can read about APC
`in the PHP Documentation <https://php.net/apc>`_. 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.
The preferred cache driver for metadata and query caches is ``PhpFileCache``.
This driver serializes cache items and writes them to a file. This allows for
opcode caching to be used and provides high performance in most scenarios.
Below is a simple example of how you could use the APC cache driver
by itself.
In order to use the ``PhpFileCache`` driver it must be able to write to
a directory.
Below is an example of how to use the ``PhpFileCache`` driver by itself.
.. code-block:: php
<?php
$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
'/path/to/writable/directory'
);
$cacheDriver->save('cache_id', 'my_data');
APCu
~~~~
In order to use the APCu cache driver you must have it compiled and
enabled in your php.ini. You can read about APCu
`in the PHP Documentation <https://php.net/apcu>`_. 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.
Below is a simple example of how you could use the APCu cache driver
by itself.
.. code-block:: php
<?php
$cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
$cacheDriver->save('cache_id', 'my_data');
The PhpFileCache is not distributed across multiple machines if you are running
your application in a distributed setup. This is ok for the metadata and query
cache but is not a good approach for the result cache.
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 <https://php.net/memcache>`_. It will
`on the PHP website <http://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.
@@ -96,7 +86,7 @@ driver by itself.
<?php
$memcache = new Memcache();
$memcache->connect('memcache_host', 11211);
$cacheDriver = new \Doctrine\Common\Cache\MemcacheCache();
$cacheDriver->setMemcache($memcache);
$cacheDriver->save('cache_id', 'my_data');
@@ -109,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 <https://php.net/memcached>`_. It will
`on the PHP website <http://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,35 +111,17 @@ driver by itself.
<?php
$memcached = new Memcached();
$memcached->addServer('memcache_host', 11211);
$cacheDriver = new \Doctrine\Common\Cache\MemcachedCache();
$cacheDriver->setMemcached($memcached);
$cacheDriver->save('cache_id', 'my_data');
Xcache
~~~~~~
In order to use the Xcache cache driver you must have it compiled
and enabled in your php.ini. You can read about Xcache
`here <https://xcache.lighttpd.net/>`_. 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.
Below is a simple example of how you could use the Xcache cache
driver by itself.
.. code-block:: php
<?php
$cacheDriver = new \Doctrine\Common\Cache\XcacheCache();
$cacheDriver->save('cache_id', 'my_data');
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 <https://redis.io/>`_. Also check
`from here <http://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.
@@ -170,7 +142,7 @@ Using Cache Drivers
-------------------
In this section we'll describe how you can fully utilize the API of
the cache drivers to save data to a cache, check if some cached data
the cache drivers to save data to a cache, check if some cached data
exists, fetch the cached data and delete the cached data. We'll use the
``ArrayCache`` implementation as our example here.
@@ -193,6 +165,7 @@ Saving some data to the cache driver is as simple as using the
The ``save()`` method accepts three arguments which are described
below:
- ``$id`` - The cache id
- ``$data`` - The cache entry/data.
- ``$lifeTime`` - The lifetime. If != false, sets a specific
@@ -242,7 +215,7 @@ Deleting
~~~~~~~~
As you might guess, deleting is just as easy as saving, checking
and fetching. You can delete by an individual ID, or you can
and fetching. You can delete by an individual ID, or you can
delete all entries.
By Cache ID
@@ -279,6 +252,8 @@ You can set the namespace a cache driver should use by using the
<?php
$cacheDriver->setNamespace('my_namespace_');
.. _integrating-with-the-orm:
Integrating with the ORM
------------------------
@@ -301,20 +276,28 @@ use on your ORM configuration.
.. code-block:: php
<?php
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
'/path/to/writable/directory'
);
$config = new \Doctrine\ORM\Configuration();
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcuCache());
$config->setQueryCacheImpl($cacheDriver);
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 again after the first time.
You just need to configure the result cache implementation.
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.
.. code-block:: php
<?php
$config->setResultCacheImpl(new \Doctrine\Common\Cache\ApcuCache());
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
'/path/to/writable/directory'
);
$config = new \Doctrine\ORM\Configuration();
$config->setResultCacheImpl($cacheDriver);
Now when you're executing DQL queries you can configure them to use
the result cache.
@@ -323,7 +306,7 @@ the result cache.
<?php
$query = $em->createQuery('select u from \Entities\User u');
$query->useResultCache(true);
$query->enableResultCache();
You can also configure an individual query to use a different
result cache driver.
@@ -331,18 +314,22 @@ result cache driver.
.. code-block:: php
<?php
$query->setResultCacheDriver(new \Doctrine\Common\Cache\ApcuCache());
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
'/path/to/writable/directory'
);
$query->setResultCacheDriver($cacheDriver);
.. note::
Setting the result cache driver on the query will
automatically enable the result cache for the query. If you want to
disable it pass false to ``useResultCache()``.
disable it use ``disableResultCache()``.
::
<?php
$query->useResultCache(false);
$query->disableResultCache();
If you want to set the time the cache has to live you can use the
``setResultCacheLifetime()`` method.
@@ -362,18 +349,18 @@ yourself with the ``setResultCacheId()`` method.
$query->setResultCacheId('my_custom_id');
You can also set the lifetime and cache ID by passing the values as
the second and third argument to ``useResultCache()``.
the first and second argument to ``enableResultCache()``.
.. code-block:: php
<?php
$query->useResultCache(true, 3600, 'my_custom_id');
$query->enableResultCache(3600, 'my_custom_id');
Metadata Cache
~~~~~~~~~~~~~~
Your class metadata can be parsed from a few different sources like
XML, Annotations, etc. Instead of parsing this information on
YAML, XML, Annotations, etc. Instead of parsing this information on
each request we should cache it using one of the cache drivers.
Just like the query and result cache we need to configure it
@@ -382,7 +369,11 @@ first.
.. code-block:: php
<?php
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcuCache());
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
'/path/to/writable/directory'
);
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl($cacheDriver);
Now the metadata information will only be parsed once and stored in
the cache driver.
@@ -418,6 +409,12 @@ To clear the result cache use the ``orm:clear-cache:result`` task.
All these tasks accept a ``--flush`` option to flush the entire
contents of the cache instead of invalidating the entries.
.. note::
None of these tasks will work with APC, APCu, or XCache drivers
because the memory that the cache is stored in is only accessible
to the webserver.
Cache Chaining
--------------
@@ -467,3 +464,4 @@ not letting your users' requests populate the cache.
You can read more about cache slams
`in this blog post <http://notmysock.org/blog/php/user-cache-timebomb.html>`_.
+17 -16
View File
@@ -30,7 +30,7 @@ Deferred Explicit
The deferred explicit policy is similar to the deferred implicit
policy in that it detects changes through a property-by-property
comparison at commit time. The difference is that Doctrine 2 only
comparison at commit time. The difference is that Doctrine ORM only
considers entities that have been explicitly marked for change detection
through a call to EntityManager#persist(entity) or through a save
cascade. All other entities are skipped. This policy therefore
@@ -71,9 +71,9 @@ follows:
.. code-block:: php
<?php
use Doctrine\Common\NotifyPropertyChanged,
Doctrine\Common\PropertyChangedListener;
use Doctrine\Persistence\NotifyPropertyChanged,
Doctrine\Persistence\PropertyChangedListener;
/**
* @Entity
* @ChangeTrackingPolicy("NOTIFY")
@@ -81,12 +81,12 @@ follows:
class MyEntity implements NotifyPropertyChanged
{
// ...
private $listeners = array();
private $_listeners = array();
public function addPropertyChangedListener(PropertyChangedListener $listener)
{
$this->listeners[] = $listener;
$this->_listeners[] = $listener;
}
}
@@ -99,30 +99,30 @@ behaviour:
<?php
// ...
class MyEntity implements NotifyPropertyChanged
{
// ...
protected function onPropertyChanged($propName, $oldValue, $newValue)
protected function _onPropertyChanged($propName, $oldValue, $newValue)
{
if ($this->listeners) {
foreach ($this->listeners as $listener) {
if ($this->_listeners) {
foreach ($this->_listeners as $listener) {
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
}
}
}
public function setData($data)
{
if ($data != $this->data) {
$this->onPropertyChanged('data', $this->data, $data);
$this->_onPropertyChanged('data', $this->data, $data);
$this->data = $data;
}
}
}
You have to invoke ``onPropertyChanged`` inside every method that
You have to invoke ``_onPropertyChanged`` inside every method that
changes the persistent state of ``MyEntity``.
The check whether the new value is different from the old one is
@@ -148,3 +148,4 @@ effectiveness. It has the best performance characteristics of the 3
policies with larger units of work and a flush() operation is very
cheap when nothing has changed.
+16 -18
View File
@@ -67,6 +67,22 @@ Or if you prefer XML:
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
Or if you prefer YAML:
.. code-block:: php
<?php
$paths = array("/path/to/yml-mappings");
$config = Setup::createYAMLMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
.. note::
If you want to use yml mapping you should add yaml dependency to your `composer.json`:
::
"symfony/yaml": "*"
Inside the ``Setup`` methods several assumptions are made:
- If `$isDevMode` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated on every request.
@@ -96,8 +112,6 @@ You need to register your applications EntityManager to the console tool
to make use of the tasks by creating a ``cli-config.php`` file with the
following content:
On Doctrine 2.4 and above:
.. code-block:: php
<?php
@@ -110,19 +124,3 @@ On Doctrine 2.4 and above:
$entityManager = GetEntityManager();
return ConsoleRunner::createHelperSet($entityManager);
On Doctrine 2.3 and below:
.. code-block:: php
<?php
// cli-config.php
require_once 'my_bootstrap.php';
// Any way to access the EntityManager from your application
$em = GetMyEntityManager();
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
+97 -127
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
@@ -18,6 +18,7 @@ querying that storage to pick a certain subset of your objects.
need to think about DQL as a query language for your object model,
not for your relational schema.
DQL is case in-sensitive, except for namespace, class and field
names, which are case sensitive.
@@ -58,6 +59,7 @@ Here is an example that selects all users with an age > 20:
Lets examine the query:
- ``u`` is a so called identification variable or alias that
refers to the ``MyProject\Model\User`` class. By placing this alias
in the SELECT clause we specify that we want all instances of the
@@ -129,6 +131,7 @@ multiple FROM clauses.
Doctrine throws an exception if you violate this constraint.
Joins
~~~~~
@@ -177,15 +180,16 @@ not need to lazy load the association with another query.
Doctrine allows you to walk all the associations between
all the objects in your domain model. Objects that were not already
loaded from the database are replaced with lazy loading proxy
instances. Non-loaded Collections are also replaced by lazy-loading
loaded from the database are replaced with lazy load proxy
instances. Non-loaded Collections are also replaced by lazy-load
instances that fetch all the contained objects upon first access.
However relying on the lazy-loading mechanism leads to many small
However relying on the lazy-load mechanism leads to many small
queries executed against the database, which can significantly
affect the performance of your application. **Fetch Joins** are the
solution to hydrate most or all of the entities that you need in a
single SELECT query.
Named and Positional Parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -204,8 +208,7 @@ This section contains a large set of DQL queries and some
explanations of what is happening. The actual result also depends
on the hydration mode.
Hydrate all User entities
^^^^^^^^^^^^^^^^^^^^^^^^^
Hydrate all User entities:
.. code-block:: php
@@ -213,8 +216,7 @@ Hydrate all User entities
$query = $em->createQuery('SELECT u FROM MyProject\Model\User u');
$users = $query->getResult(); // array of User objects
Retrieve the IDs of all CmsUsers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Retrieve the IDs of all CmsUsers:
.. code-block:: php
@@ -222,8 +224,7 @@ Retrieve the IDs of all CmsUsers
$query = $em->createQuery('SELECT u.id FROM CmsUser u');
$ids = $query->getResult(); // array of CmsUser ids
Retrieve the IDs of all users that have written an article
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Retrieve the IDs of all users that have written an article:
.. code-block:: php
@@ -231,10 +232,6 @@ Retrieve the IDs of all users that have written an article
$query = $em->createQuery('SELECT DISTINCT u.id FROM CmsArticle a JOIN a.user u');
$ids = $query->getResult(); // array of CmsUser ids
Retrieve all articles and sort them by username
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Retrieve all articles and sort them by the name of the articles
users instance:
@@ -244,8 +241,7 @@ users instance:
$query = $em->createQuery('SELECT a FROM CmsArticle a JOIN a.user u ORDER BY u.name ASC');
$articles = $query->getResult(); // array of CmsArticle objects
Retrieve the Username and Name of a CmsUser
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Retrieve the Username and Name of a CmsUser:
.. code-block:: php
@@ -254,8 +250,7 @@ Retrieve the Username and Name of a CmsUser
$users = $query->getResult(); // array of CmsUser username and name values
echo $users[0]['username'];
Retrieve a ForumUser and his single associated entity
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Retrieve a ForumUser and its single associated entity:
.. code-block:: php
@@ -264,8 +259,7 @@ Retrieve a ForumUser and his single associated entity
$users = $query->getResult(); // array of ForumUser objects with the avatar association loaded
echo get_class($users[0]->getAvatar());
Retrieve a CmsUser and fetch join all owning phonenumbers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Retrieve a CmsUser and fetch join all the phonenumbers it has:
.. code-block:: php
@@ -274,8 +268,7 @@ Retrieve a CmsUser and fetch join all owning phonenumbers
$users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded
$phonenumbers = $users[0]->getPhonenumbers();
Hydrate a result in Ascending
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hydrate a result in Ascending:
.. code-block:: php
@@ -283,8 +276,7 @@ Hydrate a result in Ascending
$query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id ASC');
$users = $query->getResult(); // array of ForumUser objects
Hydrate a result in Descending Order
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Or in Descending Order:
.. code-block:: php
@@ -292,8 +284,7 @@ Hydrate a result in Descending Order
$query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC');
$users = $query->getResult(); // array of ForumUser objects
Using Aggregate Functions
^^^^^^^^^^^^^^^^^^^^^^^^^
Using Aggregate Functions:
.. code-block:: php
@@ -304,8 +295,7 @@ Using Aggregate Functions
$query = $em->createQuery('SELECT u, count(g.id) FROM Entities\User u JOIN u.groups g GROUP BY u.id');
$result = $query->getResult();
Using WHERE Clause and Positional Parameter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With WHERE Clause and Positional Parameter:
.. code-block:: php
@@ -314,8 +304,7 @@ Using WHERE Clause and Positional Parameter
$query->setParameter(1, 321);
$users = $query->getResult(); // array of ForumUser objects
Using WHERE Clause and Named Parameter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With WHERE Clause and Named Parameter:
.. code-block:: php
@@ -324,8 +313,7 @@ Using WHERE Clause and Named Parameter
$query->setParameter('name', 'Bob');
$users = $query->getResult(); // array of ForumUser objects
Using Nested Conditions in WHERE Clause
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With Nested Conditions in WHERE Clause:
.. code-block:: php
@@ -338,8 +326,7 @@ Using Nested Conditions in WHERE Clause
));
$users = $query->getResult(); // array of ForumUser objects
COUNT DISTINCT
^^^^^^^^^^^^^^
With COUNT DISTINCT:
.. code-block:: php
@@ -347,8 +334,7 @@ COUNT DISTINCT
$query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser');
$users = $query->getResult(); // array of ForumUser objects
Using Arithmetic Expression in WHERE clause
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With Arithmetic Expression in WHERE clause:
.. code-block:: php
@@ -356,9 +342,6 @@ Using Arithmetic Expression in WHERE clause
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');
$users = $query->getResult(); // array of ForumUser objects
Hide aliased columns from the result
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Retrieve user entities with Arithmetic Expression in ORDER clause, using the ``HIDDEN`` keyword:
.. code-block:: php
@@ -367,9 +350,6 @@ Retrieve user entities with Arithmetic Expression in ORDER clause, using the ``H
$query = $em->createQuery('SELECT u, u.posts_count + u.likes_count AS HIDDEN score FROM CmsUser u ORDER BY score');
$users = $query->getResult(); // array of User objects
Select all user-ids and optionally associated article-ids
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Using a LEFT JOIN to hydrate all user-ids and optionally associated
article-ids:
@@ -379,8 +359,8 @@ article-ids:
$query = $em->createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a');
$results = $query->getResult(); // array of user ids and every article_id for each user
Restricting a JOIN clause by additional conditions specified by WITH
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Restricting a JOIN clause by additional conditions specified by
WITH:
.. code-block:: php
@@ -389,8 +369,7 @@ Restricting a JOIN clause by additional conditions specified by WITH
$query->setParameter('foo', '%foo%');
$users = $query->getResult();
Using several Fetch JOINs
^^^^^^^^^^^^^^^^^^^^^^^^^
Using several Fetch JOINs:
.. code-block:: php
@@ -398,8 +377,7 @@ Using several Fetch JOINs
$query = $em->createQuery('SELECT u, a, p, c FROM CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c');
$users = $query->getResult();
BETWEEN in WHERE clause
^^^^^^^^^^^^^^^^^^^^^^^
BETWEEN in WHERE clause:
.. code-block:: php
@@ -409,8 +387,7 @@ BETWEEN in WHERE clause
$query->setParameter(2, 321);
$usernames = $query->getResult();
DQL Functions in WHERE clause
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
DQL Functions in WHERE clause:
.. code-block:: php
@@ -418,8 +395,7 @@ DQL Functions in WHERE clause
$query = $em->createQuery("SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'");
$usernames = $query->getResult();
IN() Expression
^^^^^^^^^^^^^^^
IN() Expression:
.. code-block:: php
@@ -433,8 +409,7 @@ IN() Expression
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id NOT IN (1)');
$users = $query->getResult();
CONCAT() DQL Function
^^^^^^^^^^^^^^^^^^^^^
CONCAT() DQL Function:
.. code-block:: php
@@ -448,7 +423,6 @@ CONCAT() DQL Function
$idUsernames = $query->getResult();
EXISTS in WHERE clause with correlated Subquery
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: php
@@ -456,8 +430,7 @@ EXISTS in WHERE clause with correlated Subquery
$query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.user = u.id)');
$ids = $query->getResult();
Get all users who are members of $group
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get all users who are members of $group.
.. code-block:: php
@@ -467,7 +440,6 @@ Get all users who are members of $group
$ids = $query->getResult();
Get all users that have more than 1 phonenumber
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: php
@@ -476,7 +448,6 @@ Get all users that have more than 1 phonenumber
$users = $query->getResult();
Get all users that have no phonenumber
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: php
@@ -484,13 +455,8 @@ Get all users that have no phonenumber
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY');
$users = $query->getResult();
Get all instances of a specific type
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get all instances of a specific type, for use with inheritance hierarchies. These queries can be useful for
:doc:`inheritance mapping <inheritance-mapping>`.
.. versionadded:: 2.1
Get all instances of a specific type, for use with inheritance
hierarchies:
.. code-block:: php
@@ -499,58 +465,28 @@ Get all instances of a specific type, for use with inheritance hierarchies. Thes
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1');
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1');
Using IDENTITY() in queries
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get all users visible on a given website that have chosen certain gender.
.. versionadded:: 2.2
Get all users visible on a given website that have chosen certain gender:
.. code-block:: php
<?php
$query = $em->createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)');
.. versionadded:: 2.4
Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys
The IDENTITY() DQL function also works for composite primary keys
.. code-block:: php
<?php
$query = $em->createQuery("SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1");
Arbitrary Join
^^^^^^^^^^^^^^
Joins between entities without associations were not possible until version
2.4, where you can generate an arbitrary join with the following syntax:
Joins between entities without associations are available,
where you can generate an arbitrary join with the following syntax:
.. code-block:: php
<?php
$query = $em->createQuery('SELECT u FROM User u JOIN 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 ``Banlist``, it can look like this:
- User
- Banlist
- Banlist
- User
- Banlist
- User
- Banlist
- Banlist
- Banlist
In this form of join, the ``Banlist`` 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 Banlists is invoked on a User instance,
it loads all the related ``Banlist`` 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.
@@ -562,6 +498,7 @@ when the DQL is switched to an arbitrary join.
- HAVING is applied to the results of a query after
aggregation (GROUP BY)
Partial Object Syntax
^^^^^^^^^^^^^^^^^^^^^
@@ -591,8 +528,6 @@ You use the partial syntax when joining as well:
"NEW" Operator Syntax
^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 2.4
Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries.
- When using ``SELECT NEW`` you don't need to specify a mapped entity.
@@ -693,6 +628,7 @@ clause and using sub-selects.
``EntityManager#clear()`` and retrieve new instances of any
affected entity.
DELETE queries
--------------
@@ -713,6 +649,17 @@ The same restrictions apply for the reference of related entities.
of the query. Additionally Deletes of specified entities are *NOT*
cascaded to related entities even if specified in the metadata.
Comments in queries
-------------------
We can use comments with the SQL syntax of comments.
.. code-block:: sql
SELECT u FROM MyProject\Model\User u
-- my comment
WHERE u.age > 20 -- comment at the end of a line
Functions, Operators, Aggregates
--------------------------------
It is possible to wrap both fields and identification values into
@@ -725,12 +672,13 @@ DQL Functions
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)
- 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
- 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
@@ -745,8 +693,8 @@ clauses:
- 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, days, unit) - Add the number of days to a given date. (Supported units are YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND)
- DATE_SUB(date, days, unit) - Substract the number of days from a given date. (Supported units are YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND)
- 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
@@ -770,6 +718,7 @@ Other Expressions
DQL offers a wide-range of additional expressions that are known
from SQL, here is a list of all the supported constructs:
- ``ALL/ANY/SOME`` - Used in a WHERE clause followed by a
sub-select this works like the equivalent constructs in SQL.
- ``BETWEEN a AND b`` and ``NOT BETWEEN a AND b`` can be used to
@@ -856,7 +805,7 @@ what type of results to expect.
Single Table
~~~~~~~~~~~~
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
`Single Table Inheritance <http://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
@@ -949,11 +898,11 @@ entities:
Class Table Inheritance
~~~~~~~~~~~~~~~~~~~~~~~
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
`Class Table Inheritance <http://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
of a parent class through a foreign key constraint. Doctrine 2
of a parent class through a foreign key constraint. Doctrine ORM
implements this strategy through the use of a discriminator column
in the topmost table of the hierarchy because this is the easiest
way to achieve polymorphic queries with Class Table Inheritance.
@@ -994,6 +943,7 @@ you'll notice some differences:
) ENGINE = InnoDB;
ALTER TABLE Employee ADD FOREIGN KEY (id) REFERENCES Person(id) ON DELETE CASCADE
- The data is split between two tables
- A foreign key exists between the two tables
@@ -1009,6 +959,7 @@ automatically for you:
FROM Employee e1_ INNER JOIN Person p0_ ON e1_.id = p0_.id
WHERE p0_.name = ?
The Query class
---------------
@@ -1039,6 +990,7 @@ mode specifies a particular way in which a SQL result set is
transformed. Each hydration mode has its own dedicated method on
the Query class. Here they are:
- ``Query#getResult()``: Retrieves a collection of objects. The
result is either a plain collection of objects (pure) or an array
where the objects are nested in the result rows (mixed).
@@ -1046,8 +998,9 @@ the Query class. Here they are:
result contains more than one object, an ``NonUniqueResultException``
is thrown. If the result contains no objects, an ``NoResultException``
is thrown. The pure/mixed distinction does not apply.
- ``Query#getOneOrNullResult()``: Retrieve a single object. If no
object is found null will be returned.
- ``Query#getOneOrNullResult()``: Retrieve a single object. If the
result contains more than one object, a ``NonUniqueResultException``
is thrown. If no object is found null will be returned.
- ``Query#getArrayResult()``: Retrieves an array graph (a nested
array) that is largely interchangeable with the object graph
generated by ``Query#getResult()`` for read-only purposes.
@@ -1058,6 +1011,8 @@ the Query class. Here they are:
graph in certain scenarios due to the difference of the identity
semantics between arrays and objects.
- ``Query#getScalarResult()``: Retrieves a flat/rectangular result
set of scalar values that can contain duplicate data. The
pure/mixed distinction does not apply.
@@ -1135,11 +1090,13 @@ clause, we get a mixed result.
Conventions for mixed results are as follows:
- The object fetched in the FROM clause is always positioned with the key '0'.
- Every scalar without a name is numbered in the order given in the query, starting with 1.
- Every aliased scalar is given with its alias-name as the key. The case of the name is kept.
- If several objects are fetched from the FROM clause they alternate every row.
Here is how the result could look like:
.. code-block:: php
@@ -1179,6 +1136,7 @@ will return the rows iterating the different top-level entities.
[2] => Object (User)
[3] => Object (Group)
Hydration Modes
~~~~~~~~~~~~~~~
@@ -1188,10 +1146,11 @@ 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
^^^^^^^^^^^^^^^^
@@ -1255,8 +1214,9 @@ object graph you can use scalar hydration:
The following assumptions are made about selected fields using
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
@@ -1290,14 +1250,13 @@ creating a class which extends ``AbstractHydrator``:
<?php
namespace MyProject\Hydrators;
use Doctrine\DBAL\FetchMode;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
class CustomHydrator extends AbstractHydrator
{
protected function _hydrateAll()
{
return $this->stmt->fetchAll(FetchMode::Associative);
return $this->_stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
@@ -1339,6 +1298,7 @@ Prepared Statements that use numerical or named wildcards require
additional parameters to be executable against the database. To
pass parameters to the query the following methods can be used:
- ``AbstractQuery::setParameter($param, $value)`` - Set the
numerical or named wildcard to the given value.
- ``AbstractQuery::setParameters(array $params)`` - Set an array
@@ -1393,6 +1353,7 @@ Result Cache API:
``Doctrine\ORM\Configuration`` instance so that it is passed to
every ``Query`` and ``NativeQuery`` instance.
Query Hints
^^^^^^^^^^^
@@ -1402,21 +1363,22 @@ exist mostly internal query hints that are not be consumed in
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
reloading this data. Partially loaded objects have to be passed to
``EntityManager::refresh()`` if they are to be reloaded fully from
the database.
- ``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.
@@ -1437,6 +1399,7 @@ default. This also means you don't regularly need to fiddle with
the parameters of the Query Cache, however if you do there are
several methods to interact with it:
- ``Query::setQueryCacheDriver($driver)`` - Allows to set a Cache
instance
- ``Query::setQueryCacheLifeTime($seconds = 3600)`` - Set lifetime
@@ -1455,6 +1418,7 @@ well as specify the starting offset, Doctrine then uses a strategy
of manipulating the select query to return only the requested
number of results:
- ``Query::setMaxResults($maxResults)``
- ``Query::setFirstResult($offset)``
@@ -1480,7 +1444,7 @@ can mark a many-to-one or one-to-one association as fetched temporarily to batch
<?php
$query = $em->createQuery("SELECT u FROM MyProject\User u");
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\FetchMode::EAGER);
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
$query->execute();
Given that there are 10 users and corresponding addresses in the database the executed queries will look something like:
@@ -1499,6 +1463,7 @@ Given that there are 10 users and corresponding addresses in the database the ex
loaded**. This gives no improvement over the ``lazy`` fetch mode which will also initialize the associations on
a one-by-one basis once they are accessed.
EBNF
----
@@ -1510,6 +1475,7 @@ correct syntax for a particular query should be.
Document syntax:
~~~~~~~~~~~~~~~~
- non-terminals begin with an upper case character
- terminals begin with a lower case character
- parentheses (...) are used for grouping
@@ -1523,8 +1489,10 @@ Document syntax:
Terminals
~~~~~~~~~
- identifier (name, email, ...) must match ``[a-z_][a-z0-9_]*``
- fully_qualified_name (Doctrine\Tests\Models\CMS\CmsUser) matches PHP's fully qualified class names
- aliased_name (CMS:CmsUser) uses two identifiers, one for the namespace alias and one for the class inside it
- string ('foo', 'bar''s house', '%ninja%', ...)
- char ('/', '\\', ' ', ...)
- integer (-1, 0, 1, 34, ...)
@@ -1559,7 +1527,7 @@ Identifiers
AliasIdentificationVariable :: = identifier
/* identifier that must be a class name (the "User" of "FROM User u"), possibly as a fully qualified class name or namespace-aliased */
AbstractSchemaName ::= fully_qualified_name | identifier
AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier
/* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */
AliasResultVariable = identifier
@@ -1675,6 +1643,7 @@ Conditional Expressions
EmptyCollectionComparisonExpression | CollectionMemberExpression |
InstanceOfExpression
Collection Expressions
~~~~~~~~~~~~~~~~~~~~~~
@@ -1764,7 +1733,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 ::= ArithmeticExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
InstanceOfParameter ::= AbstractSchemaName | InputParameter
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
@@ -1805,3 +1774,4 @@ Functions
"UPPER" "(" StringPrimary ")" |
"IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")"
+127 -119
View File
@@ -1,7 +1,7 @@
Events
======
Doctrine 2 features a lightweight event system that is part of the
Doctrine ORM features a lightweight event system that is part of the
Common package. Doctrine uses it to dispatch system events, mainly
:ref:`lifecycle events <reference-events-lifecycle-events>`.
You can also use it for your own custom events.
@@ -70,7 +70,7 @@ method.
<?php
$evm->removeEventListener(array(self::preFoo, self::postFoo), $this);
The Doctrine 2 event system also has a simple concept of event
The Doctrine ORM event system also has a simple concept of event
subscribers. We can define a simple ``TestEventSubscriber`` class
which implements the ``\Doctrine\Common\EventSubscriber`` interface
and implements a ``getSubscribedEvents()`` method which returns an
@@ -124,11 +124,12 @@ Now you can test the ``$eventSubscriber`` instance to see if the
Naming convention
~~~~~~~~~~~~~~~~~
Events being used with the Doctrine 2 EventManager are best named
Events being used with the Doctrine ORM EventManager are best named
with camelcase and the value of the corresponding constant should
be the name of the constant itself, even with spelling. This has
several reasons:
- It is easy to read.
- Simplicity.
- Each method within an EventSubscriber is named after the
@@ -144,60 +145,65 @@ An example for a correct notation can be found in the example
Lifecycle Events
----------------
The EntityManager and UnitOfWork trigger a bunch of events during
the life-time of their registered entities.
The ``EntityManager`` and ``UnitOfWork`` classes trigger a bunch of
events during the life-time of their registered entities.
- preRemove - The preRemove event occurs for a given entity before
the respective EntityManager remove operation for that entity is
executed. It is not called for a DQL DELETE statement.
- postRemove - The postRemove event occurs for an entity after the
- ``preRemove`` - The ``preRemove`` event occurs for a given entity
before the respective ``EntityManager`` remove operation for that
entity is executed. It is not called for a DQL ``DELETE`` statement.
- ``postRemove`` - The ``postRemove`` event occurs for an entity after the
entity has been deleted. It will be invoked after the database
delete operations. It is not called for a DQL DELETE statement.
- prePersist - The prePersist event occurs for a given entity
before the respective EntityManager persist operation for that
delete operations. It is not called for a DQL ``DELETE`` statement.
- ``prePersist`` - The ``prePersist`` event occurs for a given entity
before the respective ``EntityManager`` persist operation for that
entity is executed. It should be noted that this event is only triggered on
*initial* persist of an entity (i.e. it does not trigger on future updates).
- postPersist - The postPersist event occurs for an entity after
- ``postPersist`` - The ``postPersist`` event occurs for an entity after
the entity has been made persistent. It will be invoked after the
database insert operations. Generated primary key values are
available in the postPersist event.
- preUpdate - The preUpdate event occurs before the database
update operations to entity data. It is not called for a DQL UPDATE statement
nor when the computed changeset is empty.
- postUpdate - The postUpdate event occurs after the database
update operations to entity data. It is not called for a DQL UPDATE statement.
- postLoad - The postLoad event occurs for an entity after the
entity has been loaded into the current EntityManager from the
- ``preUpdate`` - The ``preUpdate`` event occurs before the database
update operations to entity data. It is not called for a DQL
``UPDATE`` statement nor when the computed changeset is empty.
- ``postUpdate`` - The ``postUpdate`` event occurs after the database
update operations to entity data. It is not called for a DQL
``UPDATE`` statement.
- ``postLoad`` - The postLoad event occurs for an entity after the
entity has been loaded into the current ``EntityManager`` from the
database or after the refresh operation has been applied to it.
- loadClassMetadata - The loadClassMetadata event occurs after the
- ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
mapping metadata for a class has been loaded from a mapping source
(annotations/xml). This event is not a lifecycle callback.
- onClassMetadataNotFound - Loading class metadata for a particular
(annotations/xml/yaml). This event is not a lifecycle callback.
- ``onClassMetadataNotFound`` - Loading class metadata for a particular
requested class name failed. Manipulating the given event args instance
allows providing fallback metadata even when no actual metadata exists
or could be found. This event is not a lifecycle callback.
- preFlush - The preFlush event occurs at the very beginning of a flush
operation.
- onFlush - The onFlush event occurs after the change-sets of all
- ``preFlush`` - The ``preFlush`` event occurs at the very beginning of
a flush operation.
- ``onFlush`` - The ``onFlush`` event occurs after the change-sets of all
managed entities are computed. This event is not a lifecycle
callback.
- postFlush - The postFlush event occurs at the end of a flush operation. This
- ``postFlush`` - The ``postFlush`` event occurs at the end of a flush operation. This
event is not a lifecycle callback.
- onClear - The onClear event occurs when the EntityManager#clear() operation is
invoked, after all references to entities have been removed from the unit of
work. This event is not a lifecycle callback.
- ``onClear`` - The ``onClear`` event occurs when the
``EntityManager#clear()`` operation is invoked, after all references
to entities have been removed from the unit of work. This event is not
a lifecycle callback.
.. warning::
Note that, when using ``Doctrine\ORM\AbstractQuery#iterate()``, ``postLoad``
Note that, when using ``Doctrine\ORM\AbstractQuery#toIterable()``, ``postLoad``
events will be executed immediately after objects are being hydrated, and therefore
associations are not guaranteed to be initialized. It is not safe to combine
usage of ``Doctrine\ORM\AbstractQuery#iterate()`` and ``postLoad`` event
usage of ``Doctrine\ORM\AbstractQuery#toIterable()`` and ``postLoad`` event
handlers.
.. warning::
Note that the postRemove event or any events triggered after an entity removal
Note that the ``postRemove`` event or any events triggered after an entity removal
can receive an uninitializable proxy in case you have configured an entity to
cascade remove relations. In this case, you should load yourself the proxy in
the associated pre event.
@@ -215,22 +221,23 @@ These can be hooked into by two different types of event
listeners:
- Lifecycle Callbacks are methods on the entity classes that are
called when the event is triggered. As of v2.4 they receive some kind
called when the event is triggered. They receive some kind
of ``EventArgs`` instance.
- Lifecycle Event Listeners and Subscribers are classes with specific callback
methods that receives some kind of ``EventArgs`` instance.
The EventArgs instance received by the listener gives access to the entity,
EntityManager and other relevant data.
The ``EventArgs`` instance received by the listener gives access to the entity,
``EntityManager`` instance and other relevant data.
.. note::
All Lifecycle events that happen during the ``flush()`` of
an EntityManager have very specific constraints on the allowed
an ``EntityManager`` have very specific constraints on the allowed
operations that can be executed. Please read the
:ref:`reference-events-implementing-listeners` section very carefully
to understand which operations are allowed in which lifecycle event.
Lifecycle Callbacks
-------------------
@@ -240,6 +247,11 @@ a relevant lifecycle event. More than one callback can be defined for each
lifecycle event. Lifecycle Callbacks are best used for simple operations
specific to a particular entity class's lifecycle.
.. note::
Note that Licecycle Callbacks are not supported for Embeddables.
.. code-block:: php
<?php
@@ -292,8 +304,26 @@ Note that the methods set as lifecycle callbacks need to be public and,
when using these annotations, you have to apply the
``@HasLifecycleCallbacks`` marker annotation on the entity class.
If you want to register lifecycle callbacks from XML it would look
something like this:
If you want to register lifecycle callbacks from YAML or XML you
can do it with the following.
.. code-block:: yaml
User:
type: entity
fields:
# ...
name:
type: string(50)
lifecycleCallbacks:
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
postPersist: [ doStuffOnPostPersist ]
In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
are triggering on and the value is the method (or methods) to call. The allowed
event types are the ones listed in the previous Lifecycle Events section.
XML would look something like this:
.. code-block:: xml
@@ -319,7 +349,7 @@ In XML the ``type`` of the lifecycle-callback entry is the event that you
are triggering on and the ``method`` is the method to call. The allowed event
types are the ones listed in the previous Lifecycle Events section.
When using XML you need to remember to create public methods to match the
When using YAML or XML you need to remember to create public methods to match the
callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``,
``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be
defined on your ``User`` model.
@@ -349,12 +379,11 @@ defined on your ``User`` model.
}
}
Lifecycle Callbacks Event Argument
----------------------------------
-----------------------------------
.. versionadded:: 2.4
Since 2.4 the triggered event is given to the lifecycle-callback.
The triggered event is also given to the lifecycle-callback.
With the additional argument you have access to the
``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
@@ -383,9 +412,9 @@ sit at a level above the entities and allow you to implement re-usable
behaviors across different entity classes.
Note that they require much more detailed knowledge about the inner
workings of the EntityManager and UnitOfWork. Please read the
:ref:`reference-events-implementing-listeners` section carefully if you
are trying to write your own listener.
workings of the ``EntityManager`` and ``UnitOfWork`` classes. Please
read the :ref:`reference-events-implementing-listeners` section
carefully if you are trying to write your own listener.
For event subscribers, there are no surprises. They declare the
lifecycle events in their ``getSubscribedEvents`` method and provide
@@ -396,7 +425,7 @@ A lifecycle event listener looks like the following:
.. code-block:: php
<?php
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\Persistence\Event\LifecycleEventArgs;
class MyEventListener
{
@@ -418,8 +447,8 @@ A lifecycle event subscriber may look like this:
<?php
use Doctrine\ORM\Events;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\EventSubscriber;
use Doctrine\Persistence\Event\LifecycleEventArgs;
class MyEventSubscriber implements EventSubscriber
{
@@ -474,16 +503,16 @@ Implementing Event Listeners
----------------------------
This section explains what is and what is not allowed during
specific lifecycle events of the UnitOfWork. Although you get
passed the EntityManager in all of these events, you have to follow
these restrictions very carefully since operations in the wrong
event may produce lots of different errors, such as inconsistent
specific lifecycle events of the ``UnitOfWork`` class. Although you get
passed the ``EntityManager`` instance in all of these events, you have
to follow these restrictions very carefully since operations in the
wrong event may produce lots of different errors, such as inconsistent
data and lost updates/persists/removes.
For the described events that are also lifecycle callback events
the restrictions apply as well, with the additional restriction
that (prior to version 2.4) you do not have access to the
EntityManager or UnitOfWork APIs inside these events.
``EntityManager`` or ``UnitOfWork`` APIs inside these events.
prePersist
~~~~~~~~~~
@@ -503,6 +532,7 @@ which has access to the entity and the entity manager.
The following restrictions apply to ``prePersist``:
- If you are using a PrePersist Identity Generator such as
sequences the ID value will *NOT* be available within any
PrePersist events.
@@ -525,8 +555,9 @@ preFlush
~~~~~~~~
``preFlush`` is called at ``EntityManager#flush()`` before
anything else. ``EntityManager#flush()`` can be called safely
inside its listeners.
anything else. ``EntityManager#flush()`` should not be called inside
its listeners, since `preFlush` event is dispatched in it, which would
result in infinite loop.
.. code-block:: php
@@ -550,14 +581,15 @@ OnFlush is a very powerful event. It is called inside
entities and their associations have been computed. This means, the
``onFlush`` event has access to the sets of:
- Entities scheduled for insert
- Entities scheduled for update
- Entities scheduled for removal
- Collections scheduled for update
- Collections scheduled for removal
To make use of the onFlush event you have to be familiar with the
internal UnitOfWork API, which grants you access to the previously
To make use of the ``onFlush`` event you have to be familiar with the
internal ``UnitOfWork`` API, which grants you access to the previously
mentioned sets. See this example:
.. code-block:: php
@@ -594,6 +626,7 @@ mentioned sets. See this example:
The following restrictions apply to the onFlush event:
- If you create and persist a new entity in ``onFlush``, then
calling ``EntityManager#persist()`` is not enough.
You have to execute an additional call to
@@ -642,6 +675,7 @@ This means you have access to all the fields that have changed for
this entity with their old and new value. The following methods are
available on the ``PreUpdateEventArgs``:
- ``getEntity()`` to get access to the actual entity.
- ``getEntityChangeSet()`` to get a copy of the changeset array.
Changes to this returned array do not affect updating.
@@ -695,6 +729,7 @@ lifecycle callback when there are expensive validations to call:
Restrictions for this event:
- Changes to associations of the passed entities are not
recognized by the flush operation anymore.
- Changes to fields of the passed entities are not recognized by
@@ -702,7 +737,7 @@ Restrictions for this event:
the event to modify primitive field values, e.g. use
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
- Any calls to ``EntityManager#persist()`` or
``EntityManager#remove()``, even in combination with the UnitOfWork
``EntityManager#remove()``, even in combination with the ``UnitOfWork``
API are strongly discouraged and don't work as expected outside the
flush operation.
@@ -724,8 +759,6 @@ EntityManager.
Entity listeners
----------------
.. versionadded:: 2.4
An entity listener is a lifecycle listener class used for an entity.
- The entity listener's mapping may be applied to an entity class or mapped superclass.
@@ -753,6 +786,13 @@ An entity listener is a lifecycle listener class used for an entity.
<!-- .... -->
</entity>
</doctrine-mapping>
.. code-block:: yaml
MyProject\Entity\User:
type: entity
entityListeners:
UserListener:
# ....
.. _reference-entity-listeners:
@@ -835,13 +875,32 @@ you need to map the listener method using the event type mapping:
<!-- .... -->
</entity>
</doctrine-mapping>
.. code-block:: yaml
MyProject\Entity\User:
type: entity
entityListeners:
UserListener:
preFlush: [preFlushHandler]
postLoad: [postLoadHandler]
postPersist: [postPersistHandler]
prePersist: [prePersistHandler]
postUpdate: [postUpdateHandler]
preUpdate: [preUpdateHandler]
postRemove: [postRemoveHandler]
preRemove: [preRemoveHandler]
# ....
.. note::
The order of execution of multiple methods for the same event (e.g. multiple @PrePersist) is not guaranteed.
Entity listeners resolver
~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
Doctrine invokes the listener resolver to get the listener instance.
- A resolver allows you register a specific entity listener instance.
@@ -907,17 +966,18 @@ Load ClassMetadata Event
------------------------
When the mapping information for an entity is read, it is populated
in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. You can hook in to this
in to a ``ClassMetadataInfo`` instance. You can hook in to this
process and manipulate the instance.
.. code-block:: php
<?php
$test = new TestEventListener();
$test = new TestEvent();
$metadataFactory = $em->getMetadataFactory();
$evm = $em->getEventManager();
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $test);
$evm->addEventListener(Events::loadClassMetadata, $test);
class TestEventListener
class TestEvent
{
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
{
@@ -931,55 +991,3 @@ process and manipulate the instance.
}
}
SchemaTool Events
-----------------
It is possible to access the schema metadata during schema changes that are happening in ``Doctrine\ORM\Tools\SchemaTool``.
There are two different events where you can hook in.
postGenerateSchemaTable
~~~~~~~~~~~~~~~~~~~~~~~
This event is fired for each ``Doctrine\DBAL\Schema\Table`` instance, after one was created and built up with the current class metadata
of an entity. It is possible to access to the current state of ``Doctrine\DBAL\Schema\Schema``, the current table schema
instance and class metadata.
.. code-block:: php
<?php
$test = new TestEventListener();
$evm = $em->getEventManager();
$evm->addEventListener(\Doctrine\ORM\Tools\ToolEvents::postGenerateSchemaTable, $test);
class TestEventListener
{
public function postGenerateSchemaTable(\Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
$schema = $eventArgs->getSchema();
$table = $eventArgs->getClassTable();
}
}
postGenerateSchema
~~~~~~~~~~~~~~~~~~
This event is fired after the schema instance was successfully built and before SQL queries are generated from the
schema information of ``Doctrine\DBAL\Schema\Schema``. It allows to access the full object representation of the database schema
and the EntityManager.
.. code-block:: php
<?php
$test = new TestEventListener();
$evm = $em->getEventManager();
$evm->addEventListener(\Doctrine\ORM\Tools\ToolEvents::postGenerateSchema, $test);
class TestEventListener
{
public function postGenerateSchema(\Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs $eventArgs)
{
$schema = $eventArgs->getSchema();
$em = $eventArgs->getEntityManager();
}
}
+41 -8
View File
@@ -13,7 +13,7 @@ Database Schema
How do I set the charset and collation for MySQL tables?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can't set these values inside the annotations or xml mapping files. To make a database
You can't set these values inside the annotations, yml or xml mapping files. To make a database
work with the default charset and collation you should configure MySQL to use it as default charset,
or create the database with charset and collation details. This way they get inherited to all newly
created database tables and columns.
@@ -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 <https://www.symfony.com>`_ for example there is a Unique Entity Validator
In `Symfony2 <http://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
@@ -80,7 +80,7 @@ You can solve this exception by:
How can I filter an association?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Natively you can't filter associations in 2.0 and 2.1. You should use DQL queries to query for the filtered set of entities.
You should use DQL queries to query for the filtered set of entities.
I call clear() on a One-To-Many collection but the entities are not deleted
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -98,10 +98,11 @@ How can I add columns to a many-to-many table?
The many-to-many association is only supporting foreign keys in the table definition
To work with many-to-many tables containing extra columns you have to use the
foreign keys as primary keys feature of Doctrine introduced in version 2.1.
foreign keys as primary keys feature of Doctrine ORM.
See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`.
How can i paginate fetch-joined collections?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -111,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 <https://github.com/beberlei/DoctrineExtensions>`_
* `Pagerfanta <https://github.com/whiteoctober/pagerfanta>`_
* `DoctrineExtensions <http://github.com/beberlei/DoctrineExtensions>`_
* `Pagerfanta <http://github.com/whiteoctober/pagerfanta>`_
Why does pagination not work correctly with fetch joins?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -127,10 +128,10 @@ See the previous question for a solution to this task.
Inheritance
-----------
Can I use Inheritance with Doctrine 2?
Can I use Inheritance with Doctrine ORM?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2.
Yes, you can use Single- or Joined-Table Inheritance in ORM.
See the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>` for
the details.
@@ -142,6 +143,23 @@ If you set a many-to-one or one-to-one association target-entity to any parent c
an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of.
To find this out it has to execute a SQL query to look this information up in the database.
EntityGenerator
---------------
Why does the EntityGenerator not do X?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The EntityGenerator is not a full fledged code-generator that solves all tasks. Code-Generation
is not a first-class priority in Doctrine 2 anymore (compared to Doctrine 1). The EntityGenerator
is supposed to kick-start you, but not towards 100%.
Why does the EntityGenerator not generate inheritance correctly?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy.
This is why the generation of inherited entities does not fully work. You have to adjust some additional
code to get this one working correctly.
Performance
-----------
@@ -180,6 +198,21 @@ No, it is not supported to sort by function in DQL. If you need this functionali
use a native-query or come up with another solution. As a side note: Sorting with ORDER BY RAND() is painfully slow
starting with 1000 rows.
Is it better to write DQL or to generate it with the query builder?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The purpose of the ``QueryBuilder`` is to generate DQL dynamically,
which is useful when you have optional filters, conditional joins, etc.
But the ``QueryBuilder`` is not an alternative to DQL, it actually generates DQL
queries at runtime, which are then interpreted by Doctrine. This means that
using the ``QueryBuilder`` to build and run a query is actually always slower
than only running the corresponding DQL query.
So if you only need to generate a query and bind parameters to it,
you should use plain DQL, as this is a simpler and much more readable solution.
You should only use the ``QueryBuilder`` when you can't achieve what you want to do with a DQL query.
A Query fails, how can I debug it?
----------------------------------
+7 -5
View File
@@ -1,9 +1,7 @@
Filters
=======
.. versionadded:: 2.2
Doctrine 2.2 features a filter system that allows the developer to add SQL to
Doctrine ORM features a filter system that allows the developer to add SQL to
the conditional clauses of queries, regardless the place where the SQL is
generated (e.g. from a DQL query, or by loading associated entities).
@@ -16,6 +14,7 @@ By adding SQL to the conditional clauses of queries, the filter system filters
out rows belonging to the entities at the level of the SQL result set. This
means that the filtered entities are never hydrated (which can be expensive).
Example filter class
--------------------
Throughout this document the example ``MyLocaleFilter`` class will be used to
@@ -38,7 +37,7 @@ proper quoting of parameters.
<?php
namespace Example;
use Doctrine\ORM\Mapping\ClassMetaData,
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Query\Filter\SQLFilter;
class MyLocaleFilter extends SQLFilter
@@ -46,7 +45,7 @@ proper quoting of parameters.
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
// Check if the entity implements the LocalAware interface
if (!$targetEntity->getReflectionClass()->implementsInterface('LocaleAware')) {
if (!$targetEntity->reflClass->implementsInterface('LocaleAware')) {
return "";
}
@@ -54,6 +53,7 @@ proper quoting of parameters.
}
}
Configuration
-------------
Filter classes are added to the configuration as following:
@@ -63,9 +63,11 @@ Filter classes are added to the configuration as following:
<?php
$config->addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter");
The ``Configuration#addFilter()`` method takes a name for the filter and the name of the
class responsible for the actual filtering.
Disabling/Enabling Filters and Setting Parameters
---------------------------------------------------
Filters can be disabled and enabled via the ``FilterCollection`` which is
+18 -6
View File
@@ -4,27 +4,35 @@ Improving Performance
Bytecode Cache
--------------
It is highly recommended to make use of a bytecode cache like APC.
It is highly recommended to make use of a bytecode cache like OPcache.
A bytecode cache removes the need for parsing PHP code on every
request and can greatly improve performance.
"If you care about performance and don't use a bytecode
cache then you don't really care about performance. Please get one
and start using it."
*Stas Malyshev, Core Contributor to PHP and Zend Employee*
Metadata and Query caches
-------------------------
As already mentioned earlier in the chapter about configuring
Doctrine, it is strongly discouraged to use Doctrine without a
Metadata and Query cache (preferably with APC or Memcache as the
cache driver). Operating Doctrine without these caches means
Metadata and Query cache.
Operating Doctrine without these caches means
Doctrine will need to load your mapping information on every single
request and has to parse each DQL query on every single request.
This is a waste of resources.
The preferred cache driver for metadata and query caches is ``PhpFileCache``.
This driver serializes cache items and writes them to a file.
This allows for opcode caching to be used and provides high performance in most scenarios.
See :ref:`integrating-with-the-orm`
Alternative Query Result Formats
--------------------------------
@@ -35,12 +43,14 @@ in scenarios where data is loaded for read-only purposes.
Read-Only Entities
------------------
Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping
You can mark entities as read only (See metadata mapping
references for details). This means that the entity marked as read only is never considered
for updates, which means when you call flush on the EntityManager these entities are skipped
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing
ones, they are just not considered for updates.
See :ref:`annref_entity`
Extra-Lazy Collections
----------------------
@@ -60,7 +70,9 @@ Apply Best Practices
A lot of the points mentioned in the Best Practices chapter will
also positively affect the performance of Doctrine.
See :doc:`Best Practices <reference/best-practices>`
Change Tracking policies
------------------------
See: :doc:`Change Tracking Policies <change-tracking-policies>`
See: :doc:`Change Tracking Policies <reference/change-tracking-policies>`
+145 -42
View File
@@ -25,44 +25,36 @@ appear in the middle of an otherwise mapped inheritance hierarchy
For further support of inheritance, the single or
joined table inheritance features have to be used.
Example:
.. code-block:: php
<?php
/** @MappedSuperclass */
class Person
class MappedSuperclassBase
{
/** @Column(type="integer") */
protected $mapped1;
/** @Column(type="string") */
protected $mapped2;
/**
* @OneToOne(targetEntity="Toothbrush")
* @JoinColumn(name="toothbrush_id", referencedColumnName="id")
* @OneToOne(targetEntity="MappedSuperclassRelated1")
* @JoinColumn(name="related1_id", referencedColumnName="id")
*/
protected $toothbrush;
protected $mappedRelated1;
// ... more fields and methods
}
/** @Entity */
class Employee extends Person
class EntitySubClass extends MappedSuperclassBase
{
/** @Id @Column(type="integer") */
private $id;
/** @Column(type="string") */
private $name;
// ... more fields and methods
}
/** @Entity */
class Toothbrush
{
/** @Id @Column(type="integer") */
private $id;
// ... more fields and methods
}
@@ -81,7 +73,7 @@ defined on that class directly.
Single Table Inheritance
------------------------
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
`Single Table Inheritance <http://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
@@ -92,10 +84,10 @@ Example:
.. configuration-block::
.. code-block:: php
<?php
namespace MyProject\Model;
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
@@ -106,7 +98,7 @@ Example:
{
// ...
}
/**
* @Entity
*/
@@ -115,9 +107,25 @@ Example:
// ...
}
.. code-block:: yaml
MyProject\Model\Person:
type: entity
inheritanceType: SINGLE_TABLE
discriminatorColumn:
name: discr
type: string
discriminatorMap:
person: Person
employee: Employee
MyProject\Model\Employee:
type: entity
Things to note:
- The @InheritanceType and @DiscriminatorColumn must be specified
- The @InheritanceType and @DiscriminatorColumn must be specified
on the topmost class that is part of the mapped entity hierarchy.
- The @DiscriminatorMap specifies which values of the
discriminator column identify a row as being of a certain type. In
@@ -131,7 +139,9 @@ Things to note:
be fully qualified if the classes are contained in the same
namespace as the entity class on which the discriminator map is
applied.
- If no discriminator map is provided, an exception will be thrown.
- If no discriminator map is provided, then the map is generated
automatically. The automatically generated discriminator map
contains the lowercase short name of each class as key.
Design-time considerations
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -152,9 +162,9 @@ relationships involving types that employ this mapping strategy are
very performing.
There is a general performance consideration with Single Table
Inheritance: If the target-entity of a many-to-one or one-to-one
association is an STI entity, it is preferable for performance reasons that it
be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
Inheritance: If the target-entity of a many-to-one or one-to-one
association is an STI entity, it is preferable for performance reasons that it
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.
@@ -171,11 +181,11 @@ the root entity of the single-table inheritance hierarchy.
Class Table Inheritance
-----------------------
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
`Class Table Inheritance <http://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
of a parent class through a foreign key constraint. Doctrine 2
of a parent class through a foreign key constraint. Doctrine ORM
implements this strategy through the use of a discriminator column
in the topmost table of the hierarchy because this is the easiest
way to achieve polymorphic queries with Class Table Inheritance.
@@ -186,7 +196,7 @@ Example:
<?php
namespace MyProject\Model;
/**
* @Entity
* @InheritanceType("JOINED")
@@ -197,7 +207,7 @@ Example:
{
// ...
}
/** @Entity */
class Employee extends Person
{
@@ -206,6 +216,7 @@ Example:
Things to note:
- The @InheritanceType, @DiscriminatorColumn and @DiscriminatorMap
must be specified on the topmost class that is part of the mapped
entity hierarchy.
@@ -218,7 +229,9 @@ Things to note:
be fully qualified if the classes are contained in the same
namespace as the entity class on which the discriminator map is
applied.
- If no discriminator map is provided, an exception will be thrown.
- If no discriminator map is provided, then the map is generated
automatically. The automatically generated discriminator map
contains the lowercase short name of each class as key.
.. note::
@@ -228,6 +241,7 @@ Things to note:
``ON DELETE CASCADE`` in all database implementations. A failure to
implement this yourself will lead to dead rows in the database.
Design-time considerations
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -254,9 +268,9 @@ themselves on access of any subtype fields, so accessing fields of
subtypes after such a query is not safe.
There is a general performance consideration with Class Table
Inheritance: If the target-entity of a many-to-one or one-to-one
association is a CTI entity, it is preferable for performance reasons that it
be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
Inheritance: If the target-entity of a many-to-one or one-to-one
association is a CTI entity, it is preferable for performance reasons that it
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.
@@ -275,9 +289,16 @@ column and cascading on delete.
Overrides
---------
Used to override a mapping for an entity field or relationship.
May be applied to an entity that extends a mapped superclass
to override a relationship or field mapping defined by the mapped superclass.
Used to override a mapping for an entity field or relationship. Can only be
applied to an entity that extends a mapped superclass or uses a trait to
override a relationship or field mapping defined by the mapped superclass or
trait.
It is not possible to override attributes or associations in entity to entity
inheritance scenarios, because this can cause unforseen edge case behavior and
increases complexity in ORM internal classes.
Association Override
~~~~~~~~~~~~~~~~~~~~
@@ -300,7 +321,7 @@ Example:
*/
class User
{
// other fields mapping
//other fields mapping
/**
* @ManyToMany(targetEntity="Group", inversedBy="users")
@@ -350,7 +371,8 @@ Example:
<many-to-many field="groups" target-entity="Group" inversed-by="users">
<cascade>
<cascade-persist/>
<cascade-refresh/>
<cascade-merge/>
<cascade-detach/>
</cascade>
<join-table name="users_groups">
<join-columns>
@@ -386,6 +408,51 @@ Example:
</association-overrides>
</entity>
</doctrine-mapping>
.. code-block:: yaml
# user mapping
MyProject\Model\User:
type: mappedSuperclass
# other fields mapping
manyToOne:
address:
targetEntity: Address
joinColumn:
name: address_id
referencedColumnName: id
cascade: [ persist, merge ]
manyToMany:
groups:
targetEntity: Group
joinTable:
name: users_groups
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
group_id:
referencedColumnName: id
cascade: [ persist, merge, detach ]
# admin mapping
MyProject\Model\Admin:
type: entity
associationOverride:
address:
joinColumn:
adminaddress_id:
name: adminaddress_id
referencedColumnName: id
groups:
joinTable:
name: users_admingroups
joinColumns:
adminuser_id:
referencedColumnName: id
inverseJoinColumns:
admingroup_id:
referencedColumnName: id
Things to note:
@@ -393,7 +460,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
@@ -461,7 +528,7 @@ Could be used by an entity that extends a mapped superclass to override a field
<many-to-one field="address" target-entity="Address">
<cascade>
<cascade-persist/>
<cascade-refresh/>
<cascade-merge/>
</cascade>
<join-column name="address_id" referenced-column-name="id"/>
</many-to-one>
@@ -482,12 +549,48 @@ Could be used by an entity that extends a mapped superclass to override a field
</attribute-overrides>
</entity>
</doctrine-mapping>
.. code-block:: yaml
# user mapping
MyProject\Model\User:
type: mappedSuperclass
id:
id:
type: integer
column: user_id
length: 150
generator:
strategy: AUTO
fields:
name:
type: string
column: user_name
length: 250
nullable: true
unique: false
#other fields mapping
# guest mapping
MyProject\Model\Guest:
type: entity
attributeOverride:
id:
column: guest_id
type: integer
length: 140
name:
column: guest_name
type: string
length: 240
nullable: false
unique: true
Things to note:
- The "attribute override" specifies the overrides base on the property name.
- The column type *CANNOT* be changed. If the column type is not equal you get a ``MappingException``
- The override can redefine all the columns except the type.
- The override can redefine all the attributes except the type.
Query the Type
--------------
@@ -6,7 +6,7 @@ Therefore we think it is very important to be honest about the
current limitations to our users. Much like every other piece of
software Doctrine2 is not perfect and far from feature complete.
This section should give you an overview of current limitations of
Doctrine 2 as well as critical known issues that you should know
Doctrine ORM as well as critical known issues that you should know
about.
Current Limitations
@@ -39,7 +39,7 @@ possible either. See the following example:
name VARCHAR,
PRIMARY KEY(id)
);
CREATE TABLE product_attributes (
product_id INTEGER,
attribute_name VARCHAR,
@@ -65,6 +65,15 @@ Where the ``attribute_name`` column contains the key and
The feature request for persistence of primitive value arrays
`is described in the DDC-298 ticket <https://github.com/doctrine/orm/issues/3743>`_.
Cascade Merge with Bi-directional Associations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are two bugs now that concern the use of cascade merge in combination with bi-directional associations.
Make sure to study the behavior of cascade merge if you are using it:
- `DDC-875 <https://github.com/doctrine/orm/issues/5398>`_ Merge can sometimes add the same entity twice into a collection
- `DDC-763 <https://github.com/doctrine/orm/issues/5277>`_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability"
Custom Persisters
~~~~~~~~~~~~~~~~~
@@ -98,7 +107,7 @@ to the same entity.
Behaviors
~~~~~~~~~
Doctrine 2 will **never** include a behavior system like Doctrine 1
Doctrine ORM will **never** include a behavior system like Doctrine 1
in the core library. We don't think behaviors add more value than
they cost pain and debugging hell. Please see the many different
blog posts we have written on this topics:
@@ -106,9 +115,9 @@ blog posts we have written on this topics:
- `Doctrine2 "Behaviors" in a Nutshell <http://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_
- `A re-usable Versionable behavior for Doctrine2 <http://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_
- `Write your own ORM on top of Doctrine2 <http://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_
- `Doctrine 2 Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
- `Doctrine ORM Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
Doctrine 2 has enough hooks and extension points so that **you** can
Doctrine ORM has enough hooks and extension points so that **you** can
add whatever you want on top of it. None of this will ever become
core functionality of Doctrine2 however, you will have to rely on
third party extensions for magical behaviors.
@@ -117,12 +126,13 @@ Nested Set
~~~~~~~~~~
NestedSet was offered as a behavior in Doctrine 1 and will not be
included in the core of Doctrine 2. However there are already two
included in the core of Doctrine ORM. However there are already two
extensions out there that offer support for Nested Set with
Doctrine 2:
ORM:
- `Doctrine2 Hierarchical-Structural Behavior <https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
- `Doctrine2 NestedSet <https://github.com/blt04/doctrine2-nestedset>`_
- `Doctrine2 Hierarchical-Structural Behavior <http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
- `Doctrine2 NestedSet <http://github.com/blt04/doctrine2-nestedset>`_
Known Issues
------------
@@ -139,9 +149,10 @@ Identifier Quoting and Legacy Databases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For compatibility reasons between all the supported vendors and
edge case problems Doctrine 2 does **NOT** do automatic identifier
edge case problems Doctrine ORM does **NOT** do automatic identifier
quoting. This can lead to problems when trying to get
legacy-databases to work with Doctrine 2.
legacy-databases to work with Doctrine ORM.
- You can quote column-names as described in the
:doc:`Basic-Mapping <basic-mapping>` section.
@@ -166,3 +177,27 @@ MySQL with MyISAM tables
Doctrine cannot provide atomic operations when calling ``EntityManager#flush()`` if one
of the tables involved uses the storage engine MyISAM. You must use InnoDB or
other storage engines that support transactions if you need integrity.
Entities, Proxies and Reflection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Using methods for Reflection on entities can be prone to error, when the entity
is actually a proxy the following methods will not work correctly:
- ``new ReflectionClass``
- ``new ReflectionObject``
- ``get_class()``
- ``get_parent_class()``
This is why ``Doctrine\Common\Util\ClassUtils`` class exists that has similar
methods, which resolve the proxy problem beforehand.
.. code-block:: php
<?php
use Doctrine\Common\Util\ClassUtils;
$bookProxy = $entityManager->getReference('Acme\Book');
$reflection = ClassUtils::newReflectionClass($bookProxy);
$class = ClassUtils::getClass($bookProxy)¸
+31 -23
View File
@@ -11,8 +11,10 @@ Core Metadata Drivers
Doctrine provides a few different ways for you to specify your
metadata:
- **XML files** (XmlDriver)
- **Class DocBlock Annotations** (AnnotationDriver)
- **YAML files** (YamlDriver)
- **PHP Code in files or static functions** (PhpDriver)
Something important to note about the above drivers is they are all
@@ -35,6 +37,7 @@ an entity.
<?php
$em->getConfiguration()->setMetadataCacheImpl(new ApcuCache());
If you want to use one of the included core metadata drivers you
just need to configure it. All the drivers are in the
``Doctrine\ORM\Mapping\Driver`` namespace:
@@ -56,26 +59,26 @@ implements the ``Driver`` interface:
<?php
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
interface Driver
{
/**
* Loads the metadata for the specified class into the provided container.
*
*
* @param string $className
* @param ClassMetadata $metadata
* @param ClassMetadataInfo $metadata
*/
function loadMetadataForClass($className, ClassMetadata $metadata);
function loadMetadataForClass($className, ClassMetadataInfo $metadata);
/**
* Gets the names of all mapped classes known to this driver.
*
*
* @return array The names of all mapped classes known to this driver.
*/
function getAllClassNames();
function getAllClassNames();
/**
* Whether the class with the specified name should have its metadata loaded.
* This is only the case if it is either mapped as an Entity or a
@@ -99,22 +102,22 @@ the ``AbstractFileDriver`` implementation for you to extend from:
/**
* {@inheritdoc}
*/
protected $fileExtension = '.dcm.ext';
protected $_fileExtension = '.dcm.ext';
/**
* {@inheritdoc}
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
{
$data = $this->loadMappingFile($file);
// populate ClassMetadata instance from $data
$data = $this->_loadMappingFile($file);
// populate ClassMetadataInfo instance from $data
}
/**
* {@inheritdoc}
*/
protected function loadMappingFile($file)
protected function _loadMappingFile($file)
{
// parse contents of $file and return php data structure
}
@@ -130,6 +133,7 @@ the ``AbstractFileDriver`` implementation for you to extend from:
to name the file ``Entities.User.dcm.ext`` for it to be
recognized.
Now you can use your ``MyMetadataDriver`` implementation by setting
it with the ``setMetadataDriverImpl()`` method:
@@ -143,18 +147,21 @@ ClassMetadata
-------------
The last piece you need to know and understand about metadata in
Doctrine 2 is the API of the ``ClassMetadata`` classes. You need to
Doctrine ORM is the API of the ``ClassMetadata`` classes. You need to
be familiar with them in order to implement your own drivers but
more importantly to retrieve mapping information for a certain
entity when needed.
You have all the methods you need to manually specify the mapping
information instead of using some mapping file to populate it from.
The ``ClassMetadata`` class is responsible for only data storage
and is not meant for runtime use. It does not require that the
class actually exists yet so it is useful for describing some
The base ``ClassMetadataInfo`` class is responsible for only data
storage and is not meant for runtime use. It does not require that
the class actually exists yet so it is useful for describing some
entity before it exists and using that information to generate for
example the entities themselves.
example the entities themselves. The class ``ClassMetadata``
extends ``ClassMetadataInfo`` and adds some functionality required
for runtime usage and requires that the PHP class is present and
can be autoloaded.
You can read more about the API of the ``ClassMetadata`` classes in
the PHP Mapping chapter.
@@ -184,3 +191,4 @@ iterate over them:
echo $fieldMapping['fieldName'] . "\n";
}
+6 -3
View File
@@ -1,12 +1,14 @@
Implementing a NamingStrategy
==============================
.. versionadded:: 2.3
Using a naming strategy you can provide rules for generating database identifiers,
column or table names when the column or table name is not given. This feature helps
column or table names. This feature helps
reduce the verbosity of the mapping document, eliminating repetitive noise (eg: ``TABLE_``).
.. warning
The naming strategy is always overridden by entity mapping such as the `Table` annotation.
Configuring a naming strategy
-----------------------------
The default strategy used by Doctrine is quite minimal.
@@ -102,6 +104,7 @@ achieve such standards by implementing a naming strategy.
You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrategy``.
.. code-block:: php
<?php
+138 -18
View File
@@ -71,7 +71,7 @@ with inheritance hierarchies.
use Doctrine\ORM\Query\ResultSetMappingBuilder;
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
"FROM users u INNER JOIN address a ON u.address_id = a.id";
$rsm = new ResultSetMappingBuilder($entityManager);
@@ -80,9 +80,7 @@ with inheritance hierarchies.
The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well.
.. versionadded:: 2.4
Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause
The ``SELECT`` clause can be generated
from a ``ResultSetMappingBuilder``. You can either cast the builder
object to ``(string)`` and the DQL aliases are used as SQL table aliases
or use the ``generateSelectClause($tableAliases)`` method and pass
@@ -98,6 +96,7 @@ a mapping from DQL alias (key) to SQL alias (value)
));
$sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id";
The ResultSetMapping
--------------------
@@ -105,6 +104,7 @@ Understanding the ``ResultSetMapping`` is the key to using a
``NativeQuery``. A Doctrine result can contain the following
components:
- Entity results. These represent root result elements.
- Joined entity results. These represent joined entities in
associations of root entity results.
@@ -130,6 +130,7 @@ components:
``ResultSetMapping`` that describes how the results should be
processed by the hydration routines.
We will now look at each of the result types that can appear in a
ResultSetMapping in detail.
@@ -264,7 +265,7 @@ detail:
<?php
/**
* Adds a meta column (foreign key or discriminator column) to the result set.
*
*
* @param string $alias
* @param string $columnAlias
* @param string $columnName
@@ -319,10 +320,10 @@ entity.
$rsm->addEntityResult('User', 'u');
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$query = $this->em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);
$query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
The result would look like this:
@@ -355,10 +356,10 @@ thus owns the foreign key.
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$rsm->addMetaResult('u', 'address_id', 'address_id');
$query = $this->em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
$query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
Foreign keys are used by Doctrine for lazy-loading purposes when
@@ -384,12 +385,12 @@ associations that are lazy.
$rsm->addFieldResult('a', 'address_id', 'id');
$rsm->addFieldResult('a', 'street', 'street');
$rsm->addFieldResult('a', 'city', 'city');
$sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' .
'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?';
$query = $this->em->createNativeQuery($sql, $rsm);
$query = $this->_em->createNativeQuery($sql, $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
In this case the nested entity ``Address`` is registered with the
@@ -419,10 +420,10 @@ to map the hierarchy (both use a discriminator column).
$rsm->addFieldResult('u', 'name', 'name');
$rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column
$rsm->setDiscriminatorColumn('u', 'discr');
$query = $this->em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
$query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
Note that in the case of Class Table Inheritance, an example as
@@ -439,7 +440,8 @@ You can also map a native query using a named native query mapping.
To achieve that, you must describe the SQL resultset structure
using named native query (and sql resultset mappings if is a several resultset mappings).
Like named query, a named native query can be defined at class level or in an XML file.
Like named query, a named native query can be defined at class level or in a XML or YAML file.
A resultSetMapping parameter is defined in @NamedNativeQuery,
it represents the name of a defined @SqlResultSetMapping.
@@ -534,6 +536,47 @@ it represents the name of a defined @SqlResultSetMapping.
</sql-result-set-mappings>
</entity>
</doctrine-mapping>
.. code-block:: yaml
MyProject\Model\User:
type: entity
namedNativeQueries:
fetchMultipleJoinsEntityResults:
name: fetchMultipleJoinsEntityResults
resultSetMapping: mappingMultipleJoinsEntityResults
query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username
sqlResultSetMappings:
mappingMultipleJoinsEntityResults:
name: mappingMultipleJoinsEntityResults
columnResult:
0:
name: numphones
entityResult:
0:
entityClass: __CLASS__
fieldResult:
0:
name: id
column: u_id
1:
name: name
column: u_name
2:
name: status
column: u_status
1:
entityClass: Address
fieldResult:
0:
name: id
column: a_id
1:
name: zip
column: a_zip
2:
name: country
column: a_country
Things to note:
- The resultset mapping declares the entities retrieved by this native query.
@@ -544,6 +587,7 @@ Things to note:
column name as the one declared on the class property.
- ``__CLASS__`` is an alias for the mapped class
In the above example,
the ``fetchJoinedAddress`` named query use the joinMapping result set mapping.
This mapping returns 2 entities, User and Address, each property is declared and associated to a column name,
@@ -609,6 +653,21 @@ Let's now see an implicit declaration of the property / column.
</sql-result-set-mappings>
</entity>
</doctrine-mapping>
.. code-block:: yaml
MyProject\Model\Address:
type: entity
namedNativeQueries:
findAll:
resultSetMapping: mappingFindAll
query: SELECT * FROM addresses
sqlResultSetMappings:
mappingFindAll:
name: mappingFindAll
entityResult:
address:
entityClass: Address
In this example, we only describe the entity member of the result set mapping.
The property / column mappings is done using the entity mapping values.
@@ -618,6 +677,7 @@ a @FieldResult element should be used for each foreign key column.
The @FieldResult name is composed of the property name for the relationship,
followed by a dot ("."), followed by the name or the field or property of the primary key.
.. configuration-block::
.. code-block:: php
@@ -696,6 +756,41 @@ followed by a dot ("."), followed by the name or the field or property of the pr
</sql-result-set-mappings>
</entity>
</doctrine-mapping>
.. code-block:: yaml
MyProject\Model\User:
type: entity
namedNativeQueries:
fetchJoinedAddress:
name: fetchJoinedAddress
resultSetMapping: mappingJoinedAddress
query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ?
sqlResultSetMappings:
mappingJoinedAddress:
entityResult:
0:
entityClass: __CLASS__
fieldResult:
0:
name: id
1:
name: name
2:
name: status
3:
name: address.id
column: a_id
4:
name: address.zip
column: a_zip
5:
name: address.city
column: a_city
6:
name: address.country
column: a_country
If you retrieve a single entity and if you use the default mapping,
you can use the resultClass attribute instead of resultSetMapping:
@@ -731,6 +826,16 @@ you can use the resultClass attribute instead of resultSetMapping:
</named-native-queries>
</entity>
</doctrine-mapping>
.. code-block:: yaml
MyProject\Model\Address:
type: entity
namedNativeQueries:
findAll:
name: findAll
resultClass: Address
query: SELECT * FROM addresses
In some of your native queries, you'll have to return scalar values,
for example when building report queries.
@@ -781,3 +886,18 @@ You actually can even mix, entities and scalar returns in the same native query
</sql-result-set-mappings>
</entity>
</doctrine-mapping>
.. code-block:: yaml
MyProject\Model\Address:
type: entity
namedNativeQueries:
count:
name: count
resultSetMapping: mappingCount
query: SELECT COUNT(*) AS count FROM addresses
sqlResultSetMappings:
mappingCount:
name: mappingCount
columnResult:
count:
name: count
+2
View File
@@ -23,6 +23,7 @@ of Doctrine2 to this problem is.
to a fully-loaded object by calling ``EntityManager#refresh()``
or a DQL query with the refresh flag.
What is the problem?
--------------------
@@ -86,3 +87,4 @@ Mainly for optimization purposes, but be careful of premature
optimization as partial objects lead to potentially more fragile
code.
+57 -17
View File
@@ -1,7 +1,7 @@
PHP Mapping
===========
Doctrine 2 also allows you to provide the ORM metadata in the form
Doctrine ORM also allows you to provide the ORM metadata in the form
of plain PHP code using the ``ClassMetadata`` API. You can write
the code in PHP files or inside of a static function named
``loadMetadata($class)`` on the entity class itself.
@@ -180,29 +180,18 @@ It also has several methods that create builders (which are necessary for advanc
- ``createManyToMany($name, $targetEntity)`` returns an ``ManyToManyAssociationBuilder`` instance
- ``createOneToMany($name, $targetEntity)`` returns an ``OneToManyAssociationBuilder`` instance
ClassMetadata API
ClassMetadataInfo API
---------------------
The ``ClassMetadata`` class is the base data object for storing
The ``ClassMetadataInfo`` class is the base data object for storing
the mapping metadata for a single entity. It contains all the
getters and setters you need populate and retrieve information for
an entity.
Internal
~~~~~~~~
- ``getReflectionClass()``
- ``getReflectionProperties()``
- ``getReflectionProperty($name)``
- ``getSingleIdReflectionProperty()``
- ``getIdentifierValues($entity)``
- ``assignIdentifier($entity, $id)``
- ``setFieldValue($entity, $field, $value)``
- ``getFieldValue($entity, $field)``
General Setters
~~~~~~~~~~~~~~~
- ``setTableName($tableName)``
- ``setPrimaryTable(array $primaryTableDefinition)``
- ``setCustomRepositoryClass($repositoryClassName)``
@@ -215,6 +204,7 @@ General Setters
Inheritance Setters
~~~~~~~~~~~~~~~~~~~
- ``setInheritanceType($type)``
- ``setSubclasses(array $subclasses)``
- ``setParentClasses(array $classNames)``
@@ -224,18 +214,24 @@ Inheritance Setters
Field Mapping Setters
~~~~~~~~~~~~~~~~~~~~~
- ``addProperty(Property $property)``
- ``addAssociation(AssociationMetadata $property)``
- ``mapField(array $mapping)``
- ``mapOneToOne(array $mapping)``
- ``mapOneToMany(array $mapping)``
- ``mapManyToOne(array $mapping)``
- ``mapManyToMany(array $mapping)``
Lifecycle Callback Setters
~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``addLifecycleCallback($callback, $event)``
- ``setLifecycleCallbacks(array $callbacks)``
Versioning Setters
~~~~~~~~~~~~~~~~~~
- ``setVersionMapping(array &$mapping)``
- ``setVersioned($bool)``
- ``setVersionField()``
@@ -243,6 +239,7 @@ Versioning Setters
General Getters
~~~~~~~~~~~~~~~
- ``getTableName()``
- ``getSchemaName()``
- ``getTemporaryIdTableName()``
@@ -250,8 +247,14 @@ General Getters
Identifier Getters
~~~~~~~~~~~~~~~~~~
- ``getIdentifierColumnNames()``
- ``usesIdGenerator()``
- ``isIdentifier($fieldName)``
- ``isIdGeneratorIdentity()``
- ``isIdGeneratorSequence()``
- ``isIdGeneratorTable()``
- ``isIdentifierNatural()``
- ``getIdentifierFieldNames()``
- ``getSingleIdentifierFieldName()``
- ``getSingleIdentifierColumnName()``
@@ -259,18 +262,35 @@ Identifier Getters
Inheritance Getters
~~~~~~~~~~~~~~~~~~~
- ``isInheritanceTypeNone()``
- ``isInheritanceTypeJoined()``
- ``isInheritanceTypeSingleTable()``
- ``isInheritanceTypeTablePerClass()``
- ``isInheritedField($fieldName)``
- ``isInheritedAssociation($fieldName)``
Change Tracking Getters
~~~~~~~~~~~~~~~~~~~~~~~
- ``isChangeTrackingDeferredExplicit()``
- ``isChangeTrackingDeferredImplicit()``
- ``isChangeTrackingNotify()``
Field & Association Getters
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``isUniqueField($fieldName)``
- ``isNullable($fieldName)``
- ``getColumnName($fieldName)``
- ``getFieldMapping($fieldName)``
- ``getAssociationMapping($fieldName)``
- ``getAssociationMappings()``
- ``getFieldName($columnName)``
- ``hasField($fieldName)``
- ``getColumnNames(array $fieldNames = null)``
- ``getTypeOfField($fieldName)``
- ``getTypeOfColumn($columnName)``
- ``hasAssociation($fieldName)``
@@ -280,6 +300,26 @@ Field & Association Getters
Lifecycle Callback Getters
~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``hasLifecycleCallbacks($lifecycleEvent)``
- ``getLifecycleCallbacks($event)``
ClassMetadata API
-----------------
The ``ClassMetadata`` class extends ``ClassMetadataInfo`` and adds
the runtime functionality required by Doctrine. It adds a few extra
methods related to runtime reflection for working with the entities
themselves.
- ``getReflectionClass()``
- ``getReflectionProperties()``
- ``getReflectionProperty($name)``
- ``getSingleIdReflectionProperty()``
- ``getIdentifierValues($entity)``
- ``setIdentifierValues($entity, $id)``
- ``setFieldValue($entity, $field, $value)``
- ``getFieldValue($entity, $field)``
+37 -9
View File
@@ -9,6 +9,12 @@ programmatically build queries, and also provides a fluent API.
This means that you can change between one methodology to the other
as you want, or just pick a preferred one.
.. note::
The ``QueryBuilder`` is not an abstraction of DQL, but merely a tool to dynamically build it.
You should still use plain DQL when you can, as it is simpler and more readable.
More about this in the :doc:`FAQ <faq>`_.
Constructing a new QueryBuilder object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -38,6 +44,7 @@ good example is to inspect what type of object the
There're currently 3 possible return values for ``getType()``:
- ``QueryBuilder::SELECT``, which returns value 0
- ``QueryBuilder::DELETE``, returning value 1
- ``QueryBuilder::UPDATE``, which returns value 2
@@ -65,6 +72,7 @@ performance. Any changes that may affect the generated DQL actually
modifies the state of ``QueryBuilder`` to a stage we call
STATE\_DIRTY. One ``QueryBuilder`` can be in two different states:
- ``QueryBuilder::STATE_CLEAN``, which means DQL haven't been
altered since last retrieval or nothing were added since its
instantiation
@@ -74,10 +82,11 @@ STATE\_DIRTY. One ``QueryBuilder`` can be in two different states:
Working with QueryBuilder
~~~~~~~~~~~~~~~~~~~~~~~~~
High level API methods
^^^^^^^^^^^^^^^^^^^^^^
To simplify even more the way you build a query in Doctrine, you can take
The most straightforward way to build a dynamic query with the ``QueryBuilder`` is by taking
advantage of Helper methods. For all base code, there is a set of
useful methods to simplify a programmer's life. To illustrate how
to work with them, here is the same example 6 re-written using
@@ -94,10 +103,9 @@ to work with them, here is the same example 6 re-written using
->orderBy('u.name', 'ASC');
``QueryBuilder`` helper methods are considered the standard way to
build DQL queries. Although it is supported, using string-based
queries should be avoided. You are greatly encouraged to use
``$qb->expr()->*`` methods. Here is a converted example 8 to
suggested standard way to build queries:
use the ``QueryBuilder``. The ``$qb->expr()->*`` methods can help you
build conditional expressions dynamically. Here is a converted example 8 to
suggested way to build queries with dynamic conditions:
.. code-block:: php
@@ -244,8 +252,23 @@ while the named placeholders start with a : followed by a string.
Calling ``setParameter()`` automatically infers which type you are setting as
value. This works for integers, arrays of strings/integers, DateTime instances
and for managed entities. If you want to set a type explicitly you can call
the third argument to ``setParameter()`` explicitly. It accepts either a DBAL
Doctrine\DBAL\ParameterType::* or a DBAL Type name for conversion.
the third argument to ``setParameter()`` explicitly. It accepts either a PDO
type or a DBAL Type name for conversion.
.. note::
Even though passing DateTime instance is allowed, it impacts performance
as by default there is an attempt to load metadata for object, and if it's not found,
type is inferred from the original value.
.. code-block:: php
<?php
use Doctrine\DBAL\Types\Types;
// prevents attempt to load metadata for date time class, improving performance
$qb->setParameter('date', new \DateTimeImmutable(), Types::DATE_IMMUTABLE)
If you've got several parameters to bind to your query, you can
also use setParameters() instead of setParameter() with the
@@ -331,6 +354,7 @@ a querybuilder instance into a Query object:
// Execute Query
$result = $query->getResult();
$iterableResult = $query->toIterable();
$single = $query->getSingleResult();
$array = $query->getArrayResult();
$scalar = $query->getScalarResult();
@@ -376,6 +400,7 @@ complete list of supported helper methods available:
// Example - $qb->expr()->orX($cond1 [, $condN])->add(...)->...
public function orX($x = null); // Returns Expr\OrX instance
/** Comparison objects **/
// Example - $qb->expr()->eq('u.id', '?1') => u.id = ?1
@@ -402,6 +427,7 @@ complete list of supported helper methods available:
// Example - $qb->expr()->isNotNull('u.id') => u.id IS NOT NULL
public function isNotNull($x); // Returns string
/** Arithmetic objects **/
// Example - $qb->expr()->prod('u.id', '2') => u.id * 2
@@ -416,6 +442,7 @@ complete list of supported helper methods available:
// Example - $qb->expr()->quot('u.id', '2') => u.id / 2
public function quot($x, $y); // Returns Expr\Math instance
/** Pseudo-function objects **/
// Example - $qb->expr()->exists($qb2->getDql())
@@ -450,6 +477,7 @@ complete list of supported helper methods available:
// Example - $qb->expr()->between('u.id', '1', '10')
public function between($val, $x, $y); // Returns Expr\Func
/** Function objects **/
// Example - $qb->expr()->trim('u.firstname')
@@ -505,7 +533,7 @@ using ``addCriteria``:
// ...
$criteria = Criteria::create()
->orderBy(['firstName' => Criteria::ASC]);
->orderBy(['firstName', 'ASC']);
// $qb instanceof QueryBuilder
$qb->addCriteria($criteria);
@@ -524,6 +552,7 @@ one: ``add()``. This method is responsible of building every piece
of DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and
``$append`` (default=false)
- ``$dqlPartName``: Where the ``$dqlPart`` should be placed.
Possible values: select, from, where, groupBy, having, orderBy
- ``$dqlPart``: What should be placed in ``$dqlPartName``. Accepts
@@ -568,4 +597,3 @@ same query of example 6 written using
->add('from', new Expr\From('User', 'u'))
->add('where', new Expr\Comparison('u.id', '=', '?1'))
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
+118 -46
View File
@@ -18,6 +18,7 @@ There are some flavors of caching available, but is better to cache read-only da
Be aware that caches are not aware of changes made to the persistent store by another application.
They can, however, be configured to regularly expire cached data.
Caching Regions
---------------
@@ -41,6 +42,7 @@ Something like below for an entity region :
'region_name:entity_3_hash' => ['id'=> 3, 'name' => 'Bar', 'associationName'=>['id'=>22]]
];
If the entity holds a collection that also needs to be cached.
An collection region could look something like :
@@ -64,35 +66,34 @@ A query region might be something like :
'region_name:query_3_hash' => ['list' => [2, 4]]
];
.. note::
The following data structures represents now the cache will looks like, this is not actual cached data.
.. _reference-second-level-cache-regions:
Cache Regions
-------------
``Doctrine\ORM\Cache\Region\DefaultRegion`` It's the default implementation.
``Doctrine\ORM\Cache\Region\DefaultRegion`` is the default implementation.
A simplest cache region compatible with all doctrine-cache drivers but does not support locking.
``Doctrine\ORM\Cache\Region`` and ``Doctrine\ORM\Cache\ConcurrentRegion``
Defines contracts that should be implemented by a cache provider.
define contracts that should be implemented by a cache provider.
It allows you to provide your own cache implementation that might take advantage of specific cache driver.
If you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise.
Cache region
~~~~~~~~~~~~
Defines a contract for accessing a particular region.
``Doctrine\ORM\Cache\Region`` defines a contract for accessing a particular
cache region.
``Doctrine\ORM\Cache\Region``
Defines a contract for accessing a particular cache region.
`See API Doc <https://www.doctrine-project.org/api/orm/latest/Doctrine/ORM/Cache/Region.html>`_.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Region.html>`_.
Concurrent cache region
~~~~~~~~~~~~~~~~~~~~~~~
@@ -102,9 +103,7 @@ By default, Doctrine provides a very simple implementation based on file locks `
If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region.
``Doctrine\ORM\Cache\ConcurrentRegion``
Defines contract for concurrently managed data region.
``Doctrine\ORM\Cache\ConcurrentRegion`` defines a contract for concurrently managed data region.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/ConcurrentRegion.html>`_.
@@ -133,6 +132,7 @@ Caching mode
* Read Write Cache doesnt employ any locks but can do reads, inserts, updates and deletes.
* Good if the application needs to update data rarely.
* ``READ_WRITE``
@@ -141,6 +141,7 @@ Caching mode
* Slowest strategy.
* To use it a the cache region implementation must support locking.
Built-in cached persisters
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -166,6 +167,7 @@ Configuration
-------------
Doctrine allows you to specify configurations and some points of extension for the second-level-cache
Enable Second Level Cache
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -175,11 +177,10 @@ To enable the second-level-cache, you should provide a cache factory.
.. code-block:: php
<?php
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $cacheConfig */
/** @var \Doctrine\Common\Cache\Cache $cache */
/** @var \Doctrine\ORM\Configuration $config */
/* @var $config \Doctrine\ORM\Cache\RegionsConfiguration */
/* @var $cache \Doctrine\Common\Cache\Cache */
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($cacheConfig, $cache);
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($config, $cache);
// Enable second-level-cache
$config->setSecondLevelCacheEnabled();
@@ -188,6 +189,7 @@ To enable the second-level-cache, you should provide a cache factory.
$config->getSecondLevelCacheConfiguration()
->setCacheFactory($factory);
Cache Factory
~~~~~~~~~~~~~
@@ -195,11 +197,16 @@ Cache Factory is the main point of extension.
It allows you to provide a specific implementation of the following components :
* ``QueryCache`` Store and retrieve query cache results.
* ``CachedEntityPersister`` Store and retrieve entity results.
* ``CachedCollectionPersister`` Store and retrieve query results.
* ``EntityHydrator`` Transform an entity into a cache entry and cache entry into entities
* ``CollectionHydrator`` Transform a collection into a cache entry and cache entry into collection
``QueryCache``
stores and retrieves query cache results.
``CachedEntityPersister``
stores and retrieves entity results.
``CachedCollectionPersister``
stores and retrieves query results.
``EntityHydrator``
transforms entities into a cache entries and cache entries into entities
``CollectionHydrator``
transforms collections into cache entries and cache entries into collections
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
@@ -211,15 +218,15 @@ To specify a default lifetime for all regions or specify a different lifetime fo
.. code-block:: php
<?php
/** @var \Doctrine\ORM\Configuration $config */
/** @var \Doctrine\ORM\Cache\CacheConfiguration $cacheConfig */
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $regionConfig */
/* @var $config \Doctrine\ORM\Configuration */
/* @var $cacheConfig \Doctrine\ORM\Cache\CacheConfiguration */
$cacheConfig = $config->getSecondLevelCacheConfiguration();
$regionConfig = $cacheConfig->getRegionsConfiguration();
// Cache Region lifetime
$regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region; In seconds
$regionConfig->setDefaultLifetime(7200); // Default time to live; In seconds
$regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region (in seconds)
$regionConfig->setDefaultLifetime(7200); // Default time to live (in seconds)
Cache Log
~~~~~~~~~
@@ -230,7 +237,7 @@ By providing a cache logger you should be able to get information about all cach
.. code-block:: php
<?php
/** @var \Doctrine\ORM\Configuration $config */
/* @var $config \Doctrine\ORM\Configuration */
$logger = new \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger();
// Cache logger
@@ -238,6 +245,7 @@ By providing a cache logger you should be able to get information about all cach
$config->getSecondLevelCacheConfiguration()
->setCacheLogger($logger);
// Collect cache statistics
// Get the number of entries successfully retrieved from a specific region.
@@ -258,8 +266,9 @@ By providing a cache logger you should be able to get information about all cach
// Get the total number of cached entries *not* found in all regions.
$logger->getMissCount();
If you want to get more information you should implement ``\Doctrine\ORM\Cache\Logging\CacheLogger``.
and collect all information you want.
If you want to get more information you should implement
``\Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
all the information you want.
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
@@ -268,8 +277,12 @@ Entity cache definition
-----------------------
* Entity cache configuration allows you to define the caching strategy and region for an entity.
* ``usage`` Specifies the caching strategy: ``READ_ONLY``, ``NONSTRICT_READ_WRITE``, ``READ_WRITE``. see :ref:`reference-second-level-cache-mode`
* ``region`` Optional value that specifies the name of the second level cache region.
* ``usage`` specifies the caching strategy: ``READ_ONLY``,
``NONSTRICT_READ_WRITE``, ``READ_WRITE``.
See :ref:`reference-second-level-cache-mode`.
* ``region`` is an optional value that specifies the name of the second
level cache region.
.. configuration-block::
@@ -310,11 +323,30 @@ Entity cache definition
</entity>
</doctrine-mapping>
.. code-block:: yaml
Country:
type: entity
cache:
usage : READ_ONLY
region : my_entity_region
id:
id:
type: integer
id: true
generator:
strategy: IDENTITY
fields:
name:
type: string
Association cache definition
----------------------------
The most common use case is to cache entities. But we can also cache relationships.
It caches the primary keys of association and cache each element will be cached into its region.
.. configuration-block::
.. code-block:: php
@@ -367,7 +399,7 @@ It caches the primary keys of association and cache each element will be cached
</id>
<field name="name" type="string" column="name"/>
<many-to-one field="country" target-entity="Country">
<cache usage="NONSTRICT_READ_WRITE" />
@@ -382,6 +414,39 @@ It caches the primary keys of association and cache each element will be cached
</entity>
</doctrine-mapping>
.. code-block:: yaml
State:
type: entity
cache:
usage : NONSTRICT_READ_WRITE
id:
id:
type: integer
id: true
generator:
strategy: IDENTITY
fields:
name:
type: string
manyToOne:
state:
targetEntity: Country
joinColumns:
country_id:
referencedColumnName: id
cache:
usage : NONSTRICT_READ_WRITE
oneToMany:
cities:
targetEntity:City
mappedBy: state
cache:
usage : NONSTRICT_READ_WRITE
> Note: for this to work, the target entity must also be marked as cacheable.
Cache usage
@@ -399,8 +464,8 @@ Basic entity cache
$country1 = $em->find('Country', 1); // Retrieve item from cache
$country1->setName("New Name");
$country->setName("New Name");
$em->persist($country);
$em->flush(); // Hit database to update the row and update cache
$em->clear(); // Clear entity manager
@@ -408,6 +473,7 @@ Basic entity cache
$country2 = $em->find('Country', 1); // Retrieve item from cache
// Notice that $country1 and $country2 are not the same instance.
Association cache
.. code-block:: php
@@ -475,7 +541,7 @@ The query cache stores the results of the query but as identifiers, entity value
.. code-block:: php
<?php
/** @var \Doctrine\ORM\EntityManager $em */
/* @var $em \Doctrine\ORM\EntityManager */
// Execute database query, store query cache and entity cache
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
@@ -502,7 +568,7 @@ The Cache Mode controls how a particular query interacts with the second-level c
.. code-block:: php
<?php
/** @var \Doctrine\ORM\EntityManager $em */
/* @var $em \Doctrine\ORM\EntityManager */
// Will refresh the query cache and all entities the cache as it reads from the database.
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
->setCacheMode(Cache::MODE_GET)
@@ -516,38 +582,42 @@ The Cache Mode controls how a particular query interacts with the second-level c
DELETE / UPDATE queries
~~~~~~~~~~~~~~~~~~~~~~~
DQL UPDATE / DELETE statements are ported directly into a database and bypass the second-level cache,
DQL UPDATE / DELETE statements are ported directly into a database and bypass
the second-level cache.
Entities that are already cached will NOT be invalidated.
However the cached data could be evicted using the cache API or an special query hint.
Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT``
.. code-block:: php
<?php
// Execute and invalidate
$this->em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
->setHint(Query::HINT_CACHE_EVICT, true)
->execute();
Execute the ``UPDATE`` and invalidate ``all cache entries`` using the cache API
.. code-block:: php
<?php
// Execute
$this->em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
->execute();
// Invoke Cache API
$em->getCache()->evictEntityRegion('Entity\Country');
Execute the ``UPDATE`` and invalidate ``a specific cache entry`` using the cache API
.. code-block:: php
<?php
// Execute
$this->em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
->execute();
// Invoke Cache API
$em->getCache()->evictEntity('Entity\Country', 1);
@@ -556,7 +626,7 @@ Using the repository query cache
--------------------------------
As well as ``Query Cache`` all persister queries store only identifier values for an individual query.
All persister use a single timestamps cache region keeps track of the last update for each persister,
All persisters use a single timestamp cache region to keep track of the last update for each persister,
When a query is loaded from cache, the timestamp region is checked for the last update for that persister.
Using the last update timestamps as part of the query key invalidate the cache key when an update occurs.
@@ -575,7 +645,7 @@ Using the last update timestamps as part of the query key invalidate the cache k
$em->clear();
// Reload from database.
// At this point the query cache key if not logger valid, the select goes straight
// At this point the query cache key is no longer valid, the select goes straight to the database
$entities = $em->getRepository('Entity\Country')->findAll();
Cache API
@@ -587,7 +657,7 @@ However, you can use the cache API to check / invalidate cache entries.
.. code-block:: php
<?php
/** @var \Doctrine\ORM\Cache $cache */
/* @var $cache \Doctrine\ORM\Cache */
$cache = $em->getCache();
$cache->containsEntity('Entity\State', 1) // Check if the cache exists
@@ -632,11 +702,11 @@ For performance reasons the cache API does not extract from composite primary ke
}
// Supported
/** @var Article $article */
/* @var $article Article */
$article = $em->find('Article', 1);
// Supported
/** @var Article $article */
/* @var $article Article */
$article = $em->find('Article', $article);
// Supported
@@ -657,8 +727,10 @@ should be used in conjunction with distributed caching system such as memcached,
Caches should be used with care when using a load-balancer if you don't share the cache.
While using APC or any file based cache update occurred in a specific machine would not reflect to the cache in other machines.
Paginator
~~~~~~~~~
Count queries generated by ``Doctrine\ORM\Tools\Pagination\Paginator`` are not cached by second-level cache.
Although entities and query result are cached count queries will hit the database every time.
Although entities and query result are cached, count queries will hit the
database every time.
+2 -1
View File
@@ -81,6 +81,7 @@ this is technically impossible. The correct way is:
$query = $entityManager->createQuery($dql);
$query->setParameter(1, $_GET['status']);
Preventing Mass Assignment Vulnerabilities
------------------------------------------
@@ -117,7 +118,7 @@ entity might look like this:
}
}
Now the possibility of mass-assignment exists on this entity and can
Now the possiblity of mass-asignment exists on this entity and can
be exploited by attackers to set the "isAdmin" flag to true on any
object when you pass the whole request data to this method like:
+187 -55
View File
@@ -5,7 +5,7 @@ Doctrine Console
----------------
The Doctrine Console is a Command Line Interface tool for simplifying common
administration tasks during the development of a project that uses Doctrine 2.
administration tasks during the development of a project that uses ORM.
Take a look at the :doc:`Installation and Configuration <configuration>`
chapter for more information how to setup the console command.
@@ -22,66 +22,37 @@ about the use of generate entities for example, you can call:
$> php vendor/bin/doctrine orm:generate-entities --help
Configuration
~~~~~~~~~~~~~
Whenever the ``doctrine`` command line tool is invoked, it can
access all Commands that were registered by developer. There is no
access all Commands that were registered by a developer. There is no
auto-detection mechanism at work. The Doctrine binary
already registers all the commands that currently ship with
Doctrine DBAL and ORM. If you want to use additional commands you
have to register them yourself.
All the commands of the Doctrine Console require access to the ``EntityManager``
or ``DBAL`` Connection. You have to inject them into the console application
using so called Helper-Sets. This requires either the ``db``
or the ``em`` helpers to be defined in order to work correctly.
All the commands of the Doctrine Console require access to the
``EntityManager``. You have to inject it into the console application with
``ConsoleRunner::createHelperSet``. Whenever you invoke the Doctrine
binary, it searches the current directory for the file ``cli-config.php``.
This file contains the project-specific configuration.
Whenever you invoke the Doctrine binary the current folder is searched for a
``cli-config.php`` file. This file contains the project specific configuration:
Here is an example of a the project-specific ``cli-config.php``:
.. code-block:: php
<?php
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($conn)
));
$cli->setHelperSet($helperSet);
use Doctrine\ORM\Tools\Console\ConsoleRunner;
When dealing with the ORM package, the EntityManagerHelper is
required:
// replace this with the path to your own project bootstrap file.
require_once 'bootstrap.php';
.. code-block:: php
// replace with mechanism to retrieve EntityManager in your app
$entityManager = GetEntityManager();
<?php
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
$cli->setHelperSet($helperSet);
The HelperSet instance has to be generated in a separate file (i.e.
``cli-config.php``) that contains typical Doctrine bootstrap code
and predefines the needed HelperSet attributes mentioned above. A
sample ``cli-config.php`` file looks as follows:
.. code-block:: php
<?php
// cli-config.php
require_once 'my_bootstrap.php';
// Any way to access the EntityManager from your application
$em = GetMyEntityManager();
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
It is important to define a correct HelperSet that Doctrine binary
script will ultimately use. The Doctrine Binary will automatically
find the first instance of HelperSet in the global variable
namespace and use this.
return ConsoleRunner::createHelperSet($entityManager);
.. note::
@@ -94,6 +65,7 @@ Command Overview
The following Commands are currently available:
- ``help`` Displays help for a command (?)
- ``list`` Lists commands
- ``dbal:import`` Import SQL file(s) directly to Database.
@@ -107,10 +79,16 @@ The following Commands are currently available:
cache drivers.
- ``orm:convert-d1-schema`` Converts Doctrine 1.X schema into a
Doctrine 2.X schema.
- ``orm:convert-mapping`` Convert mapping information between
supported formats.
- ``orm:ensure-production-settings`` Verify that Doctrine is
properly configured for a production environment.
- ``orm:generate-entities`` Generate entity classes and method
stubs from your mapping information.
- ``orm:generate-proxies`` Generates proxy classes for entity
classes.
- ``orm:generate-repositories`` Generate repository classes from
your mapping information.
- ``orm:run-dql`` Executes arbitrary DQL directly from the command
line.
- ``orm:schema-tool:create`` Processes the schema and either
@@ -125,8 +103,12 @@ The following Commands are currently available:
For these commands are also available aliases:
- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``.
- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``.
- ``orm:generate:entities`` is alias for ``orm:generate-entities``.
- ``orm:generate:proxies`` is alias for ``orm:generate-proxies``.
- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``.
.. note::
@@ -152,6 +134,7 @@ Database Schema Generation
they are not related to the current project that is using Doctrine.
Please be careful!
To generate your database schema from your Doctrine mapping files
you can use the ``SchemaTool`` class or the ``schema-tool`` Console
Command.
@@ -160,7 +143,7 @@ When using the SchemaTool class directly, create your schema using
the ``createSchema()`` method. First create an instance of the
``SchemaTool`` and pass it an instance of the ``EntityManager``
that you want to use to create the schema. This method receives an
array of ``ClassMetadata`` instances.
array of ``ClassMetadataInfo`` instances.
.. code-block:: php
@@ -191,8 +174,8 @@ tables of the current model to clean up with orphaned tables.
You can also use database introspection to update your schema
easily with the ``updateSchema()`` method. It will compare your
existing database schema to the passed array of ``ClassMetadata``
instances.
existing database schema to the passed array of
``ClassMetadataInfo`` instances.
.. code-block:: php
@@ -239,19 +222,167 @@ will output the SQL for the ran operation.
Before using the orm:schema-tool commands, remember to configure
your cli-config.php properly.
Entity Generation
-----------------
Generate entity classes and method stubs from your mapping information.
.. code-block:: php
$ php doctrine orm:generate-entities
$ php doctrine orm:generate-entities --update-entities
$ php doctrine orm:generate-entities --regenerate-entities
This command is not suited for constant usage. It is a little helper and does
not support all the mapping edge cases very well. You still have to put work
in your entities after using this command.
It is possible to use the EntityGenerator on code that you have already written. It will
not be lost. The EntityGenerator will only append new code to your
file and will not delete the old code. However this approach may still be prone
to error and we suggest you use code repositories such as GIT or SVN to make
backups of your code.
It makes sense to generate the entity code if you are using entities as Data
Access Objects only and don't put much additional logic on them. If you are
however putting much more logic on the entities you should refrain from using
the entity-generator and code your entities manually.
.. note::
When using the Annotation Mapping Driver you have to either setup
your autoloader in the cli-config.php correctly to find all the
entities, or you can use the second argument of the
``EntityManagerHelper`` to specify all the paths of your entities
(or mapping files), i.e.
``new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em, $mappingPaths);``
Even if you specified Inheritance options in your
XML or YAML Mapping files the generator cannot generate the base and
child classes for you correctly, because it doesn't know which
class is supposed to extend which. You have to adjust the entity
code manually for inheritance to work!
Convert Mapping Information
---------------------------
Convert mapping information between supported formats.
This is an **execute one-time** command. It should not be necessary for
you to call this method multiple times, especially when using the ``--from-database``
flag.
Converting an existing database schema into mapping files only solves about 70-80%
of the necessary mapping information. Additionally the detection from an existing
database cannot detect inverse associations, inheritance types,
entities with foreign keys as primary keys and many of the
semantical operations on associations such as cascade.
.. note::
There is no need to convert YAML or XML mapping files to annotations
every time you make changes. All mapping drivers are first class citizens
in Doctrine 2 and can be used as runtime mapping for the ORM. See the
docs on XML and YAML Mapping for an example how to register this metadata
drivers as primary mapping source.
To convert some mapping information between the various supported
formats you can use the ``ClassMetadataExporter`` to get exporter
instances for the different formats:
.. code-block:: php
<?php
$cme = new \Doctrine\ORM\Tools\Export\ClassMetadataExporter();
Once you have a instance you can use it to get an exporter. For
example, the yml exporter:
.. code-block:: php
<?php
$exporter = $cme->getExporter('yml', '/path/to/export/yml');
Now you can export some ``ClassMetadata`` instances:
.. code-block:: php
<?php
$classes = array(
$em->getClassMetadata('Entities\User'),
$em->getClassMetadata('Entities\Profile')
);
$exporter->setMetadata($classes);
$exporter->export();
This functionality is also available from the command line to
convert your loaded mapping information to another format. The
``orm:convert-mapping`` command accepts two arguments, the type to
convert to and the path to generate it:
.. code-block:: php
$ php doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml
Reverse Engineering
-------------------
You can use the ``DatabaseDriver`` to reverse engineer a database
to an array of ``ClassMetadataInfo`` instances and generate YAML,
XML, etc. from them.
.. note::
Reverse Engineering is a **one-time** process that can get you started with a project.
Converting an existing database schema into mapping files only detects about 70-80%
of the necessary mapping information. Additionally the detection from an existing
database cannot detect inverse associations, inheritance types,
entities with foreign keys as primary keys and many of the
semantical operations on associations such as cascade.
First you need to retrieve the metadata instances with the
``DatabaseDriver``:
.. code-block:: php
<?php
$em->getConfiguration()->setMetadataDriverImpl(
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
)
);
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();
Now you can get an exporter instance and export the loaded metadata
to yml:
.. code-block:: php
<?php
$cme = new \Doctrine\ORM\Tools\Export\ClassMetadataExporter();
$exporter = $cme->getExporter('yml', '/path/to/export/yml');
$exporter->setMetadata($metadata);
$exporter->export();
You can also reverse engineer a database using the
``orm:convert-mapping`` command:
.. code-block:: php
$ php doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml
.. note::
Reverse Engineering is not always working perfectly
depending on special cases. It will only detect Many-To-One
relations (even if they are One-To-One) and will try to create
entities from Many-To-Many tables. It also has problems with naming
of foreign keys that have multiple column names. Any Reverse
Engineered Database-Schema needs considerable manual work to become
a useful domain model.
Runtime vs Development Mapping Validation
-----------------------------------------
For performance reasons Doctrine 2 has to skip some of the
For performance reasons Doctrine ORM has to skip some of the
necessary validation of metadata mappings. You have to execute
this validation in your development workflow to verify the
associations are correctly defined.
@@ -293,6 +424,7 @@ number of elements with error messages.
prefix backslash. PHP does this with ``get_class()`` or Reflection
methods for backwards compatibility reasons.
Adding own commands
-------------------
@@ -338,6 +470,7 @@ defined ones) is possible through the command:
new \MyProject\Tools\Console\Commands\OneMoreCommand(),
));
Re-use console application
--------------------------
@@ -354,4 +487,3 @@ HelperSet, like it is described in the configuration section.
// Runs console application
$cli->run();
@@ -16,13 +16,13 @@ transaction. Without any explicit transaction demarcation from your
side, this quickly results in poor performance because transactions
are not cheap.
For the most part, Doctrine 2 already takes care of proper
For the most part, Doctrine ORM already takes care of proper
transaction demarcation for you: All the write operations
(INSERT/UPDATE/DELETE) are queued until ``EntityManager#flush()``
is invoked which wraps all of these changes in a single
transaction.
However, Doctrine 2 also allows (and encourages) you to take over
However, Doctrine ORM also allows (and encourages) you to take over
and control transaction demarcation yourself.
These are two ways to deal with transactions when using the
@@ -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,16 +98,24 @@ 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);
});
.. warning::
For historical reasons, ``EntityManager#transactional($func)`` will return
``true`` whenever the return value of ``$func`` is loosely false.
Some examples of this include ``array()``, ``"0"``, ``""``, ``0``, and
``null``.
The difference between ``Connection#transactional($func)`` and
``EntityManager#transactional($func)`` is that the latter
abstraction flushes the ``EntityManager`` prior to transaction
commit.
commit and rolls back the transaction when an
exception occurs.
.. _transactions-and-concurrency_exception-handling:
@@ -146,7 +154,7 @@ occurred you should do that with a new ``EntityManager``.
Locking Support
---------------
Doctrine 2 offers support for Pessimistic- and Optimistic-locking
Doctrine ORM offers support for Pessimistic- and Optimistic-locking
strategies natively. This allows to take very fine-grained control
over what kind of locking is required for your Entities in your
application.
@@ -200,6 +208,15 @@ example we'll use an integer.
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
fields:
version:
type: integer
version: true
Alternatively a datetime type can be used (which maps to a SQL
timestamp or datetime):
@@ -224,6 +241,15 @@ timestamp or datetime):
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
fields:
version:
type: datetime
version: true
Version numbers (not timestamps) should however be preferred as
they can not potentially conflict in a highly concurrent
environment, unlike timestamps where this is a possibility,
@@ -253,15 +279,15 @@ either when calling ``EntityManager#find()``:
<?php
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\OptimisticLockException;
$theEntityId = 1;
$expectedVersion = 184;
try {
$entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);
// do the work
$em->flush();
} catch(OptimisticLockException $e) {
echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
@@ -274,16 +300,16 @@ Or you can use ``EntityManager#lock()`` to find out:
<?php
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\OptimisticLockException;
$theEntityId = 1;
$expectedVersion = 184;
$entity = $em->find('User', $theEntityId);
try {
// assert version
$em->lock($entity, LockMode::OPTIMISTIC, $expectedVersion);
} catch(OptimisticLockException $e) {
echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
}
@@ -322,7 +348,7 @@ See the example code, The form (GET Request):
<?php
$post = $em->find('BlogPost', 123456);
echo '<input type="hidden" name="id" value="' . $post->getId() . '" />';
echo '<input type="hidden" name="version" value="' . $post->getCurrentVersion() . '" />';
@@ -333,7 +359,7 @@ And the change headline action (POST Request):
<?php
$postId = (int)$_GET['id'];
$postVersion = (int)$_GET['version'];
$post = $em->find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion);
.. _transactions-and-concurrency_pessimistic-locking:
@@ -341,7 +367,7 @@ And the change headline action (POST Request):
Pessimistic Locking
~~~~~~~~~~~~~~~~~~~
Doctrine 2 supports Pessimistic Locking at the database level. No
Doctrine ORM supports Pessimistic Locking at the database level. No
attempt is being made to implement pessimistic locking inside
Doctrine, rather vendor-specific and ANSI-SQL commands are used to
acquire row-level locks. Every Entity can be part of a pessimistic
@@ -350,11 +376,12 @@ lock, there is no special metadata required to use this feature.
However for Pessimistic Locking to work you have to disable the
Auto-Commit Mode of your Database and start a transaction around
your pessimistic lock use-case using the "Approach 2: Explicit
Transaction Demarcation" described above. Doctrine 2 will throw an
Transaction Demarcation" described above. Doctrine ORM will throw an
Exception if you attempt to acquire an pessimistic lock and no
transaction is running.
Doctrine 2 currently supports two pessimistic lock modes:
Doctrine ORM currently supports two pessimistic lock modes:
- Pessimistic Write
(``Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE``), locks the
@@ -365,6 +392,7 @@ Doctrine 2 currently supports two pessimistic lock modes:
You can use pessimistic locks in three different scenarios:
1. Using
``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)``
or
@@ -378,3 +406,4 @@ You can use pessimistic locks in three different scenarios:
or
``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ)``
@@ -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.
@@ -39,7 +39,7 @@ side of the association and these 2 references both represent the
same association but can change independently of one another. Of
course, in a correct application the semantics of the bidirectional
association are properly maintained by the application developer
(that's his responsibility). Doctrine needs to know which of these
(that's their responsibility). Doctrine needs to know which of these
two in-memory references is the one that should be persisted and
which not. This is what the owning/inverse concept is mainly used
for.
+12 -11
View File
@@ -36,15 +36,13 @@ will still end up with the same reference:
public function testIdentityMapReference()
{
/** @var EntityName|\ProxyManager\Proxy\GhostObjectInterface $objectA */
$objectA = $this->entityManager->getReference(EntityName::class, 1);
$objectA = $this->entityManager->getReference('EntityName', 1);
// check for proxyinterface
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $objectA);
self::assertInstanceOf(\ProxyManager\Proxy\GhostObjectInterface::class, $objectA);
self::assertFalse($objectA->isProxyInitialized());
$objectB = $this->entityManager->find('EntityName', 1);
$objectB = $this->entityManager->find(EntityName::class, 1);
self::assertSame($objectA, $objectB)
$this->assertSame($objectA, $objectB)
}
The identity map being indexed by primary keys only allows shortcuts when you
@@ -106,7 +104,7 @@ How Doctrine Detects Changes
Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI).
This means you map php objects into a relational database that don't
necessarily know about the database at all. A natural question would now be,
"how does Doctrine even detect objects have changed?".
"how does Doctrine even detect objects have changed?".
For this Doctrine keeps a second map inside the UnitOfWork. Whenever you fetch
an object from the database Doctrine will keep a copy of all the properties and
@@ -131,10 +129,12 @@ optimize the performance of the Flush Operation:
- Temporarily mark entities as read only. If you have a very large UnitOfWork
but know that a large set of entities has not changed, just mark them as read
only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``.
- Flush only a single entity with ``$entityManager->flush($entity)``.
- Use :doc:`Change Tracking Policies <change-tracking-policies>` to use more
explicit strategies of notifying the UnitOfWork what objects/properties
changed.
Query Internals
---------------
@@ -148,15 +148,15 @@ Hydration
~~~~~~~~~
Responsible for creating a final result from a raw database statement and a
result-set mapping object. The developer can choose which kind of result he
wishes to be hydrated. Default result-types include:
result-set mapping object. The developer can choose which kind of result they
wish to be hydrated. Default result-types include:
- SQL to Entities
- SQL to structured Arrays
- SQL to simple scalar result arrays
- SQL to a single result variable
Hydration to entities and arrays is one of most complex parts of Doctrine
Hydration to entities and arrays is one of the most complex parts of Doctrine
algorithm-wise. It can build results with for example:
- Single table selects
@@ -198,3 +198,4 @@ ClassMetadataFactory
~~~~~~~~~~~~~~~~~~~~
tbr
+55 -49
View File
@@ -37,7 +37,7 @@ information about its type and if it's the owning or inverse side.
{
/** @Id @GeneratedValue @Column(type="string") */
private $id;
/**
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
*
@@ -45,7 +45,7 @@ information about its type and if it's the owning or inverse side.
* @JoinTable(name="user_favorite_comments")
*/
private $favorites;
/**
* Unidirectional - Many users have marked many comments as read
*
@@ -53,14 +53,14 @@ information about its type and if it's the owning or inverse side.
* @JoinTable(name="user_read_comments")
*/
private $commentsRead;
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* @OneToMany(targetEntity="Comment", mappedBy="author")
*/
private $commentsAuthored;
/**
* Unidirectional - Many-To-One
*
@@ -68,20 +68,20 @@ information about its type and if it's the owning or inverse side.
*/
private $firstComment;
}
/** @Entity */
class Comment
{
/** @Id @GeneratedValue @Column(type="string") */
private $id;
/**
* Bidirectional - Many comments are favorited by many users (INVERSE SIDE)
*
* @ManyToMany(targetEntity="User", mappedBy="favorites")
*/
private $userFavorites;
/**
* Bidirectional - Many Comments are authored by one user (OWNING SIDE)
*
@@ -100,19 +100,19 @@ definitions omitted):
firstComment_id VARCHAR(255) DEFAULT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
CREATE TABLE Comment (
id VARCHAR(255) NOT NULL,
author_id VARCHAR(255) DEFAULT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
CREATE TABLE user_favorite_comments (
user_id VARCHAR(255) NOT NULL,
favorite_comment_id VARCHAR(255) NOT NULL,
PRIMARY KEY(user_id, favorite_comment_id)
) ENGINE = InnoDB;
CREATE TABLE user_read_comments (
user_id VARCHAR(255) NOT NULL,
comment_id VARCHAR(255) NOT NULL,
@@ -135,7 +135,7 @@ relations of the ``User``:
public function getReadComments() {
return $this->commentsRead;
}
public function setFirstComment(Comment $c) {
$this->firstComment = $c;
}
@@ -148,17 +148,17 @@ The interaction code would then look like in the following snippet
<?php
$user = $em->find('User', $userId);
// unidirectional many to many
$comment = $em->find('Comment', $readCommentId);
$user->getReadComments()->add($comment);
$em->flush();
// unidirectional many to one
$myFirstComment = new Comment();
$user->setFirstComment($myFirstComment);
$em->persist($myFirstComment);
$em->flush();
@@ -171,40 +171,40 @@ fields on both sides:
class User
{
// ..
public function getAuthoredComments() {
return $this->commentsAuthored;
}
public function getFavoriteComments() {
return $this->favorites;
}
}
class Comment
{
// ...
public function getUserFavorites() {
return $this->userFavorites;
}
public function setAuthor(User $author = null) {
$this->author = $author;
}
}
// Many-to-Many
$user->getFavorites()->add($favoriteComment);
$favoriteComment->getUserFavorites()->add($user);
$em->flush();
// Many-To-One / One-To-Many Bidirectional
$newComment = new Comment();
$user->getAuthoredComments()->add($newComment);
$newComment->setAuthor($user);
$em->persist($newComment);
$em->flush();
@@ -225,10 +225,10 @@ element. Here are some examples:
// Remove by Elements
$user->getComments()->removeElement($comment);
$comment->setAuthor(null);
$user->getFavorites()->removeElement($comment);
$comment->getUserFavorites()->removeElement($user);
// Remove by Key
$user->getComments()->remove($ithComment);
$comment->setAuthor(null);
@@ -240,7 +240,7 @@ Notice how both sides of the bidirectional association are always
updated. Unidirectional associations are consequently simpler to
handle.
Also note that if you use type-hinting in your methods, you will
Also note that if you use type-hinting in your methods, you will
have to specify a nullable type, i.e. ``setAddress(?Address $address)``,
otherwise ``setAddress(null)`` will fail to remove the association.
Another way to deal with this is to provide a special method, like
@@ -262,6 +262,7 @@ where n is the size of the map.
can often be used to improve performance by avoiding the loading of
the inverse collection.
You can also clear the contents of a whole collection using the
``Collections::clear()`` method. You should be aware that using
this method can lead to a straight and optimized database delete or
@@ -270,8 +271,8 @@ entities that have been re-added to the collection.
Say you clear a collection of tags by calling
``$post->getTags()->clear();`` and then call
``$post->getTags()->add($tag)``. This will not recognize the tag having
already been added previously and will consequently issue two separate database
``$post->getTags()->add($tag)``. This will not recognize the tag having
already been added previously and will consequently issue two separate database
calls.
Association Management Methods
@@ -290,12 +291,12 @@ example that encapsulate much of the association management code:
<?php
class User
{
// ...
//...
public function markCommentRead(Comment $comment) {
// Collections implement ArrayAccess
$this->commentsRead[] = $comment;
}
public function addComment(Comment $comment) {
if (count($this->commentsAuthored) == 0) {
$this->setFirstComment($comment);
@@ -303,30 +304,30 @@ example that encapsulate much of the association management code:
$this->comments[] = $comment;
$comment->setAuthor($this);
}
private function setFirstComment(Comment $c) {
$this->firstComment = $c;
}
public function addFavorite(Comment $comment) {
$this->favorites->add($comment);
$comment->addUserFavorite($this);
}
public function removeFavorite(Comment $comment) {
$this->favorites->removeElement($comment);
$comment->removeUserFavorite($this);
}
}
class Comment
{
// ..
public function addUserFavorite(User $user) {
$this->userFavorites[] = $user;
}
public function removeUserFavorite(User $user) {
$this->userFavorites->removeElement($user);
}
@@ -350,6 +351,7 @@ the details inside the classes can be challenging.
entity cannot circumvent the logic you implement on your entity for
association management. For example:
.. code-block:: php
<?php
@@ -371,7 +373,7 @@ as your preferences.
Synchronizing Bidirectional Collections
---------------------------------------
In the case of Many-To-Many associations you as the developer have the
In the case of Many-To-Many associations you as the developer have the
responsibility of keeping the collections on the owning and inverse side
in sync when you apply changes to them. Doctrine can only
guarantee a consistent state for the hydration, not for your client
@@ -385,12 +387,13 @@ can show the possible caveats you can encounter:
<?php
$user->getFavorites()->add($favoriteComment);
// not calling $favoriteComment->getUserFavorites()->add($user);
$user->getFavorites()->contains($favoriteComment); // TRUE
$favoriteComment->getUserFavorites()->contains($user); // FALSE
There are two approaches to handle this problem in your code:
1. Ignore updating the inverse side of bidirectional collections,
BUT never read from them in requests that changed their state. In
the next request Doctrine hydrates the consistent collection state
@@ -404,10 +407,10 @@ There are two approaches to handle this problem in your code:
Transitive persistence / Cascade Operations
-------------------------------------------
Doctrine 2 provides a mechanism for transitive persistence through cascading of certain operations.
Doctrine ORM provides a mechanism for transitive persistence through cascading of certain operations.
Each association to another entity or a collection of
entities can be configured to automatically cascade the following operations to the associated entities:
``persist``, ``remove``, ``refresh`` or ``all``.
``persist``, ``remove``, ``merge``, ``detach``, ``refresh`` or ``all``.
The main use case for ``cascade: persist`` is to avoid "exposing" associated entities to your PHP application.
Continuing with the User-Comment example of this chapter, this is how the creation of a new user and a new
@@ -419,7 +422,7 @@ comment might look like in your controller (without ``cascade: persist``):
$user = new User();
$myFirstComment = new Comment();
$user->addComment($myFirstComment);
$em->persist($user);
$em->persist($myFirstComment); // required, if `cascade: persist` is not set
$em->flush();
@@ -460,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:
@@ -477,7 +480,7 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
<?php
$user = new User();
$user->comment('Lorem ipsum', new DateTime());
$em->persist($user);
$em->flush();
@@ -526,6 +529,7 @@ operation. During each ``flush()`` operation Doctrine detects if there
are new entities in any collection and three possible cases can
happen:
1. New entities in a collection marked as ``cascade: persist`` will be
directly persisted by Doctrine.
2. New entities in a collection not marked as ``cascade: persist`` will
@@ -606,10 +610,10 @@ Now two examples of what happens when you remove the references:
$em->flush();
In this case you have not only changed the ``Contact`` entity itself but
you have also removed the references for standing data and as well as one
address reference. When flush is called not only are the references removed
but both the old standing data and the one address entity are also deleted
In this case you have not only changed the ``Contact`` entity itself but
you have also removed the references for standing data and as well as one
address reference. When flush is called not only are the references removed
but both the old standing data and the one address entity are also deleted
from the database.
.. _filtering-collections:
@@ -712,9 +716,11 @@ methods:
* ``in($field, array $values)``
* ``notIn($field, array $values)``
* ``contains($field, $value)``
* ``memberOf($value, $field)``
* ``startsWith($field, $value)``
* ``endsWith($field, $value)``
.. note::
There is a limitation on the compatibility of Criteria comparisons.
+159 -41
View File
@@ -53,7 +53,7 @@ headline "Hello World" with the ID 1234:
echo $article2->getHeadline();
In this case the Article is accessed from the entity manager twice,
but modified in between. Doctrine 2 realizes this and will only
but modified in between. Doctrine ORM realizes this and will only
ever give you access to one instance of the Article with ID 1234,
no matter how often do you retrieve it from the EntityManager and
even no matter what kind of Query method you are using (find,
@@ -162,28 +162,25 @@ your code. See the following code:
}
A slice of the generated proxy classes code looks like the
following piece of code. A real proxy class override all non-identity
non-transient object state at instantiation time in order to
enable lazy-loading mechanisms:
following piece of code. A real proxy class override ALL public
methods along the lines of the ``getName()`` method shown below:
.. code-block:: php
<?php
class UserProxyHASH extends User implements GhostObjectInterface
class UserProxy extends User implements Proxy
{
// ... generated code
public static function staticProxyConstructor($initializer)
private function _load()
{
// ... generated code
// lazy loading code
}
private function callInitializerHASH($methodName, array $parameters)
public function getName()
{
// ... generated code
$this->_load();
return parent::getName();
}
// ... generated code
// .. other public methods of User
}
.. warning::
@@ -193,6 +190,7 @@ enable lazy-loading mechanisms:
to heavily. Make sure to use DQL to fetch-join all the parts of the
object-graph that you need as efficiently as possible.
Persisting entities
-------------------
@@ -215,6 +213,7 @@ be properly synchronized with the database when
database in the most efficient way and a single, short transaction,
taking care of maintaining referential integrity.
Example:
.. code-block:: php
@@ -235,9 +234,11 @@ Example:
generated identifier being not available after a failed flush
operation.
The semantics of the persist operation, applied on an entity X, are
as follows:
- If X is a new entity, it becomes managed. The entity X will be
entered into the database as a result of the flush operation.
- If X is a preexisting managed entity, it is ignored by the
@@ -249,6 +250,12 @@ as follows:
- If X is a detached entity, an exception will be thrown on
flush.
.. caution::
Do not pass detached entities to the persist operation. The persist operation always
considers entities that are not yet known to the ``EntityManager`` as new entities
(refer to the ``STATE_NEW`` constant inside the ``UnitOfWork``).
Removing entities
-----------------
@@ -269,6 +276,7 @@ which means that its persistent state will be deleted once
the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
for more information.
Example:
.. code-block:: php
@@ -280,6 +288,7 @@ Example:
The semantics of the remove operation, applied to an entity X are
as follows:
- If X is a new entity, it is ignored by the remove operation.
However, the remove operation is cascaded to entities referenced by
X, if the relationship from X to these other entities is mapped
@@ -308,10 +317,11 @@ foreign key semantics of onDelete="CASCADE".
Deleting an object with all its associated objects can be achieved
in multiple ways with very different performance impacts.
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2
will fetch this association. If it is a Single association it will
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM
will fetch this association. If its a Single association it will
pass this entity to
´EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to ``EntityManager#remove()\`.
``EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()``.
In both cases the cascade remove semantics are applied recursively.
For large object graphs this removal strategy can be very costly.
2. Using a DQL ``DELETE`` statement allows you to delete multiple
@@ -326,42 +336,143 @@ in multiple ways with very different performance impacts.
because Doctrine will fetch and remove all associated entities
explicitly nevertheless.
.. note::
Calling ``remove`` on an entity will remove the object from the identiy
map and therefore detach it. Querying the same entity again, for example
via a lazy loaded relation, will return a new object.
Detaching entities
------------------
All entities are detached from an EntityManager and thus no longer
managed by it after invoking the ``EntityManager#clear()`` method.
Changes made to the detached entities, if any (including their removal),
will not be synchronized to the database after they have been
An entity is detached from an EntityManager and thus no longer
managed by invoking the ``EntityManager#detach($entity)`` method on
it or by cascading the detach operation to it. Changes made to the
detached entity, if any (including removal of the entity), will not
be synchronized to the database after the entity has been
detached.
Doctrine will not hold on to any references to detached entities.
Doctrine will not hold on to any references to a detached entity.
Example:
.. code-block:: php
<?php
$em->clear();
$em->detach($entity);
The semantics of the detach operation, applied to an entity X are
as follows:
- If X is a managed entity, the ``clear`` operation causes it to
become detached. Entities which previously referenced X
- If X is a managed entity, the detach operation causes it to
become detached. The detach operation is cascaded to entities
referenced by X, if the relationships from X to these other
entities is mapped with cascade=DETACH or cascade=ALL (see
":ref:`transitive-persistence`"). Entities which previously referenced X
will continue to reference X.
- If X is a new or detached entity, it is ignored by the detach
operation.
- If X is a removed entity, it will become detached, and therefore
no longer scheduled to be removed. Entities which previously
referenced X will continue to reference X.
- If X is a removed entity, the detach operation is cascaded to
entities referenced by X, if the relationships from X to these
other entities is mapped with cascade=DETACH or cascade=ALL (see
":ref:`transitive-persistence`"). Entities which previously referenced X
will continue to reference X.
There are several situations in which an entity is detached
automatically:
automatically without invoking the ``detach`` method:
- When ``EntityManager#clear()`` is invoked, all entities that are
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).
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).
The ``detach`` operation is usually not as frequently needed and
used as ``persist`` and ``remove``.
Merging entities
----------------
Merging entities refers to the merging of (usually detached)
entities into the context of an EntityManager so that they become
managed again. To merge the state of an entity into an
EntityManager use the ``EntityManager#merge($entity)`` method. The
state of the passed entity will be merged into a managed copy of
this entity and this copy will subsequently be returned.
Example:
.. code-block:: php
<?php
$detachedEntity = unserialize($serializedEntity); // some detached entity
$entity = $em->merge($detachedEntity);
// $entity now refers to the fully managed copy returned by the merge operation.
// The EntityManager $em now manages the persistence of $entity as usual.
.. note::
When you want to serialize/unserialize entities you
have to make all entity properties protected, never private. The
reason for this is, if you serialize a class that was a proxy
instance before, the private variables won't be serialized and a
PHP Notice is thrown.
The semantics of the merge operation, applied to an entity X, are
as follows:
- If X is a detached entity, the state of X is copied onto a
pre-existing managed entity instance X' of the same identity.
- If X is a new entity instance, a new managed copy X' will be
created and the state of X is copied onto this managed instance.
- If X is a removed entity instance, an InvalidArgumentException
will be thrown.
- If X is a managed entity, it is ignored by the merge operation,
however, the merge operation is cascaded to entities referenced by
relationships from X if these relationships have been mapped with
the cascade element value MERGE or ALL (see ":ref:`transitive-persistence`").
- For all entities Y referenced by relationships from X having the
cascade element value MERGE or ALL, Y is merged recursively as Y'.
For all such Y referenced by X, X' is set to reference Y'. (Note
that if X is managed then X is the same object as X'.)
- If X is an entity merged to X', with a reference to another
entity Y, where cascade=MERGE or cascade=ALL is not specified, then
navigation of the same association from X' yields a reference to a
managed object Y' with the same persistent identity as Y.
The ``merge`` operation will throw an ``OptimisticLockException``
if the entity being merged uses optimistic locking through a
version field and the versions of the entity being merged and the
managed copy don't match. This usually means that the entity has
been modified while being detached.
The ``merge`` operation is usually not as frequently needed and
used as ``persist`` and ``remove``. The most common scenario for
the ``merge`` operation is to reattach entities to an EntityManager
that come from some cache (and are therefore detached) and you want
to modify and persist such an entity.
.. warning::
If you need to perform multiple merges of entities that share certain subparts
of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the
successive calls to ``EntityManager#merge()``. Otherwise you might end up with
multiple copies of the "same" object in the database, however with different ids.
.. note::
If you load some detached entities from a cache and you do
not need to persist or delete them or otherwise make use of them
without the need for persistence services there is no need to use
``merge``. I.e. you can simply pass detached objects from a cache
directly to the view.
Synchronization with the Database
---------------------------------
@@ -404,6 +515,7 @@ Synchronizing New and Managed Entities
The flush operation applies to a managed entity with the following
semantics:
- The entity itself is synchronized to the database using a SQL
UPDATE statement, only if at least one persistent field has
changed.
@@ -412,12 +524,14 @@ semantics:
The flush operation applies to a new entity with the following
semantics:
- The entity itself is synchronized to the database using a SQL
INSERT statement.
For all (initialized) relationships of the new or managed entity
the following semantics apply to each associated entity X:
- If X is new and persist operations are configured to cascade on
the relationship, X will be persisted.
- If X is new and no persist operations are configured to cascade
@@ -449,6 +563,7 @@ The cost of flushing
How costly a flush operation is, mainly depends on two factors:
- The size of the EntityManager's current UnitOfWork.
- The configured change tracking policies
@@ -468,13 +583,14 @@ during development.
.. note::
Do not invoke ``flush`` after every change to an entity
or every single invocation of persist/remove/refresh/... This is an
or every single invocation of persist/remove/merge/... This is an
anti-pattern and unnecessarily reduces the performance of your
application. Instead, form units of work that operate on your
objects and call ``flush`` when you are done. While serving a
single HTTP request there should be usually no need for invoking
``flush`` more than 0-2 times.
Direct access to a Unit of Work
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -494,6 +610,7 @@ instance the EntityManager is currently using.
marked as INTERNAL by not using them and carefully read the API
documentation.
Entity State
~~~~~~~~~~~~
@@ -529,14 +646,14 @@ An entity is in DETACHED state if it has persistent state and
identity but is currently not associated with an
``EntityManager``.
An entity is in NEW state if it has no persistent state and identity
An entity is in NEW state if has no persistent state and identity
and is not associated with an ``EntityManager`` (for example those
just created via the "new" operator).
Querying
--------
Doctrine 2 provides the following ways, in increasing level of
Doctrine ORM provides the following ways, in increasing level of
power and flexibility, to query for persistent objects. You should
always start with the simplest one that suits your needs.
@@ -640,10 +757,8 @@ Additionally, you can just count the result of the provided conditions when you
By Criteria
~~~~~~~~~~~
.. versionadded:: 2.3
The Repository implements the ``Doctrine\Common\Collections\Selectable``
interface. It means you can build ``Doctrine\Common\Collections\Criteria``
The Repository implement the ``Doctrine\Common\Collections\Selectable``
interface. That means you can build ``Doctrine\Common\Collections\Criteria``
and pass them to the ``matching($criteria)`` method.
See section `Filtering collections` of chapter :doc:`Working with Associations <working-with-associations>`
@@ -653,7 +768,7 @@ By Eager Loading
Whenever you query for an entity that has persistent associations
and these associations are mapped as EAGER, they will automatically
be loaded together with the entity being queried and are thus
be loaded together with the entity being queried and is thus
immediately available to your application.
By Lazy Loading
@@ -696,7 +811,9 @@ DQL and its syntax as well as the Doctrine class can be found in
:doc:`the dedicated chapter <dql-doctrine-query-language>`.
For programmatically building up queries based on conditions that
are only known at runtime, Doctrine provides the special
``Doctrine\ORM\QueryBuilder`` class. More information on
``Doctrine\ORM\QueryBuilder`` class. While this a powerful tool,
it also brings more complexity to your code compared to plain DQL,
so you should only use it when you need it. More information on
constructing queries with a QueryBuilder can be found
:doc:`in Query Builder chapter <query-builder>`.
@@ -717,7 +834,7 @@ By default the EntityManager returns a default implementation of
``Doctrine\ORM\EntityRepository`` when you call
``EntityManager#getRepository($entityClass)``. You can overwrite
this behaviour by specifying the class name of your own Entity
Repository in the Annotation or XML metadata. In large
Repository in the Annotation, XML or YAML metadata. In large
applications that require lots of specialized DQL queries using a
custom repository is one recommended way of grouping these queries
in a central location.
@@ -742,7 +859,7 @@ in a central location.
{
public function getAllAdminUsers()
{
return $this->em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
return $this->_em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
->getResult();
}
}
@@ -756,3 +873,4 @@ You can access your repository now by calling:
$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
+30 -6
View File
@@ -8,9 +8,7 @@ The XML driver is backed by an XML Schema document that describes
the structure of a mapping document. The most recent version of the
XML Schema document is available online at
`https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd <https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd>`_.
In order to point to the latest version of the document of a
particular stable release branch, just append the release number,
i.e.: doctrine-mapping-2.0.xsd The most convenient way to work with
The most convenient way to work with
XML mapping files is to use an IDE/editor that can provide
code-completion based on such an XML Schema document. The following
is an outline of a XML mapping document with the proper xmlns/xsi
@@ -31,6 +29,7 @@ The XML mapping document of a class is loaded on-demand the first
time it is requested and subsequently stored in the metadata cache.
In order to work, this requires certain conventions:
- Each entity/mapped superclass must get its own dedicated XML
mapping document.
- The name of the mapping document must consist of the fully
@@ -193,10 +192,12 @@ specified as the ``<entity />`` element as a direct child of the
Required attributes:
- name - The fully qualified class-name of the entity.
Optional attributes:
- **table** - The Table-Name to be used for this entity. Otherwise the
Unqualified Class-Name is used by default.
- **repository-class** - The fully qualified class-name of an
@@ -205,10 +206,10 @@ Optional attributes:
- **inheritance-type** - The type of inheritance, defaults to none. A
more detailed description follows in the
*Defining Inheritance Mappings* section.
- **read-only** - (>= 2.1) Specifies that this entity is marked as read only and not
- **read-only** - Specifies that this entity is marked as read only and not
considered for change-tracking. Entities of this type can be persisted
and removed though.
- **schema** - (>= 2.5) The schema the table lies in, for platforms that support schemas
- **schema** - The schema the table lies in, for platforms that support schemas
Defining Fields
~~~~~~~~~~~~~~~
@@ -238,11 +239,13 @@ entity. For the ID mapping you have to use the ``<id />`` element.
Required attributes:
- name - The name of the Property/Field on the given Entity PHP
class.
Optional attributes:
- type - The ``Doctrine\DBAL\Types\Type`` name, defaults to
"string"
- column - Name of the column in the database, defaults to the
@@ -288,7 +291,7 @@ Defining Identity and Generator Strategies
An entity has to have at least one ``<id />`` element. For
composite keys you can specify more than one id-element, however
surrogate keys are recommended for use with Doctrine 2. The Id
surrogate keys are recommended for use with Doctrine ORM. The Id
field allows to define properties of the identifier and allows a
subset of the ``<field />`` element attributes:
@@ -300,6 +303,7 @@ subset of the ``<field />`` element attributes:
Required attributes:
- name - The name of the Property/Field on the given Entity PHP
class.
- type - The ``Doctrine\DBAL\Types\Type`` name, preferably
@@ -307,6 +311,7 @@ Required attributes:
Optional attributes:
- column - Name of the column in the database, defaults to the
field name.
@@ -332,6 +337,7 @@ have to use the ``NONE`` strategy.
The following values are allowed for the ``<generator />`` strategy
attribute:
- AUTO - Automatic detection of the identifier strategy based on
the preferred solution of the database vendor.
- IDENTITY - Use of a IDENTIFY strategy such as Auto-Increment IDs
@@ -354,10 +360,12 @@ element to describe the sequence:
Required attributes for ``<sequence-generator />``:
- sequence-name - The name of the sequence
Optional attributes for ``<sequence-generator />``:
- allocation-size - By how much steps should the sequence be
incremented when a value is retrieved. Defaults to 1
- initial-value - What should the initial value of the sequence
@@ -370,6 +378,7 @@ Optional attributes for ``<sequence-generator />``:
element, if Doctrine chooses the sequence strategy for a
platform.
Defining a Mapped Superclass
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -389,6 +398,7 @@ can define it in XML using the ``<mapped-superclass />`` tag.
Required attributes:
- name - Class name of the mapped superclass.
You can nest any number of ``<field />`` and unidirectional
@@ -428,6 +438,7 @@ The allowed values for inheritance-type attribute are ``JOINED`` or
All inheritance related definitions have to be defined on the root
entity of the hierarchy.
Defining Lifecycle Callbacks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -460,6 +471,7 @@ For the inverse side the mapping is as simple as:
Required attributes for inverse One-To-One:
- field - Name of the property/field on the entity's PHP class.
- target-entity - Name of the entity associated entity class. If
this is not qualified the namespace of the current class is
@@ -477,6 +489,7 @@ For the owning side this mapping would look like:
Required attributes for owning One-to-One:
- field - Name of the property/field on the entity's PHP class.
- target-entity - Name of the entity associated entity class. If
this is not qualified the namespace of the current class is
@@ -484,6 +497,7 @@ Required attributes for owning One-to-One:
Optional attributes for owning One-to-One:
- inversed-by - If the association is bidirectional the
inversed-by attribute has to be specified with the name of the
field on the inverse entity that contains the back-reference.
@@ -526,6 +540,7 @@ like:
Required attributes:
- field - Name of the property/field on the entity's PHP class.
- target-entity - Name of the entity associated entity class. If
this is not qualified the namespace of the current class is
@@ -533,6 +548,7 @@ Required attributes:
Optional attributes:
- inversed-by - If the association is bidirectional the
inversed-by attribute has to be specified with the name of the
field on the inverse entity that contains the back-reference.
@@ -575,6 +591,7 @@ exists for bi-directional associations.
Required attributes:
- field - Name of the property/field on the entity's PHP class.
- target-entity - Name of the entity associated entity class. If
this is not qualified the namespace of the current class is
@@ -584,6 +601,7 @@ Required attributes:
Optional attributes:
- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY.
- index-by: Index the collection by a field on the target entity.
@@ -602,6 +620,7 @@ definitions and rely on their implicit values.
Required attributes:
- field - Name of the property/field on the entity's PHP class.
- target-entity - Name of the entity associated entity class. If
this is not qualified the namespace of the current class is
@@ -609,6 +628,7 @@ Required attributes:
Optional attributes:
- mapped-by - Name of the field on the owning side that contains
the owning side association if the defined many-to-many association
is on the inverse side.
@@ -664,7 +684,9 @@ tags.
Besides ``<cascade-all />`` the following operations can be
specified by their respective tags:
- ``<cascade-persist />``
- ``<cascade-merge />``
- ``<cascade-remove />``
- ``<cascade-refresh />``
@@ -677,12 +699,14 @@ key names are called that are used for joining two entities.
Required attributes:
- name - The column name of the foreign key.
- referenced-column-name - The column name of the associated
entities primary key
Optional attributes:
- unique - If the join column should contain a UNIQUE constraint.
This makes sense for Many-To-Many join-columns only to simulate a
one-to-many unidirectional using a join-table.
+158
View File
@@ -0,0 +1,158 @@
YAML Mapping
============
.. note::
The YAML driver is deprecated and will be removed in version 3.0.
It is strongly recommended to switch to one of the other mappings.
The YAML mapping driver enables you to provide the ORM metadata in
form of YAML documents.
The YAML mapping document of a class is loaded on-demand the first
time it is requested and subsequently stored in the metadata cache.
In order to work, this requires certain conventions:
- Each entity/mapped superclass must get its own dedicated YAML
mapping document.
- The name of the mapping document must consist of the fully
qualified name of the class, where namespace separators are
replaced by dots (.).
- All mapping documents should get the extension ".dcm.yml" to
identify it as a Doctrine mapping file. This is more of a
convention and you are not forced to do this. You can change the
file extension easily enough.
.. code-block:: php
<?php
$driver->setFileExtension('.yml');
It is recommended to put all YAML mapping documents in a single
folder but you can spread the documents over several folders if you
want to. In order to tell the YamlDriver where to look for your
mapping documents, supply an array of paths as the first argument
of the constructor, like this:
.. code-block:: php
<?php
use Doctrine\ORM\Mapping\Driver\YamlDriver;
// $config instanceof Doctrine\ORM\Configuration
$driver = new YamlDriver(array('/path/to/files'));
$config->setMetadataDriverImpl($driver);
Simplified YAML Driver
~~~~~~~~~~~~~~~~~~~~~~
The Symfony project sponsored a driver that simplifies usage of the YAML Driver.
The changes between the original driver are:
- File Extension is .orm.yml
- Filenames are shortened, "MyProject\\Entities\\User" will become User.orm.yml
- You can add a global file and add multiple entities in this file.
Configuration of this client works a little bit different:
.. code-block:: php
<?php
$namespaces = array(
'/path/to/files1' => 'MyProject\Entities',
'/path/to/files2' => 'OtherProject\Entities'
);
$driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver($namespaces);
$driver->setGlobalBasename('global'); // global.orm.yml
Example
-------
As a quick start, here is a small example document that makes use
of several common elements:
.. code-block:: yaml
# Doctrine.Tests.ORM.Mapping.User.dcm.yml
Doctrine\Tests\ORM\Mapping\User:
type: entity
repositoryClass: Doctrine\Tests\ORM\Mapping\UserRepository
table: cms_users
schema: schema_name # The schema the table lies in, for platforms that support schemas (Optional, >= 2.5)
readOnly: true
indexes:
name_index:
columns: [ name ]
id:
id:
type: integer
generator:
strategy: AUTO
fields:
name:
type: string
length: 50
email:
type: string
length: 32
column: user_email
unique: true
options:
fixed: true
comment: User's email address
loginCount:
type: integer
column: login_count
nullable: false
options:
unsigned: true
default: 0
oneToOne:
address:
targetEntity: Address
joinColumn:
name: address_id
referencedColumnName: id
onDelete: CASCADE
oneToMany:
phonenumbers:
targetEntity: Phonenumber
mappedBy: user
cascade: ["persist", "merge"]
manyToMany:
groups:
targetEntity: Group
joinTable:
name: cms_users_groups
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
group_id:
referencedColumnName: id
lifecycleCallbacks:
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
postPersist: [ doStuffOnPostPersist ]
Be aware that class-names specified in the YAML files should be
fully qualified.
Reference
~~~~~~~~~~~~~~~~~~~~~~
Unique Constraints
------------------
It is possible to define unique constraints by the following declaration:
.. code-block:: yaml
# ECommerceProduct.orm.yml
ECommerceProduct:
type: entity
fields:
# definition of some fields
uniqueConstraints:
search_idx:
columns: [ name, email ]
+1
View File
@@ -42,6 +42,7 @@
reference/change-tracking-policies
reference/partial-objects
reference/xml-mapping
reference/yaml-mapping
reference/annotations-reference
reference/php-mapping
reference/caching
+86
View File
@@ -0,0 +1,86 @@
Welcome to Doctrine 2 ORM's documentation!
==========================================
Tutorials
---------
.. toctree::
:maxdepth: 1
tutorials/getting-started
tutorials/getting-started-database
tutorials/getting-started-models
tutorials/working-with-indexed-associations
tutorials/extra-lazy-associations
tutorials/composite-primary-keys
tutorials/ordered-associations
tutorials/override-field-association-mappings-in-subclasses
tutorials/pagination.rst
tutorials/embeddables.rst
Reference Guide
---------------
.. toctree::
:maxdepth: 1
:numbered:
reference/architecture
reference/configuration.rst
reference/faq
reference/basic-mapping
reference/association-mapping
reference/inheritance-mapping
reference/working-with-objects
reference/working-with-associations
reference/events
reference/unitofwork
reference/unitofwork-associations
reference/transactions-and-concurrency
reference/batch-processing
reference/dql-doctrine-query-language
reference/query-builder
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/xml-mapping
reference/yaml-mapping
reference/annotations-reference
reference/php-mapping
reference/caching
reference/improving-performance
reference/tools
reference/metadata-drivers
reference/best-practices
reference/limitations-and-known-issues
tutorials/pagination
reference/filters
reference/namingstrategy
reference/advanced-configuration
reference/second-level-cache
reference/security
Cookbook
--------
.. toctree::
:maxdepth: 1
cookbook/aggregate-fields
cookbook/custom-mapping-types
cookbook/decorator-pattern
cookbook/dql-custom-walkers
cookbook/dql-user-defined-functions
cookbook/implementing-arrayaccess-for-domain-objects
cookbook/implementing-the-notify-changetracking-policy
cookbook/implementing-wakeup-or-clone
cookbook/resolve-target-entity-listener
cookbook/sql-table-prefixes
cookbook/strategy-cookbook-introduction
cookbook/validation-of-entities
cookbook/working-with-datetime
cookbook/mysql-enums
cookbook/advanced-field-value-conversion-using-custom-mapping-types
cookbook/entities-in-session
+88 -56
View File
@@ -1,11 +1,9 @@
Composite and Foreign Keys as Primary Key
=========================================
.. versionadded:: 2.1
Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept
and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases.
For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as
Doctrine ORM supports composite primary keys natively. Composite keys are a very powerful relational database concept
and we took good care to make sure Doctrine ORM supports as many of the composite primary key use-cases.
For Doctrine ORM composite keys of primitive data-types are supported, even foreign keys as
primary keys are supported.
This tutorial shows how the semantics of composite primary keys work and how they map to the database.
@@ -19,7 +17,7 @@ the ID fields have to have their values set before you call ``EntityManager#pers
Primitive Types only
~~~~~~~~~~~~~~~~~~~~
Even in version 2.0 you can have composite keys as long as they only consist of the primitive types
You can have composite keys as long as they only consist of the primitive types
``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name
and year of production as primary keys:
@@ -28,19 +26,16 @@ and year of production as primary keys:
.. code-block:: php
<?php
namespace VehicleCatalogue\Model;
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @Entity
*/
class Car
{
/** @ORM\Id @ORM\Column(type="string") */
/** @Id @Column(type="string") */
private $name;
/** @ORM\Id @ORM\Column(type="integer") */
/** @Id @Column(type="integer") */
private $year;
public function __construct($name, $year)
@@ -74,6 +69,16 @@ and year of production as primary keys:
</entity>
</doctrine-mapping>
.. code-block:: yaml
VehicleCatalogue\Model\Car:
type: entity
id:
name:
type: string
year:
type: integer
Now you can use this entity:
.. code-block:: php
@@ -113,10 +118,6 @@ and to ``year`` to the related entities.
Identity through foreign Entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
Identity through foreign entities is only supported with Doctrine 2.1
There are tons of use-cases where the identity of an Entity should be determined by the entity
of one or many parent entities.
@@ -130,8 +131,9 @@ of one or many parent entities.
The semantics of mapping identity through foreign entities are easy:
- Only allowed on Many-To-One or One-To-One associations.
- Plug an ``@ORM\Id`` annotation onto every association.
- Plug an ``@Id`` annotation onto every association.
- Set an attribute ``association-key`` with the field name of the association in XML.
- Set a key ``associationKey:`` with the field name of the association in YAML.
Use-Case 1: Dynamic Attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -146,21 +148,19 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
namespace Application\Model;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @Entity
*/
class Article
{
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @ORM\Column(type="string") */
/** @Column(type="string") */
private $title;
/**
* @ORM\OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
* @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
*/
private $attributes;
@@ -171,17 +171,17 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
}
/**
* @ORM\Entity
* @Entity
*/
class ArticleAttribute
{
/** @ORM\Id @ORM\ManyToOne(targetEntity="Article", inversedBy="attributes") */
/** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */
private $article;
/** @ORM\Id @ORM\Column(type="string") */
/** @Id @Column(type="string") */
private $attribute;
/** @ORM\Column(type="string") */
/** @Column(type="string") */
private $value;
public function __construct($name, $value, $article)
@@ -202,7 +202,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
<entity name="Application\Model\ArticleAttribute">
<id name="article" association-key="true" />
<id name="attribute" type="string" />
<field name="value" type="string" />
<many-to-one field="article" target-entity="Article" inversed-by="attributes" />
@@ -210,6 +210,24 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
</doctrine-mapping>
.. code-block:: yaml
Application\Model\ArticleAttribute:
type: entity
id:
article:
associationKey: true
attribute:
type: string
fields:
value:
type: string
manyToOne:
article:
targetEntity: Article
inversedBy: attributes
Use-Case 2: Simple Derived Identity
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -222,27 +240,44 @@ One good example for this is a user-address relationship:
.. code-block:: php
<?php
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @Entity
*/
class User
{
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
}
/**
* @ORM\Entity
* @Entity
*/
class Address
{
/** @ORM\Id @ORM\OneToOne(targetEntity="User") */
/** @Id @OneToOne(targetEntity="User") */
private $user;
}
.. code-block:: yaml
User:
type: entity
id:
id:
type: integer
generator:
strategy: AUTO
Address:
type: entity
id:
user:
associationKey: true
oneToOne:
user:
targetEntity: User
Use-Case 3: Join-Table with Metadata
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -254,27 +289,23 @@ of products purchased and maybe even the current price.
<?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class Order
{
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @ORM\ManyToOne(targetEntity="Customer") */
/** @ManyToOne(targetEntity="Customer") */
private $customer;
/** @ORM\OneToMany(targetEntity="OrderItem", mappedBy="order") */
/** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
private $items;
/** @ORM\Column(type="boolean") */
private $paid = false;
/** @ORM\Column(type="boolean") */
/** @Column(type="boolean") */
private $payed = false;
/** @Column(type="boolean") */
private $shipped = false;
/** @ORM\Column(type="datetime") */
/** @Column(type="datetime") */
private $created;
public function __construct(Customer $customer)
@@ -285,16 +316,16 @@ of products purchased and maybe even the current price.
}
}
/** @ORM\Entity */
/** @Entity */
class Product
{
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @ORM\Column(type="string") */
/** @Column(type="string") */
private $name;
/** @ORM\Column(type="decimal") */
/** @Column(type="decimal") */
private $currentPrice;
public function getCurrentPrice()
@@ -303,19 +334,19 @@ of products purchased and maybe even the current price.
}
}
/** @ORM\Entity */
/** @Entity */
class OrderItem
{
/** @ORM\Id @ORM\ManyToOne(targetEntity="Order") */
/** @Id @ManyToOne(targetEntity="Order") */
private $order;
/** @ORM\Id @ORM\ManyToOne(targetEntity="Product") */
/** @Id @ManyToOne(targetEntity="Product") */
private $product;
/** @ORM\Column(type="integer") */
/** @Column(type="integer") */
private $amount = 1;
/** @ORM\Column(type="decimal") */
/** @Column(type="decimal") */
private $offeredPrice;
public function __construct(Order $order, Product $product, $amount = 1)
@@ -326,6 +357,7 @@ of products purchased and maybe even the current price.
}
}
Performance Considerations
~~~~~~~~~~~~~~~~~~~~~~~~~~
+47 -18
View File
@@ -4,7 +4,7 @@ Separating Concerns using Embeddables
Embeddables are classes which are not entities themselves, but are embedded
in entities and can also be queried in DQL. You'll mostly want to use them
to reduce duplication or separating concerns. Value objects such as date range
or address are the primary use case for this feature.
or address are the primary use case for this feature.
.. note::
@@ -21,28 +21,26 @@ instead of simply adding the respective columns to the ``User`` class.
<?php
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class User
{
/** @ORM\Embedded(class = "Address") */
/** @Embedded(class = "Address") */
private $address;
}
/** @ORM\Embeddable */
/** @Embeddable */
class Address
{
/** @ORM\Column(type = "string") */
/** @Column(type = "string") */
private $street;
/** @ORM\Column(type = "string") */
/** @Column(type = "string") */
private $postalCode;
/** @ORM\Column(type = "string") */
/** @Column(type = "string") */
private $city;
/** @ORM\Column(type = "string") */
/** @Column(type = "string") */
private $country;
}
@@ -61,6 +59,22 @@ instead of simply adding the respective columns to the ``User`` class.
</embeddable>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
embedded:
address:
class: Address
Address:
type: embeddable
fields:
street: { type: string }
postalCode: { type: string }
city: { type: string }
country: { type: string }
In terms of your database schema, Doctrine will automatically inline all
columns from the ``Address`` class into the table of the ``User`` class,
just as if you had declared them directly there.
@@ -99,12 +113,10 @@ The following example shows you how to set your prefix to ``myPrefix_``:
<?php
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class User
{
/** @ORM\Embedded(class = "Address", columnPrefix = "myPrefix_") */
/** @Embedded(class = "Address", columnPrefix = "myPrefix_") */
private $address;
}
@@ -114,6 +126,15 @@ The following example shows you how to set your prefix to ``myPrefix_``:
<embedded name="address" class="Address" column-prefix="myPrefix_" />
</entity>
.. code-block:: yaml
User:
type: entity
embedded:
address:
class: Address
columnPrefix: myPrefix_
To have Doctrine drop the prefix and use the value object's property name
directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML):
@@ -123,21 +144,29 @@ directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML):
<?php
use Doctrine\ORM\Annotation as ORM;
/** @ORM\Entity */
/** @Entity */
class User
{
/** @ORM\Embedded(class = "Address", columnPrefix = false) */
/** @Embedded(class = "Address", columnPrefix = false) */
private $address;
}
.. code-block:: yaml
User:
type: entity
embedded:
address:
class: Address
columnPrefix: false
.. code-block:: xml
<entity name="User">
<embedded name="address" class="Address" use-column-prefix="false" />
</entity>
DQL
---
+29 -10
View File
@@ -5,19 +5,19 @@ Extra Lazy Associations
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog.
where posts can be commented, you always have to assume that a post draws hundreds of comments.
In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This
In Doctrine ORM if you accessed an association it would always get loaded completely into memory. This
can lead to pretty serious performance problems, if your associations contain several hundreds or thousands
of entities.
With Doctrine 2.1 a feature called **Extra Lazy** is introduced for associations. Associations
Doctrine ORM includes a feature called **Extra Lazy** for associations. Associations
are marked as **Lazy** by default, which means the whole collection object for an association is populated
the first time its accessed. If you mark an association as extra lazy the following methods on collections
can be called without triggering a full load of the collection:
- ``Collection#contains($entity)``
- ``Collection#containsKey($key)`` (available with Doctrine 2.5)
- ``Collection#containsKey($key)``
- ``Collection#count()``
- ``Collection#get($key)`` (available with Doctrine 2.4)
- ``Collection#get($key)``
- ``Collection#slice($offset, $length = null)``
For each of the above methods the following semantics apply:
@@ -25,7 +25,7 @@ For each of the above methods the following semantics apply:
- For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database.
- For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed.
Additionally even with Doctrine 2.0 the following methods do not trigger the collection load:
Additionally even with Doctrine ORM the following methods do not trigger the collection load:
- ``Collection#add($entity)``
- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does
@@ -34,6 +34,16 @@ Additionally even with Doctrine 2.0 the following methods do not trigger the col
With extra lazy collections you can now not only add entities to large collections but also paginate them
easily using a combination of ``count`` and ``slice``.
.. warning::
``removeElement`` directly issued DELETE queries to the database from
version 2.4.0 to 2.7.0. This circumvents the flush operation and might run
outside a transactional boundary if you don't create one yourself. We
consider this a critical bug in the assumptio of how the ORM works and
reverted ``removeElement`` EXTRA_LAZY behavior in 2.7.1.
Enabling Extra-Lazy Associations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -45,18 +55,15 @@ switch to extra lazy as shown in these examples:
.. code-block:: php
<?php
namespace Doctrine\Tests\Models\CMS;
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity
* @Entity
*/
class CmsGroup
{
/**
* @ORM\ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
* @ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
*/
public $users;
}
@@ -74,3 +81,15 @@ switch to extra lazy as shown in these examples:
<many-to-many field="users" target-entity="CmsUser" mapped-by="groups" fetch="EXTRA_LAZY" />
</entity>
</doctrine-mapping>
.. code-block:: yaml
Doctrine\Tests\Models\CMS\CmsGroup:
type: entity
# ...
manyToMany:
users:
targetEntity: CmsUser
mappedBy: groups
fetch: EXTRA_LAZY

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