Compare commits

...

292 Commits
2.7.4 ... 2.8.5

Author SHA1 Message Date
Grégoire Paris
a6577b89a2 Merge pull request #8701 from jderusse/symfony6
Allow Symfony 6.0
2021-05-20 07:58:49 +02:00
Jérémy Derussé
0ca87566a9 Allow Symfony 6.0 2021-05-20 07:48:21 +02:00
Grégoire Paris
5d01f94a36 Merge pull request #8699 from greg0ire/fix-psalm
Fix some static analysis issues
2021-05-20 07:43:12 +02:00
Grégoire Paris
3d02b02636 Update static analysis baseline files
These issues were not introduced with new code, but with upgrades.
2021-05-20 00:03:39 +02:00
Grégoire Paris
6de321cb09 Address Psalm issues introduced by persistence 2021-05-20 00:03:39 +02:00
Grégoire Paris
535bc92dc8 Merge pull request #8700 from deguif/fix-undefined-offset
Fix undefined index
2021-05-18 12:20:38 +02:00
François-Xavier de Guillebon
ebb5d03f7a Fix undefined offset 2021-05-18 10:00:19 +02:00
Grégoire Paris
8e13369621 Merge pull request #8698 from deguif/cache-deprecation
Fix cache deprecation
2021-05-17 22:15:13 +02:00
François-Xavier de Guillebon
8eff4b775a Fix cache deprecation 2021-05-17 21:33:52 +02:00
tweet9ra
3a194ad699 SimpleObjectHydrator: skip unsuit custom type before converting it (#8566)
When using inheritance, it is possible to map the same column to properties of
different child classes. This can result in the same column being selecting several
times with different aliases in one SQL query, and only one aliased field needs
to be hydrated per row.
We now check that such an aliased value is mapped to the class we are hydrated
before attempting to convert it as it might result in an error when using a custom
type that does not get the expected data to initialize php value.

Co-authored-by: Sergey Naumov <s.naumov@lamoda.ru>
2021-05-15 09:37:16 +02:00
Grégoire Paris
4ccc4e19fc Merge pull request #8600 from VincentLanglet/computeChangeset
Remove internal tag from computeChangeSet
2021-05-05 19:27:39 +02:00
Grégoire Paris
c25b822217 Merge pull request #8673 from Seldaek/patch-1
Add hint for ->iterate() deprecation
2021-05-05 13:31:30 +02:00
Jordi Boggiano
c3dcc5af91 Add hint for ->iterate() deprecation 2021-05-05 10:31:33 +02:00
Alexandr Li
d141f27875 ConvertDoctrine1Schema: Fix Doctrine 1 notnull field import (#8649)
* ConvertDoctrine1Schema: Fix Doctrine 1 `notnull` field import

* cs fix

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-05-01 13:56:41 +02:00
Grégoire Paris
8555fc1d34 Merge pull request #8659 from greg0ire/make-tests-independent
Make tests independent
2021-04-29 09:07:26 +02:00
Grégoire Paris
850d57827f Make tests independent
It seems like IdentityMapTest cannot be run on its own when the second
level cache is enabled (with ENABLE_SECOND_LEVEL_CACHE=1).
It does work when running the whole test suite because
ExtraLazyCollectionTest disables part of that cache in its setUp()
method.
In this patch, we restore the class metadata as it was before running
setUp() and put the test in IdentityMapTest inside the group that is
excluded when running with ENABLE_SECOND_LEVEL_CACHE=1 on the CI.
2021-04-28 14:12:31 +02:00
Grégoire Paris
9a48450481 Merge pull request #7608 from mavroprovato/patch-1
Avoid unnecessary flush after processing first row
2021-04-24 14:23:09 +02:00
Kostas Kokkoros
cff8b96dd6 Avoid unnecessary flush after processing first row
The code as is needlessly flushes after just one row is updated or
removed. It makes more sense to update after ``$batchSize`` elements are
updated or removed, just as it is in the insert case.
2021-04-24 13:01:33 +02:00
Grégoire Paris
996c1c74b3 Merge pull request #8644 from greg0ire/more-accurate-return-type
Describe return types more accurately
2021-04-22 22:31:05 +02:00
Grégoire Paris
48612e6dc6 Merge pull request #8641 from Jean85/remove-deprecated-proxy-usage
Replace deprecated Proxy usages with parent interface to reduce baseline
2021-04-19 23:37:22 +02:00
Grégoire Paris
eb860a704e Change incorrect DBAL return types 2021-04-19 19:27:00 +02:00
Grégoire Paris
51ffcb4891 Describe return types more accurately
This fixes an SA regression introduced when using stricter types in
SchemaTool.

Fixes #8642
2021-04-19 19:09:10 +02:00
Alessandro Lai
9ea0769d78 Replace deprecated Proxy usages with parent interface to reduce baseline 2021-04-19 10:43:49 +02:00
Grégoire Paris
06fadcdd8c Merge pull request #8630 from Jean85/reduce-baseline
Reduce baseline
2021-04-18 18:50:59 +02:00
Alessandro Lai
7c56aa2141 Reduce baseline with a nullable return where needed 2021-04-18 18:39:20 +02:00
Alessandro Lai
4cdcb5f760 Reduce baseline for AbstractCollectionPersister 2021-04-18 18:39:20 +02:00
Alessandro Lai
b542b36e45 Remove baseline for DefaultCacheFactory 2021-04-18 18:39:20 +02:00
Alessandro Lai
e5a7a13e1e Remove single baseline rule from DefaultCache 2021-04-18 18:39:20 +02:00
Alessandro Lai
8336dd3779 Remove baseline for AbstractQuery 2021-04-18 18:39:14 +02:00
Grégoire Paris
a959a474fd Merge pull request #8636 from greg0ire/update-gitattributes
Update ignore rules to reflect current situation
2021-04-18 17:56:47 +02:00
Grégoire Paris
5ee71c54d4 Update ignore rules to reflect current situation
We no longer use Travis, we do not use git submodules as far as I know,
and we now use baseline files as well as project metadata.
2021-04-18 10:49:48 +02:00
Grégoire Paris
da3a9fa361 Merge pull request #8634 from orklah/static-upgrade
upgrade static tools
2021-04-17 15:41:51 +02:00
orklah
4fd81d26ff upgrade static tools 2021-04-17 13:12:35 +02:00
Grégoire Paris
e2e9f8fa97 Merge pull request #8627 from greg0ire/add-baselines
Add baseline files for static analyzers
2021-04-16 19:04:04 +02:00
Grégoire Paris
f7249ec709 Declare return type
This helps SA tools figure out that it is fine to call count on the
return value of that method.
As a side-effect, using $metadata->name is not really an option since it
is not part of the ClassMetadata interface.
2021-04-16 13:14:54 +02:00
Grégoire Paris
87dbcca454 Add baseline files for static analyzers
There are many CS and SA-related changes in the ORM as we pursue better
code quality, and easier contributions. These often involve huge
changes, which can be hard to review and inevitably lead to some
regressions. I know some of those could have been avoided if we were
using stricter levels for PHPStan and Psalm.

By adding baselines, we ensure new code is at level 5 for both tools,
which should allow us to catch the most interesting issues.
2021-04-16 09:23:11 +02:00
Grégoire Paris
305e0d6664 Merge pull request #8617 from greg0ire/cs9
Upgrade to doctrine/coding-standard 9
2021-04-13 18:48:12 +02:00
Grégoire Paris
199be94e6d Upgrade to doctrine/coding-standard 9 2021-04-13 09:00:33 +02:00
Grégoire Paris
fa588af3b1 Merge pull request #8604 from janatjak/2.8.x
Fix psalm param typehint for OneToManyAssociationBuilder::setOrderBy method
2021-04-09 12:57:12 +02:00
Grégoire Paris
d4741720fa Merge pull request #8605 from greg0ire/fix-phpdoc-lsp-violations 2021-04-09 11:59:28 +02:00
Grégoire Paris
343385d060 Pin squizlabs/php_codesniffer
We are referencing rules in phpcs.xml.dist, and may experience
unexpected BC-breaks because of that when they get renamed.
2021-04-09 09:27:44 +02:00
Grégoire Paris
6d04dced03 Address sniff rename
This sniff seems to have been renamed or split in the latest version of
phpcs.
2021-04-09 09:19:10 +02:00
Grégoire Paris
22fa3a8556 Document actual return types
Some executors may return integers, for instance executors that only
execute update or delete statements.
Also, in case an integer is not returned, what's actually returned is a
Doctrine\DBAL\Driver\ResultStatement, and not a Doctrine\DBAL\Driver\Statement
2021-04-09 08:52:56 +02:00
Jakub Janata
eb05756dc3 Fix psalm param typehint for OneToManyAssociationBuilder::setOrderBy method 2021-04-08 22:52:50 +02:00
Grégoire Paris
5bb7e20708 Merge pull request #8602 from NicoHaase/fix-8599
Adjusted return type annotation for getOriginalEntityData
2021-04-07 23:22:19 +02:00
Nico Haase
a9076313c7 Adjusted return type 2021-04-07 21:06:58 +02:00
Grégoire Paris
2a87821b28 Merge pull request #8552 from acoulton/maint-phpunit-upgrade 2021-04-07 15:33:06 +02:00
acoulton
da5877d60c Only polyfill older phpunit methods when required 2021-04-07 12:08:55 +01:00
Andrew Coulton
67dfe8e1af Simplify mock building calls
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-04-07 11:07:35 +01:00
Vincent Langlet
2dfe51b396 Remove internal tag 2021-04-07 11:41:34 +02:00
Grégoire Paris
5ac036de02 Merge pull request #8594 from greg0ire/make-sure-test-is-run 2021-04-06 17:47:17 +02:00
Grégoire Paris
23e1fd8ad6 Drop assertion about not being an instance of proxy
We do not want to enforce it as it is an internal details that seems to
vary from environment to environment.
2021-04-06 14:55:25 +02:00
Grégoire Paris
a588555ecd Merge pull request #8586 from KartaviK/patch-3
Additional psalm param typehint for orderBy argument in findBy method
2021-04-05 20:38:36 +02:00
Grégoire Paris
501057da83 Ensure test is suffixed with Test 2021-04-05 14:42:17 +02:00
Grégoire Paris
7de84537f6 Merge pull request #8591 from DmitriiBezborodnikov/case_insensive_parenthesis
Return case insensitive check
2021-04-05 14:40:51 +02:00
Grégoire Paris
97f8325dad Make sure tests are suffixed with Test
They will not be taken into account when running vendor/bin/phpunit
otherwise.
2021-04-05 14:32:40 +02:00
Grégoire Paris
0ebd7052d7 Drop create table at shutdown
It makes tests more isolated from each other: another test relying on
some tables including some of the ones created here may fail creating
the tables it needs because they already exist.
2021-04-05 14:03:49 +02:00
Dmitrii Bezborodnikov
5d73378b92 Return case insensitive check 2021-04-05 14:03:49 +02:00
Grégoire Paris
10572ec441 Merge pull request #8590 from VincentLanglet/patch-2
Fix phpdoc of ClassMetadataInfo::getIdentifierValues
2021-04-04 23:47:09 +02:00
Vincent Langlet
76278d801d Fix phpdoc 2021-04-04 21:19:54 +02:00
Roman Varkuta
ca80830b26 Describe $orderBy parameter as a hash
A list of string is incorrect, it actually looks like this:
['someField' => 'DESC', 'someOtherField' => 'ASC'…]`
2021-04-03 12:45:24 +02:00
Grégoire Paris
bcb4889a2c Merge pull request #8583 from greg0ire/sync-static-analysis-workflows
Synchronize static analysis jobs with upstream
2021-04-02 08:58:50 +02:00
Grégoire Paris
961da8b0cc Synchronize static analysis jobs with upstream 2021-04-01 23:32:04 +02:00
Benjamin Eberlei
657a30f8ce [GH-6394] Bugfix: IdentifierFlattener support for association non-object values. (#8384)
* [GH-6394] Bugfix: IdentifierFlattener support for association non-object values

* [GH-6394] Bugfix: BasicEntityPersister::update used wrong identifiers for version assignment.

* Exclude MissingNativeTypeHint phpcs rule as 7.4 is not lowest version.
2021-04-01 23:16:53 +02:00
Grégoire Paris
0b25d4d8b0 Merge pull request #8573 from greg0ire/fix-build
Fix build issues
2021-04-01 07:49:27 +02:00
Grégoire Paris
a88242ee6c Adapt test logic to PHP and SQLite
There seems to be at least 2 camps in the software world when it comes
to the question "What's today minus one month", today being at the end
of march.

While PHP and SQLite agree that that would be the 2nd of March, other
RDBMS than SQLite and humans will tell you that it's the last day of
February.

This patch ensures that we check one logic for SQLite, and the other
logic for other platforms.
2021-03-30 21:08:29 +02:00
Grégoire Paris
fe4964008d Accommodate 2 behaviors of symfony/console in test
Decorated text used to be wrapped too early in SymfonyStyle->block()
See https://github.com/symfony/symfony/pull/40348
The fix was not contributed to version 3, which means we have to rewrite
the test so that it passes for both the correct and the buggy version.
2021-03-30 08:41:10 +02:00
Grégoire Paris
3f3de70c3e Merge pull request #8564 from cybercitizen7/featureIncludeDirectory
Adding DIR to include statement to fix issue with pathing
2021-03-26 19:46:40 +01:00
darkw1z
eb4e317144 Adding DIR to include statement to fix issue with pathing 2021-03-26 14:04:46 +01:00
Grégoire Paris
c8f2f61ea1 Merge pull request #8556 from VincentLanglet/patch-2
Fix fieldMapping phpdoc
2021-03-26 08:26:40 +01:00
Vincent Langlet
c9502d3d0b Fix fieldMapping phpdoc 2021-03-24 15:07:08 +01:00
Grégoire Paris
3358ccde39 Merge pull request #8547 from greg0ire/psalm-lv6-phpdoc
Make phpdoc types correct
2021-03-21 22:11:51 +01:00
acoulton
1f4e6ebeeb Add a forward-compatibility wrapper for phpunit8 assertions
While doctrine still supports php7.2 the test cases need to run
under phpunit8 as well. However some assertion methods produce
deprecation warnings in the test output with phpunit >= 9.

This commit adds a thin forward compatiblity wrapper for the
new assertion method names so that they can be used with both
supported phpunit versions.
2021-03-18 11:25:18 +00:00
acoulton
a94db4f5c0 Fix remaining warnings from the phpunit9 upgrade
Some tests were still using deprecated assertion and mocking methods,
resulting in a long list of warnings in the phpunit output.

This commit resolves all the warnings:

* Fixes a couple of test names in `@depends` tags (presumably these
  tests were renamed at some point for coding standards).

* Changes how mocks are configured when asserting the same method
  is called multiple times with a sequence of arguments / sequence
  of return values. The old `->at` expectation is deprecated because
  it can be brittle and give unreliable results. Some of this
  mocking could probably still be refactored further, but my focus
  was on solving the deprecation with the existing setup.

* Removes one use of prophecy for mocking, in favour of using
  phpunit mock objects. Prophecy now requires an extra composer
  dependency and a trait which seems overkill given it was only
  used in one place.

* Updates to the new names for assertFileExists and assertRegExp
  (and their `not` versions) - these are functionally equivalent.

* Replaces the last few references to old PHPUnit_Framework_XXX
  classes with their namespaced equivalent. These were mostly in
  comments, or in native php `assert()` statements that were sanity
  checking mocked object types. These asserts are probably redundant
  (and are clearly not running in CI since the classes they referenced
  no longer exist).
2021-03-18 10:51:43 +00:00
Grégoire Paris
47475f3a67 Merge pull request #8532 from acoulton/bug-fix-ci-db-connection
All CI runs are using the sqlite fallback connection instead of the expected driver
2021-03-17 19:49:56 +01:00
acoulton
61c4a5da0a Rename tmpdb_ to privileged_db in test config and TestUtil
To avoid confusion, the `tmpdb_` test config values are now named
`privileged_db_` and better documented in the phpunit.xml.dist.

The TestUtil class has been refactored to more closely mirror the
structure and method / variable naming of the equivalent in
doctrine/dbal. This does not introduce any significant functional
changes. The only real difference is that the test output now prints
the selected database driver the first time it is referenced,
rather than repeating this through the test run.
2021-03-17 10:31:22 +00:00
acoulton
dd34bca4eb Upgrade previously-skipped tests to phpunit 9
These tests had not been running in CI so missed the previous
phpunit upgrade.

Note that assertions in decimal/floaty values in GH7941Test have
been changed to compare numerically with a reasonable level of
precision, instead of using regex. This is because the types
returned by the different drivers are inconsistent, and phpunit
now only allows regex comparisons on strings.
2021-03-17 10:31:22 +00:00
acoulton
3e21c50f61 Fix unit test and CI database driver / credential configuration
Builds using the github actions phpunit.xml files were not properly
recognising driver-specific configuration values, so were all
falling back to use the in-memory sqlite database instead of the
expected driver. This also meant a number of tests were skipped
as they rely on functionality not available in sqlite.

This commit addresses that by:

* REMOVING the automatic fallback to the sqlite memory database -
  phpunit.xml must now always specify explicit parameters for the
  desired connection.

* Displaying the active driver in the build output for visibility
  and debugging.

* Changing the way TestUtil loads the database config in line
  with the equivalent logic in doctrine/dbal, and to support the
  way that the config is/was specified in the phpunit.xml files
  for CI.

Note that this means a couple of the expected config variable names
have changed. Developers that are using customised phpunit.xml files
locally will need to update them to provide:

* Database config variables if they want to use the sqlite/memory
  driver - see phpunit.xml.dist for details.
* `db_driver` instead of `db_type`
* `db_user` instead of `db_username`
* `db_dbname` instead of `db_name`
* And, if in use, the equivalent changes to the `tmpdb_` values

The other change is that now if you provide any value for
`db_driver` we will attempt to create that connection type and
that will throw if other details (username / password / etc as
required by the driver) are not provided. Previously providing
partial configuration would cause TestUtil to silently fall back
to the in-memory sqlite driver.
2021-03-17 10:31:22 +00:00
Grégoire Paris
bc3592bcc8 Make phpdoc type correct 2021-03-16 19:20:11 +01:00
Grégoire Paris
5247c56fce Merge pull request #8539 from greg0ire/cs-20210311
🎉 Final CS batch 🎉
2021-03-14 18:47:03 +01:00
Grégoire Paris
cc37c490c2 Synchronize coding standard workflow with upstream
Now that there no longer are cs issues, we can thank diff-sniffer and
kiss it goodbye!
2021-03-13 09:55:38 +01:00
Grégoire Paris
95824efd61 Manually fix cs 2021-03-13 09:46:38 +01:00
Grégoire Paris
44d4712e64 Ignore rule about annotation phpdoc
These phpdoc is parsed by doctrine/annotations, and that package does
not understand things like array<string, mixed> yet.
2021-03-13 09:46:38 +01:00
Grégoire Paris
930f44c02f Ignore rule for externally-defined property 2021-03-13 00:08:03 +01:00
Grégoire Paris
6e3c011e65 Ignore rule about superflous comment
We can fix it with a breaking change.
2021-03-13 00:08:03 +01:00
Grégoire Paris
a82de0d422 Ignore rule about unused method
It cannot work when you call the private method like a callable.
2021-03-13 00:08:03 +01:00
Grégoire Paris
9917488179 Ignore rule about empty statements
This should be implemented in a separate pull request.
2021-03-12 08:20:46 +01:00
Grégoire Paris
93f31d2c33 Merge pull request #8533 from acoulton/test-string-lock-version
Add test coverage for passing optimistic lock version as string
2021-03-11 21:41:32 +01:00
acoulton
77356b954f Add test coverage for passing optimistic lock version as string
As discussed in #8527, when using optimistic locking with integer
version columns, Doctrine has always supported passing the lock
version as a string. For example when passing in a version
received in POST / GET.

Technically speaking this does not comply with the docs and phdoc
(which show the app explicitly casting to int before passing).

Nonetheless the maintainers decided it should continue to be valid
for now and reinstated the old soft-equals logic with #8531.

This modified test just avoids accidental changes in future.
2021-03-11 13:24:53 +00:00
Grégoire Paris
92f764206e Ignore broken rule 2021-03-11 09:11:24 +01:00
Grégoire Paris
141539673e Merge pull request #8530 from doctrine/cs-20210310
CS-batch 25/26 🤩
2021-03-11 00:05:31 +01:00
Grégoire Paris
23dc804c9b Merge pull request #8531 from beberlei/GH-8527-RevertLockEquals
[GH-8527] Revert cs fixes for entity version compares in lock+merge
2021-03-10 23:30:25 +01:00
Benjamin Eberlei
9e3baa7baa [GH-8527] Revert cs fixes for entity version compares in lock+merge 2021-03-10 22:46:07 +01:00
Grégoire Paris
322ea51ecf Manually fix cs 2021-03-10 22:17:31 +01:00
Grégoire Paris
21b046452b Merge pull request #8529 from greg0ire/cs-20210308
CS batch 24/26 🤞
2021-03-10 19:53:52 +01:00
Grégoire Paris
c57b81ada4 Manually fix cs 2021-03-09 21:15:33 +01:00
Grégoire Paris
4fa7c9c6de Merge pull request #8521 from greg0ire/cs-20210228
CS batch 23/an estimated 26
2021-03-08 19:57:18 +01:00
Grégoire Paris
3580517aac Manually fix cs 2021-03-01 21:49:10 +01:00
Grégoire Paris
f9e7c3c2d8 Merge pull request #8516 from greg0ire/cs-20210227
CS batch 22/an estimated 26
2021-02-28 21:27:52 +01:00
Grégoire Paris
3600c0fbca Manually fix cs 2021-02-28 18:31:38 +01:00
Grégoire Paris
f779513042 Remove unused properties
They should have been removed as part of a6b43b93ac
2021-02-28 18:31:38 +01:00
Diego Rin Martín
07d426edf5 Changed lock function to compare timestamps instead of DateTimeInterface objects directly. (#8508)
When using optimistic lock with DateTimeInterface based version field a bug appears due to the use of the === operator for comparing the lock version and the entity version. This comparison always resolves to false because the === operator when comparing objects is only true when both sides are the exact same instance of the object.

To fix the issue I have decided to compare timestamps instead the DateTimeInterface based objects directly, calling getTimestamp() method and doing a strict comparison.

Modified OptimisticLockException to use DateTimeInterface instead of DateTime class.

Added test suite to cover case.

Fixes #8499
2021-02-27 18:40:48 +01:00
Grégoire Paris
4afd4069be Merge pull request #8515 from greg0ire/cs-20210226
CS batch 21/an estimated 26
2021-02-27 14:30:25 +01:00
Grégoire Paris
239215c2e5 Merge pull request #8502 from greg0ire/rework-contributing-md
Rework CONTRIBUTING.md
2021-02-27 13:03:20 +01:00
Grégoire Paris
e6f11652d2 Add section for 2.9.x branch 2021-02-27 12:16:49 +01:00
Grégoire Paris
2910a73927 Rework badges urls
Some .x were missing, and ugly urlencoding can be avoided.
2021-02-27 12:10:47 +01:00
Grégoire Paris
3959b2743c Remove references to Travis 2021-02-27 12:03:35 +01:00
Grégoire Paris
658e54027e Remove trailing whitespace 2021-02-27 12:01:06 +01:00
Grégoire Paris
ba882451b0 Refer to our actual coding/standard
We do much more than just PSR 1 and 2
2021-02-27 12:01:05 +01:00
Grégoire Paris
1ed9840123 Refer to global workflow policy
We are doing things differently now, and the how is already documented.
2021-02-27 12:01:05 +01:00
Grégoire Paris
71044894a1 Manually fix cs 2021-02-26 21:30:49 +01:00
Grégoire Paris
1a41d6b87c Fix configuration mix up 2021-02-26 08:25:44 +01:00
Grégoire Paris
5dfcb08999 Merge pull request #8512 from greg0ire/cs-20210225
CS batch 20/an estimated 26
2021-02-25 23:23:24 +01:00
Grégoire Paris
284bd6fd03 Manually fix cs 2021-02-25 21:22:50 +01:00
Grégoire Paris
e40ac3e1d0 Merge pull request #8510 from greg0ire/cs-20210224
CS batch 19/an estimated 26
2021-02-24 22:41:28 +01:00
Grégoire Paris
0bce2472f2 Manually fix cs 2021-02-24 18:51:02 +01:00
Grégoire Paris
89f57de884 Merge pull request #8504 from greg0ire/cs-20210223
CS batch 18/an estimated 26
2021-02-23 23:18:42 +01:00
Grégoire Paris
ae19f40958 Merge pull request #8495 from Warxcell/fix_to_iterable_with_cache
Fix bug when using Result Cache with Query::toIterable
2021-02-23 20:16:39 +01:00
Grégoire Paris
c2d69a3c48 Merge pull request #8507 from greg0ire/address-move-away-from-master
Address move away from master
2021-02-23 18:19:47 +01:00
Grégoire Paris
6ce91dd37b Address move away from master 2021-02-23 09:24:40 +01:00
Grégoire Paris
57e6ba25c9 Merge pull request #8505 from dbu/patch-1
fix typo in changelog
2021-02-23 08:56:31 +01:00
David Buchmann
9d2e67bbb4 fix typo in changelog 2021-02-23 08:17:31 +01:00
Grégoire Paris
2dce5b20ad Manually fix cs 2021-02-22 23:50:09 +01:00
Warxcell
930859f803 Fix bug when using ResultCache with Query::toIterable.
Signed-off-by: Warxcell <warxcell@gmail.com>
2021-02-22 23:35:22 +02:00
Yup
a70c73ae3a Use RegEx to match if queryPart contains OR/AND (#8453)
This allows fixes cases of queries that contain line feeds or tabs but
do not benefit from automatic wrapping of parenthesis.
2021-02-22 20:58:06 +01:00
David Buchmann
074346b8d5 Note deprecation of AbstractQuery::iterator (#8497) 2021-02-22 20:30:05 +01:00
Grégoire Paris
9ed4a8c043 Merge pull request #8498 from greg0ire/cs-20210222
CS batch 17/an estimated 26
2021-02-22 20:25:06 +01:00
Grégoire Paris
9c917811e5 Manually fix cs 2021-02-22 09:12:04 +01:00
Grégoire Paris
f883820257 Ignore rule about wrong comment style 2021-02-22 08:48:46 +01:00
Grégoire Paris
a32045dd51 Ignore rule violated by external package 2021-02-22 07:52:57 +01:00
Grégoire Paris
672b04a55d Merge pull request #8483 from olsavmic/fix-single-scalar-hydrator-memory-leak-on-exception
Fix single scalar hydrator memory leak on exception
2021-02-21 21:12:50 +01:00
Grégoire Paris
261334aca2 Merge pull request #8494 from greg0ire/cs-20210221
CS batch 16/an estimated 26
2021-02-21 20:29:47 +01:00
Warxcell
7f6ed094cd Add test to verify that using ResultCache with Query::toIterable is failing. 2021-02-21 21:12:52 +02:00
Grégoire Paris
a792655813 Manually fix cs 2021-02-21 12:20:19 +01:00
Grégoire Paris
fb71204910 Relax assertion (#8493)
EntityManager is too restrictive, any implementation can actually be
returned here.

Closes #8488
2021-02-21 07:43:56 +01:00
Grégoire Paris
b918661cf1 Merge pull request #8492 from greg0ire/cs-20210220
CS batch 15/an estimated 26
2021-02-20 20:49:25 +01:00
Michael Olšavský
7971a53164 Method hydrateAll() does not take into account possible exception
from hydrateAllData() which in turn does not call cleanup()
2021-02-20 18:58:50 +01:00
Grégoire Paris
a175f96ae8 Manually fix cs 2021-02-20 15:37:15 +01:00
Grégoire Paris
7c1cde6471 Ignore rule that triggers on external property 2021-02-20 15:32:26 +01:00
Grégoire Paris
1ffc0cacf4 Merge pull request #8491 from greg0ire/cs-20210219
CS batch 14/an estimated 26
2021-02-20 11:10:27 +01:00
Grégoire Paris
e979d0d50f Manually fix cs 2021-02-20 00:01:41 +01:00
Grégoire Paris
553ea03079 Ignore rule about case mismatch
@group does not have to do with the Group entity at all.
2021-02-19 23:33:25 +01:00
Grégoire Paris
149014879d Ignore rule about property defined externally 2021-02-19 23:11:43 +01:00
Grégoire Paris
b991c58988 Merge pull request #8490 from greg0ire/cs-20210218
CS batch 13/an estimated 26
2021-02-19 23:08:25 +01:00
Aleksandr Frolov
ee9627b82e Update QueryBuilder::setParameters docs (#8487)
Use `ArrayCollection` instead of plain array (which is supported only for bc)
2021-02-19 01:45:14 +01:00
Grégoire Paris
e3f03414f9 Manually fix cs 2021-02-18 23:17:03 +01:00
Grégoire Paris
1f406fd3df Merge pull request #8484 from greg0ire/cs-20210217
CS batch 12/an estimated 26
2021-02-18 21:54:43 +01:00
Grégoire Paris
b747bf15ff Manually fix cs 2021-02-17 16:32:39 +01:00
Grégoire Paris
5fe85bfc03 Ignore rule about underscore in method name
We inherit from a class defined in another package.
2021-02-17 15:55:21 +01:00
Grégoire Paris
0dccf05ca8 Automatically fix cs 2021-02-17 15:55:21 +01:00
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
Simon Podlipsky
f1365b78d5 Rename getIterable to toIterable (#8268) 2020-09-25 20:42:09 +02:00
Simon Podlipsky
60cd524443 Introduce getIterable() on AbstractQuery (#7885) 2020-08-14 11:12:40 +02:00
Simon Podlipsky
045d1f3bf2 Bump Doctrine CS to v6 (#8241) 2020-08-13 09:10:43 +02:00
Grégoire Paris
1ae6f18fe9 Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-08-12 22:26:19 +02:00
Zacharias Luiten
91b9dd90f4 Add DQL support for ORDER BY CASE (#8188) 2020-08-05 22:49:51 +02:00
Grégoire Paris
505d658e3d Merge remote-tracking branch 'origin/2.7' into 2.8.x 2020-06-21 13:50:48 +02:00
Grégoire Paris
bee8decd18 Require persistence 2 and common 3 (#8166)
This allows us to drop the autoload-calls-based BC layer.
2020-06-17 14:56:17 +02:00
Laurent VOULLEMIER
6bce7e9cab Add iterable support for IN criterias (#8162) 2020-06-03 18:40:53 +02:00
Grégoire Paris
4d8418fe6f Merge remote-tracking branch 'origin/2.7' into drop-persistence-bc-layer 2020-06-02 18:40:48 +02:00
Benjamin Eberlei
850d57e791 [GH-8137] Update doctrine/inflector dependency to 1.4|2.0 (#8147)
* [GH-8137] Update doctrine/inflector dependency to 1.4|2.0 and resolve deprecations.

* [GH-8137] Address review comments

* Address phpcs issues.

* More CS

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

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

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

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

* Update unit tests

* Fix doctrine persistence version to avoid deprecations changes

* Apply changes from 2.8.x

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

View File

@@ -7,26 +7,32 @@
"versions": [
{
"name": "3.0",
"branchName": "master",
"branchName": "3.0.x",
"slug": "latest",
"upcoming": true
},
{
"name": "2.9",
"branchName": "2.9.x",
"slug": "2.9",
"upcoming": true
},
{
"name": "2.8",
"branchName": "2.8.x",
"slug": "2.8",
"upcoming": true
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"current": true,
"aliases": [
"current",
"stable"
]
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"maintained": false
},
{
"name": "2.6",
"branchName": "2.6",

6
.gitattributes vendored
View File

@@ -1,10 +1,10 @@
/tests export-ignore
/tools export-ignore
/docs export-ignore
/.github export-ignore
.doctrine-project.json export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
.travis.yml export-ignore
build.properties export-ignore
build.properties.dev export-ignore
build.xml export-ignore
@@ -14,4 +14,6 @@ run-all.sh export-ignore
phpcs.xml.dist export-ignore
phpbench.json export-ignore
phpstan.neon export-ignore
phpstan-baseline.neon export-ignore
psalm.xml export-ignore
psalm-baseline.xml export-ignore

View File

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

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

@@ -0,0 +1,39 @@
name: "Coding Standards"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
jobs:
coding-standards:
name: "Coding Standards"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: "cs2pr"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
dependency-versions: "highest"
# https://github.com/doctrine/.github/issues/3
- name: "Run PHP_CodeSniffer"
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"

View File

@@ -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

View File

@@ -23,6 +23,7 @@ jobs:
"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"
@@ -34,16 +35,6 @@ jobs:
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create and/or Switch to new Release Branch"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor"
env:
"GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_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:

65
.github/workflows/static-analysis.yml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: "Static Analysis"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
jobs:
static-analysis-phpstan:
name: "Static Analysis with PHPStan"
runs-on: "ubuntu-20.04"
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"
with:
dependency-versions: "highest"
- name: "Run a static analysis with phpstan/phpstan"
run: "vendor/bin/phpstan analyse"
static-analysis-psalm:
name: "Static Analysis with Psalm"
runs-on: "ubuntu-20.04"
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"
with:
dependency-versions: "highest"
- name: "Run a static analysis with vimeo/psalm"
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"

2
.gitignore vendored
View File

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

View File

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

View File

@@ -6,30 +6,17 @@ Before we can merge your Pull-Request here are some guidelines that you need to
These guidelines exist not to annoy you, but to keep the code base clean,
unified and future proof.
## We only accept PRs to "master"
Doctrine has [general contributing guidelines][contributor workflow], make
sure you follow them.
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.
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.
[contributor workflow]: https://www.doctrine-project.org/contribute/index.html
## Coding Standard
We use PSR-1 and PSR-2:
This project follows [`doctrine/coding-standard`][coding standard homepage].
You may fix many some of the issues with `vendor/bin/phpcbf`.
* 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
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)``
[coding standard homepage]: https://github.com/doctrine/coding-standard
## Unit-Tests
@@ -56,25 +43,19 @@ curl -sS https://getcomposer.org/installer | php --
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:
take a look at the ``ci/github/phpunit`` directory 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 class.
See `https://github.com/doctrine/orm/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
See `https://github.com/doctrine/orm/tree/2.8.x/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
example.
## Travis
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
Please allow us time to review your pull requests. We will give our best to review

View File

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

View File

@@ -10,8 +10,8 @@ we cannot protect you from SQL injection.
Please read the documentation chapter on Security in Doctrine DBAL and ORM to
understand the assumptions we make.
- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst)
- [ORM Security Page](https://github.com/doctrine/orm/blob/master/docs/en/reference/security.rst)
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html)
If you find a Security bug in Doctrine, please report it on Jira and change the
Security Level to "Security Issues". It will be visible to Doctrine Core

View File

@@ -1,3 +1,15 @@
# Upgrade to 2.8
## Minor BC BREAK: Failed commit now throw OptimisticLockException
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
## Deprecated: `Doctrine\ORM\AbstractQuery#iterator()`
The method `Doctrine\ORM\AbstractQuery#iterator()` is deprecated in favor of `Doctrine\ORM\AbstractQuery#toIterable()`.
Note that `toIterable()` yields results of the query, unlike `iterator()` which yielded each result wrapped into an array.
# Upgrade to 2.7
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods
@@ -30,7 +42,7 @@ Method `Doctrine\ORM\AbstractQuery#useResultCache()` is deprecated because it is
and `disableResultCache()`. It will be removed in 3.0.
## Deprecated code generators and related console commands
These console commands have been deprecated:
* `orm:convert-mapping`

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env php
<?php
include('doctrine.php');
include(__DIR__ . '/doctrine.php');

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
<?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>
<!-- use an in-memory sqlite database -->
<var name="db_driver" value="pdo_sqlite"/>
<var name="db_memory" value="true"/>
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>

View File

@@ -13,33 +13,31 @@
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
],
"config": {
"platform": {
"php": "7.1.3"
},
"sort-packages": true
},
"require": {
"php": "^7.1",
"php": "^7.2|^8.0",
"ext-pdo": "*",
"composer/package-versions-deprecated": "^1.8",
"doctrine/annotations": "^1.8",
"doctrine/annotations": "^1.11.1",
"doctrine/cache": "^1.9.1",
"doctrine/collections": "^1.5",
"doctrine/common": "^2.11 || ^3.0",
"doctrine/dbal": "^2.9.3",
"doctrine/common": "^3.0.3",
"doctrine/dbal": "^2.10.0",
"doctrine/event-manager": "^1.1",
"doctrine/inflector": "^1.0",
"doctrine/inflector": "^1.4|^2.0",
"doctrine/instantiator": "^1.3",
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^1.3.3 || ^2.0",
"symfony/console": "^3.0|^4.0|^5.0"
"doctrine/persistence": "^2.2",
"symfony/console": "^3.0|^4.0|^5.0|^6.0"
},
"require-dev": {
"doctrine/coding-standard": "^5.0",
"phpstan/phpstan": "^0.12.18",
"phpunit/phpunit": "^7.5",
"symfony/yaml": "^3.4|^4.0|^5.0",
"vimeo/psalm": "^3.11"
"doctrine/coding-standard": "^9.0",
"phpstan/phpstan": "^0.12.83",
"phpunit/phpunit": "^8.5|^9.4",
"squizlabs/php_codesniffer": "3.6.0",
"symfony/yaml": "^3.4|^4.0|^5.0|^6.0",
"vimeo/psalm": "4.7.0"
},
"suggest": {
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
@@ -54,12 +52,7 @@
}
},
"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"]
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
}
}

3634
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -185,7 +185,7 @@ Class Table Inheritance
is an inheritance mapping strategy where each class in a hierarchy
is mapped to several tables: its own table and the tables of all
parent classes. The table of a child class is linked to the table
of a parent class through a foreign key constraint. Doctrine 2
of a parent class through a foreign key constraint. Doctrine ORM
implements this strategy through the use of a discriminator column
in the topmost table of the hierarchy because this is the easiest
way to achieve polymorphic queries with Class Table Inheritance.
@@ -289,9 +289,15 @@ column and cascading on delete.
Overrides
---------
Used to override a mapping for an entity field or relationship.
May be applied to an entity that extends a mapped superclass
to override a relationship or field mapping defined by the mapped superclass.
Used to override a mapping for an entity field or relationship. Can only be
applied to an entity that extends a mapped superclass or uses a trait to
override a relationship or field mapping defined by the mapped superclass or
trait.
It is not possible to override attributes or associations in entity to entity
inheritance scenarios, because this can cause unforseen edge case behavior and
increases complexity in ORM internal classes.
Association Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -277,10 +277,17 @@ following syntax:
.. code-block:: php
<?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\Parameter;
// $qb instanceof QueryBuilder
// Query here...
$qb->setParameters(array(1 => 'value for ?1', 2 => 'value for ?2'));
$qb->setParameters(new ArrayCollection([
new Parameter('1', 'value for ?1'),
new Parameter('2', 'value for ?2')
]));
Getting already bound parameters is easy - simply use the above
mentioned syntax with "getParameter()" or "getParameters()":
@@ -354,6 +361,7 @@ a querybuilder instance into a Query object:
// Execute Query
$result = $query->getResult();
$iterableResult = $query->toIterable();
$single = $query->getSingleResult();
$array = $query->getArrayResult();
$scalar = $query->getScalarResult();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,9 @@
Working with Indexed Associations
=================================
.. note::
This feature is available from version 2.1 of Doctrine.
Doctrine 2 collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in
Doctrine ORM collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in
the first version of Doctrine keys retrieved from the database were always numerical unless ``INDEX BY``
was used. Starting with Doctrine 2.1 you can index your collections by a value in the related entity.
was used. You can index your collections by a value in the related entity.
This is a first step towards full ordered hashmap support through the Doctrine ORM.
The feature works like an implicit ``INDEX BY`` for the selected association but has several
downsides also:
@@ -291,6 +287,5 @@ Outlook into the Future
~~~~~~~~~~~~~~~~~~~~~~~
For the inverse side of a many-to-many associations there will be a way to persist the keys and the order
as a third and fourth parameter into the join table. This feature is discussed in `DDC-213 <http://www.doctrine-project.org/jira/browse/DDC-213>`_
as a third and fourth parameter into the join table. This feature is discussed in `#2817 <https://github.com/doctrine/orm/issues/2817>`_
This feature cannot be implemented for one-to-many associations, because they are never the owning side.

View File

@@ -1,4 +1,5 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -19,25 +20,43 @@
namespace Doctrine\ORM;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Collections\Collection;
use Countable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\ORM\Cache\TimestampCacheKey;
use Doctrine\ORM\Internal\Hydration\IterableResult;
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Persistence\Mapping\MappingException;
use Traversable;
use function array_map;
use function array_shift;
use function count;
use function is_array;
use function is_numeric;
use function is_object;
use function is_scalar;
use function iterator_count;
use function iterator_to_array;
use function ksort;
use function reset;
use function serialize;
use function sha1;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/
abstract class AbstractQuery
{
@@ -46,27 +65,27 @@ abstract class AbstractQuery
/**
* Hydrates an object graph. This is the default behavior.
*/
const HYDRATE_OBJECT = 1;
public const HYDRATE_OBJECT = 1;
/**
* Hydrates an array graph.
*/
const HYDRATE_ARRAY = 2;
public const HYDRATE_ARRAY = 2;
/**
* Hydrates a flat, rectangular result set with scalar values.
*/
const HYDRATE_SCALAR = 3;
public const HYDRATE_SCALAR = 3;
/**
* Hydrates a single scalar value.
*/
const HYDRATE_SINGLE_SCALAR = 4;
public const HYDRATE_SINGLE_SCALAR = 4;
/**
* Very simple object hydrator (optimized for performance).
*/
const HYDRATE_SIMPLEOBJECT = 5;
public const HYDRATE_SIMPLEOBJECT = 5;
/**
* The parameter map of this query.
@@ -79,7 +98,7 @@ abstract class AbstractQuery
/**
* The user-specified ResultSetMapping to use.
*
* @var \Doctrine\ORM\Query\ResultSetMapping
* @var ResultSetMapping
*/
protected $_resultSetMapping;
@@ -93,7 +112,7 @@ abstract class AbstractQuery
/**
* The map of query hints.
*
* @var array
* @psalm-var array<string, mixed>
*/
protected $_hints = [];
@@ -104,33 +123,27 @@ abstract class AbstractQuery
*/
protected $_hydrationMode = self::HYDRATE_OBJECT;
/**
* @var \Doctrine\DBAL\Cache\QueryCacheProfile
*/
/** @var QueryCacheProfile|null */
protected $_queryCacheProfile;
/**
* Whether or not expire the result cache.
*
* @var boolean
* @var bool
*/
protected $_expireResultCache = false;
/**
* @var \Doctrine\DBAL\Cache\QueryCacheProfile
*/
/** @var QueryCacheProfile */
protected $_hydrationCacheProfile;
/**
* Whether to use second level cache, if available.
*
* @var boolean
* @var bool
*/
protected $cacheable = false;
/**
* @var boolean
*/
/** @var bool */
protected $hasCache = false;
/**
@@ -143,31 +156,25 @@ abstract class AbstractQuery
/**
* Second level query cache mode.
*
* @var integer|null
* @var int|null
*/
protected $cacheMode;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger|null
*/
/** @var CacheLogger|null */
protected $cacheLogger;
/**
* @var integer
*/
/** @var int */
protected $lifetime = 0;
/**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
* @param \Doctrine\ORM\EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->_em = $em;
$this->parameters = new ArrayCollection();
$this->_hints = $em->getConfiguration()->getDefaultQueryHints();
$this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
$this->_em = $em;
$this->parameters = new ArrayCollection();
$this->_hints = $em->getConfiguration()->getDefaultQueryHints();
$this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
if ($this->hasCache) {
$this->cacheLogger = $em->getConfiguration()
@@ -179,19 +186,19 @@ abstract class AbstractQuery
/**
* Enable/disable second level query (result) caching for this query.
*
* @param boolean $cacheable
* @param bool $cacheable
*
* @return static This query instance.
*/
public function setCacheable($cacheable)
{
$this->cacheable = (boolean) $cacheable;
$this->cacheable = (bool) $cacheable;
return $this;
}
/**
* @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise.
* @return bool TRUE if the query results are enable for second level cache, FALSE otherwise.
*/
public function isCacheable()
{
@@ -211,17 +218,17 @@ abstract class AbstractQuery
}
/**
* Obtain the name of the second level query cache region in which query results will be stored
*
* @return string|null The cache region name; NULL indicates the default region.
*/
* Obtain the name of the second level query cache region in which query results will be stored
*
* @return string|null The cache region name; NULL indicates the default region.
*/
public function getCacheRegion()
{
return $this->cacheRegion;
}
/**
* @return boolean TRUE if the query cache and second level cache are enabled, FALSE otherwise.
* @return bool TRUE if the query cache and second level cache are enabled, FALSE otherwise.
*/
protected function isCacheEnabled()
{
@@ -229,7 +236,7 @@ abstract class AbstractQuery
}
/**
* @return integer
* @return int
*/
public function getLifetime()
{
@@ -239,19 +246,19 @@ abstract class AbstractQuery
/**
* Sets the life-time for this query into second level cache.
*
* @param integer $lifetime
* @param int $lifetime
*
* @return static This query instance.
*/
public function setLifetime($lifetime)
{
$this->lifetime = (integer) $lifetime;
$this->lifetime = (int) $lifetime;
return $this;
}
/**
* @return integer
* @return int
*/
public function getCacheMode()
{
@@ -259,13 +266,13 @@ abstract class AbstractQuery
}
/**
* @param integer $cacheMode
* @param int $cacheMode
*
* @return static This query instance.
*/
public function setCacheMode($cacheMode)
{
$this->cacheMode = (integer) $cacheMode;
$this->cacheMode = (int) $cacheMode;
return $this;
}
@@ -282,7 +289,7 @@ abstract class AbstractQuery
/**
* Retrieves the associated EntityManager of this Query instance.
*
* @return \Doctrine\ORM\EntityManager
* @return EntityManagerInterface
*/
public function getEntityManager()
{
@@ -325,7 +332,7 @@ abstract class AbstractQuery
$key = Query\Parameter::normalizeName($key);
$filteredParameters = $this->parameters->filter(
function (Query\Parameter $parameter) use ($key) : bool {
static function (Query\Parameter $parameter) use ($key): bool {
$parameterName = $parameter->getName();
return $key === $parameterName;
@@ -339,10 +346,9 @@ abstract class AbstractQuery
* Sets a collection of query parameters.
*
* @param ArrayCollection|mixed[] $parameters
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*
* @return static This query instance.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*/
public function setParameters($parameters)
{
@@ -395,10 +401,9 @@ abstract class AbstractQuery
* @param mixed $value
*
* @return mixed[]|string|int|float|bool
*
* @throws \Doctrine\ORM\ORMInvalidArgumentException
*
* @psalm-return array|scalar
*
* @throws ORMInvalidArgumentException
*/
public function processParameterValue($value)
{
@@ -407,14 +412,11 @@ abstract class AbstractQuery
}
if ($value instanceof Collection) {
$value = $value->toArray();
$value = iterator_to_array($value);
}
if (is_array($value)) {
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
$value = $this->processArrayParameterValue($value);
return $value;
}
@@ -434,9 +436,47 @@ abstract class AbstractQuery
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
}
} catch (MappingException | ORMMappingException $e) {
// Silence any mapping exceptions. These can occur if the object in
// question is not a mapped entity, in which case we just don't do
// any preparation on the value.
/* Silence any mapping exceptions. These can occur if the object in
question is not a mapped entity, in which case we just don't do
any preparation on the value.
Depending on MappingDriver, either MappingException or
ORMMappingException is thrown. */
$value = $this->potentiallyProcessIterable($value);
}
return $value;
}
/**
* If no mapping is detected, trying to resolve the value as a Traversable
*
* @param mixed $value
*
* @return mixed
*/
private function potentiallyProcessIterable($value)
{
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
$value = $this->processArrayParameterValue($value);
}
return $value;
}
/**
* Process a parameter value which was previously identified as an array
*
* @param mixed[] $value
*
* @return mixed[]
*/
private function processArrayParameterValue(array $value): array
{
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
return $value;
@@ -445,8 +485,6 @@ abstract class AbstractQuery
/**
* Sets the ResultSetMapping that should be used for hydration.
*
* @param \Doctrine\ORM\Query\ResultSetMapping $rsm
*
* @return static This query instance.
*/
public function setResultSetMapping(Query\ResultSetMapping $rsm)
@@ -460,7 +498,7 @@ abstract class AbstractQuery
/**
* Gets the ResultSetMapping used for hydration.
*
* @return \Doctrine\ORM\Query\ResultSetMapping
* @return ResultSetMapping
*/
protected function getResultSetMapping()
{
@@ -470,17 +508,15 @@ abstract class AbstractQuery
/**
* Allows to translate entity namespaces to full qualified names.
*
* @param Query\ResultSetMapping $rsm
*
* @return void
*/
private function translateNamespaces(Query\ResultSetMapping $rsm)
{
$translate = function ($alias) : string {
$translate = function ($alias): string {
return $this->_em->getClassMetadata($alias)->getName();
};
$rsm->aliasMap = array_map($translate, $rsm->aliasMap);
$rsm->aliasMap = array_map($translate, $rsm->aliasMap);
$rsm->declaringClasses = array_map($translate, $rsm->declaringClasses);
}
@@ -496,21 +532,19 @@ abstract class AbstractQuery
* some form of caching with UnitOfWork registration you should use
* {@see AbstractQuery::setResultCacheProfile()}.
*
* @return static This query instance.
*
* @example
* $lifetime = 100;
* $resultKey = "abc";
* $query->setHydrationCacheProfile(new QueryCacheProfile());
* $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
*
* @return static This query instance.
*/
public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
public function setHydrationCacheProfile(?QueryCacheProfile $profile = null)
{
if ($profile !== null && ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_hydrationCacheProfile = $profile;
@@ -519,7 +553,7 @@ abstract class AbstractQuery
}
/**
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
* @return QueryCacheProfile
*/
public function getHydrationCacheProfile()
{
@@ -532,15 +566,13 @@ abstract class AbstractQuery
* If no result cache driver is set in the QueryCacheProfile, the default
* result cache driver is used from the configuration.
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
*
* @return static This query instance.
*/
public function setResultCacheProfile(QueryCacheProfile $profile = null)
public function setResultCacheProfile(?QueryCacheProfile $profile = null)
{
if ($profile !== null && ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_queryCacheProfile = $profile;
@@ -559,6 +591,7 @@ abstract class AbstractQuery
*/
public function setResultCacheDriver($resultCacheDriver = null)
{
/** @phpstan-ignore-next-line */
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver();
}
@@ -614,7 +647,7 @@ abstract class AbstractQuery
*
* @return static This query instance.
*/
public function enableResultCache(?int $lifetime = null, ?string $resultCacheId = null) : self
public function enableResultCache(?int $lifetime = null, ?string $resultCacheId = null): self
{
$this->setResultCacheLifetime($lifetime);
$this->setResultCacheId($resultCacheId);
@@ -627,7 +660,7 @@ abstract class AbstractQuery
*
* @return static This query instance.
*/
public function disableResultCache() : self
public function disableResultCache(): self
{
$this->_queryCacheProfile = null;
@@ -643,7 +676,7 @@ abstract class AbstractQuery
*/
public function setResultCacheLifetime($lifetime)
{
$lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
$lifetime = $lifetime !== null ? (int) $lifetime : 0;
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setLifetime($lifetime)
@@ -657,7 +690,7 @@ abstract class AbstractQuery
*
* @deprecated
*
* @return integer
* @return int
*/
public function getResultCacheLifetime()
{
@@ -667,7 +700,7 @@ abstract class AbstractQuery
/**
* Defines if the result cache is active or not.
*
* @param boolean $expire Whether or not to force resultset cache expiration.
* @param bool $expire Whether or not to force resultset cache expiration.
*
* @return static This query instance.
*/
@@ -681,7 +714,7 @@ abstract class AbstractQuery
/**
* Retrieves if the resultset cache is active or not.
*
* @return boolean
* @return bool
*/
public function getExpireResultCache()
{
@@ -689,7 +722,7 @@ abstract class AbstractQuery
}
/**
* @return QueryCacheProfile
* @return QueryCacheProfile|null
*/
public function getQueryCacheProfile()
{
@@ -762,7 +795,7 @@ abstract class AbstractQuery
*
* Alias for execute(null, HYDRATE_ARRAY).
*
* @return array
* @return mixed[]
*/
public function getArrayResult()
{
@@ -774,7 +807,7 @@ abstract class AbstractQuery
*
* Alias for execute(null, HYDRATE_SCALAR).
*
* @return array
* @return mixed[]
*/
public function getScalarResult()
{
@@ -798,17 +831,16 @@ abstract class AbstractQuery
return null;
}
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
return null;
}
if ( ! is_array($result)) {
if (! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
throw new NonUniqueResultException();
}
return array_shift($result);
@@ -834,15 +866,15 @@ abstract class AbstractQuery
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
throw new NoResultException();
}
if ( ! is_array($result)) {
if (! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
throw new NonUniqueResultException();
}
return array_shift($result);
@@ -887,7 +919,7 @@ abstract class AbstractQuery
*/
public function getHint($name)
{
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
return $this->_hints[$name] ?? false;
}
/**
@@ -905,7 +937,7 @@ abstract class AbstractQuery
/**
* Return the key value map of query hints that are currently set.
*
* @return array
* @return array<string,mixed>
*/
public function getHints()
{
@@ -916,18 +948,25 @@ abstract class AbstractQuery
* Executes the query and returns an IterableResult that can be used to incrementally
* iterate over the result.
*
* @param ArrayCollection|array|null $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
* @deprecated 2.8 Use {@see toIterable} instead. See https://github.com/doctrine/orm/issues/8463
*
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
* @param ArrayCollection|mixed[]|null $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
*
* @return IterableResult
*/
public function iterate($parameters = null, $hydrationMode = null)
{
@trigger_error(
'Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
E_USER_DEPRECATED
);
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
if ( ! empty($parameters)) {
if (! empty($parameters)) {
$this->setParameters($parameters);
}
@@ -937,11 +976,44 @@ abstract class AbstractQuery
return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints);
}
/**
* Executes the query and returns an iterable that can be used to incrementally
* iterate over the result.
*
* @param ArrayCollection|array|mixed[] $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
*
* @return iterable<mixed>
*/
public function toIterable(iterable $parameters = [], $hydrationMode = null): iterable
{
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
if (
($this->isCountable($parameters) && count($parameters) !== 0)
|| ($parameters instanceof Traversable && iterator_count($parameters) !== 0)
) {
$this->setParameters($parameters);
}
$rsm = $this->getResultSetMapping();
if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
throw QueryException::iterateWithMixedResultNotAllowed();
}
$stmt = $this->_doExecute();
return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt, $rsm, $this->_hints);
}
/**
* Executes the query.
*
* @param ArrayCollection|array|null $parameters Query parameters.
* @param string|int|null $hydrationMode Processing mode to be used during the hydration process.
* @param ArrayCollection|mixed[]|null $parameters Query parameters.
* @param string|int|null $hydrationMode Processing mode to be used during the hydration process.
*
* @return mixed
*/
@@ -957,8 +1029,8 @@ abstract class AbstractQuery
/**
* Execute query ignoring second level cache.
*
* @param ArrayCollection|array|null $parameters
* @param string|int|null $hydrationMode
* @param ArrayCollection|mixed[]|null $parameters
* @param string|int|null $hydrationMode
*
* @return mixed
*/
@@ -968,11 +1040,11 @@ abstract class AbstractQuery
$this->setHydrationMode($hydrationMode);
}
if ( ! empty($parameters)) {
if (! empty($parameters)) {
$this->setParameters($parameters);
}
$setCacheEntry = static function () : void {
$setCacheEntry = static function ($data): void {
};
if ($this->_hydrationCacheProfile !== null) {
@@ -986,11 +1058,11 @@ abstract class AbstractQuery
return $result[$realCacheKey];
}
if ( ! $result) {
if (! $result) {
$result = [];
}
$setCacheEntry = static function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) : void {
$setCacheEntry = static function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile): void {
$result[$realCacheKey] = $data;
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
@@ -1016,8 +1088,8 @@ abstract class AbstractQuery
/**
* Load from second level cache or executes the query and put into cache.
*
* @param ArrayCollection|array|null $parameters
* @param string|int|null $hydrationMode
* @param ArrayCollection|mixed[]|null $parameters
* @param string|int|null $hydrationMode
*
* @return mixed
*/
@@ -1032,7 +1104,7 @@ abstract class AbstractQuery
$this->getTimestampKey()
);
$result = $queryCache->get($queryKey, $rsm, $this->_hints);
$result = $queryCache->get($queryKey, $rsm, $this->_hints);
if ($result !== null) {
if ($this->cacheLogger) {
@@ -1057,7 +1129,7 @@ abstract class AbstractQuery
}
/**
* @return \Doctrine\ORM\Cache\TimestampCacheKey|null
* @return TimestampCacheKey|null
*/
private function getTimestampKey()
{
@@ -1130,7 +1202,9 @@ abstract class AbstractQuery
/**
* Executes the query and returns a the resulting Statement object.
*
* @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
* @return ResultStatement|int The executed database statement that holds
* the results, or an integer indicating how
* many rows were affected.
*/
abstract protected function _doExecute();
@@ -1156,10 +1230,12 @@ abstract class AbstractQuery
{
$query = $this->getSQL();
$hints = $this->getHints();
$params = array_map(function(Parameter $parameter) {
$params = array_map(function (Parameter $parameter) {
$value = $parameter->getValue();
// Small optimization
// Does not invoke processParameterValue for scalar values
if (is_scalar($value = $parameter->getValue())) {
// Does not invoke processParameterValue for scalar value
if (is_scalar($value)) {
return $value;
}
@@ -1170,4 +1246,10 @@ abstract class AbstractQuery
return sha1($query . '-' . serialize($params) . '-' . serialize($hints));
}
/** @param iterable<mixed> $subject */
private function isCountable(iterable $subject): bool
{
return $subject instanceof Countable || is_array($subject);
}
}

View File

@@ -20,44 +20,44 @@
namespace Doctrine\ORM;
use Doctrine\ORM\Cache\QueryCache;
use Doctrine\ORM\Cache\Region;
/**
* Provides an API for querying/managing the second level cache regions.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface Cache
{
const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
public const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
public const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
/**
* May read items from the cache, but will not add items.
*/
const MODE_GET = 1;
public const MODE_GET = 1;
/**
* Will never read items from the cache,
* but will add items to the cache as it reads them from the database.
*/
const MODE_PUT = 2;
public const MODE_PUT = 2;
/**
* May read items from the cache, and add items to the cache.
*/
const MODE_NORMAL = 3;
public const MODE_NORMAL = 3;
/**
* The query will never read items from the cache,
* but will refresh items to the cache as it reads them from the database.
*/
const MODE_REFRESH = 4;
public const MODE_REFRESH = 4;
/**
* @param string $className The entity class.
*
* @return \Doctrine\ORM\Cache\Region|null
* @return Region|null
*/
public function getEntityCacheRegion($className);
@@ -65,7 +65,7 @@ interface Cache
* @param string $className The entity class.
* @param string $association The field name that represents the association.
*
* @return \Doctrine\ORM\Cache\Region|null
* @return Region|null
*/
public function getCollectionCacheRegion($className, $association);
@@ -75,7 +75,7 @@ interface Cache
* @param string $className The entity class.
* @param mixed $identifier The entity identifier
*
* @return boolean true if the underlying cache contains corresponding data; false otherwise.
* @return bool true if the underlying cache contains corresponding data; false otherwise.
*/
public function containsEntity($className, $identifier);
@@ -112,7 +112,7 @@ interface Cache
* @param string $association The field name that represents the association.
* @param mixed $ownerIdentifier The identifier of the owning entity.
*
* @return boolean true if the underlying cache contains corresponding data; false otherwise.
* @return bool true if the underlying cache contains corresponding data; false otherwise.
*/
public function containsCollection($className, $association, $ownerIdentifier);
@@ -149,7 +149,7 @@ interface Cache
*
* @param string $regionName The cache name given to the query.
*
* @return boolean true if the underlying cache contains corresponding data; false otherwise.
* @return bool true if the underlying cache contains corresponding data; false otherwise.
*/
public function containsQuery($regionName);
@@ -172,7 +172,7 @@ interface Cache
*
* @param string|null $regionName Query cache region name, or default query cache if the region name is NULL.
*
* @return \Doctrine\ORM\Cache\QueryCache The Query Cache associated with the region name.
* @return QueryCache The Query Cache associated with the region name.
*/
public function getQueryCache($regionName = null);
}

View File

@@ -22,16 +22,13 @@ namespace Doctrine\ORM\Cache;
/**
* Association cache entry
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class AssociationCacheEntry implements CacheEntry
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The entity identifier
* @var array<string, mixed> The entity identifier
*/
public $identifier;
@@ -43,13 +40,13 @@ class AssociationCacheEntry implements CacheEntry
public $class;
/**
* @param string $class The entity class.
* @param array $identifier The entity identifier.
* @param string $class The entity class.
* @param array<string, mixed> $identifier The entity identifier.
*/
public function __construct($class, array $identifier)
{
$this->class = $class;
$this->identifier = $identifier;
$this->class = $class;
$this->identifier = $identifier;
}
/**
@@ -57,7 +54,7 @@ class AssociationCacheEntry implements CacheEntry
*
* This method allow Doctrine\Common\Cache\PhpFileCache compatibility
*
* @param array $values array containing property values
* @param array<string, mixed> $values array containing property values
*
* @return AssociationCacheEntry
*/

View File

@@ -24,34 +24,23 @@ use Doctrine\ORM\Cache\Logging\CacheLogger;
/**
* Configuration container for second-level cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CacheConfiguration
{
/**
* @var \Doctrine\ORM\Cache\CacheFactory|null
*/
/** @var CacheFactory|null */
private $cacheFactory;
/**
* @var \Doctrine\ORM\Cache\RegionsConfiguration|null
*/
/** @var RegionsConfiguration|null */
private $regionsConfig;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger|null
*/
/** @var CacheLogger|null */
private $cacheLogger;
/**
* @var \Doctrine\ORM\Cache\QueryCacheValidator|null
*/
/** @var QueryCacheValidator|null */
private $queryValidator;
/**
* @return \Doctrine\ORM\Cache\CacheFactory|null
* @return CacheFactory|null
*/
public function getCacheFactory()
{
@@ -59,8 +48,6 @@ class CacheConfiguration
}
/**
* @param \Doctrine\ORM\Cache\CacheFactory $factory
*
* @return void
*/
public function setCacheFactory(CacheFactory $factory)
@@ -69,23 +56,20 @@ class CacheConfiguration
}
/**
* @return \Doctrine\ORM\Cache\Logging\CacheLogger|null
* @return CacheLogger|null
*/
public function getCacheLogger()
{
return $this->cacheLogger;
}
/**
* @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger
*/
public function setCacheLogger(CacheLogger $logger)
{
$this->cacheLogger = $logger;
}
/**
* @return \Doctrine\ORM\Cache\RegionsConfiguration
* @return RegionsConfiguration
*/
public function getRegionsConfiguration()
{
@@ -96,16 +80,13 @@ class CacheConfiguration
return $this->regionsConfig;
}
/**
* @param \Doctrine\ORM\Cache\RegionsConfiguration $regionsConfig
*/
public function setRegionsConfiguration(RegionsConfiguration $regionsConfig)
{
$this->regionsConfig = $regionsConfig;
}
/**
* @return \Doctrine\ORM\Cache\QueryCacheValidator
* @return QueryCacheValidator
*/
public function getQueryValidator()
{
@@ -118,9 +99,6 @@ class CacheConfiguration
return $this->queryValidator;
}
/**
* @param \Doctrine\ORM\Cache\QueryCacheValidator $validator
*/
public function setQueryValidator(QueryCacheValidator $validator)
{
$this->queryValidator = $validator;

View File

@@ -26,11 +26,7 @@ namespace Doctrine\ORM\Cache;
* <b>IMPORTANT NOTE:</b>
*
* Fields of classes that implement CacheEntry are public for performance reason.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CacheEntry
{
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -21,11 +22,10 @@ namespace Doctrine\ORM\Cache;
use Doctrine\ORM\ORMException;
use function sprintf;
/**
* Exception for cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CacheException extends ORMException
{
@@ -33,7 +33,7 @@ class CacheException extends ORMException
* @param string $sourceEntity
* @param string $fieldName
*
* @return \Doctrine\ORM\Cache\CacheException
* @return CacheException
*/
public static function updateReadOnlyCollection($sourceEntity, $fieldName)
{
@@ -43,7 +43,7 @@ class CacheException extends ORMException
/**
* @param string $entityName
*
* @return \Doctrine\ORM\Cache\CacheException
* @return CacheException
*/
public static function updateReadOnlyEntity($entityName)
{
@@ -53,7 +53,7 @@ class CacheException extends ORMException
/**
* @param string $entityName
*
* @return \Doctrine\ORM\Cache\CacheException
* @return CacheException
*/
public static function nonCacheableEntity($entityName)
{

View File

@@ -20,93 +20,91 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
/**
* Contract for building second level cache regions components.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CacheFactory
{
/**
* Build an entity persister for the given entity metadata.
*
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister that will be cached.
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param EntityManagerInterface $em The entity manager.
* @param EntityPersister $persister The entity persister that will be cached.
* @param ClassMetadata $metadata The entity metadata.
*
* @return \Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister
* @return CachedEntityPersister
*/
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata);
/**
* Build a collection persister for the given relation mapping.
*
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached.
* @param array $mapping The association mapping.
* @param EntityManagerInterface $em The entity manager.
* @param CollectionPersister $persister The collection persister that will be cached.
* @param mixed[] $mapping The association mapping.
*
* @return \Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister
* @return CachedCollectionPersister
*/
public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping);
/**
* Build a query cache based on the given region name
*
* @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager.
* @param string $regionName The region name.
* @param EntityManagerInterface $em The Entity manager.
* @param string $regionName The region name.
*
* @return \Doctrine\ORM\Cache\QueryCache The built query cache.
* @return QueryCache The built query cache.
*/
public function buildQueryCache(EntityManagerInterface $em, $regionName = null);
/**
* Build an entity hydrator
*
* @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager.
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param EntityManagerInterface $em The Entity manager.
* @param ClassMetadata $metadata The entity metadata.
*
* @return \Doctrine\ORM\Cache\EntityHydrator The built entity hydrator.
* @return EntityHydrator The built entity hydrator.
*/
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata);
/**
* Build a collection hydrator
*
* @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager.
* @param array $mapping The association mapping.
* @param EntityManagerInterface $em The Entity manager.
* @param mixed[] $mapping The association mapping.
*
* @return \Doctrine\ORM\Cache\CollectionHydrator The built collection hydrator.
* @return CollectionHydrator The built collection hydrator.
*/
public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping);
/**
* Build a cache region
*
* @param array $cache The cache configuration.
* @param array<string,mixed> $cache The cache configuration.
*
* @return \Doctrine\ORM\Cache\Region The cache region.
* @return Region The cache region.
*/
public function getRegion(array $cache);
/**
* Build timestamp cache region
*
* @return \Doctrine\ORM\Cache\TimestampRegion The timestamp region.
* @return TimestampRegion The timestamp region.
*/
public function getTimestampRegion();
/**
* Build \Doctrine\ORM\Cache
*
* @param EntityManagerInterface $entityManager
*
* @return \Doctrine\ORM\Cache
* @return Cache
*/
public function createCache(EntityManagerInterface $entityManager);
}

View File

@@ -23,9 +23,6 @@ namespace Doctrine\ORM\Cache;
/**
* Defines entity / collection / query key to be stored in the cache region.
* Allows multiple roles to be stored in the same cache region.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
abstract class CacheKey
{

View File

@@ -22,9 +22,6 @@ namespace Doctrine\ORM\Cache;
/**
* Collection cache entry
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CollectionCacheEntry implements CacheEntry
{
@@ -48,7 +45,7 @@ class CollectionCacheEntry implements CacheEntry
*
* This method allows for Doctrine\Common\Cache\PhpFileCache compatibility
*
* @param array $values array containing property values
* @param array<string, mixed> $values array containing property values
*
* @return CollectionCacheEntry
*/

View File

@@ -20,18 +20,20 @@
namespace Doctrine\ORM\Cache;
use function implode;
use function ksort;
use function str_replace;
use function strtolower;
/**
* Defines entity collection roles to be stored in the cache region.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CollectionCacheKey extends CacheKey
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The owner entity identifier
* @var array<string, mixed> The owner entity identifier
*/
public $ownerIdentifier;
@@ -50,17 +52,17 @@ class CollectionCacheKey extends CacheKey
public $association;
/**
* @param string $entityClass The entity class.
* @param string $association The field name that represents the association.
* @param array $ownerIdentifier The identifier of the owning entity.
* @param string $entityClass The entity class.
* @param string $association The field name that represents the association.
* @param array<string, mixed> $ownerIdentifier The identifier of the owning entity.
*/
public function __construct($entityClass, $association, array $ownerIdentifier)
{
ksort($ownerIdentifier);
$this->ownerIdentifier = $ownerIdentifier;
$this->entityClass = (string) $entityClass;
$this->association = (string) $association;
$this->hash = str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association;
$this->ownerIdentifier = $ownerIdentifier;
$this->entityClass = (string) $entityClass;
$this->association = (string) $association;
$this->hash = str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association;
}
}

View File

@@ -20,33 +20,31 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
/**
* Hydrator cache entry for collections
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CollectionHydrator
{
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key.
* @param array|\Doctrine\Common\Collections\Collection $collection The collection.
* @param ClassMetadata $metadata The entity metadata.
* @param CollectionCacheKey $key The cached collection key.
* @param array|mixed[]|Collection $collection The collection.
*
* @return \Doctrine\ORM\Cache\CollectionCacheEntry
* @return CollectionCacheEntry
*/
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection);
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The owning entity metadata.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key.
* @param \Doctrine\ORM\Cache\CollectionCacheEntry $entry The cached collection entry.
* @param \Doctrine\ORM\PersistentCollection $collection The collection to load the cache into.
* @param ClassMetadata $metadata The owning entity metadata.
* @param CollectionCacheKey $key The cached collection key.
* @param CollectionCacheEntry $entry The cached collection entry.
* @param PersistentCollection $collection The collection to load the cache into.
*
* @return array
* @return mixed[]
*/
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection);
}

View File

@@ -26,32 +26,29 @@ namespace Doctrine\ORM\Cache;
*
* When a entry is locked another process should not be able to read or write the entry.
* All evict operation should not consider locks, even though an entry is locked evict should be able to delete the entry and its lock.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface ConcurrentRegion extends Region
{
/**
* Attempts to read lock the mapping for the given key.
*
* @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to lock.
* @param CacheKey $key The key of the item to lock.
*
* @return \Doctrine\ORM\Cache\Lock A lock instance or NULL if the lock already exists.
* @return Lock A lock instance or NULL if the lock already exists.
*
* @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region.
* @throws LockException Indicates a problem accessing the region.
*/
public function lock(CacheKey $key);
/**
* Attempts to read unlock the mapping for the given key.
*
* @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to unlock.
* @param \Doctrine\ORM\Cache\Lock $lock The lock previously obtained from {@link readLock}
* @param CacheKey $key The key of the item to unlock.
* @param Lock $lock The lock previously obtained from {@link readLock}
*
* @return void
*
* @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region.
* @throws LockException Indicates a problem accessing the region.
*/
public function unlock(CacheKey $key, Lock $lock);
}

View File

@@ -20,44 +20,35 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\ORMInvalidArgumentException;
use Doctrine\ORM\UnitOfWork;
use function is_array;
use function is_object;
/**
* Provides an API for querying/managing the second level cache regions.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultCache implements Cache
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* @var \Doctrine\ORM\Cache\CacheFactory
*/
/** @var CacheFactory */
private $cacheFactory;
/**
* @var \Doctrine\ORM\Cache\QueryCache[]
*/
/** @var QueryCache[] */
private $queryCaches = [];
/**
* @var \Doctrine\ORM\Cache\QueryCache
*/
/** @var QueryCache|null */
private $defaultQueryCache;
public function __construct(EntityManagerInterface $em)
@@ -77,7 +68,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return null;
}
@@ -92,7 +83,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return null;
}
@@ -104,10 +95,10 @@ class DefaultCache implements Cache
*/
public function containsEntity($className, $identifier)
{
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return false;
}
@@ -122,7 +113,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -137,7 +128,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -154,7 +145,7 @@ class DefaultCache implements Cache
foreach ($metadatas as $metadata) {
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
continue;
}
@@ -170,7 +161,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return false;
}
@@ -185,7 +176,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -200,7 +191,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -215,16 +206,14 @@ class DefaultCache implements Cache
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
foreach ($metadatas as $metadata) {
foreach ($metadata->associationMappings as $association) {
if ( ! $association['type'] & ClassMetadata::TO_MANY) {
if (! $association['type'] & ClassMetadata::TO_MANY) {
continue;
}
$persister = $this->uow->getCollectionPersister($association);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
continue;
}
@@ -279,7 +268,7 @@ class DefaultCache implements Cache
$this->defaultQueryCache = $this->cacheFactory->buildQueryCache($this->em);
}
if ( ! isset($this->queryCaches[$regionName])) {
if (! isset($this->queryCaches[$regionName])) {
$this->queryCaches[$regionName] = $this->cacheFactory->buildQueryCache($this->em, $regionName);
}
@@ -287,14 +276,14 @@ class DefaultCache implements Cache
}
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
*
* @return \Doctrine\ORM\Cache\EntityCacheKey
*/
* @param ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
*
* @return EntityCacheKey
*/
private function buildEntityCacheKey(ClassMetadata $metadata, $identifier)
{
if ( ! is_array($identifier)) {
if (! is_array($identifier)) {
$identifier = $this->toIdentifierArray($metadata, $identifier);
}
@@ -302,15 +291,15 @@ class DefaultCache implements Cache
}
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param string $association The field name that represents the association.
* @param mixed $ownerIdentifier The identifier of the owning entity.
* @param ClassMetadata $metadata The entity metadata.
* @param string $association The field name that represents the association.
* @param mixed $ownerIdentifier The identifier of the owning entity.
*
* @return \Doctrine\ORM\Cache\CollectionCacheKey
* @return CollectionCacheKey
*/
private function buildCollectionCacheKey(ClassMetadata $metadata, $association, $ownerIdentifier)
{
if ( ! is_array($ownerIdentifier)) {
if (! is_array($ownerIdentifier)) {
$ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier);
}
@@ -318,10 +307,10 @@ class DefaultCache implements Cache
}
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
* @param ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
*
* @return array
* @return array<string, mixed>
*/
private function toIdentifierArray(ClassMetadata $metadata, $identifier)
{
@@ -335,5 +324,4 @@ class DefaultCache implements Cache
return [$metadata->identifier[0] => $identifier];
}
}

View File

@@ -38,42 +38,30 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use InvalidArgumentException;
use LogicException;
use function sprintf;
use const DIRECTORY_SEPARATOR;
/**
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultCacheFactory implements CacheFactory
{
/**
* @var CacheAdapter
*/
/** @var CacheAdapter */
private $cache;
/**
* @var \Doctrine\ORM\Cache\RegionsConfiguration
*/
/** @var RegionsConfiguration */
private $regionsConfig;
/**
* @var \Doctrine\ORM\Cache\TimestampRegion|null
*/
/** @var TimestampRegion|null */
private $timestampRegion;
/**
* @var \Doctrine\ORM\Cache\Region[]
*/
/** @var Region[] */
private $regions = [];
/**
* @var string|null
*/
/** @var string|null */
private $fileLockRegionDirectory;
/**
* @param RegionsConfiguration $cacheConfig
* @param CacheAdapter $cache
*/
public function __construct(RegionsConfiguration $cacheConfig, CacheAdapter $cache)
{
$this->cache = $cache;
@@ -96,17 +84,11 @@ class DefaultCacheFactory implements CacheFactory
return $this->fileLockRegionDirectory;
}
/**
* @param \Doctrine\ORM\Cache\Region $region
*/
public function setRegion(Region $region)
{
$this->regions[$region->getName()] = $region;
}
/**
* @param \Doctrine\ORM\Cache\TimestampRegion $region
*/
public function setTimestampRegion(TimestampRegion $region)
{
$this->timestampRegion = $region;
@@ -117,8 +99,8 @@ class DefaultCacheFactory implements CacheFactory
*/
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata)
{
$region = $this->getRegion($metadata->cache);
$usage = $metadata->cache['usage'];
$region = $this->getRegion($metadata->cache);
$usage = $metadata->cache['usage'];
if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) {
return new ReadOnlyCachedEntityPersister($persister, $region, $em, $metadata);
@@ -129,10 +111,14 @@ class DefaultCacheFactory implements CacheFactory
}
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (! $region instanceof ConcurrentRegion) {
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
}
return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata);
}
throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage));
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
}
/**
@@ -140,8 +126,8 @@ class DefaultCacheFactory implements CacheFactory
*/
public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping)
{
$usage = $mapping['cache']['usage'];
$region = $this->getRegion($mapping['cache']);
$usage = $mapping['cache']['usage'];
$region = $this->getRegion($mapping['cache']);
if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) {
return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping);
@@ -152,10 +138,14 @@ class DefaultCacheFactory implements CacheFactory
}
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (! $region instanceof ConcurrentRegion) {
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
}
return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);
}
throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage));
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
}
/**
@@ -168,7 +158,7 @@ class DefaultCacheFactory implements CacheFactory
$this->getRegion(
[
'region' => $regionName ?: Cache::DEFAULT_QUERY_REGION_NAME,
'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE
'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE,
]
)
);
@@ -203,24 +193,23 @@ class DefaultCacheFactory implements CacheFactory
$cacheAdapter = $this->createRegionCache($name);
$lifetime = $this->regionsConfig->getLifetime($cache['region']);
$region = ($cacheAdapter instanceof MultiGetCache)
$region = $cacheAdapter instanceof MultiGetCache
? new DefaultMultiGetRegion($name, $cacheAdapter, $lifetime)
: new DefaultRegion($name, $cacheAdapter, $lifetime);
if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (
'' === $this->fileLockRegionDirectory ||
null === $this->fileLockRegionDirectory
$this->fileLockRegionDirectory === '' ||
$this->fileLockRegionDirectory === null
) {
throw new \LogicException(
throw new LogicException(
'If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, ' .
'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). '
);
}
$directory = $this->fileLockRegionDirectory . DIRECTORY_SEPARATOR . $cache['region'];
$region = new FileLockRegion($region, $directory, $this->regionsConfig->getLockLifetime($cache['region']));
$region = new FileLockRegion($region, $directory, (string) $this->regionsConfig->getLockLifetime($cache['region']));
}
return $this->regions[$cache['region']] = $region;
@@ -235,13 +224,13 @@ class DefaultCacheFactory implements CacheFactory
{
$cacheAdapter = clone $this->cache;
if (!$cacheAdapter instanceof CacheProvider) {
if (! $cacheAdapter instanceof CacheProvider) {
return $cacheAdapter;
}
$namespace = $cacheAdapter->getNamespace();
if ('' !== $namespace) {
if ($namespace !== '') {
$namespace .= ':';
}

View File

@@ -20,36 +20,32 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Query;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Query;
use Doctrine\ORM\UnitOfWork;
use function array_walk;
use function assert;
/**
* Default hydrator cache for collections
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultCollectionHydrator implements CollectionHydrator
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* @var array
*/
/** @var array<string,mixed> */
private static $hints = [Query::HINT_CACHE_ENABLED => true];
/**
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param EntityManagerInterface $em The entity manager.
*/
public function __construct(EntityManagerInterface $em)
{
@@ -77,23 +73,23 @@ class DefaultCollectionHydrator implements CollectionHydrator
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection)
{
$assoc = $metadata->associationMappings[$key->association];
/* @var $targetPersister \Doctrine\ORM\Cache\Persister\CachedPersister */
$targetPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
$targetRegion = $targetPersister->getCacheRegion();
$list = [];
assert($targetPersister instanceof CachedPersister);
$targetRegion = $targetPersister->getCacheRegion();
$list = [];
/** @var EntityCacheEntry[]|null $entityEntries */
$entityEntries = $targetRegion->getMultiple($entry);
if ($entityEntries === null) {
return null;
}
/* @var $entityEntries \Doctrine\ORM\Cache\EntityCacheEntry[] */
foreach ($entityEntries as $index => $entityEntry) {
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
}
array_walk($list, function($entity, $index) use ($collection) {
array_walk($list, static function ($entity, $index) use ($collection) {
$collection->hydrateSet($index, $entity);
});

View File

@@ -21,49 +21,45 @@
namespace Doctrine\ORM\Cache;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Query;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Utility\IdentifierFlattener;
use function array_merge;
use function is_array;
use function is_object;
use function reset;
/**
* Default hydrator cache for entities
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultEntityHydrator implements EntityHydrator
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* The IdentifierFlattener used for manipulating identifiers
*
* @var \Doctrine\ORM\Utility\IdentifierFlattener
* @var IdentifierFlattener
*/
private $identifierFlattener;
/**
* @var array
*/
/** @var array<string,mixed> */
private static $hints = [Query::HINT_CACHE_ENABLED => true];
/**
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param EntityManagerInterface $em The entity manager.
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$this->uow = $em->getUnitOfWork();
$this->em = $em;
$this->uow = $em->getUnitOfWork();
$this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());
}
@@ -80,19 +76,19 @@ class DefaultEntityHydrator implements EntityHydrator
}
foreach ($metadata->associationMappings as $name => $assoc) {
if ( ! isset($data[$name])) {
if (! isset($data[$name])) {
continue;
}
if ( ! ($assoc['type'] & ClassMetadata::TO_ONE)) {
if (! ($assoc['type'] & ClassMetadata::TO_ONE)) {
unset($data[$name]);
continue;
}
if ( ! isset($assoc['cache'])) {
if (! isset($assoc['cache'])) {
$targetClassMetadata = $this->em->getClassMetadata($assoc['targetEntity']);
$owningAssociation = ( ! $assoc['isOwningSide'])
$owningAssociation = ! $assoc['isOwningSide']
? $targetClassMetadata->associationMappings[$assoc['mappedBy']]
: $assoc;
$associationIds = $this->identifierFlattener->flattenIdentifier(
@@ -113,7 +109,7 @@ class DefaultEntityHydrator implements EntityHydrator
$targetAssoc = $targetClassMetadata->associationMappings[$fieldName];
foreach($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) {
foreach ($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) {
if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) {
$data[$localColumn] = $fieldValue;
}
@@ -123,7 +119,7 @@ class DefaultEntityHydrator implements EntityHydrator
continue;
}
if ( ! isset($assoc['id'])) {
if (! isset($assoc['id'])) {
$targetClass = ClassUtils::getClass($data[$name]);
$targetId = $this->uow->getEntityIdentifier($data[$name]);
$data[$name] = new AssociationCacheEntry($targetClass, $targetId);
@@ -138,7 +134,7 @@ class DefaultEntityHydrator implements EntityHydrator
// @TODO - fix it !
// handle UnitOfWork#createEntity hash generation
if ( ! is_array($targetId)) {
if (! is_array($targetId)) {
$data[reset($assoc['joinColumnFieldNames'])] = $targetId;
$targetEntity = $this->em->getClassMetadata($assoc['targetEntity']);
@@ -160,20 +156,20 @@ class DefaultEntityHydrator implements EntityHydrator
$hints = self::$hints;
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
$hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
}
foreach ($metadata->associationMappings as $name => $assoc) {
if ( ! isset($assoc['cache']) || ! isset($data[$name])) {
if (! isset($assoc['cache']) || ! isset($data[$name])) {
continue;
}
$assocClass = $data[$name]->class;
$assocId = $data[$name]->identifier;
$isEagerLoad = ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ($assoc['type'] === ClassMetadata::ONE_TO_ONE && ! $assoc['isOwningSide']));
$assocClass = $data[$name]->class;
$assocId = $data[$name]->identifier;
$isEagerLoad = ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ($assoc['type'] === ClassMetadata::ONE_TO_ONE && ! $assoc['isOwningSide']));
if ( ! $isEagerLoad) {
if (! $isEagerLoad) {
$data[$name] = $this->em->getReference($assocClass, $assocId);
continue;

View File

@@ -21,68 +21,63 @@
namespace Doctrine\ORM\Cache;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\UnitOfWork;
use function array_key_exists;
use function array_map;
use function array_shift;
use function array_unshift;
use function assert;
use function count;
use function is_array;
use function key;
use function reset;
/**
* Default query cache implementation.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultQueryCache implements QueryCache
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* @var \Doctrine\ORM\Cache\Region
*/
/** @var Region */
private $region;
/**
* @var \Doctrine\ORM\Cache\QueryCacheValidator
*/
/** @var QueryCacheValidator */
private $validator;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger
*/
/** @var CacheLogger */
protected $cacheLogger;
/**
* @var array
*/
/** @var array<string,mixed> */
private static $hints = [Query::HINT_CACHE_ENABLED => true];
/**
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param \Doctrine\ORM\Cache\Region $region The query region.
* @param EntityManagerInterface $em The entity manager.
* @param Region $region The query region.
*/
public function __construct(EntityManagerInterface $em, Region $region)
{
$cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration();
$this->em = $em;
$this->region = $region;
$this->uow = $em->getUnitOfWork();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->validator = $cacheConfig->getQueryValidator();
$this->em = $em;
$this->region = $region;
$this->uow = $em->getUnitOfWork();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->validator = $cacheConfig->getQueryValidator();
}
/**
@@ -90,17 +85,17 @@ class DefaultQueryCache implements QueryCache
*/
public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [])
{
if ( ! ($key->cacheMode & Cache::MODE_GET)) {
if (! ($key->cacheMode & Cache::MODE_GET)) {
return null;
}
$cacheEntry = $this->region->get($key);
if ( ! $cacheEntry instanceof QueryCacheEntry) {
if (! $cacheEntry instanceof QueryCacheEntry) {
return null;
}
if ( ! $this->validator->isValid($key, $cacheEntry)) {
if (! $this->validator->isValid($key, $cacheEntry)) {
$this->region->evict($key);
return null;
@@ -117,7 +112,7 @@ class DefaultQueryCache implements QueryCache
$cm = $this->em->getClassMetadata($entityName);
$generateKeys = static function (array $entry) use ($cm) : EntityCacheKey {
$generateKeys = static function (array $entry) use ($cm): EntityCacheKey {
return new EntityCacheKey($cm->rootEntityName, $entry['identifier']);
};
@@ -140,8 +135,8 @@ class DefaultQueryCache implements QueryCache
$this->cacheLogger->entityCacheHit($regionName, $cacheKeys->identifiers[$index]);
}
if ( ! $hasRelation) {
$result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
if (! $hasRelation) {
$result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
continue;
}
@@ -156,9 +151,10 @@ class DefaultQueryCache implements QueryCache
$assocMetadata = $this->em->getClassMetadata($assoc['targetEntity']);
if ($assoc['type'] & ClassMetadata::TO_ONE) {
$assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assoc['identifier']);
$assocEntry = $assocRegion->get($assocKey);
if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assoc['identifier']))) === null) {
if ($assocEntry === null) {
if ($this->cacheLogger !== null) {
$this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey);
}
@@ -177,11 +173,11 @@ class DefaultQueryCache implements QueryCache
continue;
}
if ( ! isset($assoc['list']) || empty($assoc['list'])) {
if (! isset($assoc['list']) || empty($assoc['list'])) {
continue;
}
$generateKeys = function ($id) use ($assocMetadata): EntityCacheKey {
$generateKeys = static function ($id) use ($assocMetadata): EntityCacheKey {
return new EntityCacheKey($assocMetadata->rootEntityName, $id);
};
@@ -249,29 +245,29 @@ class DefaultQueryCache implements QueryCache
public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = [])
{
if ($rsm->scalarMappings) {
throw new CacheException("Second level cache does not support scalar results.");
throw new CacheException('Second level cache does not support scalar results.');
}
if (count($rsm->entityMappings) > 1) {
throw new CacheException("Second level cache does not support multiple root entities.");
throw new CacheException('Second level cache does not support multiple root entities.');
}
if ( ! $rsm->isSelect) {
throw new CacheException("Second-level cache query supports only select statements.");
if (! $rsm->isSelect) {
throw new CacheException('Second-level cache query supports only select statements.');
}
if (($hints[Query\SqlWalker::HINT_PARTIAL] ?? false) === true || ($hints[Query::HINT_FORCE_PARTIAL_LOAD] ?? false) === true) {
throw new CacheException("Second level cache does not support partial entities.");
throw new CacheException('Second level cache does not support partial entities.');
}
if ( ! ($key->cacheMode & Cache::MODE_PUT)) {
if (! ($key->cacheMode & Cache::MODE_PUT)) {
return false;
}
$data = [];
$entityName = reset($rsm->aliasMap);
$rootAlias = key($rsm->aliasMap);
$persister = $this->uow->getEntityPersister($entityName);
$data = [];
$entityName = reset($rsm->aliasMap);
$rootAlias = key($rsm->aliasMap);
$persister = $this->uow->getEntityPersister($entityName);
if (! $persister instanceof CachedEntityPersister) {
throw CacheException::nonCacheableEntity($entityName);
@@ -288,7 +284,7 @@ class DefaultQueryCache implements QueryCache
if (($key->cacheMode & Cache::MODE_REFRESH) || ! $region->contains($entityKey)) {
// Cancel put result if entity put fail
if ( ! $persister->storeEntityCache($entity, $entityKey)) {
if (! $persister->storeEntityCache($entity, $entityKey)) {
return false;
}
}
@@ -298,11 +294,11 @@ class DefaultQueryCache implements QueryCache
// @TODO - move to cache hydration components
foreach ($rsm->relationMap as $alias => $name) {
$parentAlias = $rsm->parentAliasMap[$alias];
$parentClass = $rsm->aliasMap[$parentAlias];
$metadata = $this->em->getClassMetadata($parentClass);
$assoc = $metadata->associationMappings[$name];
$assocValue = $this->getAssociationValue($rsm, $alias, $entity);
$parentAlias = $rsm->parentAliasMap[$alias];
$parentClass = $rsm->aliasMap[$parentAlias];
$metadata = $this->em->getClassMetadata($parentClass);
$assoc = $metadata->associationMappings[$name];
$assocValue = $this->getAssociationValue($rsm, $alias, $entity);
if ($assocValue === null) {
continue;
@@ -311,7 +307,8 @@ class DefaultQueryCache implements QueryCache
// root entity association
if ($rootAlias === $parentAlias) {
// Cancel put result if association put fail
if ( ($assocInfo = $this->storeAssociationCache($key, $assoc, $assocValue)) === null) {
$assocInfo = $this->storeAssociationCache($key, $assoc, $assocValue);
if ($assocInfo === null) {
return false;
}
@@ -321,7 +318,7 @@ class DefaultQueryCache implements QueryCache
}
// store single nested association
if ( ! is_array($assocValue)) {
if (! is_array($assocValue)) {
// Cancel put result if association put fail
if ($this->storeAssociationCache($key, $assoc, $assocValue) === null) {
return false;
@@ -344,12 +341,10 @@ class DefaultQueryCache implements QueryCache
}
/**
* @param \Doctrine\ORM\Cache\QueryCacheKey $key
* @param array $assoc
* @param mixed $assocValue
* @param array<string,mixed> $assoc
* @param mixed $assocValue
*
* @return mixed[]|null
*
* @psalm-return array{targetEntity: string, type: mixed, list?: array[], identifier?: array}|null
*/
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue)
@@ -363,9 +358,9 @@ class DefaultQueryCache implements QueryCache
$assocIdentifier = $this->uow->getEntityIdentifier($assocValue);
$entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier);
if ( ! $assocValue instanceof Proxy && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
if (! $assocValue instanceof Proxy && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
// Entity put fail
if ( ! $assocPersister->storeEntityCache($assocValue, $entityKey)) {
if (! $assocPersister->storeEntityCache($assocValue, $entityKey)) {
return null;
}
}
@@ -373,7 +368,7 @@ class DefaultQueryCache implements QueryCache
return [
'targetEntity' => $assocMetadata->rootEntityName,
'identifier' => $assocIdentifier,
'type' => $assoc['type']
'type' => $assoc['type'],
];
}
@@ -386,7 +381,7 @@ class DefaultQueryCache implements QueryCache
if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
// Entity put fail
if ( ! $assocPersister->storeEntityCache($assocItem, $entityKey)) {
if (! $assocPersister->storeEntityCache($assocItem, $entityKey)) {
return null;
}
}
@@ -402,11 +397,10 @@ class DefaultQueryCache implements QueryCache
}
/**
* @param \Doctrine\ORM\Query\ResultSetMapping $rsm
* @param string $assocAlias
* @param object $entity
* @param string $assocAlias
* @param object $entity
*
* @return array|object
* @return array<object>|object
*/
private function getAssociationValue(ResultSetMapping $rsm, $assocAlias, $entity)
{
@@ -420,9 +414,8 @@ class DefaultQueryCache implements QueryCache
array_unshift($path, [
'field' => $field,
'class' => $class
]
);
'class' => $class,
]);
$alias = $parent;
}
@@ -431,10 +424,10 @@ class DefaultQueryCache implements QueryCache
}
/**
* @param mixed $value
* @param array $path
* @param mixed $value
* @param array<mixed> $path
*
* @return array|object|null
* @return mixed
*/
private function getAssociationPathValue($value, array $path)
{

View File

@@ -22,18 +22,17 @@ namespace Doctrine\ORM\Cache;
use Doctrine\ORM\EntityManagerInterface;
use function array_map;
/**
* Entity cache entry
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class EntityCacheEntry implements CacheEntry
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The entity map data
* @var array<string,mixed> The entity map data
*/
public $data;
@@ -41,12 +40,14 @@ class EntityCacheEntry implements CacheEntry
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var string The entity class name
* @psalm-var class-string
*/
public $class;
/**
* @param string $class The entity class.
* @param array $data The entity data.
* @param string $class The entity class.
* @param array<string,mixed> $data The entity data.
* @psalm-param class-string $class
*/
public function __construct($class, array $data)
{
@@ -59,7 +60,7 @@ class EntityCacheEntry implements CacheEntry
*
* This method allow Doctrine\Common\Cache\PhpFileCache compatibility
*
* @param array $values array containing property values
* @param array<string,mixed> $values array containing property values
*
* @return EntityCacheEntry
*/
@@ -71,14 +72,12 @@ class EntityCacheEntry implements CacheEntry
/**
* Retrieves the entity data resolving cache entries
*
* @param \Doctrine\ORM\EntityManagerInterface $em
*
* @return array
* @return array<string, mixed>
*/
public function resolveAssociationEntries(EntityManagerInterface $em)
{
return array_map(function($value) use ($em) {
if ( ! ($value instanceof AssociationCacheEntry)) {
return array_map(static function ($value) use ($em) {
if (! ($value instanceof AssociationCacheEntry)) {
return $value;
}

View File

@@ -20,18 +20,20 @@
namespace Doctrine\ORM\Cache;
use function implode;
use function ksort;
use function str_replace;
use function strtolower;
/**
* Defines entity classes roles to be stored in the cache region.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class EntityCacheKey extends CacheKey
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The entity identifier
* @var array<string, mixed> The entity identifier
*/
public $identifier;
@@ -43,8 +45,8 @@ class EntityCacheKey extends CacheKey
public $entityClass;
/**
* @param string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class.
* @param array $identifier The entity identifier
* @param string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class.
* @param array<string, mixed> $identifier The entity identifier
*/
public function __construct($entityClass, array $identifier)
{

View File

@@ -24,26 +24,23 @@ use Doctrine\ORM\Mapping\ClassMetadata;
/**
* Hydrator cache entry for entities
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface EntityHydrator
{
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key.
* @param object $entity The entity.
* @param ClassMetadata $metadata The entity metadata.
* @param EntityCacheKey $key The entity cache key.
* @param object $entity The entity.
*
* @return \Doctrine\ORM\Cache\EntityCacheEntry
* @return EntityCacheEntry
*/
public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity);
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key.
* @param \Doctrine\ORM\Cache\EntityCacheEntry $entry The entity cache entry.
* @param object $entity The entity to load the cache into. If not specified, a new entity is created.
* @param ClassMetadata $metadata The entity metadata.
* @param EntityCacheKey $key The entity cache key.
* @param EntityCacheEntry $entry The entity cache entry.
* @param object $entity The entity to load the cache into. If not specified, a new entity is created.
*/
public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null);
}

View File

@@ -20,27 +20,23 @@
namespace Doctrine\ORM\Cache;
use function time;
use function uniqid;
/**
* Cache Lock
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class Lock
{
/**
* @var string
*/
/** @var string */
public $value;
/**
* @var integer
*/
/** @var int */
public $time;
/**
* @param string $value
* @param integer $time
* @param string $value
* @param int $time
*/
public function __construct($value, $time = null)
{
@@ -49,7 +45,7 @@ class Lock
}
/**
* @return \Doctrine\ORM\Cache\Lock
* @return Lock
*/
public static function createLockRead()
{

View File

@@ -22,11 +22,7 @@ namespace Doctrine\ORM\Cache;
/**
* Lock exception for cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class LockException extends CacheException
{
}

View File

@@ -26,81 +26,78 @@ use Doctrine\ORM\Cache\QueryCacheKey;
/**
* Interface for logging.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CacheLogger
{
/**
* Log an entity put into second level cache.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity.
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*/
public function entityCachePut($regionName, EntityCacheKey $key);
/**
* Log an entity get from second level cache resulted in a hit.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity.
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*/
public function entityCacheHit($regionName, EntityCacheKey $key);
/**
* Log an entity get from second level cache resulted in a miss.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity.
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*/
public function entityCacheMiss($regionName, EntityCacheKey $key);
/**
* Log an entity put into second level cache.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection.
*/
* Log an entity put into second level cache.
*
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*/
public function collectionCachePut($regionName, CollectionCacheKey $key);
/**
* Log an entity get from second level cache resulted in a hit.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection.
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*/
public function collectionCacheHit($regionName, CollectionCacheKey $key);
/**
* Log an entity get from second level cache resulted in a miss.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection.
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*/
public function collectionCacheMiss($regionName, CollectionCacheKey $key);
/**
* Log a query put into the query cache.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query.
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*/
public function queryCachePut($regionName, QueryCacheKey $key);
/**
* Log a query get from the query cache resulted in a hit.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query.
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*/
public function queryCacheHit($regionName, QueryCacheKey $key);
/**
* Log a query get from the query cache resulted in a miss.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query.
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*/
public function queryCacheMiss($regionName, QueryCacheKey $key);
}

View File

@@ -26,20 +26,14 @@ use Doctrine\ORM\Cache\QueryCacheKey;
/**
* Cache logger chain
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CacheLoggerChain implements CacheLogger
{
/**
* @var array<\Doctrine\ORM\Cache\Logging\CacheLogger>
*/
/** @var array<CacheLogger> */
private $loggers = [];
/**
* @param string $name
* @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger
* @param string $name
*/
public function setLogger($name, CacheLogger $logger)
{
@@ -49,15 +43,15 @@ class CacheLoggerChain implements CacheLogger
/**
* @param string $name
*
* @return \Doctrine\ORM\Cache\Logging\CacheLogger|null
* @return CacheLogger|null
*/
public function getLogger($name)
{
return isset($this->loggers[$name]) ? $this->loggers[$name] : null;
return $this->loggers[$name] ?? null;
}
/**
* @return array<\Doctrine\ORM\Cache\Logging\CacheLogger>
* @return array<CacheLogger>
*/
public function getLoggers()
{

View File

@@ -24,27 +24,20 @@ use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\QueryCacheKey;
use function array_sum;
/**
* Provide basic second level cache statistics.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class StatisticsCacheLogger implements CacheLogger
{
/**
* @var int[]
*/
/** @var int[] */
private $cacheMissCountMap = [];
/**
* @var int[]
*/
/** @var int[] */
private $cacheHitCountMap = [];
/**
* @var int[]
*/
/** @var int[] */
private $cachePutCountMap = [];
/**
@@ -146,7 +139,7 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function getRegionHitCount($regionName)
{
return isset($this->cacheHitCountMap[$regionName]) ? $this->cacheHitCountMap[$regionName] : 0;
return $this->cacheHitCountMap[$regionName] ?? 0;
}
/**
@@ -158,7 +151,7 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function getRegionMissCount($regionName)
{
return isset($this->cacheMissCountMap[$regionName]) ? $this->cacheMissCountMap[$regionName] : 0;
return $this->cacheMissCountMap[$regionName] ?? 0;
}
/**
@@ -170,11 +163,11 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function getRegionPutCount($regionName)
{
return isset($this->cachePutCountMap[$regionName]) ? $this->cachePutCountMap[$regionName] : 0;
return $this->cachePutCountMap[$regionName] ?? 0;
}
/**
* @return array
* @return array<string, int>
*/
public function getRegionsMiss()
{
@@ -182,7 +175,7 @@ class StatisticsCacheLogger implements CacheLogger
}
/**
* @return array
* @return array<string, int>
*/
public function getRegionsHit()
{
@@ -190,7 +183,7 @@ class StatisticsCacheLogger implements CacheLogger
}
/**
* @return array
* @return array<string, int>
*/
public function getRegionsPut()
{

View File

@@ -24,9 +24,6 @@ namespace Doctrine\ORM\Cache;
* Defines a region that supports multi-get reading.
*
* With one method call we can get multiple items.
*
* @since 2.5
* @author Asmir Mustafic
*/
interface MultiGetRegion
{

View File

@@ -1,4 +1,5 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -19,11 +20,10 @@
namespace Doctrine\ORM\Cache\Persister;
use Doctrine\ORM\Cache\Region;
/**
* Interface for persister that support second level cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CachedPersister
{
@@ -40,7 +40,7 @@ interface CachedPersister
/**
* Gets the The region access.
*
* @return \Doctrine\ORM\Cache\Region
* @return Region
*/
public function getCacheRegion();
}

View File

@@ -21,99 +21,82 @@
namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache\Region;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\CollectionHydrator;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\Cache\Region;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\UnitOfWork;
use function array_values;
use function assert;
use function count;
use function is_array;
/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
abstract class AbstractCollectionPersister implements CachedCollectionPersister
{
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
protected $uow;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadataFactory
*/
/** @var ClassMetadataFactory */
protected $metadataFactory;
/**
* @var \Doctrine\ORM\Persisters\Collection\CollectionPersister
*/
/** @var CollectionPersister */
protected $persister;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
/** @var ClassMetadata */
protected $sourceEntity;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
/** @var ClassMetadata */
protected $targetEntity;
/**
* @var array
*/
/** @var mixed[] */
protected $association;
/**
* @var array
*/
/** @var mixed[] */
protected $queuedCache = [];
/**
* @var \Doctrine\ORM\Cache\Region
*/
/** @var Region */
protected $region;
/**
* @var string
*/
/** @var string */
protected $regionName;
/**
* @var \Doctrine\ORM\Cache\CollectionHydrator
*/
/** @var CollectionHydrator */
protected $hydrator;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger
*/
/** @var CacheLogger|null */
protected $cacheLogger;
/**
* @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached.
* @param \Doctrine\ORM\Cache\Region $region The collection region.
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param array $association The association mapping.
* @param CollectionPersister $persister The collection persister that will be cached.
* @param Region $region The collection region.
* @param EntityManagerInterface $em The entity manager.
* @param mixed[] $association The association mapping.
*/
public function __construct(CollectionPersister $persister, Region $region, EntityManagerInterface $em, array $association)
{
$configuration = $em->getConfiguration();
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();
$configuration = $em->getConfiguration();
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();
$this->region = $region;
$this->persister = $persister;
$this->association = $association;
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association);
$this->sourceEntity = $em->getClassMetadata($association['sourceEntity']);
$this->targetEntity = $em->getClassMetadata($association['targetEntity']);
$this->region = $region;
$this->persister = $persister;
$this->association = $association;
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association);
$this->sourceEntity = $em->getClassMetadata($association['sourceEntity']);
$this->targetEntity = $em->getClassMetadata($association['targetEntity']);
}
/**
@@ -141,22 +124,17 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
}
/**
* @param \Doctrine\ORM\PersistentCollection $collection
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key
*
* @return object[]|null
*/
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key)
{
if (($cache = $this->region->get($key)) === null) {
$cache = $this->region->get($key);
if ($cache === null) {
return null;
}
if (($cache = $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection)) === null) {
return null;
}
return $cache;
return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection);
}
/**
@@ -164,14 +142,14 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
*/
public function storeCollectionCache(CollectionCacheKey $key, $elements)
{
/* @var $targetPersister CachedEntityPersister */
$associationMapping = $this->sourceEntity->associationMappings[$key->association];
$targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName);
$targetRegion = $targetPersister->getCacheRegion();
$targetHydrator = $targetPersister->getEntityHydrator();
assert($targetPersister instanceof CachedEntityPersister);
$targetRegion = $targetPersister->getCacheRegion();
$targetHydrator = $targetPersister->getEntityHydrator();
// Only preserve ordering if association configured it
if ( ! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) {
if (! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) {
// Elements may be an array or a Collection
$elements = array_values(is_array($elements) ? $elements : $elements->getValues());
}
@@ -183,15 +161,15 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
continue;
}
$class = $this->targetEntity;
$className = ClassUtils::getClass($elements[$index]);
$class = $this->targetEntity;
$className = ClassUtils::getClass($elements[$index]);
if ($className !== $this->targetEntity->name) {
$class = $this->metadataFactory->getMetadataFor($className);
}
$entity = $elements[$index];
$entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);
$entity = $elements[$index];
$entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);
$targetRegion->put($entityKey, $entityEntry);
}
@@ -261,8 +239,6 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
/**
* Clears cache entries related to the current collection
*
* @param PersistentCollection $collection
*/
protected function evictCollectionCache(PersistentCollection $collection)
{
@@ -285,10 +261,10 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
*/
protected function evictElementCache($targetEntity, $element)
{
/* @var $targetPersister CachedEntityPersister */
$targetPersister = $this->uow->getEntityPersister($targetEntity);
$targetRegion = $targetPersister->getCacheRegion();
$key = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element));
assert($targetPersister instanceof CachedEntityPersister);
$targetRegion = $targetPersister->getCacheRegion();
$key = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element));
$targetRegion->evict($key);

View File

@@ -20,44 +20,39 @@
namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
/**
* Interface for second level cache collection persisters.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.5
*/
interface CachedCollectionPersister extends CachedPersister, CollectionPersister
{
/**
* @return \Doctrine\ORM\Mapping\ClassMetadata
* @return ClassMetadata
*/
public function getSourceEntityMetadata();
/**
* @return \Doctrine\ORM\Mapping\ClassMetadata
* @return ClassMetadata
*/
public function getTargetEntityMetadata();
/**
* Loads a collection from cache
*
* @param \Doctrine\ORM\PersistentCollection $collection
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key
*
* @return \Doctrine\ORM\PersistentCollection|null
* @return PersistentCollection|null
*/
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key);
/**
* Stores a collection into cache
*
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key
* @param array|\Doctrine\Common\Collections\Collection $elements
* @param array|mixed[]|Collection $elements
*
* @return void
*/

View File

@@ -23,10 +23,8 @@ namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\PersistentCollection;
/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.5
*/
use function spl_object_hash;
class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister
{
/**
@@ -78,7 +76,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
$isInitialized = $collection->isInitialized();
$isDirty = $collection->isDirty();
if ( ! $isInitialized && ! $isDirty) {
if (! $isInitialized && ! $isDirty) {
return;
}
@@ -98,7 +96,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
$this->queuedCache['update'][spl_object_hash($collection)] = [
'key' => $key,
'list' => $collection
'list' => $collection,
];
}
}

View File

@@ -20,19 +20,15 @@
namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Cache\CacheException;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Cache\CacheException;
use Doctrine\ORM\PersistentCollection;
/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.5
*/
class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister
{
/**
* {@inheritdoc}
*/
* {@inheritdoc}
*/
public function update(PersistentCollection $collection)
{
if ($collection->isDirty() && $collection->getSnapshot()) {

View File

@@ -20,23 +20,21 @@
namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\ConcurrentRegion;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use function spl_object_hash;
/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.5
*/
class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
{
/**
* @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached.
* @param \Doctrine\ORM\Cache\ConcurrentRegion $region The collection region.
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param array $association The association mapping.
* @param CollectionPersister $persister The collection persister that will be cached.
* @param ConcurrentRegion $region The collection region.
* @param EntityManagerInterface $em The entity manager.
* @param mixed[] $association The association mapping.
*/
public function __construct(CollectionPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, array $association)
{
@@ -100,7 +98,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
$this->queuedCache['delete'][spl_object_hash($collection)] = [
'key' => $key,
'lock' => $lock
'lock' => $lock,
];
}
@@ -112,7 +110,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
$isInitialized = $collection->isInitialized();
$isDirty = $collection->isDirty();
if ( ! $isInitialized && ! $isDirty) {
if (! $isInitialized && ! $isDirty) {
return;
}
@@ -128,7 +126,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
$this->queuedCache['update'][spl_object_hash($collection)] = [
'key' => $key,
'lock' => $lock
'lock' => $lock,
];
}
}

View File

@@ -20,117 +20,97 @@
namespace Doctrine\ORM\Cache\Persister\Entity;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Region;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\TimestampCacheKey;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\EntityHydrator;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\ORM\Cache\Region;
use Doctrine\ORM\Cache\TimestampCacheKey;
use Doctrine\ORM\Cache\TimestampRegion;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\ORM\UnitOfWork;
use function assert;
use function serialize;
use function sha1;
/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.5
*/
abstract class AbstractEntityPersister implements CachedEntityPersister
{
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
protected $uow;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadataFactory
*/
/** @var ClassMetadataFactory */
protected $metadataFactory;
/**
* @var \Doctrine\ORM\Persisters\Entity\EntityPersister
*/
/** @var EntityPersister */
protected $persister;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
/** @var ClassMetadata */
protected $class;
/**
* @var array
*/
/** @var mixed[] */
protected $queuedCache = [];
/**
* @var \Doctrine\ORM\Cache\Region
*/
/** @var Region */
protected $region;
/**
* @var \Doctrine\ORM\Cache\TimestampRegion
*/
/** @var TimestampRegion */
protected $timestampRegion;
/**
* @var \Doctrine\ORM\Cache\TimestampCacheKey
*/
/** @var TimestampCacheKey */
protected $timestampKey;
/**
* @var \Doctrine\ORM\Cache\EntityHydrator
*/
/** @var EntityHydrator */
protected $hydrator;
/**
* @var \Doctrine\ORM\Cache
*/
/** @var Cache */
protected $cache;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger
*/
/** @var CacheLogger|null */
protected $cacheLogger;
/**
* @var string
*/
/** @var string */
protected $regionName;
/**
* Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.
*
* @var array|null
* @var array<string>|null
*/
protected $joinedAssociations;
/**
* @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache.
* @param \Doctrine\ORM\Cache\Region $region The entity cache region.
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param \Doctrine\ORM\Mapping\ClassMetadata $class The entity metadata.
* @param EntityPersister $persister The entity persister to cache.
* @param Region $region The entity cache region.
* @param EntityManagerInterface $em The entity manager.
* @param ClassMetadata $class The entity metadata.
*/
public function __construct(EntityPersister $persister, Region $region, EntityManagerInterface $em, ClassMetadata $class)
{
$configuration = $em->getConfiguration();
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();
$configuration = $em->getConfiguration();
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();
$this->class = $class;
$this->region = $region;
$this->persister = $persister;
$this->cache = $em->getCache();
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->timestampRegion = $cacheFactory->getTimestampRegion();
$this->hydrator = $cacheFactory->buildEntityHydrator($em, $class);
$this->timestampKey = new TimestampCacheKey($this->class->rootEntityName);
$this->class = $class;
$this->region = $region;
$this->persister = $persister;
$this->cache = $em->getCache();
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->timestampRegion = $cacheFactory->getTimestampRegion();
$this->hydrator = $cacheFactory->buildEntityHydrator($em, $class);
$this->timestampKey = new TimestampCacheKey($this->class->rootEntityName);
}
/**
@@ -152,7 +132,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/**
* {@inheritdoc}
*/
public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null)
public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, ?array $orderBy = null)
{
return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy);
}
@@ -192,9 +172,9 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/**
* {@inheritdoc}
*/
public function exists($entity, Criteria $extraConditions = null)
public function exists($entity, ?Criteria $extraConditions = null)
{
if (null === $extraConditions) {
if ($extraConditions === null) {
$key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity));
if ($this->region->contains($key)) {
@@ -214,7 +194,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
}
/**
* @return \Doctrine\ORM\Cache\EntityHydrator
* @return EntityHydrator
*/
public function getEntityHydrator()
{
@@ -226,8 +206,8 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
*/
public function storeEntityCache($entity, EntityCacheKey $key)
{
$class = $this->class;
$className = ClassUtils::getClass($entity);
$class = $this->class;
$className = ClassUtils::getClass($entity);
if ($className !== $this->class->name) {
$class = $this->metadataFactory->getMetadataFor($className);
@@ -252,10 +232,11 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$associations = [];
foreach ($this->class->associationMappings as $name => $assoc) {
if (isset($assoc['cache']) &&
if (
isset($assoc['cache']) &&
($assoc['type'] & ClassMetadata::TO_ONE) &&
($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ! $assoc['isOwningSide'])) {
($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ! $assoc['isOwningSide'])
) {
$associations[] = $name;
}
}
@@ -291,7 +272,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
*
* @return string
*/
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
protected function getHash($query, $criteria, ?array $orderBy = null, $limit = null, $offset = null)
{
[$params] = $criteria instanceof Criteria
? $this->persister->expandCriteriaParameters($criteria)
@@ -361,7 +342,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/**
* {@inheritdoc}
*/
public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, array $orderBy = null)
public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, ?array $orderBy = null)
{
if ($entity !== null || $assoc !== null || ! empty($hints) || $lockMode !== null) {
return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);
@@ -383,7 +364,9 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
return $result[0];
}
if (($result = $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy)) === null) {
$result = $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);
if ($result === null) {
return null;
}
@@ -405,7 +388,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/**
* {@inheritdoc}
*/
public function loadAll(array $criteria = [], array $orderBy = null, $limit = null, $offset = null)
public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = null, $offset = null)
{
$query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
$hash = $this->getHash($query, $criteria, null, null, null);
@@ -469,8 +452,8 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
return null;
}
$class = $this->class;
$className = ClassUtils::getClass($entity);
$class = $this->class;
$className = ClassUtils::getClass($entity);
if ($className !== $this->class->name) {
$class = $this->metadataFactory->getMetadataFor($className);
@@ -479,7 +462,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity);
$cached = $this->region->put($cacheKey, $cacheEntry);
if ($cached && (null === $this->joinedAssociations || $this->joinedAssociations)) {
if ($cached && ($this->joinedAssociations === null || $this->joinedAssociations)) {
$this->storeJoinedAssociations($entity);
}
@@ -549,7 +532,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$persister = $this->uow->getCollectionPersister($assoc);
$hasCache = ($persister instanceof CachedPersister);
if ( ! $hasCache) {
if (! $hasCache) {
return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);
}
@@ -584,7 +567,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$persister = $this->uow->getCollectionPersister($assoc);
$hasCache = ($persister instanceof CachedPersister);
if ( ! $hasCache) {
if (! $hasCache) {
return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);
}
@@ -636,15 +619,15 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
}
/**
* @param array $association
* @param array $ownerId
* @param array<string, mixed> $association
* @param array<string, mixed> $ownerId
*
* @return CollectionCacheKey
*/
protected function buildCollectionCacheKey(array $association, $ownerId)
{
/** @var ClassMetadata $metadata */
$metadata = $this->metadataFactory->getMetadataFor($association['sourceEntity']);
assert($metadata instanceof ClassMetadata);
return new CollectionCacheKey($metadata->rootEntityName, $association['fieldName'], $ownerId);
}

View File

@@ -21,27 +21,24 @@
namespace Doctrine\ORM\Cache\Persister\Entity;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\EntityHydrator;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
/**
* Interface for second level cache entity persisters.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.5
*/
interface CachedEntityPersister extends CachedPersister, EntityPersister
{
/**
* @return \Doctrine\ORM\Cache\EntityHydrator
* @return EntityHydrator
*/
public function getEntityHydrator();
/**
* @param object $entity
* @param \Doctrine\ORM\Cache\EntityCacheKey $key
* @param object $entity
*
* @return boolean
* @return bool
*/
public function storeEntityCache($entity, EntityCacheKey $key);
}

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