mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebae57eb96 | ||
|
|
30a7c2aa67 | ||
|
|
3a9b8fde9b | ||
|
|
4f864bc178 | ||
|
|
d76cbd755f | ||
|
|
4aece04ae7 | ||
|
|
f31dbf8d4e | ||
|
|
416f35dba9 | ||
|
|
c29370e061 | ||
|
|
4e0f6837d0 | ||
|
|
e45d212f02 | ||
|
|
8f62bd39b5 | ||
|
|
5e11afcdf1 | ||
|
|
f833222017 | ||
|
|
c7f39ebbde | ||
|
|
15f08ed006 | ||
|
|
b6fd4b5ef3 | ||
|
|
5e4dae88f3 | ||
|
|
7312ddeda7 | ||
|
|
9a67b6f699 | ||
|
|
f59a0c349b | ||
|
|
4c8831f716 | ||
|
|
01ca442be7 | ||
|
|
01374ca2ab | ||
|
|
94e8b1d43c | ||
|
|
61d0f96c17 | ||
|
|
41729be80a | ||
|
|
6dbaa39016 | ||
|
|
58c95a92d1 | ||
|
|
4958180b02 | ||
|
|
8312ff0cb5 | ||
|
|
fb9b9b276e | ||
|
|
792a9a9149 | ||
|
|
0f655f9fb6 | ||
|
|
2d7acbd07f | ||
|
|
b06679cc14 | ||
|
|
2693a93aed | ||
|
|
8724589c6e | ||
|
|
424305ef38 | ||
|
|
9d01f6a45c | ||
|
|
7ed487b534 | ||
|
|
40f3925589 | ||
|
|
f92c3dba32 | ||
|
|
bcbd4401b8 | ||
|
|
d6aca8e146 | ||
|
|
8f1911a4fe | ||
|
|
7f30cd3102 | ||
|
|
f01fe3e050 | ||
|
|
210c2ee6a4 | ||
|
|
497dfd1a84 | ||
|
|
d9f0e2a27f | ||
|
|
9a40ac6e2a | ||
|
|
1687d9c479 | ||
|
|
1a46ed8901 | ||
|
|
36d0352c01 | ||
|
|
15eacd787b | ||
|
|
5b3f9bdd7b | ||
|
|
e00dba94f4 | ||
|
|
ab0e4007a5 | ||
|
|
32266c54f9 | ||
|
|
dd2120cd41 | ||
|
|
8991df0785 | ||
|
|
ca31923a39 | ||
|
|
68bc00b6c6 | ||
|
|
5b55b8c6cf | ||
|
|
10f381bc95 | ||
|
|
40aa8fe5db | ||
|
|
5801474ba3 | ||
|
|
9dbd960631 | ||
|
|
544df89055 | ||
|
|
378944dd27 | ||
|
|
8b749642cd | ||
|
|
277b53a970 | ||
|
|
2febb4509a | ||
|
|
21d2c88013 | ||
|
|
e7d33eb1a9 | ||
|
|
cab7a4558d | ||
|
|
242cf1a33d | ||
|
|
da225a0db8 | ||
|
|
3ef5a30102 | ||
|
|
418587bc25 | ||
|
|
01187c9260 | ||
|
|
35cf4810c1 | ||
|
|
404edd418b | ||
|
|
011d3c21eb | ||
|
|
3d46e07887 | ||
|
|
b1ac293a50 | ||
|
|
f4ebded63c | ||
|
|
51bc596502 | ||
|
|
95f1b48422 | ||
|
|
385b5a2f80 | ||
|
|
f7d8b155db | ||
|
|
fa6fe09647 | ||
|
|
539ffea390 | ||
|
|
c270eba678 | ||
|
|
2f0eb95c90 | ||
|
|
b13b2e8bab | ||
|
|
4bfc84f035 | ||
|
|
ca27cc3f72 | ||
|
|
53dc5b2ac3 | ||
|
|
f1219f1418 | ||
|
|
072066f746 | ||
|
|
5fde5801c1 | ||
|
|
18d96fcc02 | ||
|
|
4d2908a065 | ||
|
|
8d250f5921 | ||
|
|
59fd9b5ea7 | ||
|
|
8fcc70cfbe | ||
|
|
7d84a49980 | ||
|
|
bb64fc953d | ||
|
|
e0eb82a3b1 | ||
|
|
79cdcde9ec | ||
|
|
f4524a8bb0 | ||
|
|
f1365b78d5 | ||
|
|
d810ea4111 | ||
|
|
107ba93d79 | ||
|
|
706670215d | ||
|
|
ab2b4987b3 | ||
|
|
717ef9106c | ||
|
|
ccae8f7176 | ||
|
|
da18985aca | ||
|
|
60cd524443 | ||
|
|
045d1f3bf2 | ||
|
|
1ae6f18fe9 | ||
|
|
1e2ed07731 | ||
|
|
424241f29c | ||
|
|
8230afcde9 | ||
|
|
7cffba8743 | ||
|
|
91b9dd90f4 | ||
|
|
7e5fe79349 | ||
|
|
efd25484f4 | ||
|
|
271f3480c8 | ||
|
|
aab589b596 | ||
|
|
190218b267 | ||
|
|
181114f2c7 | ||
|
|
3689b76a86 | ||
|
|
75fe18ea5f | ||
|
|
6c73a6b720 | ||
|
|
775d91c2a3 | ||
|
|
64c3f68734 | ||
|
|
2a2a0b2980 | ||
|
|
505d658e3d | ||
|
|
a438e90046 | ||
|
|
6a670d7d6d | ||
|
|
765521d257 | ||
|
|
5ced62bf83 | ||
|
|
bee8decd18 | ||
|
|
93867f8d77 | ||
|
|
6bce7e9cab | ||
|
|
4d8418fe6f | ||
|
|
d95e03ba66 | ||
|
|
825ceb6b7a | ||
|
|
de2e2a1d74 | ||
|
|
850d57e791 | ||
|
|
c3dd71704b | ||
|
|
6780a963f7 | ||
|
|
0b305e5bd3 | ||
|
|
4d172e2591 | ||
|
|
21a98234d0 | ||
|
|
061207861b | ||
|
|
8a9954e46c | ||
|
|
527fff53cc | ||
|
|
70fb1ecd78 | ||
|
|
73ec483e9d | ||
|
|
8d67eec812 | ||
|
|
a418cf6418 | ||
|
|
6138afdca9 | ||
|
|
3c91792dd8 | ||
|
|
431d0a3c5e | ||
|
|
1da002ca2f | ||
|
|
ef639d4de6 | ||
|
|
31f4dd671a | ||
|
|
60c4867ed3 | ||
|
|
0ee1716b26 | ||
|
|
a236a83fa8 | ||
|
|
37f1bd7606 | ||
|
|
af4cb282ba | ||
|
|
ce4914ba0e | ||
|
|
bdfd6c1677 | ||
|
|
8e0157d97d | ||
|
|
1767f4b8e7 | ||
|
|
ca95b0ee13 | ||
|
|
4aa09861dd | ||
|
|
24e9a7caaf | ||
|
|
d90df59118 | ||
|
|
f9103a7b41 | ||
|
|
9891477094 | ||
|
|
59e3a55110 |
55
.doctrine-project.json
Normal file
55
.doctrine-project.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"active": true,
|
||||
"name": "Object Relational Mapper",
|
||||
"shortName": "ORM",
|
||||
"slug": "orm",
|
||||
"docsSlug": "doctrine-orm",
|
||||
"versions": [
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "master",
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.9",
|
||||
"branchName": "2.9.x",
|
||||
"slug": "2.9",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.8",
|
||||
"branchName": "2.8.x",
|
||||
"slug": "2.8",
|
||||
"current": true,
|
||||
"aliases": [
|
||||
"current",
|
||||
"stable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "2.7",
|
||||
"branchName": "2.7",
|
||||
"slug": "2.7",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.6",
|
||||
"branchName": "2.6",
|
||||
"slug": "2.6",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.5",
|
||||
"branchName": "2.5",
|
||||
"slug": "2.5",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.4",
|
||||
"branchName": "2.4",
|
||||
"slug": "2.4",
|
||||
"maintained": false
|
||||
}
|
||||
]
|
||||
}
|
||||
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -1,5 +1,7 @@
|
||||
/tests export-ignore
|
||||
/tools export-ignore
|
||||
/docs export-ignore
|
||||
/.github export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitmodules export-ignore
|
||||
@@ -11,3 +13,6 @@ CONTRIBUTING.md export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
run-all.sh export-ignore
|
||||
phpcs.xml.dist export-ignore
|
||||
phpbench.json export-ignore
|
||||
phpstan.neon export-ignore
|
||||
psalm.xml export-ignore
|
||||
|
||||
43
.github/workflows/coding-standard.yml
vendored
Normal file
43
.github/workflows/coding-standard.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: "Coding Standards"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
name: "Coding Standards"
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 10
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
|
||||
- name: "Install diff-sniffer"
|
||||
run: "wget https://github.com/diff-sniffer/diff-sniffer/releases/download/0.5.1/diff-sniffer.phar"
|
||||
|
||||
- name: "Fetch head branch"
|
||||
run: "git remote set-branches --add origin $GITHUB_BASE_REF && git fetch origin $GITHUB_BASE_REF"
|
||||
|
||||
- name: "Run diff-sniffer"
|
||||
run: "php diff-sniffer.phar origin/$GITHUB_BASE_REF...$GITHUB_SHA --report=checkstyle | cs2pr"
|
||||
|
||||
- name: "Run phpcbf"
|
||||
run: "vendor/bin/phpcbf"
|
||||
|
||||
254
.github/workflows/continuous-integration.yml
vendored
Normal file
254
.github/workflows/continuous-integration.yml
vendored
Normal 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
|
||||
46
.github/workflows/release-on-milestone-closed.yml
vendored
Normal file
46
.github/workflows/release-on-milestone-closed.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: "Automatic Releases"
|
||||
|
||||
on:
|
||||
milestone:
|
||||
types:
|
||||
- "closed"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: "Git tag, release & create merge-up PR"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Release"
|
||||
uses: "laminas/automatic-releases@v1"
|
||||
with:
|
||||
command-name: "laminas:automatic-releases:release"
|
||||
env:
|
||||
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
|
||||
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
|
||||
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
"SHELL_VERBOSITY": "3"
|
||||
|
||||
- name: "Create Merge-Up Pull Request"
|
||||
uses: "laminas/automatic-releases@v1"
|
||||
with:
|
||||
command-name: "laminas:automatic-releases:create-merge-up-pull-request"
|
||||
env:
|
||||
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
|
||||
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
|
||||
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
|
||||
- name: "Create new milestones"
|
||||
uses: "laminas/automatic-releases@v1"
|
||||
with:
|
||||
command-name: "laminas:automatic-releases:create-milestones"
|
||||
env:
|
||||
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
|
||||
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
|
||||
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
56
.github/workflows/static-analysis.yml
vendored
Normal file
56
.github/workflows/static-analysis.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Static Analysis
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
static-analysis-phpstan:
|
||||
name: "PHPStan"
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: cs2pr
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "php vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
|
||||
|
||||
static-analysis-psalm:
|
||||
name: "Psalm"
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
|
||||
- name: "Run a static analysis with vimeo/psalm"
|
||||
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,3 +14,5 @@ lib/Doctrine/DBAL
|
||||
vendor/
|
||||
/tests/Doctrine/Performance/history.db
|
||||
/.phpcs-cache
|
||||
composer.lock
|
||||
/.phpunit.result.cache
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
environment:
|
||||
php:
|
||||
version: 7.1
|
||||
cache:
|
||||
disabled: false
|
||||
directories:
|
||||
- ~/.composer/cache
|
||||
|
||||
project_setup:
|
||||
override: true
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
|
||||
before_commands:
|
||||
- "composer install --no-dev --prefer-source"
|
||||
|
||||
tools:
|
||||
external_code_coverage:
|
||||
timeout: 3600
|
||||
|
||||
filter:
|
||||
excluded_paths:
|
||||
- docs
|
||||
- tools
|
||||
|
||||
build_failure_conditions:
|
||||
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
|
||||
- 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
|
||||
- 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection
|
||||
- 'patches.label("Unused Use Statements").new.exists' # No new unused imports patches allowed
|
||||
131
.travis.yml
131
.travis.yml
@@ -1,131 +0,0 @@
|
||||
dist: trusty
|
||||
sudo: false
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- 7.4
|
||||
|
||||
env:
|
||||
- DB=sqlite
|
||||
- DB=mysql
|
||||
- DB=pgsql
|
||||
|
||||
before_install:
|
||||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
|
||||
- composer self-update
|
||||
|
||||
install: travis_retry composer validate --strict && composer update --prefer-dist
|
||||
|
||||
script:
|
||||
- if [[ "$DB" == "mysql" || "$DB" == "mariadb" ]]; then mysql -e "CREATE SCHEMA doctrine_tests; GRANT ALL PRIVILEGES ON doctrine_tests.* to travis@'%'"; fi
|
||||
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml
|
||||
- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: Test
|
||||
env: DB=mariadb
|
||||
addons:
|
||||
mariadb: 10.1
|
||||
|
||||
- stage: Test
|
||||
dist: xenial
|
||||
env: DB=mysql MYSQL_VERSION=5.7
|
||||
php: 7.1
|
||||
services:
|
||||
- mysql
|
||||
before_script:
|
||||
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
|
||||
sudo: required
|
||||
|
||||
- stage: Test
|
||||
dist: xenial
|
||||
env: DB=mysql MYSQL_VERSION=5.7
|
||||
php: 7.2
|
||||
services:
|
||||
- mysql
|
||||
before_script:
|
||||
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
|
||||
sudo: required
|
||||
|
||||
- stage: Test
|
||||
dist: xenial
|
||||
env: DB=mysql MYSQL_VERSION=5.7
|
||||
php: 7.4
|
||||
services:
|
||||
- mysql
|
||||
before_script:
|
||||
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
|
||||
sudo: required
|
||||
|
||||
- stage: Test
|
||||
env: DB=sqlite DEPENDENCIES=low
|
||||
install: travis_retry composer update --prefer-dist --prefer-lowest
|
||||
|
||||
- stage: Test
|
||||
if: type = cron
|
||||
php: 7.3
|
||||
env: DB=sqlite DEV_DEPENDENCIES
|
||||
install:
|
||||
- composer config minimum-stability dev
|
||||
- travis_retry composer update --prefer-dist
|
||||
|
||||
- stage: Test
|
||||
env: DB=sqlite COVERAGE
|
||||
before_script:
|
||||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,}
|
||||
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
|
||||
script:
|
||||
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --coverage-clover ./build/logs/clover.xml
|
||||
after_script:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml
|
||||
|
||||
- stage: Code Quality
|
||||
env: DB=none STATIC_ANALYSIS
|
||||
php: 7.4
|
||||
install: travis_retry composer install --prefer-dist
|
||||
before_script:
|
||||
- echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
- echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
- travis_retry composer require --dev --prefer-dist --prefer-stable phpstan/phpstan:^0.9
|
||||
script: vendor/bin/phpstan analyse -l 1 -c phpstan.neon lib
|
||||
|
||||
- stage: Code Quality
|
||||
env: DB=none BENCHMARK
|
||||
before_script: wget https://phpbench.github.io/phpbench/phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar.pubkey
|
||||
script: php phpbench.phar run -l dots --report=default
|
||||
|
||||
- stage: Code Quality
|
||||
if: NOT type = pull_request
|
||||
env: DB=none CODING_STANDARDS
|
||||
php: 7.4
|
||||
install: travis_retry composer install --prefer-dist
|
||||
script:
|
||||
- ./vendor/bin/phpcs
|
||||
|
||||
- stage: Code Quality
|
||||
if: type = pull_request
|
||||
env: DB=none PULL_REQUEST_CODING_STANDARDS
|
||||
php: 7.1
|
||||
install: travis_retry composer install --prefer-dist
|
||||
script:
|
||||
- |
|
||||
if [ $TRAVIS_BRANCH != "master" ]; then
|
||||
git remote set-branches --add origin $TRAVIS_BRANCH;
|
||||
git fetch origin $TRAVIS_BRANCH;
|
||||
fi
|
||||
- git merge-base origin/$TRAVIS_BRANCH $TRAVIS_PULL_REQUEST_SHA || git fetch origin +refs/pull/$TRAVIS_PULL_REQUEST/merge --unshallow
|
||||
- wget https://github.com/diff-sniffer/git/releases/download/0.2.0/git-phpcs.phar
|
||||
- php git-phpcs.phar origin/$TRAVIS_BRANCH...$TRAVIS_PULL_REQUEST_SHA
|
||||
|
||||
allow_failures:
|
||||
- stage: Code Quality
|
||||
env: DB=none CODING_STANDARDS
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
20
README.md
20
README.md
@@ -1,7 +1,7 @@
|
||||
| [Master][Master] | [2.5][2.5] |
|
||||
| [Master][Master] | [2.8.x][2.8] |
|
||||
|:----------------:|:----------:|
|
||||
| [![Build status][Master image]][Master] | [![Build status][2.5 image]][2.5] |
|
||||
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.5 coverage image]][2.5 coverage] |
|
||||
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] |
|
||||
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
|
||||
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
@@ -13,14 +13,14 @@ without requiring unnecessary code duplication.
|
||||
## More resources:
|
||||
|
||||
* [Website](http://www.doctrine-project.org)
|
||||
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
|
||||
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
|
||||
|
||||
|
||||
[Master image]: https://img.shields.io/travis/doctrine/orm/master.svg?style=flat-square
|
||||
[Master]: https://travis-ci.org/doctrine/orm
|
||||
[Master coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/master.svg?style=flat-square
|
||||
[Master coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=master
|
||||
[2.5 image]: https://img.shields.io/travis/doctrine/orm/2.5.svg?style=flat-square
|
||||
[2.5]: https://github.com/doctrine/orm/tree/2.5
|
||||
[2.5 coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/2.5.svg?style=flat-square
|
||||
[2.5 coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=2.5
|
||||
[Master coverage image]: https://codecov.io/gh/doctrine/orm/branch/master/graph/badge.svg
|
||||
[Master coverage]: https://codecov.io/gh/doctrine/orm/branch/master
|
||||
[2.8 image]: https://img.shields.io/travis/doctrine/orm/2.8.svg?style=flat-square
|
||||
[2.8]: https://github.com/doctrine/orm/tree/2.8
|
||||
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8/graph/badge.svg
|
||||
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8
|
||||
|
||||
25
UPGRADE.md
25
UPGRADE.md
@@ -1,3 +1,10 @@
|
||||
# Upgrade to 2.8
|
||||
|
||||
## Minor BC BREAK: Failed commit now throw OptimisticLockException
|
||||
|
||||
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
|
||||
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
|
||||
|
||||
# Upgrade to 2.7
|
||||
|
||||
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods
|
||||
@@ -30,7 +37,7 @@ Method `Doctrine\ORM\AbstractQuery#useResultCache()` is deprecated because it is
|
||||
and `disableResultCache()`. It will be removed in 3.0.
|
||||
|
||||
## Deprecated code generators and related console commands
|
||||
|
||||
|
||||
These console commands have been deprecated:
|
||||
|
||||
* `orm:convert-mapping`
|
||||
@@ -47,7 +54,7 @@ Whole Doctrine\ORM\Tools\Export namespace with all its members have been depreca
|
||||
## Deprecated `Doctrine\ORM\Proxy\Proxy` marker interface
|
||||
|
||||
Proxy objects in Doctrine ORM 3.0 will no longer implement `Doctrine\ORM\Proxy\Proxy` nor
|
||||
`Doctrine\Common\Persistence\Proxy`: instead, they implement
|
||||
`Doctrine\Persistence\Proxy`: instead, they implement
|
||||
`ProxyManager\Proxy\GhostObjectInterface`.
|
||||
|
||||
These related classes have been deprecated:
|
||||
@@ -439,17 +446,17 @@ above you must implement these new methods.
|
||||
|
||||
## Metadata Drivers
|
||||
|
||||
Metadata drivers have been rewritten to reuse code from Doctrine\Common. Anyone who is using the
|
||||
Metadata drivers have been rewritten to reuse code from `Doctrine\Persistence`. Anyone who is using the
|
||||
`Doctrine\ORM\Mapping\Driver\Driver` interface should instead refer to
|
||||
`Doctrine\Common\Persistence\Mapping\Driver\MappingDriver`. Same applies to
|
||||
`Doctrine\Persistence\Mapping\Driver\MappingDriver`. Same applies to
|
||||
`Doctrine\ORM\Mapping\Driver\AbstractFileDriver`: you should now refer to
|
||||
`Doctrine\Common\Persistence\Mapping\Driver\FileDriver`.
|
||||
`Doctrine\Persistence\Mapping\Driver\FileDriver`.
|
||||
|
||||
Also, following mapping drivers have been deprecated, please use their replacements in Doctrine\Common as listed:
|
||||
|
||||
* `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain`
|
||||
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\PHPDriver`
|
||||
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver`
|
||||
* `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Persistence\Mapping\Driver\MappingDriverChain`
|
||||
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Persistence\Mapping\Driver\PHPDriver`
|
||||
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Persistence\Mapping\Driver\StaticPHPDriver`
|
||||
|
||||
# Upgrade to 2.2
|
||||
|
||||
@@ -538,7 +545,7 @@ Previously EntityManager#find(null) returned null. It now throws an exception.
|
||||
|
||||
## Interface for EntityRepository
|
||||
|
||||
The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
|
||||
The EntityRepository now has an interface Doctrine\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
|
||||
|
||||
## AnnotationReader changes
|
||||
|
||||
|
||||
@@ -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" %*
|
||||
|
||||
38
ci/github/phpunit/mysqli.xml
Normal file
38
ci/github/phpunit/mysqli.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<var name="db_driver" value="mysqli"/>
|
||||
<var name="db_host" value="127.0.0.1" />
|
||||
<var name="db_port" value="3306"/>
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
39
ci/github/phpunit/pdo_mysql.xml
Normal file
39
ci/github/phpunit/pdo_mysql.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<var name="db_driver" value="pdo_mysql"/>
|
||||
<var name="db_host" value="127.0.0.1" />
|
||||
<var name="db_port" value="3306"/>
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
38
ci/github/phpunit/pdo_pgsql.xml
Normal file
38
ci/github/phpunit/pdo_pgsql.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<var name="db_driver" value="pdo_pgsql"/>
|
||||
<var name="db_host" value="localhost" />
|
||||
<var name="db_user" value="postgres" />
|
||||
<var name="db_password" value="postgres" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
32
ci/github/phpunit/sqlite.xml
Normal file
32
ci/github/phpunit/sqlite.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
@@ -16,23 +16,27 @@
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"php": "^7.2|^8.0",
|
||||
"ext-pdo": "*",
|
||||
"doctrine/annotations": "^1.8",
|
||||
"composer/package-versions-deprecated": "^1.8",
|
||||
"doctrine/annotations": "^1.11.1",
|
||||
"doctrine/cache": "^1.9.1",
|
||||
"doctrine/collections": "^1.5",
|
||||
"doctrine/common": "^2.11",
|
||||
"doctrine/dbal": "^2.9.3",
|
||||
"doctrine/common": "^3.0.3",
|
||||
"doctrine/dbal": "^2.10.0",
|
||||
"doctrine/event-manager": "^1.1",
|
||||
"doctrine/inflector": "^1.4|^2.0",
|
||||
"doctrine/instantiator": "^1.3",
|
||||
"doctrine/persistence": "^1.2",
|
||||
"ocramius/package-versions": "^1.2",
|
||||
"doctrine/lexer": "^1.0",
|
||||
"doctrine/persistence": "^2.0",
|
||||
"symfony/console": "^3.0|^4.0|^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^5.0",
|
||||
"phpunit/phpunit": "^7.5",
|
||||
"symfony/yaml": "^3.4|^4.0|^5.0"
|
||||
"doctrine/coding-standard": "^8.0",
|
||||
"phpstan/phpstan": "^0.12.18",
|
||||
"phpunit/phpunit": "^8.5|^9.4",
|
||||
"symfony/yaml": "^3.4|^4.0|^5.0",
|
||||
"vimeo/psalm": "4.1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
|
||||
2793
composer.lock
generated
2793
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,7 @@ Aggregate Fields
|
||||
You will often come across the requirement to display aggregate
|
||||
values of data that can be computed by using the MIN, MAX, COUNT or
|
||||
SUM SQL functions. For any ORM this is a tricky issue
|
||||
traditionally. Doctrine 2 offers several ways to get access to
|
||||
traditionally. Doctrine ORM offers several ways to get access to
|
||||
these values and this article will describe all of them from
|
||||
different perspectives.
|
||||
|
||||
@@ -22,7 +22,7 @@ into the account can either be of positive or negative money
|
||||
values. Each account has a credit limit and the account is never
|
||||
allowed to have a balance below that value.
|
||||
|
||||
For simplicity we live in a world were money is composed of
|
||||
For simplicity we live in a world where money is composed of
|
||||
integers only. Also we omit the receiver/sender name, stated reason
|
||||
for transfer and the execution date. These all would have to be
|
||||
added on the ``Entry`` object.
|
||||
@@ -32,30 +32,39 @@ Our entities look like:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace Bank\Entities;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Account
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/** @Column(type="string", unique=true) */
|
||||
private $no;
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
|
||||
* @ORM\Column(type="string", unique=true)
|
||||
*/
|
||||
private $entries;
|
||||
private string $no;
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
|
||||
*/
|
||||
private $maxCredit = 0;
|
||||
private array $entries;
|
||||
|
||||
public function __construct($no, $maxCredit = 0)
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private int $maxCredit = 0;
|
||||
|
||||
public function __construct(string $no, int $maxCredit = 0)
|
||||
{
|
||||
$this->no = $no;
|
||||
$this->maxCredit = $maxCredit;
|
||||
@@ -64,31 +73,35 @@ Our entities look like:
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Entry
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
private $id;
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Account", inversedBy="entries")
|
||||
* @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
|
||||
*/
|
||||
private $account;
|
||||
private Account $account;
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $amount;
|
||||
private int $amount;
|
||||
|
||||
public function __construct($account, $amount)
|
||||
public function __construct(Account $account, int $amount)
|
||||
{
|
||||
$this->account = $account;
|
||||
$this->amount = $amount;
|
||||
// more stuff here, from/to whom, stated reason, execution date and such
|
||||
}
|
||||
|
||||
public function getAmount()
|
||||
public function getAmount(): Amount
|
||||
{
|
||||
return $this->amount;
|
||||
}
|
||||
@@ -146,12 +159,14 @@ collection, which means we can compute this value at runtime:
|
||||
class Account
|
||||
{
|
||||
// .. previous code
|
||||
public function getBalance()
|
||||
|
||||
public function getBalance(): int
|
||||
{
|
||||
$balance = 0;
|
||||
foreach ($this->entries as $entry) {
|
||||
$balance += $entry->getAmount();
|
||||
}
|
||||
|
||||
return $balance;
|
||||
}
|
||||
}
|
||||
@@ -175,13 +190,12 @@ relation with this method:
|
||||
<?php
|
||||
class Account
|
||||
{
|
||||
public function addEntry($amount)
|
||||
public function addEntry(int $amount): void
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +204,10 @@ Now look at the following test-code for our entities:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class AccountTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AccountTest extends TestCase
|
||||
{
|
||||
public function testAddEntry()
|
||||
{
|
||||
@@ -208,7 +225,7 @@ Now look at the following test-code for our entities:
|
||||
{
|
||||
$account = new Account("123456", $maxCredit = 200);
|
||||
|
||||
$this->setExpectedException("Exception");
|
||||
$this->expectException(Exception::class);
|
||||
$account->addEntry(-1000);
|
||||
}
|
||||
}
|
||||
@@ -219,9 +236,12 @@ To enforce our rule we can now implement the assertion in
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class Account
|
||||
{
|
||||
private function assertAcceptEntryAllowed($amount)
|
||||
// .. previous code
|
||||
|
||||
private function assertAcceptEntryAllowed(int $amount): void
|
||||
{
|
||||
$futureBalance = $this->getBalance() + $amount;
|
||||
$allowedMinimalBalance = ($this->maxCredit * -1);
|
||||
@@ -266,23 +286,22 @@ entries collection) we want to add an aggregate field called
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $balance = 0;
|
||||
private int $balance = 0;
|
||||
|
||||
public function getBalance()
|
||||
public function getBalance(): int
|
||||
{
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
public function addEntry($amount)
|
||||
public function addEntry(int $amount): void
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
$this->balance += $amount;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,12 +325,15 @@ potentially lead to inconsistent state. See this example:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Bank\Entities\Account;
|
||||
|
||||
// The Account $accId has a balance of 0 and a max credit limit of 200:
|
||||
// request 1 account
|
||||
$account1 = $em->find('Bank\Entities\Account', $accId);
|
||||
$account1 = $em->find(Account::class, $accId);
|
||||
|
||||
// request 2 account
|
||||
$account2 = $em->find('Bank\Entities\Account', $accId);
|
||||
$account2 = $em->find(Account::class, $accId);
|
||||
|
||||
$account1->addEntry(-200);
|
||||
$account2->addEntry(-200);
|
||||
@@ -332,10 +354,14 @@ Optimistic locking is as easy as adding a version column:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class Account
|
||||
{
|
||||
/** @Column(type="integer") @Version */
|
||||
private $version;
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Version
|
||||
*/
|
||||
private int $version;
|
||||
}
|
||||
|
||||
The previous example would then throw an exception in the face of
|
||||
@@ -349,9 +375,11 @@ the database using a FOR UPDATE.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Bank\Entities\Account;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
$account = $em->find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ);
|
||||
|
||||
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ);
|
||||
|
||||
Keeping Updates and Deletes in Sync
|
||||
-----------------------------------
|
||||
@@ -372,5 +400,3 @@ field that offers serious performance benefits over iterating all
|
||||
the related objects that make up an aggregate value. Finally I
|
||||
showed how you can ensure that your aggregate fields do not get out
|
||||
of sync due to race-conditions and concurrent access.
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Extending DQL in Doctrine 2: Custom AST Walkers
|
||||
Extending DQL in Doctrine ORM: Custom AST Walkers
|
||||
===============================================
|
||||
|
||||
.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
|
||||
@@ -12,7 +12,7 @@ the Doctrine ORM.
|
||||
|
||||
In Doctrine 1 the DQL language was not implemented using a real
|
||||
parser. This made modifications of the DQL by the user impossible.
|
||||
Doctrine 2 in contrast has a real parser for the DQL language,
|
||||
Doctrine ORM in contrast has a real parser for the DQL language,
|
||||
which transforms the DQL statement into an
|
||||
`Abstract Syntax Tree <http://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
|
||||
and generates the appropriate SQL statement for it. Since this
|
||||
@@ -167,7 +167,7 @@ can be set via ``Query::setHint($name, $value)`` as shown in the
|
||||
previous example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint.
|
||||
|
||||
We will implement a custom Output Walker that allows to specify the
|
||||
SQL\_NO\_CACHE query hint.
|
||||
``SQL_NO_CACHE`` query hint.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -180,7 +180,7 @@ SQL\_NO\_CACHE query hint.
|
||||
|
||||
Our ``MysqlWalker`` will extend the default ``SqlWalker``. We will
|
||||
modify the generation of the SELECT clause, adding the
|
||||
SQL\_NO\_CACHE on those queries that need it:
|
||||
``SQL_NO_CACHE`` on those queries that need it:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ implement the ``NotifyPropertyChanged`` interface from the
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\NotifyPropertyChanged;
|
||||
use Doctrine\Common\PropertyChangedListener;
|
||||
use Doctrine\Persistence\NotifyPropertyChanged;
|
||||
use Doctrine\Persistence\PropertyChangedListener;
|
||||
|
||||
abstract class DomainObject implements NotifyPropertyChanged
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
226
docs/en/make.bat
226
docs/en/make.bat
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -619,22 +619,17 @@ Examples:
|
||||
This annotation is used in the context of relations in
|
||||
:ref:`@ManyToOne <annref_manytoone>`, :ref:`@OneToOne <annref_onetoone>` fields
|
||||
and in the Context of :ref:`@JoinTable <annref_jointable>` nested inside
|
||||
a @ManyToMany. This annotation is not required. If it is not
|
||||
specified the attributes *name* and *referencedColumnName* are
|
||||
inferred from the table and primary key names.
|
||||
|
||||
Required attributes:
|
||||
a @ManyToMany. If this annotation or both *name* and *referencedColumnName*
|
||||
are missing they will be computed considering the field's name and the current
|
||||
:doc:`naming strategy <namingstrategy>`.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **name**: Column name that holds the foreign key identifier for
|
||||
this relation. In the context of @JoinTable it specifies the column
|
||||
name in the join table.
|
||||
- **referencedColumnName**: Name of the primary key identifier that
|
||||
is used for joining of this relation.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
is used for joining of this relation. Defaults to *id*.
|
||||
- **unique**: Determines whether this relation is exclusive between the
|
||||
affected entities and should be enforced as such on the database
|
||||
constraint level. Defaults to false.
|
||||
@@ -817,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:
|
||||
@@ -1236,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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ This chapter is split into three different sections.
|
||||
|
||||
One tip for working with relations is to read the relation from left to right, where the left word refers to the current Entity. For example:
|
||||
|
||||
- OneToMany - One instance of the current Entity has Many instances (references) to the refered Entity.
|
||||
- ManyToOne - Many instances of the current Entity refer to One instance of the refered Entity.
|
||||
- OneToOne - One instance of the current Entity refers to One instance of the refered Entity.
|
||||
- OneToMany - One instance of the current Entity has Many instances (references) to the referred Entity.
|
||||
- ManyToOne - Many instances of the current Entity refer to One instance of the referred Entity.
|
||||
- OneToOne - One instance of the current Entity refers to One instance of the referred Entity.
|
||||
|
||||
See below for all the possible relations.
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ Iterating results
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
An alternative solution for bulk updates is to use the
|
||||
``Query#iterate()`` facility to iterate over the query results step
|
||||
``Query#toIterable()`` facility to iterate over the query results step
|
||||
by step instead of loading the whole result into memory at once.
|
||||
The following example shows how to do this, combining the iteration
|
||||
with the batching strategy that was already used for bulk inserts:
|
||||
@@ -86,9 +86,7 @@ with the batching strategy that was already used for bulk inserts:
|
||||
$batchSize = 20;
|
||||
$i = 1;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
foreach ($iterableResult as $row) {
|
||||
$user = $row[0];
|
||||
foreach ($q->toIterable() as $user) {
|
||||
$user->increaseCredit();
|
||||
$user->calculateNewBonuses();
|
||||
if (($i % $batchSize) === 0) {
|
||||
@@ -137,7 +135,7 @@ Iterating results
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
An alternative solution for bulk deletes is to use the
|
||||
``Query#iterate()`` facility to iterate over the query results step
|
||||
``Query#toIterable()`` facility to iterate over the query results step
|
||||
by step instead of loading the whole result into memory at once.
|
||||
The following example shows how to do this:
|
||||
|
||||
@@ -147,9 +145,8 @@ The following example shows how to do this:
|
||||
$batchSize = 20;
|
||||
$i = 1;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
while (($row = $iterableResult->next()) !== false) {
|
||||
$em->remove($row[0]);
|
||||
foreach($q->toIterable() as $row) {
|
||||
$em->remove($row);
|
||||
if (($i % $batchSize) === 0) {
|
||||
$em->flush(); // Executes all deletions.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
@@ -168,20 +165,18 @@ The following example shows how to do this:
|
||||
Iterating Large Results for Data-Processing
|
||||
-------------------------------------------
|
||||
|
||||
You can use the ``iterate()`` method just to iterate over a large
|
||||
result and no UPDATE or DELETE intention. The ``IterableResult``
|
||||
instance returned from ``$query->iterate()`` implements the
|
||||
Iterator interface so you can process a large result without memory
|
||||
You can use the ``toIterable()`` method just to iterate over a large
|
||||
result and no UPDATE or DELETE intention. ``$query->toIterable()`` returns ``iterable``
|
||||
so you can process a large result without memory
|
||||
problems using the following approach:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
foreach ($iterableResult as $row) {
|
||||
// do stuff with the data in the row, $row[0] is always the object
|
||||
|
||||
foreach ($q->toIterable() as $row) {
|
||||
// do stuff with the data in the row
|
||||
|
||||
// detach from Doctrine, so that it can be Garbage-Collected immediately
|
||||
$this->_em->detach($row[0]);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Caching
|
||||
=======
|
||||
|
||||
Doctrine provides cache drivers in the ``Common`` package for some
|
||||
of the most popular caching implementations such as APC, Memcache
|
||||
Doctrine provides cache drivers in the ``doctrine/cache`` package for some
|
||||
of the most popular caching implementations such as APCu, Memcache
|
||||
and Xcache. We also provide an ``ArrayCache`` driver which stores
|
||||
the data in a PHP array. Obviously, when using ``ArrayCache``, the
|
||||
cache does not persist between requests, but this is useful for
|
||||
@@ -306,7 +306,7 @@ the result cache.
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('select u from \Entities\User u');
|
||||
$query->useResultCache(true);
|
||||
$query->enableResultCache();
|
||||
|
||||
You can also configure an individual query to use a different
|
||||
result cache driver.
|
||||
@@ -317,19 +317,18 @@ result cache driver.
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$query->setResultCacheDriver($cacheDriver);
|
||||
|
||||
.. note::
|
||||
|
||||
Setting the result cache driver on the query will
|
||||
automatically enable the result cache for the query. If you want to
|
||||
disable it pass false to ``useResultCache()``.
|
||||
disable it use ``disableResultCache()``.
|
||||
|
||||
::
|
||||
|
||||
<?php
|
||||
$query->useResultCache(false);
|
||||
$query->disableResultCache();
|
||||
|
||||
|
||||
If you want to set the time the cache has to live you can use the
|
||||
@@ -350,12 +349,12 @@ yourself with the ``setResultCacheId()`` method.
|
||||
$query->setResultCacheId('my_custom_id');
|
||||
|
||||
You can also set the lifetime and cache ID by passing the values as
|
||||
the second and third argument to ``useResultCache()``.
|
||||
the first and second argument to ``enableResultCache()``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query->useResultCache(true, 3600, 'my_custom_id');
|
||||
$query->enableResultCache(3600, 'my_custom_id');
|
||||
|
||||
Metadata Cache
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@@ -30,7 +30,7 @@ Deferred Explicit
|
||||
|
||||
The deferred explicit policy is similar to the deferred implicit
|
||||
policy in that it detects changes through a property-by-property
|
||||
comparison at commit time. The difference is that Doctrine 2 only
|
||||
comparison at commit time. The difference is that Doctrine ORM only
|
||||
considers entities that have been explicitly marked for change detection
|
||||
through a call to EntityManager#persist(entity) or through a save
|
||||
cascade. All other entities are skipped. This policy therefore
|
||||
@@ -71,8 +71,8 @@ follows:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\NotifyPropertyChanged,
|
||||
Doctrine\Common\PropertyChangedListener;
|
||||
use Doctrine\Persistence\NotifyPropertyChanged,
|
||||
Doctrine\Persistence\PropertyChangedListener;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
|
||||
@@ -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)
|
||||
));
|
||||
|
||||
@@ -458,8 +458,6 @@ Get all users that have no phonenumber
|
||||
Get all instances of a specific type, for use with inheritance
|
||||
hierarchies:
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -469,29 +467,25 @@ hierarchies:
|
||||
|
||||
Get all users visible on a given website that have chosen certain gender:
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)');
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys:
|
||||
The IDENTITY() DQL function also works for composite primary keys
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery("SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1");
|
||||
|
||||
Joins between entities without associations were not possible until version
|
||||
2.4, where you can generate an arbitrary join with the following syntax:
|
||||
Joins between entities without associations are available,
|
||||
where you can generate an arbitrary join with the following syntax:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
|
||||
$query = $em->createQuery('SELECT u FROM User u JOIN Banlist b WITH u.email = b.email');
|
||||
|
||||
.. note::
|
||||
The differences between WHERE, WITH and HAVING clauses may be
|
||||
@@ -534,8 +528,6 @@ You use the partial syntax when joining as well:
|
||||
"NEW" Operator Syntax
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries.
|
||||
|
||||
- When using ``SELECT NEW`` you don't need to specify a mapped entity.
|
||||
@@ -657,6 +649,16 @@ The same restrictions apply for the reference of related entities.
|
||||
of the query. Additionally Deletes of specified entities are *NOT*
|
||||
cascaded to related entities even if specified in the metadata.
|
||||
|
||||
Comments in queries
|
||||
-------------------
|
||||
|
||||
We can use comments with the SQL syntax of comments.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u FROM MyProject\Model\User u
|
||||
-- my comment
|
||||
WHERE u.age > 20 -- comment at the end of a line
|
||||
|
||||
Functions, Operators, Aggregates
|
||||
--------------------------------
|
||||
@@ -900,7 +902,7 @@ Class Table Inheritance
|
||||
is an inheritance mapping strategy where each class in a hierarchy
|
||||
is mapped to several tables: its own table and the tables of all
|
||||
parent classes. The table of a child class is linked to the table
|
||||
of a parent class through a foreign key constraint. Doctrine 2
|
||||
of a parent class through a foreign key constraint. Doctrine ORM
|
||||
implements this strategy through the use of a discriminator column
|
||||
in the topmost table of the hierarchy because this is the easiest
|
||||
way to achieve polymorphic queries with Class Table Inheritance.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Events
|
||||
======
|
||||
|
||||
Doctrine 2 features a lightweight event system that is part of the
|
||||
Doctrine ORM features a lightweight event system that is part of the
|
||||
Common package. Doctrine uses it to dispatch system events, mainly
|
||||
:ref:`lifecycle events <reference-events-lifecycle-events>`.
|
||||
You can also use it for your own custom events.
|
||||
@@ -70,7 +70,7 @@ method.
|
||||
<?php
|
||||
$evm->removeEventListener(array(self::preFoo, self::postFoo), $this);
|
||||
|
||||
The Doctrine 2 event system also has a simple concept of event
|
||||
The Doctrine ORM event system also has a simple concept of event
|
||||
subscribers. We can define a simple ``TestEventSubscriber`` class
|
||||
which implements the ``\Doctrine\Common\EventSubscriber`` interface
|
||||
and implements a ``getSubscribedEvents()`` method which returns an
|
||||
@@ -124,7 +124,7 @@ Now you can test the ``$eventSubscriber`` instance to see if the
|
||||
Naming convention
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Events being used with the Doctrine 2 EventManager are best named
|
||||
Events being used with the Doctrine ORM EventManager are best named
|
||||
with camelcase and the value of the corresponding constant should
|
||||
be the name of the constant itself, even with spelling. This has
|
||||
several reasons:
|
||||
@@ -145,61 +145,65 @@ An example for a correct notation can be found in the example
|
||||
Lifecycle Events
|
||||
----------------
|
||||
|
||||
The EntityManager and UnitOfWork trigger a bunch of events during
|
||||
the life-time of their registered entities.
|
||||
The ``EntityManager`` and ``UnitOfWork`` classes trigger a bunch of
|
||||
events during the life-time of their registered entities.
|
||||
|
||||
|
||||
- preRemove - The preRemove event occurs for a given entity before
|
||||
the respective EntityManager remove operation for that entity is
|
||||
executed. It is not called for a DQL DELETE statement.
|
||||
- postRemove - The postRemove event occurs for an entity after the
|
||||
|
||||
- ``preRemove`` - The ``preRemove`` event occurs for a given entity
|
||||
before the respective ``EntityManager`` remove operation for that
|
||||
entity is executed. It is not called for a DQL ``DELETE`` statement.
|
||||
- ``postRemove`` - The ``postRemove`` event occurs for an entity after the
|
||||
entity has been deleted. It will be invoked after the database
|
||||
delete operations. It is not called for a DQL DELETE statement.
|
||||
- prePersist - The prePersist event occurs for a given entity
|
||||
before the respective EntityManager persist operation for that
|
||||
delete operations. It is not called for a DQL ``DELETE`` statement.
|
||||
- ``prePersist`` - The ``prePersist`` event occurs for a given entity
|
||||
before the respective ``EntityManager`` persist operation for that
|
||||
entity is executed. It should be noted that this event is only triggered on
|
||||
*initial* persist of an entity (i.e. it does not trigger on future updates).
|
||||
- postPersist - The postPersist event occurs for an entity after
|
||||
- ``postPersist`` - The ``postPersist`` event occurs for an entity after
|
||||
the entity has been made persistent. It will be invoked after the
|
||||
database insert operations. Generated primary key values are
|
||||
available in the postPersist event.
|
||||
- preUpdate - The preUpdate event occurs before the database
|
||||
update operations to entity data. It is not called for a DQL UPDATE statement
|
||||
nor when the computed changeset is empty.
|
||||
- postUpdate - The postUpdate event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL UPDATE statement.
|
||||
- postLoad - The postLoad event occurs for an entity after the
|
||||
entity has been loaded into the current EntityManager from the
|
||||
- ``preUpdate`` - The ``preUpdate`` event occurs before the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement nor when the computed changeset is empty.
|
||||
- ``postUpdate`` - The ``postUpdate`` event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement.
|
||||
- ``postLoad`` - The postLoad event occurs for an entity after the
|
||||
entity has been loaded into the current ``EntityManager`` from the
|
||||
database or after the refresh operation has been applied to it.
|
||||
- loadClassMetadata - The loadClassMetadata event occurs after the
|
||||
- ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(annotations/xml/yaml). This event is not a lifecycle callback.
|
||||
- onClassMetadataNotFound - Loading class metadata for a particular
|
||||
- ``onClassMetadataNotFound`` - Loading class metadata for a particular
|
||||
requested class name failed. Manipulating the given event args instance
|
||||
allows providing fallback metadata even when no actual metadata exists
|
||||
or could be found. This event is not a lifecycle callback.
|
||||
- preFlush - The preFlush event occurs at the very beginning of a flush
|
||||
operation.
|
||||
- onFlush - The onFlush event occurs after the change-sets of all
|
||||
- ``preFlush`` - The ``preFlush`` event occurs at the very beginning of
|
||||
a flush operation.
|
||||
- ``onFlush`` - The ``onFlush`` event occurs after the change-sets of all
|
||||
managed entities are computed. This event is not a lifecycle
|
||||
callback.
|
||||
- postFlush - The postFlush event occurs at the end of a flush operation. This
|
||||
- ``postFlush`` - The ``postFlush`` event occurs at the end of a flush operation. This
|
||||
event is not a lifecycle callback.
|
||||
- onClear - The onClear event occurs when the EntityManager#clear() operation is
|
||||
invoked, after all references to entities have been removed from the unit of
|
||||
work. This event is not a lifecycle callback.
|
||||
- ``onClear`` - The ``onClear`` event occurs when the
|
||||
``EntityManager#clear()`` operation is invoked, after all references
|
||||
to entities have been removed from the unit of work. This event is not
|
||||
a lifecycle callback.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that, when using ``Doctrine\ORM\AbstractQuery#iterate()``, ``postLoad``
|
||||
Note that, when using ``Doctrine\ORM\AbstractQuery#toIterable()``, ``postLoad``
|
||||
events will be executed immediately after objects are being hydrated, and therefore
|
||||
associations are not guaranteed to be initialized. It is not safe to combine
|
||||
usage of ``Doctrine\ORM\AbstractQuery#iterate()`` and ``postLoad`` event
|
||||
usage of ``Doctrine\ORM\AbstractQuery#toIterable()`` and ``postLoad`` event
|
||||
handlers.
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that the postRemove event or any events triggered after an entity removal
|
||||
Note that the ``postRemove`` event or any events triggered after an entity removal
|
||||
can receive an uninitializable proxy in case you have configured an entity to
|
||||
cascade remove relations. In this case, you should load yourself the proxy in
|
||||
the associated pre event.
|
||||
@@ -217,18 +221,18 @@ These can be hooked into by two different types of event
|
||||
listeners:
|
||||
|
||||
- Lifecycle Callbacks are methods on the entity classes that are
|
||||
called when the event is triggered. As of v2.4 they receive some kind
|
||||
called when the event is triggered. They receive some kind
|
||||
of ``EventArgs`` instance.
|
||||
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
||||
methods that receives some kind of ``EventArgs`` instance.
|
||||
|
||||
The EventArgs instance received by the listener gives access to the entity,
|
||||
EntityManager and other relevant data.
|
||||
The ``EventArgs`` instance received by the listener gives access to the entity,
|
||||
``EntityManager`` instance and other relevant data.
|
||||
|
||||
.. note::
|
||||
|
||||
All Lifecycle events that happen during the ``flush()`` of
|
||||
an EntityManager have very specific constraints on the allowed
|
||||
an ``EntityManager`` have very specific constraints on the allowed
|
||||
operations that can be executed. Please read the
|
||||
:ref:`reference-events-implementing-listeners` section very carefully
|
||||
to understand which operations are allowed in which lifecycle event.
|
||||
@@ -379,9 +383,7 @@ defined on your ``User`` model.
|
||||
Lifecycle Callbacks Event Argument
|
||||
-----------------------------------
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Since 2.4 the triggered event is given to the lifecycle-callback.
|
||||
The triggered event is also given to the lifecycle-callback.
|
||||
|
||||
With the additional argument you have access to the
|
||||
``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
|
||||
@@ -410,9 +412,9 @@ sit at a level above the entities and allow you to implement re-usable
|
||||
behaviors across different entity classes.
|
||||
|
||||
Note that they require much more detailed knowledge about the inner
|
||||
workings of the EntityManager and UnitOfWork. Please read the
|
||||
:ref:`reference-events-implementing-listeners` section carefully if you
|
||||
are trying to write your own listener.
|
||||
workings of the ``EntityManager`` and ``UnitOfWork`` classes. Please
|
||||
read the :ref:`reference-events-implementing-listeners` section
|
||||
carefully if you are trying to write your own listener.
|
||||
|
||||
For event subscribers, there are no surprises. They declare the
|
||||
lifecycle events in their ``getSubscribedEvents`` method and provide
|
||||
@@ -423,7 +425,7 @@ A lifecycle event listener looks like the following:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
class MyEventListener
|
||||
{
|
||||
@@ -445,8 +447,8 @@ A lifecycle event subscriber may look like this:
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use Doctrine\EventSubscriber;
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
class MyEventSubscriber implements EventSubscriber
|
||||
{
|
||||
@@ -501,16 +503,16 @@ Implementing Event Listeners
|
||||
----------------------------
|
||||
|
||||
This section explains what is and what is not allowed during
|
||||
specific lifecycle events of the UnitOfWork. Although you get
|
||||
passed the EntityManager in all of these events, you have to follow
|
||||
these restrictions very carefully since operations in the wrong
|
||||
event may produce lots of different errors, such as inconsistent
|
||||
specific lifecycle events of the ``UnitOfWork`` class. Although you get
|
||||
passed the ``EntityManager`` instance in all of these events, you have
|
||||
to follow these restrictions very carefully since operations in the
|
||||
wrong event may produce lots of different errors, such as inconsistent
|
||||
data and lost updates/persists/removes.
|
||||
|
||||
For the described events that are also lifecycle callback events
|
||||
the restrictions apply as well, with the additional restriction
|
||||
that (prior to version 2.4) you do not have access to the
|
||||
EntityManager or UnitOfWork APIs inside these events.
|
||||
``EntityManager`` or ``UnitOfWork`` APIs inside these events.
|
||||
|
||||
prePersist
|
||||
~~~~~~~~~~
|
||||
@@ -586,8 +588,8 @@ entities and their associations have been computed. This means, the
|
||||
- Collections scheduled for update
|
||||
- Collections scheduled for removal
|
||||
|
||||
To make use of the onFlush event you have to be familiar with the
|
||||
internal UnitOfWork API, which grants you access to the previously
|
||||
To make use of the ``onFlush`` event you have to be familiar with the
|
||||
internal ``UnitOfWork`` API, which grants you access to the previously
|
||||
mentioned sets. See this example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -735,7 +737,7 @@ Restrictions for this event:
|
||||
the event to modify primitive field values, e.g. use
|
||||
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
|
||||
- Any calls to ``EntityManager#persist()`` or
|
||||
``EntityManager#remove()``, even in combination with the UnitOfWork
|
||||
``EntityManager#remove()``, even in combination with the ``UnitOfWork``
|
||||
API are strongly discouraged and don't work as expected outside the
|
||||
flush operation.
|
||||
|
||||
@@ -757,8 +759,6 @@ EntityManager.
|
||||
Entity listeners
|
||||
----------------
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
An entity listener is a lifecycle listener class used for an entity.
|
||||
|
||||
- The entity listener's mapping may be applied to an entity class or mapped superclass.
|
||||
@@ -991,4 +991,3 @@ process and manipulate the instance.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -20,12 +20,17 @@ Metadata and Query caches
|
||||
|
||||
As already mentioned earlier in the chapter about configuring
|
||||
Doctrine, it is strongly discouraged to use Doctrine without a
|
||||
Metadata and Query cache (preferably with APC or Memcache as the
|
||||
cache driver). Operating Doctrine without these caches means
|
||||
Metadata and Query cache.
|
||||
|
||||
Operating Doctrine without these caches means
|
||||
Doctrine will need to load your mapping information on every single
|
||||
request and has to parse each DQL query on every single request.
|
||||
This is a waste of resources.
|
||||
|
||||
The preferred cache driver for metadata and query caches is ``PhpFileCache``.
|
||||
This driver serializes cache items and writes them to a file.
|
||||
This allows for opcode caching to be used and provides high performance in most scenarios.
|
||||
|
||||
See :ref:`integrating-with-the-orm`
|
||||
|
||||
Alternative Query Result Formats
|
||||
@@ -38,7 +43,7 @@ in scenarios where data is loaded for read-only purposes.
|
||||
Read-Only Entities
|
||||
------------------
|
||||
|
||||
Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping
|
||||
You can mark entities as read only (See metadata mapping
|
||||
references for details). This means that the entity marked as read only is never considered
|
||||
for updates, which means when you call flush on the EntityManager these entities are skipped
|
||||
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing
|
||||
|
||||
@@ -185,7 +185,7 @@ Class Table Inheritance
|
||||
is an inheritance mapping strategy where each class in a hierarchy
|
||||
is mapped to several tables: its own table and the tables of all
|
||||
parent classes. The table of a child class is linked to the table
|
||||
of a parent class through a foreign key constraint. Doctrine 2
|
||||
of a parent class through a foreign key constraint. Doctrine ORM
|
||||
implements this strategy through the use of a discriminator column
|
||||
in the topmost table of the hierarchy because this is the easiest
|
||||
way to achieve polymorphic queries with Class Table Inheritance.
|
||||
@@ -289,9 +289,15 @@ column and cascading on delete.
|
||||
|
||||
Overrides
|
||||
---------
|
||||
Used to override a mapping for an entity field or relationship.
|
||||
May be applied to an entity that extends a mapped superclass
|
||||
to override a relationship or field mapping defined by the mapped superclass.
|
||||
|
||||
Used to override a mapping for an entity field or relationship. Can only be
|
||||
applied to an entity that extends a mapped superclass or uses a trait to
|
||||
override a relationship or field mapping defined by the mapped superclass or
|
||||
trait.
|
||||
|
||||
It is not possible to override attributes or associations in entity to entity
|
||||
inheritance scenarios, because this can cause unforseen edge case behavior and
|
||||
increases complexity in ORM internal classes.
|
||||
|
||||
|
||||
Association Override
|
||||
@@ -584,7 +590,7 @@ Things to note:
|
||||
|
||||
- The "attribute override" specifies the overrides base on the property name.
|
||||
- The column type *CANNOT* be changed. If the column type is not equal you get a ``MappingException``
|
||||
- The override can redefine all the columns except the type.
|
||||
- The override can redefine all the attributes except the type.
|
||||
|
||||
Query the Type
|
||||
--------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_``).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -255,6 +255,21 @@ and for managed entities. If you want to set a type explicitly you can call
|
||||
the third argument to ``setParameter()`` explicitly. It accepts either a PDO
|
||||
type or a DBAL Type name for conversion.
|
||||
|
||||
.. note::
|
||||
|
||||
Even though passing DateTime instance is allowed, it impacts performance
|
||||
as by default there is an attempt to load metadata for object, and if it's not found,
|
||||
type is inferred from the original value.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
|
||||
// prevents attempt to load metadata for date time class, improving performance
|
||||
$qb->setParameter('date', new \DateTimeImmutable(), Types::DATE_IMMUTABLE)
|
||||
|
||||
If you've got several parameters to bind to your query, you can
|
||||
also use setParameters() instead of setParameter() with the
|
||||
following syntax:
|
||||
@@ -339,6 +354,7 @@ a querybuilder instance into a Query object:
|
||||
|
||||
// Execute Query
|
||||
$result = $query->getResult();
|
||||
$iterableResult = $query->toIterable();
|
||||
$single = $query->getSingleResult();
|
||||
$array = $query->getArrayResult();
|
||||
$scalar = $query->getScalarResult();
|
||||
@@ -581,4 +597,3 @@ same query of example 6 written using
|
||||
->add('from', new Expr\From('User', 'u'))
|
||||
->add('where', new Expr\Comparison('u.id', '=', '?1'))
|
||||
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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``.
|
||||
|
||||
@@ -27,7 +27,7 @@ Work that have not yet been persisted are lost.
|
||||
|
||||
.. note::
|
||||
|
||||
Doctrine does NEVER touch the public API of methods in your entity
|
||||
Doctrine does NEVER touch the public API of methods in your entity
|
||||
classes (like getters and setters) nor the constructor method.
|
||||
Instead, it uses reflection to get/set data from/to your entity objects.
|
||||
When Doctrine fetches data from DB and saves it back,
|
||||
@@ -48,12 +48,12 @@ headline "Hello World" with the ID 1234:
|
||||
<?php
|
||||
$article = $entityManager->find('CMS\Article', 1234);
|
||||
$article->setHeadline('Hello World dude!');
|
||||
|
||||
|
||||
$article2 = $entityManager->find('CMS\Article', 1234);
|
||||
echo $article2->getHeadline();
|
||||
|
||||
In this case the Article is accessed from the entity manager twice,
|
||||
but modified in between. Doctrine 2 realizes this and will only
|
||||
but modified in between. Doctrine ORM realizes this and will only
|
||||
ever give you access to one instance of the Article with ID 1234,
|
||||
no matter how often do you retrieve it from the EntityManager and
|
||||
even no matter what kind of Query method you are using (find,
|
||||
@@ -100,25 +100,25 @@ from newly opened EntityManager.
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
|
||||
/** @Column(type="string") */
|
||||
private $headline;
|
||||
|
||||
|
||||
/** @ManyToOne(targetEntity="User") */
|
||||
private $author;
|
||||
|
||||
|
||||
/** @OneToMany(targetEntity="Comment", mappedBy="article") */
|
||||
private $comments;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
|
||||
public function getAuthor() { return $this->author; }
|
||||
public function getComments() { return $this->comments; }
|
||||
}
|
||||
|
||||
|
||||
$article = $em->find('Article', 1);
|
||||
|
||||
This code only retrieves the ``Article`` instance with id 1 executing
|
||||
@@ -139,22 +139,22 @@ your code. See the following code:
|
||||
|
||||
<?php
|
||||
$article = $em->find('Article', 1);
|
||||
|
||||
|
||||
// accessing a method of the user instance triggers the lazy-load
|
||||
echo "Author: " . $article->getAuthor()->getName() . "\n";
|
||||
|
||||
|
||||
// Lazy Loading Proxies pass instanceof tests:
|
||||
if ($article->getAuthor() instanceof User) {
|
||||
// a User Proxy is a generated "UserProxy" class
|
||||
}
|
||||
|
||||
|
||||
// accessing the comments as an iterator triggers the lazy-load
|
||||
// retrieving ALL the comments of this article from the database
|
||||
// using a single SELECT statement
|
||||
foreach ($article->getComments() as $comment) {
|
||||
echo $comment->getText() . "\n\n";
|
||||
}
|
||||
|
||||
|
||||
// Article::$comments passes instanceof tests for the Collection interface
|
||||
// But it will NOT pass for the ArrayCollection interface
|
||||
if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) {
|
||||
@@ -174,7 +174,7 @@ methods along the lines of the ``getName()`` method shown below:
|
||||
{
|
||||
// lazy loading code
|
||||
}
|
||||
|
||||
|
||||
public function getName()
|
||||
{
|
||||
$this->_load();
|
||||
@@ -250,6 +250,12 @@ as follows:
|
||||
- If X is a detached entity, an exception will be thrown on
|
||||
flush.
|
||||
|
||||
.. caution::
|
||||
|
||||
Do not pass detached entities to the persist operation. The persist operation always
|
||||
considers entities that are not yet known to the ``EntityManager`` as new entities
|
||||
(refer to the ``STATE_NEW`` constant inside the ``UnitOfWork``).
|
||||
|
||||
Removing entities
|
||||
-----------------
|
||||
|
||||
@@ -269,7 +275,7 @@ which means that its persistent state will be deleted once
|
||||
for and appear in query and collection results. See
|
||||
the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
|
||||
for more information.
|
||||
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
@@ -312,10 +318,10 @@ 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()\`.
|
||||
``EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()``.
|
||||
In both cases the cascade remove semantics are applied recursively.
|
||||
For large object graphs this removal strategy can be very costly.
|
||||
2. Using a DQL ``DELETE`` statement allows you to delete multiple
|
||||
@@ -330,6 +336,13 @@ in multiple ways with very different performance impacts.
|
||||
because Doctrine will fetch and remove all associated entities
|
||||
explicitly nevertheless.
|
||||
|
||||
.. note::
|
||||
|
||||
Calling ``remove`` on an entity will remove the object from the identiy
|
||||
map and therefore detach it. Querying the same entity again, for example
|
||||
via a lazy loaded relation, will return a new object.
|
||||
|
||||
|
||||
Detaching entities
|
||||
------------------
|
||||
|
||||
@@ -640,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.
|
||||
|
||||
@@ -687,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'));
|
||||
|
||||
@@ -729,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');
|
||||
|
||||
@@ -744,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.
|
||||
@@ -787,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();
|
||||
@@ -832,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()
|
||||
@@ -859,7 +870,7 @@ You can access your repository now by calling:
|
||||
|
||||
<?php
|
||||
// $em instanceof EntityManager
|
||||
|
||||
|
||||
$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@ or address are the primary use case for this feature.
|
||||
|
||||
.. note::
|
||||
|
||||
Embeddables can not contain references to entities. They can however compose
|
||||
other embeddables in addition to holding properties with basic ``@Column``
|
||||
mapping.
|
||||
Embeddables can only contain properties with basic ``@Column`` mapping.
|
||||
|
||||
For the purposes of this tutorial, we will assume that you have a ``User``
|
||||
class in your application and you would like to store an address in
|
||||
|
||||
@@ -5,19 +5,19 @@ Extra Lazy Associations
|
||||
|
||||
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog.
|
||||
where posts can be commented, you always have to assume that a post draws hundreds of comments.
|
||||
In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This
|
||||
In Doctrine ORM if you accessed an association it would always get loaded completely into memory. This
|
||||
can lead to pretty serious performance problems, if your associations contain several hundreds or thousands
|
||||
of entities.
|
||||
|
||||
With Doctrine 2.1 a feature called **Extra Lazy** is introduced for associations. Associations
|
||||
Doctrine ORM includes a feature called **Extra Lazy** for associations. Associations
|
||||
are marked as **Lazy** by default, which means the whole collection object for an association is populated
|
||||
the first time its accessed. If you mark an association as extra lazy the following methods on collections
|
||||
can be called without triggering a full load of the collection:
|
||||
|
||||
- ``Collection#contains($entity)``
|
||||
- ``Collection#containsKey($key)`` (available with Doctrine 2.5)
|
||||
- ``Collection#containsKey($key)``
|
||||
- ``Collection#count()``
|
||||
- ``Collection#get($key)`` (available with Doctrine 2.4)
|
||||
- ``Collection#get($key)``
|
||||
- ``Collection#slice($offset, $length = null)``
|
||||
|
||||
For each of the above methods the following semantics apply:
|
||||
@@ -25,7 +25,7 @@ For each of the above methods the following semantics apply:
|
||||
- For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database.
|
||||
- For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed.
|
||||
|
||||
Additionally even with Doctrine 2.0 the following methods do not trigger the collection load:
|
||||
Additionally even with Doctrine ORM the following methods do not trigger the collection load:
|
||||
|
||||
- ``Collection#add($entity)``
|
||||
- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,7 +483,7 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="BugRepository")
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="bugs")
|
||||
*/
|
||||
class Bug
|
||||
@@ -563,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
|
||||
@@ -611,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
|
||||
@@ -667,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
|
||||
@@ -680,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
|
||||
@@ -833,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;
|
||||
@@ -937,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
|
||||
@@ -976,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
|
||||
*/
|
||||
@@ -1216,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.
|
||||
|
||||
|
||||
@@ -1232,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
|
||||
|
||||
@@ -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``.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,26 +20,43 @@
|
||||
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\Common\Persistence\Mapping\MappingException;
|
||||
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\Statement;
|
||||
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\DBAL\Cache\QueryCacheProfile;
|
||||
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
|
||||
{
|
||||
@@ -47,39 +65,40 @@ 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.
|
||||
*
|
||||
* @var ArrayCollection|Parameter[]
|
||||
* @psalm-var ArrayCollection<int, Parameter>
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* The user-specified ResultSetMapping to use.
|
||||
*
|
||||
* @var \Doctrine\ORM\Query\ResultSetMapping
|
||||
* @var ResultSetMapping
|
||||
*/
|
||||
protected $_resultSetMapping;
|
||||
|
||||
@@ -104,33 +123,27 @@ abstract class AbstractQuery
|
||||
*/
|
||||
protected $_hydrationMode = self::HYDRATE_OBJECT;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\DBAL\Cache\QueryCacheProfile
|
||||
*/
|
||||
/** @var QueryCacheProfile */
|
||||
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 \Doctrine\ORM\AbstractQuery This query instance.
|
||||
* @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 \Doctrine\ORM\AbstractQuery This query instance.
|
||||
* @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 EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
@@ -322,11 +329,13 @@ abstract class AbstractQuery
|
||||
*/
|
||||
public function getParameter($key)
|
||||
{
|
||||
$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 || (string) $key === (string) $parameterName;
|
||||
return $key === $parameterName;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -339,11 +348,14 @@ abstract class AbstractQuery
|
||||
* @param ArrayCollection|mixed[] $parameters
|
||||
*
|
||||
* @return static This query instance.
|
||||
*
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
*/
|
||||
public function setParameters($parameters)
|
||||
{
|
||||
// BC compatibility with 2.3-
|
||||
if (is_array($parameters)) {
|
||||
/** @psalm-var ArrayCollection<int, Parameter> $parameterCollection */
|
||||
$parameterCollection = new ArrayCollection();
|
||||
|
||||
foreach ($parameters as $key => $value) {
|
||||
@@ -389,9 +401,11 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array|string
|
||||
* @return mixed[]|string|int|float|bool
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMInvalidArgumentException
|
||||
* @throws ORMInvalidArgumentException
|
||||
*
|
||||
* @psalm-return array|scalar
|
||||
*/
|
||||
public function processParameterValue($value)
|
||||
{
|
||||
@@ -400,14 +414,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;
|
||||
}
|
||||
@@ -427,9 +438,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;
|
||||
@@ -438,8 +487,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)
|
||||
@@ -453,7 +500,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Gets the ResultSetMapping used for hydration.
|
||||
*
|
||||
* @return \Doctrine\ORM\Query\ResultSetMapping
|
||||
* @return ResultSetMapping
|
||||
*/
|
||||
protected function getResultSetMapping()
|
||||
{
|
||||
@@ -463,17 +510,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) {
|
||||
$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);
|
||||
}
|
||||
|
||||
@@ -489,21 +534,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;
|
||||
@@ -512,7 +555,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
|
||||
* @return QueryCacheProfile
|
||||
*/
|
||||
public function getHydrationCacheProfile()
|
||||
{
|
||||
@@ -525,15 +568,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;
|
||||
@@ -607,7 +648,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);
|
||||
@@ -620,7 +661,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @return static This query instance.
|
||||
*/
|
||||
public function disableResultCache() : self
|
||||
public function disableResultCache(): self
|
||||
{
|
||||
$this->_queryCacheProfile = null;
|
||||
|
||||
@@ -630,13 +671,13 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Defines how long the result cache will be active before expire.
|
||||
*
|
||||
* @param integer $lifetime How long the cache entry is valid.
|
||||
* @param int|null $lifetime How long the cache entry is valid.
|
||||
*
|
||||
* @return static This query instance.
|
||||
*/
|
||||
public function setResultCacheLifetime($lifetime)
|
||||
{
|
||||
$lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
|
||||
$lifetime = $lifetime !== null ? (int) $lifetime : 0;
|
||||
|
||||
$this->_queryCacheProfile = $this->_queryCacheProfile
|
||||
? $this->_queryCacheProfile->setLifetime($lifetime)
|
||||
@@ -650,7 +691,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
public function getResultCacheLifetime()
|
||||
{
|
||||
@@ -660,7 +701,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.
|
||||
*/
|
||||
@@ -674,7 +715,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Retrieves if the resultset cache is active or not.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function getExpireResultCache()
|
||||
{
|
||||
@@ -755,7 +796,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* Alias for execute(null, HYDRATE_ARRAY).
|
||||
*
|
||||
* @return array
|
||||
* @return array<int,mixed>
|
||||
*/
|
||||
public function getArrayResult()
|
||||
{
|
||||
@@ -767,7 +808,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* Alias for execute(null, HYDRATE_SCALAR).
|
||||
*
|
||||
* @return array
|
||||
* @return array<int,mixed>
|
||||
*/
|
||||
public function getScalarResult()
|
||||
{
|
||||
@@ -791,17 +832,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);
|
||||
@@ -827,15 +867,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);
|
||||
@@ -880,7 +920,7 @@ abstract class AbstractQuery
|
||||
*/
|
||||
public function getHint($name)
|
||||
{
|
||||
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
|
||||
return $this->_hints[$name] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -898,7 +938,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()
|
||||
{
|
||||
@@ -909,18 +949,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
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
@@ -930,11 +977,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|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
|
||||
*/
|
||||
@@ -950,8 +1030,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
|
||||
*/
|
||||
@@ -961,14 +1041,15 @@ abstract class AbstractQuery
|
||||
$this->setHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
if ( ! empty($parameters)) {
|
||||
if (! empty($parameters)) {
|
||||
$this->setParameters($parameters);
|
||||
}
|
||||
|
||||
$setCacheEntry = function() {};
|
||||
$setCacheEntry = static function (): void {
|
||||
};
|
||||
|
||||
if ($this->_hydrationCacheProfile !== null) {
|
||||
list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
|
||||
[$cacheKey, $realCacheKey] = $this->getHydrationCacheId();
|
||||
|
||||
$queryCacheProfile = $this->getHydrationCacheProfile();
|
||||
$cache = $queryCacheProfile->getResultCacheDriver();
|
||||
@@ -978,11 +1059,11 @@ abstract class AbstractQuery
|
||||
return $result[$realCacheKey];
|
||||
}
|
||||
|
||||
if ( ! $result) {
|
||||
if (! $result) {
|
||||
$result = [];
|
||||
}
|
||||
|
||||
$setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
|
||||
$setCacheEntry = static function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile): void {
|
||||
$result[$realCacheKey] = $data;
|
||||
|
||||
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
|
||||
@@ -1008,8 +1089,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
|
||||
*/
|
||||
@@ -1024,7 +1105,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) {
|
||||
@@ -1049,7 +1130,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Doctrine\ORM\Cache\TimestampCacheKey|null
|
||||
* @return TimestampCacheKey|null
|
||||
*/
|
||||
private function getTimestampKey()
|
||||
{
|
||||
@@ -1069,7 +1150,7 @@ abstract class AbstractQuery
|
||||
* Will return the configured id if it exists otherwise a hash will be
|
||||
* automatically generated for you.
|
||||
*
|
||||
* @return array ($key, $hash)
|
||||
* @return array<string, string> ($key, $hash)
|
||||
*/
|
||||
protected function getHydrationCacheId()
|
||||
{
|
||||
@@ -1122,7 +1203,7 @@ 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 Statement The executed database statement that holds the results.
|
||||
*/
|
||||
abstract protected function _doExecute();
|
||||
|
||||
@@ -1148,10 +1229,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;
|
||||
}
|
||||
|
||||
@@ -1162,4 +1245,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -20,49 +20,37 @@
|
||||
|
||||
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 */
|
||||
private $defaultQueryCache;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
@@ -80,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;
|
||||
}
|
||||
|
||||
@@ -95,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;
|
||||
}
|
||||
|
||||
@@ -107,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;
|
||||
}
|
||||
|
||||
@@ -125,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;
|
||||
}
|
||||
|
||||
@@ -140,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;
|
||||
}
|
||||
|
||||
@@ -157,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;
|
||||
}
|
||||
|
||||
@@ -173,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;
|
||||
}
|
||||
|
||||
@@ -188,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;
|
||||
}
|
||||
|
||||
@@ -203,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;
|
||||
}
|
||||
|
||||
@@ -218,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;
|
||||
}
|
||||
|
||||
@@ -282,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);
|
||||
}
|
||||
|
||||
@@ -290,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);
|
||||
}
|
||||
|
||||
@@ -305,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);
|
||||
}
|
||||
|
||||
@@ -321,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)
|
||||
{
|
||||
@@ -338,5 +324,4 @@ class DefaultCache implements Cache
|
||||
|
||||
return [$metadata->identifier[0] => $identifier];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -132,7 +114,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
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 +122,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);
|
||||
@@ -155,7 +137,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
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 +150,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,17 +185,16 @@ 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(). '
|
||||
);
|
||||
@@ -235,13 +216,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 .= ':';
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,11 +341,12 @@ 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 array|null
|
||||
* @return mixed[]|null
|
||||
*
|
||||
* @psalm-return array{targetEntity: string, type: mixed, list?: array[], identifier?: array}|null
|
||||
*/
|
||||
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue)
|
||||
{
|
||||
@@ -361,9 +359,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;
|
||||
}
|
||||
}
|
||||
@@ -371,7 +369,7 @@ class DefaultQueryCache implements QueryCache
|
||||
return [
|
||||
'targetEntity' => $assocMetadata->rootEntityName,
|
||||
'identifier' => $assocIdentifier,
|
||||
'type' => $assoc['type']
|
||||
'type' => $assoc['type'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -384,7 +382,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;
|
||||
}
|
||||
}
|
||||
@@ -400,11 +398,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)
|
||||
{
|
||||
@@ -418,9 +415,8 @@ class DefaultQueryCache implements QueryCache
|
||||
|
||||
array_unshift($path, [
|
||||
'field' => $field,
|
||||
'class' => $class
|
||||
]
|
||||
);
|
||||
'class' => $class,
|
||||
]);
|
||||
|
||||
$alias = $parent;
|
||||
}
|
||||
@@ -429,10 +425,10 @@ class DefaultQueryCache implements QueryCache
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $path
|
||||
* @param mixed $value
|
||||
* @param array<mixed> $path
|
||||
*
|
||||
* @return array|object|null
|
||||
* @return array<object>|object|null
|
||||
*/
|
||||
private function getAssociationPathValue($value, array $path)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,8 +44,8 @@ class EntityCacheEntry implements CacheEntry
|
||||
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.
|
||||
*/
|
||||
public function __construct($class, array $data)
|
||||
{
|
||||
@@ -59,7 +58,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 +70,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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 array
|
||||
*/
|
||||
/** @var int[] */
|
||||
private $cacheMissCountMap = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @var int[] */
|
||||
private $cacheHitCountMap = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @var int[] */
|
||||
private $cachePutCountMap = [];
|
||||
|
||||
/**
|
||||
@@ -142,11 +135,11 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
*
|
||||
* @param string $regionName The name of the cache region.
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
public function getRegionHitCount($regionName)
|
||||
{
|
||||
return isset($this->cacheHitCountMap[$regionName]) ? $this->cacheHitCountMap[$regionName] : 0;
|
||||
return $this->cacheHitCountMap[$regionName] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,11 +147,11 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
*
|
||||
* @param string $regionName The name of the cache region.
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
public function getRegionMissCount($regionName)
|
||||
{
|
||||
return isset($this->cacheMissCountMap[$regionName]) ? $this->cacheMissCountMap[$regionName] : 0;
|
||||
return $this->cacheMissCountMap[$regionName] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,15 +159,15 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
*
|
||||
* @param string $regionName The name of the cache region.
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
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()
|
||||
{
|
||||
@@ -222,7 +215,7 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
/**
|
||||
* Get the total number of put in cache.
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
public function getPutCount()
|
||||
{
|
||||
@@ -232,7 +225,7 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
/**
|
||||
* Get the total number of entries successfully retrieved from cache.
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
public function getHitCount()
|
||||
{
|
||||
@@ -242,7 +235,7 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
/**
|
||||
* Get the total number of cached entries *not* found in cache.
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
public function getMissCount()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
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 \Doctrine\ORM\PersistentCollection|null
|
||||
* @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);
|
||||
|
||||
|
||||
@@ -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 mixed[]|Collection $elements
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -283,17 +264,17 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* Generates a string of currently query
|
||||
*
|
||||
* @param array $query
|
||||
* @param string $criteria
|
||||
* @param array $orderBy
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
* @param string $query
|
||||
* @param string[]|Criteria $criteria
|
||||
* @param string[] $orderBy
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
protected function getHash($query, $criteria, ?array $orderBy = null, $limit = null, $offset = null)
|
||||
{
|
||||
list($params) = ($criteria instanceof Criteria)
|
||||
[$params] = $criteria instanceof Criteria
|
||||
? $this->persister->expandCriteriaParameters($criteria)
|
||||
: $this->persister->expandParameters($criteria);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -544,19 +527,18 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection)
|
||||
{
|
||||
$persister = $this->uow->getCollectionPersister($assoc);
|
||||
$hasCache = ($persister instanceof CachedPersister);
|
||||
$key = null;
|
||||
|
||||
if ( ! $hasCache) {
|
||||
return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll);
|
||||
if (! $hasCache) {
|
||||
return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);
|
||||
}
|
||||
|
||||
$ownerId = $this->uow->getEntityIdentifier($coll->getOwner());
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
|
||||
$list = $persister->loadCollectionCache($coll, $key);
|
||||
$list = $persister->loadCollectionCache($collection, $key);
|
||||
|
||||
if ($list !== null) {
|
||||
if ($this->cacheLogger) {
|
||||
@@ -566,7 +548,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $list;
|
||||
}
|
||||
|
||||
$list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll);
|
||||
$list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $collection);
|
||||
|
||||
$persister->storeCollectionCache($key, $list);
|
||||
|
||||
@@ -580,18 +562,18 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection)
|
||||
{
|
||||
$persister = $this->uow->getCollectionPersister($assoc);
|
||||
$hasCache = ($persister instanceof CachedPersister);
|
||||
|
||||
if ( ! $hasCache) {
|
||||
return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll);
|
||||
if (! $hasCache) {
|
||||
return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);
|
||||
}
|
||||
|
||||
$ownerId = $this->uow->getEntityIdentifier($coll->getOwner());
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
|
||||
$list = $persister->loadCollectionCache($coll, $key);
|
||||
$list = $persister->loadCollectionCache($collection, $key);
|
||||
|
||||
if ($list !== null) {
|
||||
if ($this->cacheLogger) {
|
||||
@@ -601,7 +583,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $list;
|
||||
}
|
||||
|
||||
$list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll);
|
||||
$list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $collection);
|
||||
|
||||
$persister->storeCollectionCache($key, $list);
|
||||
|
||||
@@ -637,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);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user