mirror of
https://github.com/doctrine/orm.git
synced 2026-04-25 07:28:04 +02:00
Compare commits
202 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bc4ff3cab | |||
| 39f2f0eb91 | |||
| a5a7c879fc | |||
| 5670912d0d | |||
| fae0f6a29a | |||
| f45cf2629e | |||
| 4d846c1992 | |||
| 37516d7548 | |||
| 24c4ec91e5 | |||
| f31f088f0b | |||
| 51da937bbc | |||
| 801e7f0ef7 | |||
| f0e6408005 | |||
| c398f8c2c2 | |||
| 060bbb1366 | |||
| 34fad084a7 | |||
| bdc54d481c | |||
| 60462919f2 | |||
| 5a8a017a66 | |||
| c701e8b9a6 | |||
| df99353f19 | |||
| 8b5dae30a5 | |||
| 78770f9da8 | |||
| 2f57c4fef9 | |||
| 684ae859ce | |||
| 5f9dc2e5bc | |||
| 2dbe28a150 | |||
| ea3856673d | |||
| 7c02af8896 | |||
| 61c18ce046 | |||
| 7f5620a41c | |||
| ae198d5e45 | |||
| 705c33bc35 | |||
| 3cef0fdbfa | |||
| ccbe849a72 | |||
| 2b8e03ff12 | |||
| c4e93cf68c | |||
| cfdef3bf19 | |||
| 8bb1d5448b | |||
| bebacf79d8 | |||
| d46fa4adeb | |||
| 64061bafaf | |||
| a69584a841 | |||
| 8fc1c34b29 | |||
| 0d683c1897 | |||
| 60b75fefed | |||
| 0a7e0617cc | |||
| 072e1eee7b | |||
| c0d3cdbdfb | |||
| a50ae2c898 | |||
| dbbe7a4be5 | |||
| 406e177062 | |||
| fc19c3b53d | |||
| 5c5abb6771 | |||
| 42226dadd1 | |||
| 84f8ef5ca4 | |||
| 8a13376d42 | |||
| 8b5632cb65 | |||
| 859465b691 | |||
| 530c01b5e3 | |||
| 63c5758070 | |||
| 0fb236f451 | |||
| 2a9a53ae9d | |||
| 0081319712 | |||
| 78ceda7ecf | |||
| 1f4810e370 | |||
| 376a3ac3b6 | |||
| ab87dd6325 | |||
| 2ae245db30 | |||
| 9d36e855c0 | |||
| f7c87cddd8 | |||
| 1566b8a057 | |||
| 333fa1090a | |||
| 0262b083bb | |||
| 60c9c27c08 | |||
| 8b75e3563a | |||
| cbf16f1cf8 | |||
| b84c828ea1 | |||
| 55b7e4cff2 | |||
| e38af55100 | |||
| 7338c2d1f8 | |||
| 0ff3cdf150 | |||
| 24c5c54c1e | |||
| 6bb367f488 | |||
| 213cc5c695 | |||
| a949e87ca8 | |||
| f18d0e093b | |||
| 0e139055f9 | |||
| b9c6659b70 | |||
| 5c06121d94 | |||
| 5bfa56aee0 | |||
| 0363a5548d | |||
| 3764e49e6c | |||
| 6ee20204a5 | |||
| d9b0c87ded | |||
| 8594e5c4da | |||
| 5f821f3b98 | |||
| b566525099 | |||
| 215c4a03e1 | |||
| b3ccd6466b | |||
| b596bbb29f | |||
| c204e6c6a1 | |||
| 0bc94589e1 | |||
| f37856829f | |||
| 157c793810 | |||
| 72d838a804 | |||
| 58f8dc5d4c | |||
| 7d3ecd9481 | |||
| 1bb55703a7 | |||
| 56cbcec13d | |||
| 837c19bfc0 | |||
| 7b8f09ee4a | |||
| 488a4dc78a | |||
| 1364b6acc6 | |||
| 3dbe181762 | |||
| a3acaab65c | |||
| f183d25a33 | |||
| 7c8350094e | |||
| c613410ba6 | |||
| 6bb7581dd7 | |||
| ab71dab7d1 | |||
| 2c114756bd | |||
| 45496f040d | |||
| b40866c624 | |||
| a89cc7abea | |||
| 5ac111e5f8 | |||
| c5f66e6e7f | |||
| b59f495875 | |||
| 3829b9c28b | |||
| 65bcdbf4c7 | |||
| 95d000e51b | |||
| 3657df3b01 | |||
| 1661ffae9a | |||
| b424a5cf14 | |||
| 2767a4eec4 | |||
| 9486867279 | |||
| 6f2bb08972 | |||
| da2d3b406e | |||
| c4b7d3fbea | |||
| 84373d05a4 | |||
| e82e7147fa | |||
| e23ed2250d | |||
| 192bb6fd21 | |||
| 0f3679f034 | |||
| 1d2cd82706 | |||
| b983d86612 | |||
| b11f01643c | |||
| b58fb8f5d4 | |||
| 925a22b71d | |||
| 0f0d8abd67 | |||
| 470c15ce05 | |||
| 3cc5fc0252 | |||
| fd0657089a | |||
| de3b237292 | |||
| 1221cc3a2a | |||
| 9efbc1fa71 | |||
| 57705e0d78 | |||
| 82bb6b78cd | |||
| 64c56b21aa | |||
| b04e2e6364 | |||
| a70f9b7f49 | |||
| c88a7c1ffe | |||
| c206728c96 | |||
| e8d420c641 | |||
| fdcab7eae8 | |||
| 45d7d5234f | |||
| 159ca79b81 | |||
| 2b148a27e0 | |||
| 0aef57f60c | |||
| fef1e0286c | |||
| 4a38534150 | |||
| 1de22adb16 | |||
| 62b4160887 | |||
| dbb7c4d2bf | |||
| e8978ee365 | |||
| c095b88804 | |||
| efe4208ba6 | |||
| 453a56670d | |||
| ec36e2c866 | |||
| e250572cb4 | |||
| 758955e183 | |||
| 5b8d6a1486 | |||
| 3f1003fee9 | |||
| 7e241e89b8 | |||
| 67c1e1d2b1 | |||
| 261eacdbfc | |||
| 43df821691 | |||
| 11d09702da | |||
| f9f14139cf | |||
| 39f4d46d36 | |||
| 1dae8d318f | |||
| a361a7c1cb | |||
| 6a73608baf | |||
| f9955152b2 | |||
| 5aad1df149 | |||
| 243832555b | |||
| ae12fa6b5b | |||
| edaf9b6813 | |||
| b324a21abf | |||
| ff34aaaa2c | |||
| 9767a814a6 | |||
| e6007575e1 |
@@ -0,0 +1,4 @@
|
||||
# for php-coveralls
|
||||
service_name: travis-ci
|
||||
src_dir: lib
|
||||
coverage_clover: build/logs/clover.xml
|
||||
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"active": true,
|
||||
"name": "Object Relational Mapper",
|
||||
"shortName": "ORM",
|
||||
"slug": "orm",
|
||||
"docsSlug": "doctrine-orm",
|
||||
"versions": [
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "3.0.x",
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.10",
|
||||
"branchName": "2.10.x",
|
||||
"slug": "2.10",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.9",
|
||||
"branchName": "2.9.x",
|
||||
"slug": "2.9",
|
||||
"current": true,
|
||||
"aliases": [
|
||||
"current",
|
||||
"stable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "2.8",
|
||||
"branchName": "2.8.x",
|
||||
"slug": "2.8",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
+2
-9
@@ -1,19 +1,12 @@
|
||||
/tests export-ignore
|
||||
/tools export-ignore
|
||||
/docs export-ignore
|
||||
/.github export-ignore
|
||||
.doctrine-project.json export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitmodules export-ignore
|
||||
.travis.yml export-ignore
|
||||
build.properties export-ignore
|
||||
build.properties.dev export-ignore
|
||||
build.xml export-ignore
|
||||
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
|
||||
phpstan-baseline.neon export-ignore
|
||||
psalm.xml export-ignore
|
||||
psalm-baseline.xml export-ignore
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
name: "Coding Standards"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
name: "Coding Standards"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
# https://github.com/doctrine/.github/issues/3
|
||||
- name: "Run PHP_CodeSniffer"
|
||||
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
|
||||
@@ -1,279 +0,0 @@
|
||||
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"
|
||||
|
||||
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"
|
||||
|
||||
- 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.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"
|
||||
|
||||
phpunit-lower-php-versions:
|
||||
name: "PHPUnit with SQLite"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.1"
|
||||
deps:
|
||||
- "highest"
|
||||
- "lowest"
|
||||
|
||||
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 }}"
|
||||
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"
|
||||
|
||||
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
|
||||
@@ -1,46 +0,0 @@
|
||||
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 }}
|
||||
@@ -1,65 +0,0 @@
|
||||
|
||||
name: "Static Analysis"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
jobs:
|
||||
static-analysis-phpstan:
|
||||
name: "Static Analysis with PHPStan"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "vendor/bin/phpstan analyse"
|
||||
|
||||
static-analysis-psalm:
|
||||
name: "Static Analysis with Psalm"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with vimeo/psalm"
|
||||
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
|
||||
@@ -10,10 +10,5 @@ lib/Doctrine/DBAL
|
||||
.buildpath
|
||||
.project
|
||||
.idea
|
||||
*.iml
|
||||
vendor/
|
||||
/tests/Doctrine/Performance/history.db
|
||||
/.phpcs-cache
|
||||
composer.lock
|
||||
/.phpunit.result.cache
|
||||
/*.phpunit.xml
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
env:
|
||||
- DB=mysql
|
||||
- DB=pgsql
|
||||
- DB=sqlite
|
||||
|
||||
before_script:
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests_tmp;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi"
|
||||
- composer install --prefer-dist --dev
|
||||
|
||||
script: phpunit --configuration tests/travis/$DB.travis.xml
|
||||
|
||||
after_script:
|
||||
- php vendor/bin/coveralls -v
|
||||
+33
-26
@@ -6,21 +6,34 @@ Before we can merge your Pull-Request here are some guidelines that you need to
|
||||
These guidelines exist not to annoy you, but to keep the code base clean,
|
||||
unified and future proof.
|
||||
|
||||
Doctrine has [general contributing guidelines][contributor workflow], make
|
||||
sure you follow them.
|
||||
## We only accept PRs to "master"
|
||||
|
||||
[contributor workflow]: https://www.doctrine-project.org/contribute/index.html
|
||||
Our branching strategy is summed up with "everything to master first", even
|
||||
bugfixes and we then merge them into the stable branches. You should only
|
||||
open pull requests against the master branch. Otherwise we cannot accept the PR.
|
||||
|
||||
There is one exception to the rule, when we merged a bug into some stable branches
|
||||
we do occasionally accept pull requests that merge the same bug fix into earlier
|
||||
branches.
|
||||
|
||||
## Coding Standard
|
||||
|
||||
This project follows [`doctrine/coding-standard`][coding standard homepage].
|
||||
You may fix many some of the issues with `vendor/bin/phpcbf`.
|
||||
We use PSR-1 and PSR-2:
|
||||
|
||||
[coding standard homepage]: https://github.com/doctrine/coding-standard
|
||||
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
|
||||
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
|
||||
|
||||
with some exceptions/differences:
|
||||
|
||||
* Keep the nesting of control structures per method as small as possible
|
||||
* Align equals (=) signs
|
||||
* Add spaces between assignment, control and return statements
|
||||
* Prefer early exit over nesting conditions
|
||||
* Add spaces around a negation if condition ``if ( ! $cond)``
|
||||
|
||||
## Unit-Tests
|
||||
|
||||
Please try to add a test for your pull-request.
|
||||
Always add a test for your pull-request.
|
||||
|
||||
* If you want to fix a bug or provide a reproduce case, create a test file in
|
||||
``tests/Doctrine/Tests/ORM/Functional/Ticket`` with the name of the ticket,
|
||||
@@ -28,33 +41,27 @@ Please try to add a test for your pull-request.
|
||||
* If you want to contribute new functionality add unit- or functional tests
|
||||
depending on the scope of the feature.
|
||||
|
||||
You can run the unit-tests by calling ``vendor/bin/phpunit`` from the root of the project.
|
||||
You can run the unit-tests by calling ``phpunit`` from the root of the project.
|
||||
It will run all the tests with an in memory SQLite database.
|
||||
|
||||
In order to do that, you will need a fresh copy of the ORM, and you
|
||||
will have to run a composer installation in the project:
|
||||
|
||||
```sh
|
||||
git clone git@github.com:doctrine/orm.git
|
||||
cd orm
|
||||
curl -sS https://getcomposer.org/installer | php --
|
||||
./composer.phar install
|
||||
```
|
||||
|
||||
To run the testsuite against another database, copy the ``phpunit.xml.dist``
|
||||
to for example ``mysql.phpunit.xml`` and edit the parameters. You can
|
||||
take a look at the ``ci/github/phpunit`` directory for some examples. Then run:
|
||||
take a look at the ``tests/travis`` folder for some examples. Then run:
|
||||
|
||||
vendor/bin/phpunit -c mysql.phpunit.xml
|
||||
phpunit -c mysql.phpunit.xml
|
||||
|
||||
If you do not provide these parameters, the test suite will use an in-memory
|
||||
sqlite database.
|
||||
## Travis
|
||||
|
||||
Tips for creating unit tests:
|
||||
We automatically run your pull request through [Travis CI](http://www.travis-ci.org)
|
||||
against SQLite, MySQL and PostgreSQL. If you break the tests, we cannot merge your code,
|
||||
so please make sure that your code is working before opening up a Pull-Request.
|
||||
|
||||
1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class.
|
||||
See `https://github.com/doctrine/orm/tree/2.8.x/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
|
||||
example.
|
||||
## DoctrineBot, Tickets and Jira
|
||||
|
||||
DoctrineBot will synchronize your Pull-Request into our [Jira](http://www.doctrine-project.org).
|
||||
Make sure to add any existing Jira ticket into the Pull-Request Title, for example:
|
||||
|
||||
"[DDC-123] My Pull Request"
|
||||
|
||||
## Getting merged
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2006-2015 Doctrine Project
|
||||
Copyright (c) 2006-2012 Doctrine Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Doctrine 2 ORM
|
||||
|
||||
Master: [](http://travis-ci.org/doctrine/doctrine2)
|
||||
2.3: [](http://travis-ci.org/doctrine/doctrine2)
|
||||
2.2: [](http://travis-ci.org/doctrine/doctrine2)
|
||||
2.1: [](http://travis-ci.org/doctrine/doctrine2)
|
||||
|
||||
Master: [](https://coveralls.io/r/doctrine/doctrine2?branch=master)
|
||||
|
||||
[](https://packagist.org/packages/doctrine/orm) [](https://packagist.org/packages/doctrine/orm)
|
||||
|
||||
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||
inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
|
||||
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)
|
||||
* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC)
|
||||
* [Downloads](http://github.com/doctrine/doctrine2/downloads)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
| [3.0.x][3.0] | [2.9.x][2.9] | [2.8.x][2.8] |
|
||||
|:----------------:|:----------------:|:----------:|
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.9 image]][2.9] | [![Build status][2.8 image]][2.8] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.9 coverage image]][2.9 coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
|
||||
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||
inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
|
||||
without requiring unnecessary code duplication.
|
||||
|
||||
|
||||
## More resources:
|
||||
|
||||
* [Website](http://www.doctrine-project.org)
|
||||
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
|
||||
|
||||
|
||||
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
|
||||
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
|
||||
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
|
||||
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
|
||||
[2.9 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.9.x
|
||||
[2.9]: https://github.com/doctrine/orm/tree/2.9.x
|
||||
[2.9 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.9.x/graph/badge.svg
|
||||
[2.9 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.9.x
|
||||
[2.8 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg
|
||||
[2.8]: https://github.com/doctrine/orm/tree/2.8
|
||||
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8.x/graph/badge.svg
|
||||
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8.x
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
Security
|
||||
========
|
||||
|
||||
The Doctrine library is operating very close to your database and as such needs
|
||||
to handle and make assumptions about SQL injection vulnerabilities.
|
||||
|
||||
It is vital that you understand how Doctrine approaches security, because
|
||||
we cannot protect you from SQL injection.
|
||||
|
||||
Please read the documentation chapter on Security in Doctrine DBAL and ORM to
|
||||
understand the assumptions we make.
|
||||
|
||||
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)
|
||||
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html)
|
||||
|
||||
If you find a Security bug in Doctrine, please report it on Jira and change the
|
||||
Security Level to "Security Issues". It will be visible to Doctrine Core
|
||||
developers and you only.
|
||||
+7
-387
@@ -1,383 +1,3 @@
|
||||
# Upgrade to 2.9
|
||||
|
||||
## Minor BC BREAK: Setup tool needs cache implementation
|
||||
|
||||
With the deprecation of doctrine/cache, the setup tool might no longer work as expected without a different cache
|
||||
implementation. To work around this:
|
||||
* Install symfony/cache: `composer require symfony/cache`. This will keep previous behaviour without any changes
|
||||
* Instantiate caches yourself: to use a different cache implementation, pass a cache instance when calling any
|
||||
configuration factory in the setup tool:
|
||||
```diff
|
||||
- $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir);
|
||||
+ $cache = \Doctrine\Common\Cache\Psr6\DoctrineProvider::wrap($anyPsr6Implementation);
|
||||
+ $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);
|
||||
```
|
||||
* As a quick workaround, you can lock the doctrine/cache dependency to work around this: `composer require doctrine/cache ^1.11`.
|
||||
Note that this is only recommended as a bandaid fix, as future versions of ORM will no longer work with doctrine/cache
|
||||
1.11.
|
||||
|
||||
## Deprecated: doctrine/cache for metadata caching
|
||||
|
||||
The `Doctrine\ORM\Configuration#setMetadataCacheImpl()` method is deprecated and should no longer be used. Please use
|
||||
`Doctrine\ORM\Configuration#setMetadataCache()` with any PSR-6 cache adapter instead.
|
||||
|
||||
## Removed: flushing metadata cache
|
||||
|
||||
To support PSR-6 caches, the `--flush` option for the `orm:clear-cache:metadata` command is ignored. Metadata cache is
|
||||
now always cleared regardless of the cache adapter being used.
|
||||
|
||||
# Upgrade to 2.8
|
||||
|
||||
## Minor BC BREAK: Failed commit now throw OptimisticLockException
|
||||
|
||||
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
|
||||
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
|
||||
|
||||
## Deprecated: `Doctrine\ORM\AbstractQuery#iterator()`
|
||||
|
||||
The method `Doctrine\ORM\AbstractQuery#iterator()` is deprecated in favor of `Doctrine\ORM\AbstractQuery#toIterable()`.
|
||||
Note that `toIterable()` yields results of the query, unlike `iterator()` which yielded each result wrapped into an array.
|
||||
|
||||
# Upgrade to 2.7
|
||||
|
||||
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods
|
||||
|
||||
Method `Doctrine\ORM\AbstractQuery#useResultCache()` which could be used for both enabling and disabling the cache
|
||||
(depending on passed flag) was split into two.
|
||||
|
||||
## Minor BC BREAK: paginator output walkers aren't be called anymore on sub-queries for queries without max results
|
||||
|
||||
To optimize DB interaction, `Doctrine\ORM\Tools\Pagination\Paginator` no longer fetches identifiers to be able to
|
||||
perform the pagination with join collections when max results isn't set in the query.
|
||||
|
||||
## Minor BC BREAK: tables filtered with `schema_filter` are no longer created
|
||||
|
||||
When generating schema diffs, if a source table is filtered out by a `schema_filter` expression, then a `CREATE TABLE` was
|
||||
always generated, even if the table already existed. This has been changed in this release and the table will no longer
|
||||
be created.
|
||||
|
||||
## Deprecated number unaware `Doctrine\ORM\Mapping\UnderscoreNamingStrategy`
|
||||
|
||||
In the last patch of the `v2.6.x` series, we fixed a bug that was not converting names properly when they had numbers
|
||||
(e.g.: `base64Encoded` was wrongly converted to `base64encoded` instead of `base64_encoded`).
|
||||
|
||||
In order to not break BC we've introduced a way to enable the fixed behavior using a boolean constructor argument. This
|
||||
argument will be removed in 3.0 and the default behavior will be the fixed one.
|
||||
|
||||
## Deprecated: `Doctrine\ORM\AbstractQuery#useResultCache()`
|
||||
|
||||
Method `Doctrine\ORM\AbstractQuery#useResultCache()` is deprecated because it is split into `enableResultCache()`
|
||||
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`
|
||||
* `orm:generate:entities`
|
||||
* `orm:generate-repositories`
|
||||
|
||||
These classes have been deprecated:
|
||||
|
||||
* `Doctrine\ORM\Tools\EntityGenerator`
|
||||
* `Doctrine\ORM\Tools\EntityRepositoryGenerator`
|
||||
|
||||
Whole Doctrine\ORM\Tools\Export namespace with all its members have been deprecated as well.
|
||||
|
||||
## Deprecated `Doctrine\ORM\Proxy\Proxy` marker interface
|
||||
|
||||
Proxy objects in Doctrine ORM 3.0 will no longer implement `Doctrine\ORM\Proxy\Proxy` nor
|
||||
`Doctrine\Persistence\Proxy`: instead, they implement
|
||||
`ProxyManager\Proxy\GhostObjectInterface`.
|
||||
|
||||
These related classes have been deprecated:
|
||||
|
||||
* `Doctrine\ORM\Proxy\ProxyFactory`
|
||||
* `Doctrine\ORM\Proxy\Autoloader` - we suggest using the composer autoloader instead
|
||||
|
||||
These methods have been deprecated:
|
||||
|
||||
* `Doctrine\ORM\Configuration#getAutoGenerateProxyClasses()`
|
||||
* `Doctrine\ORM\Configuration#getProxyDir()`
|
||||
* `Doctrine\ORM\Configuration#getProxyNamespace()`
|
||||
|
||||
## Deprecated `Doctrine\ORM\Version`
|
||||
|
||||
The `Doctrine\ORM\Version` class is now deprecated and will be removed in Doctrine ORM 3.0:
|
||||
please refrain from checking the ORM version at runtime or use
|
||||
[ocramius/package-versions](https://github.com/Ocramius/PackageVersions/).
|
||||
|
||||
## Deprecated `EntityManager#merge()` and `EntityManager#detach()` methods
|
||||
|
||||
Merge and detach semantics were a poor fit for the PHP "share-nothing" architecture.
|
||||
In addition to that, merging/detaching caused multiple issues with data integrity
|
||||
in the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.
|
||||
|
||||
The following API methods were therefore deprecated:
|
||||
|
||||
* `EntityManager#merge()`
|
||||
* `EntityManager#detach()`
|
||||
* `UnitOfWork#merge()`
|
||||
* `UnitOfWork#detach()`
|
||||
|
||||
Users are encouraged to migrate `EntityManager#detach()` calls to `EntityManager#clear()`.
|
||||
|
||||
In order to maintain performance on batch processing jobs, it is endorsed to enable
|
||||
the second level cache (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
|
||||
on entities that are frequently reused across multiple `EntityManager#clear()` calls.
|
||||
|
||||
An alternative to `EntityManager#merge()` will not be provided by ORM 3.0, since the merging
|
||||
semantics should be part of the business domain rather than the persistence domain of an
|
||||
application. If your application relies heavily on CRUD-alike interactions and/or `PATCH`
|
||||
restful operations, you should look at alternatives such as [JMSSerializer](https://github.com/schmittjoh/serializer).
|
||||
|
||||
## Extending `EntityManager` is deprecated
|
||||
|
||||
Final keyword will be added to the `EntityManager::class` in Doctrine ORM 3.0 in order to ensure that EntityManager
|
||||
is not used as valid extension point. Valid extension point should be EntityManagerInterface.
|
||||
|
||||
## Deprecated `EntityManager#clear($entityName)`
|
||||
|
||||
If your code relies on clearing a single entity type via `EntityManager#clear($entityName)`,
|
||||
the signature has been changed to `EntityManager#clear()`.
|
||||
|
||||
The main reason is that partial clears caused multiple issues with data integrity
|
||||
in the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.
|
||||
|
||||
## Deprecated `EntityManager#flush($entity)` and `EntityManager#flush($entities)`
|
||||
|
||||
If your code relies on single entity flushing optimisations via
|
||||
`EntityManager#flush($entity)`, the signature has been changed to
|
||||
`EntityManager#flush()`.
|
||||
|
||||
Said API was affected by multiple data integrity bugs due to the fact
|
||||
that change tracking was being restricted upon a subset of the managed
|
||||
entities. The ORM cannot support committing subsets of the managed
|
||||
entities while also guaranteeing data integrity, therefore this
|
||||
utility was removed.
|
||||
|
||||
The `flush()` semantics will remain the same, but the change tracking will be performed
|
||||
on all entities managed by the unit of work, and not just on the provided
|
||||
`$entity` or `$entities`, as the parameter is now completely ignored.
|
||||
|
||||
The same applies to `UnitOfWork#commit($entity)`, which will simply be
|
||||
`UnitOfWork#commit()`.
|
||||
|
||||
If you would still like to perform batching operations over small `UnitOfWork`
|
||||
instances, it is suggested to follow these paths instead:
|
||||
|
||||
* eagerly use `EntityManager#clear()` in conjunction with a specific second level
|
||||
cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
|
||||
* use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html)
|
||||
|
||||
## Deprecated `YAML` mapping drivers.
|
||||
|
||||
If your code relies on `YamlDriver` or `SimpleYamlDriver`, you **MUST** change to
|
||||
annotation or XML drivers instead.
|
||||
|
||||
## Deprecated: `Doctrine\ORM\EntityManagerInterface#copy()`
|
||||
|
||||
Method `Doctrine\ORM\EntityManagerInterface#copy()` never got its implementation and is deprecated.
|
||||
It will be removed in 3.0.
|
||||
|
||||
# Upgrade to 2.6
|
||||
|
||||
## Added `Doctrine\ORM\EntityRepository::count()` method
|
||||
|
||||
`Doctrine\ORM\EntityRepository::count()` has been added. This new method has different
|
||||
signature than `Countable::count()` (required parameter) and therefore are not compatible.
|
||||
If your repository implemented the `Countable` interface, you will have to use
|
||||
`$repository->count([])` instead and not implement `Countable` interface anymore.
|
||||
|
||||
## Minor BC BREAK: `Doctrine\ORM\Tools\Console\ConsoleRunner` is now final
|
||||
|
||||
Since it's just an utilitarian class and should not be inherited.
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\QueryException::associationPathInverseSideNotSupported()`
|
||||
|
||||
Method `Doctrine\ORM\Query\QueryException::associationPathInverseSideNotSupported()`
|
||||
now has a required parameter `$pathExpr`.
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\Parser#isInternalFunction()`
|
||||
|
||||
Method `Doctrine\ORM\Query\Parser#isInternalFunction()` was removed because
|
||||
the distinction between internal function and user defined DQL was removed.
|
||||
[#6500](https://github.com/doctrine/orm/pull/6500)
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\ORMException#overwriteInternalDQLFunctionNotAllowed()`
|
||||
|
||||
Method `Doctrine\ORM\Query\Parser#overwriteInternalDQLFunctionNotAllowed()` was
|
||||
removed because of the choice to allow users to overwrite internal functions, ie
|
||||
`AVG`, `SUM`, `COUNT`, `MIN` and `MAX`. [#6500](https://github.com/doctrine/orm/pull/6500)
|
||||
|
||||
## PHP 7.1 is now required
|
||||
|
||||
Doctrine 2.6 now requires PHP 7.1 or newer.
|
||||
|
||||
As a consequence, automatic cache setup in Doctrine\ORM\Tools\Setup::create*Configuration() was changed:
|
||||
- APCu extension (ext-apcu) will now be used instead of abandoned APC (ext-apc).
|
||||
- Memcached extension (ext-memcached) will be used instead of obsolete Memcache (ext-memcache).
|
||||
- XCache support was dropped as it doesn't work with PHP 7.
|
||||
|
||||
# Upgrade to 2.5
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()`
|
||||
|
||||
Method `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()` was unused and part
|
||||
of the internal API of the ORM, so it was removed. [#5600](https://github.com/doctrine/orm/pull/5600).
|
||||
|
||||
## Minor BC BREAK: removed $className parameter on `AbstractEntityInheritancePersister#getSelectJoinColumnSQL()`
|
||||
|
||||
As `$className` parameter was not used in the method, it was safely removed.
|
||||
|
||||
## Minor BC BREAK: query cache key time is now a float
|
||||
|
||||
As of 2.5.5, the `QueryCacheEntry#time` property will contain a float value
|
||||
instead of an integer in order to have more precision and also to be consistent
|
||||
with the `TimestampCacheEntry#time`.
|
||||
|
||||
## Minor BC BREAK: discriminator map must now include all non-transient classes
|
||||
|
||||
It is now required that you declare the root of an inheritance in the
|
||||
discriminator map.
|
||||
|
||||
When declaring an inheritance map, it was previously possible to skip the root
|
||||
of the inheritance in the discriminator map. This was actually a validation
|
||||
mistake by Doctrine2 and led to problems when trying to persist instances of
|
||||
that class.
|
||||
|
||||
If you don't plan to persist instances some classes in your inheritance, then
|
||||
either:
|
||||
|
||||
- make those classes `abstract`
|
||||
- map those classes as `MappedSuperclass`
|
||||
|
||||
## Minor BC BREAK: ``EntityManagerInterface`` instead of ``EntityManager`` in type-hints
|
||||
|
||||
As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require
|
||||
an ``EntityManagerInterface`` instead.
|
||||
If you are extending any of the following classes, then you need to check following
|
||||
signatures:
|
||||
|
||||
- ``Doctrine\ORM\Tools\DebugUnitOfWorkListener#dumpIdentityMap(EntityManagerInterface $em)``
|
||||
- ``Doctrine\ORM\Mapping\ClassMetadataFactory#setEntityManager(EntityManagerInterface $em)``
|
||||
|
||||
## Minor BC BREAK: Custom Hydrators API change
|
||||
|
||||
As of 2.5, `AbstractHydrator` does not enforce the usage of cache as part of
|
||||
API, and now provides you a clean API for column information through the method
|
||||
`hydrateColumnInfo($column)`.
|
||||
Cache variable being passed around by reference is no longer needed since
|
||||
Hydrators are per query instantiated since Doctrine 2.4.
|
||||
|
||||
## Minor BC BREAK: Entity based ``EntityManager#clear()`` calls follow cascade detach
|
||||
|
||||
Whenever ``EntityManager#clear()`` method gets called with a given entity class
|
||||
name, until 2.4, it was only detaching the specific requested entity.
|
||||
As of 2.5, ``EntityManager`` will follow configured cascades, providing a better
|
||||
memory management since associations will be garbage collected, optimizing
|
||||
resources consumption on long running jobs.
|
||||
|
||||
## BC BREAK: NamingStrategy interface changes
|
||||
|
||||
1. A new method ``embeddedFieldToColumnName($propertyName, $embeddedColumnName)``
|
||||
|
||||
This method generates the column name for fields of embedded objects. If you implement your custom NamingStrategy, you
|
||||
now also need to implement this new method.
|
||||
|
||||
2. A change to method ``joinColumnName()`` to include the $className
|
||||
|
||||
## Updates on entities scheduled for deletion are no longer processed
|
||||
|
||||
In Doctrine 2.4, if you modified properties of an entity scheduled for deletion, UnitOfWork would
|
||||
produce an UPDATE statement to be executed right before the DELETE statement. The entity in question
|
||||
was therefore present in ``UnitOfWork#entityUpdates``, which means that ``preUpdate`` and ``postUpdate``
|
||||
listeners were (quite pointlessly) called. In ``preFlush`` listeners, it used to be possible to undo
|
||||
the scheduled deletion for updated entities (by calling ``persist()`` if the entity was found in both
|
||||
``entityUpdates`` and ``entityDeletions``). This does not work any longer, because the entire changeset
|
||||
calculation logic is optimized away.
|
||||
|
||||
## Minor BC BREAK: Default lock mode changed from LockMode::NONE to null in method signatures
|
||||
|
||||
A misconception concerning default lock mode values in method signatures lead to unexpected behaviour
|
||||
in SQL statements on SQL Server. With a default lock mode of ``LockMode::NONE`` throughout the
|
||||
method signatures in ORM, the table lock hint ``WITH (NOLOCK)`` was appended to all locking related
|
||||
queries by default. This could result in unpredictable results because an explicit ``WITH (NOLOCK)``
|
||||
table hint tells SQL Server to run a specific query in transaction isolation level READ UNCOMMITTED
|
||||
instead of the default READ COMMITTED transaction isolation level.
|
||||
Therefore there now is a distinction between ``LockMode::NONE`` and ``null`` to be able to tell
|
||||
Doctrine whether to add table lock hints to queries by intention or not. To achieve this, the following
|
||||
method signatures have been changed to declare ``$lockMode = null`` instead of ``$lockMode = LockMode::NONE``:
|
||||
|
||||
- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#getSelectSQL()``
|
||||
- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#load()``
|
||||
- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#refresh()``
|
||||
- ``Doctrine\ORM\Decorator\EntityManagerDecorator#find()``
|
||||
- ``Doctrine\ORM\EntityManager#find()``
|
||||
- ``Doctrine\ORM\EntityRepository#find()``
|
||||
- ``Doctrine\ORM\Persisters\BasicEntityPersister#getSelectSQL()``
|
||||
- ``Doctrine\ORM\Persisters\BasicEntityPersister#load()``
|
||||
- ``Doctrine\ORM\Persisters\BasicEntityPersister#refresh()``
|
||||
- ``Doctrine\ORM\Persisters\EntityPersister#getSelectSQL()``
|
||||
- ``Doctrine\ORM\Persisters\EntityPersister#load()``
|
||||
- ``Doctrine\ORM\Persisters\EntityPersister#refresh()``
|
||||
- ``Doctrine\ORM\Persisters\JoinedSubclassPersister#getSelectSQL()``
|
||||
|
||||
You should update signatures for these methods if you have subclassed one of the above classes.
|
||||
Please also check the calling code of these methods in your application and update if necessary.
|
||||
|
||||
**Note:**
|
||||
This in fact is really a minor BC BREAK and should not have any affect on database vendors
|
||||
other than SQL Server because it is the only one that supports and therefore cares about
|
||||
``LockMode::NONE``. It's really just a FIX for SQL Server environments using ORM.
|
||||
|
||||
## Minor BC BREAK: `__clone` method not called anymore when entities are instantiated via metadata API
|
||||
|
||||
As of PHP 5.6, instantiation of new entities is deferred to the
|
||||
[`doctrine/instantiator`](https://github.com/doctrine/instantiator) library, which will avoid calling `__clone`
|
||||
or any public API on instantiated objects.
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Repository\DefaultRepositoryFactory` is now `final`
|
||||
|
||||
Please implement the `Doctrine\ORM\Repository\RepositoryFactory` interface instead of extending
|
||||
the `Doctrine\ORM\Repository\DefaultRepositoryFactory`.
|
||||
|
||||
## BC BREAK: New object expression DQL queries now respects user provided aliasing and not return consumed fields
|
||||
|
||||
When executing DQL queries with new object expressions, instead of returning DTOs numerically indexes, it will now respect user provided aliases. Consider the following query:
|
||||
|
||||
SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true
|
||||
|
||||
Previously, your result would be similar to this:
|
||||
|
||||
array(
|
||||
0=>array(
|
||||
0=>{UserDTO object},
|
||||
1=>{AddressDTO object},
|
||||
2=>{u.id scalar},
|
||||
3=>{u.name scalar},
|
||||
4=>{a.street scalar},
|
||||
5=>{a.postalCode scalar},
|
||||
'addressId'=>{a.id scalar},
|
||||
),
|
||||
...
|
||||
)
|
||||
|
||||
From now on, the resultset will look like this:
|
||||
|
||||
array(
|
||||
0=>array(
|
||||
'user'=>{UserDTO object},
|
||||
'address'=>{AddressDTO object},
|
||||
'addressId'=>{a.id scalar}
|
||||
),
|
||||
...
|
||||
)
|
||||
|
||||
## Minor BC BREAK: added second parameter $indexBy in EntityRepository#createQueryBuilder method signature
|
||||
|
||||
Added way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder()
|
||||
|
||||
# Upgrade to 2.4
|
||||
|
||||
## BC BREAK: Compatibility Bugfix in PersistentCollection#matching()
|
||||
@@ -479,17 +99,17 @@ above you must implement these new methods.
|
||||
|
||||
## Metadata Drivers
|
||||
|
||||
Metadata drivers have been rewritten to reuse code from `Doctrine\Persistence`. Anyone who is using the
|
||||
Metadata drivers have been rewritten to reuse code from Doctrine\Common. Anyone who is using the
|
||||
`Doctrine\ORM\Mapping\Driver\Driver` interface should instead refer to
|
||||
`Doctrine\Persistence\Mapping\Driver\MappingDriver`. Same applies to
|
||||
`Doctrine\Common\Persistence\Mapping\Driver\MappingDriver`. Same applies to
|
||||
`Doctrine\ORM\Mapping\Driver\AbstractFileDriver`: you should now refer to
|
||||
`Doctrine\Persistence\Mapping\Driver\FileDriver`.
|
||||
`Doctrine\Common\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\Persistence\Mapping\Driver\MappingDriverChain`
|
||||
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Persistence\Mapping\Driver\PHPDriver`
|
||||
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Persistence\Mapping\Driver\StaticPHPDriver`
|
||||
* `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`
|
||||
|
||||
# Upgrade to 2.2
|
||||
|
||||
@@ -578,7 +198,7 @@ Previously EntityManager#find(null) returned null. It now throws an exception.
|
||||
|
||||
## Interface for EntityRepository
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## AnnotationReader changes
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
include(__DIR__ . '/doctrine.php');
|
||||
include('doctrine.php');
|
||||
|
||||
@@ -31,7 +31,7 @@ $helperSet = null;
|
||||
if (file_exists($configFile)) {
|
||||
if ( ! is_readable($configFile)) {
|
||||
trigger_error(
|
||||
'Configuration file [' . $configFile . '] does not have read permission.', E_USER_ERROR
|
||||
'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -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" %*
|
||||
|
||||
Regular → Executable
+4
-14
@@ -20,19 +20,9 @@
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
|
||||
$autoloadFiles = [
|
||||
__DIR__ . '/../vendor/autoload.php',
|
||||
__DIR__ . '/../../../autoload.php'
|
||||
];
|
||||
(@include_once __DIR__ . '/../vendor/autoload.php') || @include_once __DIR__ . '/../../../autoload.php';
|
||||
|
||||
foreach ($autoloadFiles as $autoloadFile) {
|
||||
if (file_exists($autoloadFile)) {
|
||||
require_once $autoloadFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$directories = [getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config'];
|
||||
$directories = array(getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config');
|
||||
|
||||
$configFile = null;
|
||||
foreach ($directories as $directory) {
|
||||
@@ -53,7 +43,7 @@ if ( ! is_readable($configFile)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$commands = [];
|
||||
$commands = array();
|
||||
|
||||
$helperSet = require $configFile;
|
||||
|
||||
@@ -66,4 +56,4 @@ if ( ! ($helperSet instanceof HelperSet)) {
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleRunner::run($helperSet, $commands);
|
||||
\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet, $commands);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Version class and file
|
||||
project.version_class = Doctrine\\ORM\\Version
|
||||
project.version_file = lib/Doctrine/ORM/Version.php
|
||||
@@ -0,0 +1,16 @@
|
||||
version=2.0.0BETA2
|
||||
dependencies.common=2.0.0BETA4
|
||||
dependencies.dbal=2.0.0BETA4
|
||||
stability=beta
|
||||
build.dir=build
|
||||
dist.dir=dist
|
||||
report.dir=reports
|
||||
log.archive.dir=logs
|
||||
project.pirum_dir=
|
||||
project.download_dir=
|
||||
project.xsd_dir=
|
||||
test.phpunit_configuration_file=
|
||||
test.phpunit_generate_coverage=0
|
||||
test.pmd_reports=0
|
||||
test.pdepend_exec=
|
||||
test.phpmd_exec=
|
||||
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="DoctrineORM" default="build" basedir=".">
|
||||
<property file="build.properties" />
|
||||
|
||||
<target name="php">
|
||||
<exec executable="which" outputproperty="php_executable">
|
||||
<arg value="php" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="prepare">
|
||||
<mkdir dir="build" />
|
||||
</target>
|
||||
|
||||
<target name="build" depends="check-git-checkout-clean,prepare,php,composer">
|
||||
<exec executable="${php_executable}">
|
||||
<arg value="build/composer.phar" />
|
||||
<arg value="archive" />
|
||||
<arg value="--dir=build" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="composer" depends="php,composer-check,composer-download">
|
||||
<exec executable="${php_executable}">
|
||||
<arg value="build/composer.phar" />
|
||||
<arg value="install" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="composer-check" depends="prepare">
|
||||
<available file="build/composer.phar" property="composer.present"/>
|
||||
</target>
|
||||
|
||||
<target name="composer-download" unless="composer.present">
|
||||
<exec executable="wget">
|
||||
<arg value="-Obuild/composer.phar" />
|
||||
<arg value="http://getcomposer.org/composer.phar" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="make-release" depends="check-git-checkout-clean,prepare,php">
|
||||
<replace file="${project.version_file}" token="-DEV" value="" failOnNoReplacements="true" />
|
||||
<exec executable="${php_executable}" outputproperty="doctrine.current_version" failonerror="true">
|
||||
<arg value="-r" />
|
||||
<arg value="require_once '${project.version_file}';echo ${project.version_class}::VERSION;" />
|
||||
</exec>
|
||||
<exec executable="${php_executable}" outputproperty="doctrine.next_version" failonerror="true">
|
||||
<arg value="-r" />
|
||||
<arg value="$parts = explode('.', str_ireplace(array('-DEV', '-ALPHA', '-BETA'), '', '${doctrine.current_version}'));
|
||||
if (count($parts) != 3) {
|
||||
throw new \InvalidArgumentException('Version is assumed in format x.y.z, ${doctrine.current_version} given');
|
||||
}
|
||||
$parts[2]++;
|
||||
echo implode('.', $parts);
|
||||
" />
|
||||
</exec>
|
||||
|
||||
<git-commit file="${project.version_file}" message="Release ${doctrine.current_version}" />
|
||||
<git-tag version="${doctrine.current_version}" />
|
||||
<replace file="${project.version_file}" token="${doctrine.current_version}" value="${doctrine.next_version}-DEV" />
|
||||
<git-commit file="${project.version_file}" message="Bump version to ${doctrine.next_version}" />
|
||||
</target>
|
||||
|
||||
<target name="check-git-checkout-clean">
|
||||
<exec executable="git" failonerror="true">
|
||||
<arg value="diff-index" />
|
||||
<arg value="--quiet" />
|
||||
<arg value="HEAD" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<macrodef name="git-commit">
|
||||
<attribute name="file" default="NOT SET"/>
|
||||
<attribute name="message" default="NOT SET"/>
|
||||
|
||||
<sequential>
|
||||
<exec executable="git">
|
||||
<arg value="add" />
|
||||
<arg value="@{file}" />
|
||||
</exec>
|
||||
<exec executable="git">
|
||||
<arg value="commit" />
|
||||
<arg value="-m" />
|
||||
<arg value="@{message}" />
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<macrodef name="git-tag">
|
||||
<attribute name="version" default="NOT SET" />
|
||||
|
||||
<sequential>
|
||||
<exec executable="git">
|
||||
<arg value="tag" />
|
||||
<arg value="-m" />
|
||||
<arg value="v@{version}" />
|
||||
<arg value="v@{version}" />
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</project>
|
||||
@@ -1,38 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,39 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,38 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<!-- use an in-memory sqlite database -->
|
||||
<var name="db_driver" value="pdo_sqlite"/>
|
||||
<var name="db_memory" value="true"/>
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
+14
-36
@@ -3,60 +3,38 @@
|
||||
"type": "library",
|
||||
"description": "Object-Relational-Mapper for PHP",
|
||||
"keywords": ["orm", "database"],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
|
||||
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
|
||||
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
|
||||
],
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1|^8.0",
|
||||
"php": ">=5.3.2",
|
||||
"ext-pdo": "*",
|
||||
"composer/package-versions-deprecated": "^1.8",
|
||||
"doctrine/annotations": "^1.13",
|
||||
"doctrine/cache": "^1.11.3|^2.0.3",
|
||||
"doctrine/collections": "^1.5",
|
||||
"doctrine/common": "^3.0.3",
|
||||
"doctrine/dbal": "^2.13.0",
|
||||
"doctrine/deprecations": "^0.5.3",
|
||||
"doctrine/event-manager": "^1.1",
|
||||
"doctrine/inflector": "^1.4|^2.0",
|
||||
"doctrine/instantiator": "^1.3",
|
||||
"doctrine/lexer": "^1.0",
|
||||
"doctrine/persistence": "^2.2",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^3.0|^4.0|^5.0|^6.0"
|
||||
"doctrine/collections": "~1.1",
|
||||
"doctrine/dbal": "~2.4",
|
||||
"symfony/console": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpstan/phpstan": "^0.12.83",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.4",
|
||||
"squizlabs/php_codesniffer": "3.6.0",
|
||||
"symfony/cache": "^4.4|^5.2",
|
||||
"symfony/yaml": "^3.4|^4.0|^5.0|^6.0",
|
||||
"vimeo/psalm": "4.7.0"
|
||||
"symfony/yaml": "~2.1",
|
||||
"satooshi/php-coveralls": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\ORM\\": "lib/Doctrine/ORM" }
|
||||
"psr-0": { "Doctrine\\ORM\\": "lib/" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Tests\\": "tests/Doctrine/Tests",
|
||||
"Doctrine\\Performance\\": "tests/Doctrine/Performance"
|
||||
"bin": ["bin/doctrine", "bin/doctrine.php"],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4.x-dev"
|
||||
}
|
||||
},
|
||||
"bin": ["bin/doctrine"],
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"]
|
||||
}
|
||||
}
|
||||
|
||||
-363
@@ -1,363 +0,0 @@
|
||||
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
|
||||
|
||||
Attribution-NonCommercial-ShareAlike 3.0 Unported
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
|
||||
DAMAGES RESULTING FROM ITS USE.
|
||||
|
||||
License
|
||||
|
||||
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
|
||||
COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
|
||||
COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
|
||||
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
|
||||
|
||||
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
|
||||
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
|
||||
BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
|
||||
CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
|
||||
CONDITIONS.
|
||||
|
||||
1. Definitions
|
||||
|
||||
a. "Adaptation" means a work based upon the Work, or upon the Work and
|
||||
other pre-existing works, such as a translation, adaptation,
|
||||
derivative work, arrangement of music or other alterations of a
|
||||
literary or artistic work, or phonogram or performance and includes
|
||||
cinematographic adaptations or any other form in which the Work may be
|
||||
recast, transformed, or adapted including in any form recognizably
|
||||
derived from the original, except that a work that constitutes a
|
||||
Collection will not be considered an Adaptation for the purpose of
|
||||
this License. For the avoidance of doubt, where the Work is a musical
|
||||
work, performance or phonogram, the synchronization of the Work in
|
||||
timed-relation with a moving image ("synching") will be considered an
|
||||
Adaptation for the purpose of this License.
|
||||
b. "Collection" means a collection of literary or artistic works, such as
|
||||
encyclopedias and anthologies, or performances, phonograms or
|
||||
broadcasts, or other works or subject matter other than works listed
|
||||
in Section 1(g) below, which, by reason of the selection and
|
||||
arrangement of their contents, constitute intellectual creations, in
|
||||
which the Work is included in its entirety in unmodified form along
|
||||
with one or more other contributions, each constituting separate and
|
||||
independent works in themselves, which together are assembled into a
|
||||
collective whole. A work that constitutes a Collection will not be
|
||||
considered an Adaptation (as defined above) for the purposes of this
|
||||
License.
|
||||
c. "Distribute" means to make available to the public the original and
|
||||
copies of the Work or Adaptation, as appropriate, through sale or
|
||||
other transfer of ownership.
|
||||
d. "License Elements" means the following high-level license attributes
|
||||
as selected by Licensor and indicated in the title of this License:
|
||||
Attribution, Noncommercial, ShareAlike.
|
||||
e. "Licensor" means the individual, individuals, entity or entities that
|
||||
offer(s) the Work under the terms of this License.
|
||||
f. "Original Author" means, in the case of a literary or artistic work,
|
||||
the individual, individuals, entity or entities who created the Work
|
||||
or if no individual or entity can be identified, the publisher; and in
|
||||
addition (i) in the case of a performance the actors, singers,
|
||||
musicians, dancers, and other persons who act, sing, deliver, declaim,
|
||||
play in, interpret or otherwise perform literary or artistic works or
|
||||
expressions of folklore; (ii) in the case of a phonogram the producer
|
||||
being the person or legal entity who first fixes the sounds of a
|
||||
performance or other sounds; and, (iii) in the case of broadcasts, the
|
||||
organization that transmits the broadcast.
|
||||
g. "Work" means the literary and/or artistic work offered under the terms
|
||||
of this License including without limitation any production in the
|
||||
literary, scientific and artistic domain, whatever may be the mode or
|
||||
form of its expression including digital form, such as a book,
|
||||
pamphlet and other writing; a lecture, address, sermon or other work
|
||||
of the same nature; a dramatic or dramatico-musical work; a
|
||||
choreographic work or entertainment in dumb show; a musical
|
||||
composition with or without words; a cinematographic work to which are
|
||||
assimilated works expressed by a process analogous to cinematography;
|
||||
a work of drawing, painting, architecture, sculpture, engraving or
|
||||
lithography; a photographic work to which are assimilated works
|
||||
expressed by a process analogous to photography; a work of applied
|
||||
art; an illustration, map, plan, sketch or three-dimensional work
|
||||
relative to geography, topography, architecture or science; a
|
||||
performance; a broadcast; a phonogram; a compilation of data to the
|
||||
extent it is protected as a copyrightable work; or a work performed by
|
||||
a variety or circus performer to the extent it is not otherwise
|
||||
considered a literary or artistic work.
|
||||
h. "You" means an individual or entity exercising rights under this
|
||||
License who has not previously violated the terms of this License with
|
||||
respect to the Work, or who has received express permission from the
|
||||
Licensor to exercise rights under this License despite a previous
|
||||
violation.
|
||||
i. "Publicly Perform" means to perform public recitations of the Work and
|
||||
to communicate to the public those public recitations, by any means or
|
||||
process, including by wire or wireless means or public digital
|
||||
performances; to make available to the public Works in such a way that
|
||||
members of the public may access these Works from a place and at a
|
||||
place individually chosen by them; to perform the Work to the public
|
||||
by any means or process and the communication to the public of the
|
||||
performances of the Work, including by public digital performance; to
|
||||
broadcast and rebroadcast the Work by any means including signs,
|
||||
sounds or images.
|
||||
j. "Reproduce" means to make copies of the Work by any means including
|
||||
without limitation by sound or visual recordings and the right of
|
||||
fixation and reproducing fixations of the Work, including storage of a
|
||||
protected performance or phonogram in digital form or other electronic
|
||||
medium.
|
||||
|
||||
2. Fair Dealing Rights. Nothing in this License is intended to reduce,
|
||||
limit, or restrict any uses free from copyright or rights arising from
|
||||
limitations or exceptions that are provided for in connection with the
|
||||
copyright protection under copyright law or other applicable laws.
|
||||
|
||||
3. License Grant. Subject to the terms and conditions of this License,
|
||||
Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
perpetual (for the duration of the applicable copyright) license to
|
||||
exercise the rights in the Work as stated below:
|
||||
|
||||
a. to Reproduce the Work, to incorporate the Work into one or more
|
||||
Collections, and to Reproduce the Work as incorporated in the
|
||||
Collections;
|
||||
b. to create and Reproduce Adaptations provided that any such Adaptation,
|
||||
including any translation in any medium, takes reasonable steps to
|
||||
clearly label, demarcate or otherwise identify that changes were made
|
||||
to the original Work. For example, a translation could be marked "The
|
||||
original work was translated from English to Spanish," or a
|
||||
modification could indicate "The original work has been modified.";
|
||||
c. to Distribute and Publicly Perform the Work including as incorporated
|
||||
in Collections; and,
|
||||
d. to Distribute and Publicly Perform Adaptations.
|
||||
|
||||
The above rights may be exercised in all media and formats whether now
|
||||
known or hereafter devised. The above rights include the right to make
|
||||
such modifications as are technically necessary to exercise the rights in
|
||||
other media and formats. Subject to Section 8(f), all rights not expressly
|
||||
granted by Licensor are hereby reserved, including but not limited to the
|
||||
rights described in Section 4(e).
|
||||
|
||||
4. Restrictions. The license granted in Section 3 above is expressly made
|
||||
subject to and limited by the following restrictions:
|
||||
|
||||
a. You may Distribute or Publicly Perform the Work only under the terms
|
||||
of this License. You must include a copy of, or the Uniform Resource
|
||||
Identifier (URI) for, this License with every copy of the Work You
|
||||
Distribute or Publicly Perform. You may not offer or impose any terms
|
||||
on the Work that restrict the terms of this License or the ability of
|
||||
the recipient of the Work to exercise the rights granted to that
|
||||
recipient under the terms of the License. You may not sublicense the
|
||||
Work. You must keep intact all notices that refer to this License and
|
||||
to the disclaimer of warranties with every copy of the Work You
|
||||
Distribute or Publicly Perform. When You Distribute or Publicly
|
||||
Perform the Work, You may not impose any effective technological
|
||||
measures on the Work that restrict the ability of a recipient of the
|
||||
Work from You to exercise the rights granted to that recipient under
|
||||
the terms of the License. This Section 4(a) applies to the Work as
|
||||
incorporated in a Collection, but this does not require the Collection
|
||||
apart from the Work itself to be made subject to the terms of this
|
||||
License. If You create a Collection, upon notice from any Licensor You
|
||||
must, to the extent practicable, remove from the Collection any credit
|
||||
as required by Section 4(d), as requested. If You create an
|
||||
Adaptation, upon notice from any Licensor You must, to the extent
|
||||
practicable, remove from the Adaptation any credit as required by
|
||||
Section 4(d), as requested.
|
||||
b. You may Distribute or Publicly Perform an Adaptation only under: (i)
|
||||
the terms of this License; (ii) a later version of this License with
|
||||
the same License Elements as this License; (iii) a Creative Commons
|
||||
jurisdiction license (either this or a later license version) that
|
||||
contains the same License Elements as this License (e.g.,
|
||||
Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License").
|
||||
You must include a copy of, or the URI, for Applicable License with
|
||||
every copy of each Adaptation You Distribute or Publicly Perform. You
|
||||
may not offer or impose any terms on the Adaptation that restrict the
|
||||
terms of the Applicable License or the ability of the recipient of the
|
||||
Adaptation to exercise the rights granted to that recipient under the
|
||||
terms of the Applicable License. You must keep intact all notices that
|
||||
refer to the Applicable License and to the disclaimer of warranties
|
||||
with every copy of the Work as included in the Adaptation You
|
||||
Distribute or Publicly Perform. When You Distribute or Publicly
|
||||
Perform the Adaptation, You may not impose any effective technological
|
||||
measures on the Adaptation that restrict the ability of a recipient of
|
||||
the Adaptation from You to exercise the rights granted to that
|
||||
recipient under the terms of the Applicable License. This Section 4(b)
|
||||
applies to the Adaptation as incorporated in a Collection, but this
|
||||
does not require the Collection apart from the Adaptation itself to be
|
||||
made subject to the terms of the Applicable License.
|
||||
c. You may not exercise any of the rights granted to You in Section 3
|
||||
above in any manner that is primarily intended for or directed toward
|
||||
commercial advantage or private monetary compensation. The exchange of
|
||||
the Work for other copyrighted works by means of digital file-sharing
|
||||
or otherwise shall not be considered to be intended for or directed
|
||||
toward commercial advantage or private monetary compensation, provided
|
||||
there is no payment of any monetary compensation in con-nection with
|
||||
the exchange of copyrighted works.
|
||||
d. If You Distribute, or Publicly Perform the Work or any Adaptations or
|
||||
Collections, You must, unless a request has been made pursuant to
|
||||
Section 4(a), keep intact all copyright notices for the Work and
|
||||
provide, reasonable to the medium or means You are utilizing: (i) the
|
||||
name of the Original Author (or pseudonym, if applicable) if supplied,
|
||||
and/or if the Original Author and/or Licensor designate another party
|
||||
or parties (e.g., a sponsor institute, publishing entity, journal) for
|
||||
attribution ("Attribution Parties") in Licensor's copyright notice,
|
||||
terms of service or by other reasonable means, the name of such party
|
||||
or parties; (ii) the title of the Work if supplied; (iii) to the
|
||||
extent reasonably practicable, the URI, if any, that Licensor
|
||||
specifies to be associated with the Work, unless such URI does not
|
||||
refer to the copyright notice or licensing information for the Work;
|
||||
and, (iv) consistent with Section 3(b), in the case of an Adaptation,
|
||||
a credit identifying the use of the Work in the Adaptation (e.g.,
|
||||
"French translation of the Work by Original Author," or "Screenplay
|
||||
based on original Work by Original Author"). The credit required by
|
||||
this Section 4(d) may be implemented in any reasonable manner;
|
||||
provided, however, that in the case of a Adaptation or Collection, at
|
||||
a minimum such credit will appear, if a credit for all contributing
|
||||
authors of the Adaptation or Collection appears, then as part of these
|
||||
credits and in a manner at least as prominent as the credits for the
|
||||
other contributing authors. For the avoidance of doubt, You may only
|
||||
use the credit required by this Section for the purpose of attribution
|
||||
in the manner set out above and, by exercising Your rights under this
|
||||
License, You may not implicitly or explicitly assert or imply any
|
||||
connection with, sponsorship or endorsement by the Original Author,
|
||||
Licensor and/or Attribution Parties, as appropriate, of You or Your
|
||||
use of the Work, without the separate, express prior written
|
||||
permission of the Original Author, Licensor and/or Attribution
|
||||
Parties.
|
||||
e. For the avoidance of doubt:
|
||||
|
||||
i. Non-waivable Compulsory License Schemes. In those jurisdictions in
|
||||
which the right to collect royalties through any statutory or
|
||||
compulsory licensing scheme cannot be waived, the Licensor
|
||||
reserves the exclusive right to collect such royalties for any
|
||||
exercise by You of the rights granted under this License;
|
||||
ii. Waivable Compulsory License Schemes. In those jurisdictions in
|
||||
which the right to collect royalties through any statutory or
|
||||
compulsory licensing scheme can be waived, the Licensor reserves
|
||||
the exclusive right to collect such royalties for any exercise by
|
||||
You of the rights granted under this License if Your exercise of
|
||||
such rights is for a purpose or use which is otherwise than
|
||||
noncommercial as permitted under Section 4(c) and otherwise waives
|
||||
the right to collect royalties through any statutory or compulsory
|
||||
licensing scheme; and,
|
||||
iii. Voluntary License Schemes. The Licensor reserves the right to
|
||||
collect royalties, whether individually or, in the event that the
|
||||
Licensor is a member of a collecting society that administers
|
||||
voluntary licensing schemes, via that society, from any exercise
|
||||
by You of the rights granted under this License that is for a
|
||||
purpose or use which is otherwise than noncommercial as permitted
|
||||
under Section 4(c).
|
||||
f. Except as otherwise agreed in writing by the Licensor or as may be
|
||||
otherwise permitted by applicable law, if You Reproduce, Distribute or
|
||||
Publicly Perform the Work either by itself or as part of any
|
||||
Adaptations or Collections, You must not distort, mutilate, modify or
|
||||
take other derogatory action in relation to the Work which would be
|
||||
prejudicial to the Original Author's honor or reputation. Licensor
|
||||
agrees that in those jurisdictions (e.g. Japan), in which any exercise
|
||||
of the right granted in Section 3(b) of this License (the right to
|
||||
make Adaptations) would be deemed to be a distortion, mutilation,
|
||||
modification or other derogatory action prejudicial to the Original
|
||||
Author's honor and reputation, the Licensor will waive or not assert,
|
||||
as appropriate, this Section, to the fullest extent permitted by the
|
||||
applicable national law, to enable You to reasonably exercise Your
|
||||
right under Section 3(b) of this License (right to make Adaptations)
|
||||
but not otherwise.
|
||||
|
||||
5. Representations, Warranties and Disclaimer
|
||||
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE
|
||||
FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS
|
||||
AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
|
||||
WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
|
||||
LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
|
||||
WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
|
||||
|
||||
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
|
||||
LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
|
||||
ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
|
||||
ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
|
||||
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. Termination
|
||||
|
||||
a. This License and the rights granted hereunder will terminate
|
||||
automatically upon any breach by You of the terms of this License.
|
||||
Individuals or entities who have received Adaptations or Collections
|
||||
from You under this License, however, will not have their licenses
|
||||
terminated provided such individuals or entities remain in full
|
||||
compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
|
||||
survive any termination of this License.
|
||||
b. Subject to the above terms and conditions, the license granted here is
|
||||
perpetual (for the duration of the applicable copyright in the Work).
|
||||
Notwithstanding the above, Licensor reserves the right to release the
|
||||
Work under different license terms or to stop distributing the Work at
|
||||
any time; provided, however that any such election will not serve to
|
||||
withdraw this License (or any other license that has been, or is
|
||||
required to be, granted under the terms of this License), and this
|
||||
License will continue in full force and effect unless terminated as
|
||||
stated above.
|
||||
|
||||
8. Miscellaneous
|
||||
|
||||
a. Each time You Distribute or Publicly Perform the Work or a Collection,
|
||||
the Licensor offers to the recipient a license to the Work on the same
|
||||
terms and conditions as the license granted to You under this License.
|
||||
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
|
||||
offers to the recipient a license to the original Work on the same
|
||||
terms and conditions as the license granted to You under this License.
|
||||
c. If any provision of this License is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of
|
||||
the remainder of the terms of this License, and without further action
|
||||
by the parties to this agreement, such provision shall be reformed to
|
||||
the minimum extent necessary to make such provision valid and
|
||||
enforceable.
|
||||
d. No term or provision of this License shall be deemed waived and no
|
||||
breach consented to unless such waiver or consent shall be in writing
|
||||
and signed by the party to be charged with such waiver or consent.
|
||||
e. This License constitutes the entire agreement between the parties with
|
||||
respect to the Work licensed here. There are no understandings,
|
||||
agreements or representations with respect to the Work not specified
|
||||
here. Licensor shall not be bound by any additional provisions that
|
||||
may appear in any communication from You. This License may not be
|
||||
modified without the mutual written agreement of the Licensor and You.
|
||||
f. The rights granted under, and the subject matter referenced, in this
|
||||
License were drafted utilizing the terminology of the Berne Convention
|
||||
for the Protection of Literary and Artistic Works (as amended on
|
||||
September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
|
||||
Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
|
||||
and the Universal Copyright Convention (as revised on July 24, 1971).
|
||||
These rights and subject matter take effect in the relevant
|
||||
jurisdiction in which the License terms are sought to be enforced
|
||||
according to the corresponding provisions of the implementation of
|
||||
those treaty provisions in the applicable national law. If the
|
||||
standard suite of rights granted under applicable copyright law
|
||||
includes additional rights not granted under this License, such
|
||||
additional rights are deemed to be included in the License; this
|
||||
License is not intended to restrict the license of any rights under
|
||||
applicable law.
|
||||
|
||||
|
||||
Creative Commons Notice
|
||||
|
||||
Creative Commons is not a party to this License, and makes no warranty
|
||||
whatsoever in connection with the Work. Creative Commons will not be
|
||||
liable to You or any party on any legal theory for any damages
|
||||
whatsoever, including without limitation any general, special,
|
||||
incidental or consequential damages arising in connection to this
|
||||
license. Notwithstanding the foregoing two (2) sentences, if Creative
|
||||
Commons has expressly identified itself as the Licensor hereunder, it
|
||||
shall have all rights and obligations of Licensor.
|
||||
|
||||
Except for the limited purpose of indicating to the public that the
|
||||
Work is licensed under the CCPL, Creative Commons does not authorize
|
||||
the use by either party of the trademark "Creative Commons" or any
|
||||
related trademark or logo of Creative Commons without the prior
|
||||
written consent of Creative Commons. Any permitted use will be in
|
||||
compliance with Creative Commons' then-current trademark usage
|
||||
guidelines, as may be published on its website or otherwise made
|
||||
available upon request from time to time. For the avoidance of doubt,
|
||||
this trademark restriction does not form part of this License.
|
||||
|
||||
Creative Commons may be contacted at http://creativecommons.org/.
|
||||
|
||||
+2
-12
@@ -1,18 +1,8 @@
|
||||
# Doctrine ORM Documentation
|
||||
|
||||
## How to Generate:
|
||||
Using Ubuntu 14.04 LTS:
|
||||
## How to Generate
|
||||
|
||||
1. Run ./bin/install-dependencies.sh
|
||||
2. Run ./bin/generate-docs.sh
|
||||
|
||||
It will generate the documentation into the build directory of the checkout.
|
||||
|
||||
|
||||
## Theme issues
|
||||
|
||||
If you get a "Theme error", check if the `en/_theme` subdirectory is empty,
|
||||
in which case you will need to run:
|
||||
|
||||
1. git submodule init
|
||||
2. git submodule update
|
||||
It will generate the documentation into the build directory of the checkout.
|
||||
@@ -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,4 @@
|
||||
#!/bin/bash
|
||||
sudo apt-get update && sudo apt-get install -y python2.7 python-sphinx python-pygments
|
||||
sudo apt-get install python25 python25-dev texlive-full rubber
|
||||
sudo easy_install pygments
|
||||
sudo easy_install sphinx
|
||||
+1
-1
Submodule docs/en/_theme updated: 6f1bc8bead...68795c5888
+2
-2
@@ -11,7 +11,7 @@
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os, datetime
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
@@ -38,7 +38,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Doctrine 2 ORM'
|
||||
copyright = u'2010-%y, Doctrine Project Team'.format(datetime.date.today)
|
||||
copyright = u'2010-12, Doctrine Project Team'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
||||
@@ -11,9 +11,9 @@ There are several ways to achieve this: converting the value inside the Type
|
||||
class, converting the value on the database-level or a combination of both.
|
||||
|
||||
This article describes the third way by implementing the MySQL specific column
|
||||
type `Point <https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html>`_.
|
||||
type `Point <http://dev.mysql.com/doc/refman/5.5/en/gis-class-point.html>`_.
|
||||
|
||||
The ``Point`` type is part of the `Spatial extension <https://dev.mysql.com/doc/refman/8.0/en/spatial-extensions.html>`_
|
||||
The ``Point`` type is part of the `Spatial extension <http://dev.mysql.com/doc/refman/5.5/en/spatial-extensions.html>`_
|
||||
of MySQL and enables you to store a single location in a coordinate space by
|
||||
using x and y coordinates. You can use the Point type to store a
|
||||
longitude/latitude pair to represent a geographic location.
|
||||
@@ -150,7 +150,7 @@ Now we're going to create the ``point`` type and implement all required methods.
|
||||
return self::POINT;
|
||||
}
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
return 'POINT';
|
||||
}
|
||||
@@ -192,9 +192,9 @@ object into a string representation before saving to the database (in the
|
||||
``convertToDatabaseValue`` method) and back into an object after fetching the
|
||||
value from the database (in the ``convertToPHPValue`` method).
|
||||
|
||||
The format of the string representation format is called
|
||||
`Well-known text (WKT) <http://en.wikipedia.org/wiki/Well-known_text>`_.
|
||||
The advantage of this format is, that it is both human readable and parsable by MySQL.
|
||||
The format of the string representation format is called `Well-known text (WKT)
|
||||
<http://en.wikipedia.org/wiki/Well-known_text>`_. The advantage of this format
|
||||
is, that it is both human readable and parsable by MySQL.
|
||||
|
||||
Internally, MySQL stores geometry values in a binary format that is not
|
||||
identical to the WKT format. So, we need to let MySQL transform the WKT
|
||||
@@ -204,8 +204,8 @@ This is where the ``convertToPHPValueSQL`` and ``convertToDatabaseValueSQL``
|
||||
methods come into play.
|
||||
|
||||
This methods wrap a sql expression (the WKT representation of the Point) into
|
||||
MySQL functions `ST_PointFromText <https://dev.mysql.com/doc/refman/8.0/en/gis-wkt-functions.html#function_st-pointfromtext>`_
|
||||
and `ST_AsText <https://dev.mysql.com/doc/refman/8.0/en/gis-format-conversion-functions.html#function_st-astext>`_
|
||||
MySQL functions `PointFromText <http://dev.mysql.com/doc/refman/5.5/en/creating-spatial-values.html#function_pointfromtext>`_
|
||||
and `AsText <http://dev.mysql.com/doc/refman/5.5/en/functions-to-convert-geometries-between-formats.html#function_astext>`_
|
||||
which convert WKT strings to and from the internal format of MySQL.
|
||||
|
||||
.. note::
|
||||
@@ -232,7 +232,7 @@ Example usage
|
||||
// Setup custom mapping type
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
Type::addType('point', 'Geo\Types\PointType');
|
||||
Type::addType('point', 'Geo\Types\Point');
|
||||
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('point', 'point');
|
||||
|
||||
// Store a Location object
|
||||
@@ -249,7 +249,7 @@ Example usage
|
||||
$em->clear();
|
||||
|
||||
// Fetch the Location object
|
||||
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location l WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
|
||||
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
|
||||
$location = $query->getSingleResult();
|
||||
|
||||
/* @var Geo\ValueObject\Point */
|
||||
|
||||
@@ -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 ORM offers several ways to get access to
|
||||
traditionally. Doctrine 2 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 where money is composed of
|
||||
For simplicity we live in a world were 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,39 +32,30 @@ Our entities look like:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace Bank\Entities;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id;
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/** @Column(type="string", unique=true) */
|
||||
private $no;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", unique=true)
|
||||
* @OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
|
||||
*/
|
||||
private string $no;
|
||||
private $entries;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
private array $entries;
|
||||
private $maxCredit = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private int $maxCredit = 0;
|
||||
|
||||
public function __construct(string $no, int $maxCredit = 0)
|
||||
public function __construct($no, $maxCredit = 0)
|
||||
{
|
||||
$this->no = $no;
|
||||
$this->maxCredit = $maxCredit;
|
||||
@@ -73,35 +64,31 @@ Our entities look like:
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class Entry
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id;
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
|
||||
* @ManyToOne(targetEntity="Account", inversedBy="entries")
|
||||
*/
|
||||
private Account $account;
|
||||
private $account;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
private int $amount;
|
||||
private $amount;
|
||||
|
||||
public function __construct(Account $account, int $amount)
|
||||
public function __construct($account, $amount)
|
||||
{
|
||||
$this->account = $account;
|
||||
$this->amount = $amount;
|
||||
// more stuff here, from/to whom, stated reason, execution date and such
|
||||
}
|
||||
|
||||
public function getAmount(): Amount
|
||||
public function getAmount()
|
||||
{
|
||||
return $this->amount;
|
||||
}
|
||||
@@ -159,14 +146,12 @@ collection, which means we can compute this value at runtime:
|
||||
class Account
|
||||
{
|
||||
// .. previous code
|
||||
|
||||
public function getBalance(): int
|
||||
public function getBalance()
|
||||
{
|
||||
$balance = 0;
|
||||
foreach ($this->entries as $entry) {
|
||||
foreach ($this->entries AS $entry) {
|
||||
$balance += $entry->getAmount();
|
||||
}
|
||||
|
||||
return $balance;
|
||||
}
|
||||
}
|
||||
@@ -190,12 +175,13 @@ relation with this method:
|
||||
<?php
|
||||
class Account
|
||||
{
|
||||
public function addEntry(int $amount): void
|
||||
public function addEntry($amount)
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,10 +190,7 @@ Now look at the following test-code for our entities:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AccountTest extends TestCase
|
||||
class AccountTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testAddEntry()
|
||||
{
|
||||
@@ -225,7 +208,7 @@ Now look at the following test-code for our entities:
|
||||
{
|
||||
$account = new Account("123456", $maxCredit = 200);
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->setExpectedException("Exception");
|
||||
$account->addEntry(-1000);
|
||||
}
|
||||
}
|
||||
@@ -236,12 +219,9 @@ To enforce our rule we can now implement the assertion in
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class Account
|
||||
{
|
||||
// .. previous code
|
||||
|
||||
private function assertAcceptEntryAllowed(int $amount): void
|
||||
private function assertAcceptEntryAllowed($amount)
|
||||
{
|
||||
$futureBalance = $this->getBalance() + $amount;
|
||||
$allowedMinimalBalance = ($this->maxCredit * -1);
|
||||
@@ -286,22 +266,23 @@ entries collection) we want to add an aggregate field called
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
private int $balance = 0;
|
||||
private $balance = 0;
|
||||
|
||||
public function getBalance(): int
|
||||
public function getBalance()
|
||||
{
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
public function addEntry(int $amount): void
|
||||
public function addEntry($amount)
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
$this->balance += $amount;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,15 +306,12 @@ 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(Account::class, $accId);
|
||||
$account1 = $em->find('Bank\Entities\Account', $accId);
|
||||
|
||||
// request 2 account
|
||||
$account2 = $em->find(Account::class, $accId);
|
||||
$account2 = $em->find('Bank\Entities\Account', $accId);
|
||||
|
||||
$account1->addEntry(-200);
|
||||
$account2->addEntry(-200);
|
||||
@@ -344,7 +322,7 @@ The aggregate field ``Account::$balance`` is now -200, however the
|
||||
SUM over all entries amounts yields -400. A violation of our max
|
||||
credit rule.
|
||||
|
||||
You can use both optimistic or pessimistic locking to safe-guard
|
||||
You can use both optimistic or pessimistic locking to save-guard
|
||||
your aggregate fields against this kind of race-conditions. Reading
|
||||
Eric Evans DDD carefully he mentions that the "Aggregate Root"
|
||||
(Account in our example) needs a locking mechanism.
|
||||
@@ -354,14 +332,10 @@ Optimistic locking is as easy as adding a version column:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class Account
|
||||
class Amount
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Version
|
||||
*/
|
||||
private int $version;
|
||||
/** @Column(type="integer") @Version */
|
||||
private $version;
|
||||
}
|
||||
|
||||
The previous example would then throw an exception in the face of
|
||||
@@ -375,11 +349,9 @@ the database using a FOR UPDATE.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Bank\Entities\Account;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ);
|
||||
|
||||
$account = $em->find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ);
|
||||
|
||||
Keeping Updates and Deletes in Sync
|
||||
-----------------------------------
|
||||
@@ -400,3 +372,5 @@ field that offers serious performance benefits over iterating all
|
||||
the related objects that make up an aggregate value. Finally I
|
||||
showed how you can ensure that your aggregate fields do not get out
|
||||
of sync due to race-conditions and concurrent access.
|
||||
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
Custom Mapping Types
|
||||
====================
|
||||
|
||||
Doctrine allows you to create new mapping types. This can come in
|
||||
handy when you're missing a specific mapping type or when you want
|
||||
to replace the existing implementation of a mapping type.
|
||||
|
||||
In order to create a new mapping type you need to subclass
|
||||
``Doctrine\DBAL\Types\Type`` and implement/override the methods as
|
||||
you wish. Here is an example skeleton of such a custom type class:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace My\Project\Types;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
/**
|
||||
* My custom datatype.
|
||||
*/
|
||||
class MyType extends Type
|
||||
{
|
||||
const MYTYPE = 'mytype'; // modify to match your type name
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
// return the SQL used to create your column type. To create a portable column type, use the $platform.
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is read from the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is written to the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return self::MYTYPE; // modify to match your constant name
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
The following assumptions are applied to mapping types by the ORM:
|
||||
|
||||
- The ``UnitOfWork`` never passes values to the database convert
|
||||
method that did not change in the request.
|
||||
- The ``UnitOfWork`` internally assumes that entity identifiers are
|
||||
castable to string. Hence, when using custom types that map to PHP
|
||||
objects as IDs, such objects must implement the ``__toString()`` magic
|
||||
method.
|
||||
|
||||
When you have implemented the type you still need to let Doctrine
|
||||
know about it. This can be achieved through the
|
||||
``Doctrine\DBAL\Types\Type#addType($name, $className)``
|
||||
method. See the following example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// in bootstrapping code
|
||||
|
||||
// ...
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
// ...
|
||||
|
||||
// Register my type
|
||||
Type::addType('mytype', 'My\Project\Types\MyType');
|
||||
|
||||
To convert the underlying database type of your
|
||||
new "mytype" directly into an instance of ``MyType`` when performing
|
||||
schema operations, the type has to be registered with the database
|
||||
platform as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$conn = $em->getConnection();
|
||||
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype');
|
||||
|
||||
When registering the custom types in the configuration you specify a unique
|
||||
name for the mapping type and map that to the corresponding fully qualified
|
||||
class name. Now the new type can be used when mapping columns:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyPersistentClass
|
||||
{
|
||||
/** @Column(type="mytype") */
|
||||
private $field;
|
||||
}
|
||||
|
||||
@@ -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 ORM to persist an implementation of the
|
||||
This recipe will show you a simple example of how you can use
|
||||
Doctrine 2 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 ORM is configured and an instance of
|
||||
|
||||
// assumes Doctrine 2 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 ORM: Custom AST Walkers
|
||||
Extending DQL in Doctrine 2: 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 ORM in contrast has a real parser for the DQL language,
|
||||
Doctrine 2 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
|
||||
@@ -112,7 +112,7 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
}
|
||||
|
||||
It clones the query, resets the limit clause first and max results
|
||||
and registers the ``CountSqlWalker`` custom tree walker which
|
||||
and registers the ``CountSqlWalker`` customer tree walker which
|
||||
will modify the AST to execute a count query. The walkers
|
||||
implementation is:
|
||||
|
||||
@@ -130,7 +130,7 @@ implementation is:
|
||||
{
|
||||
$parent = null;
|
||||
$parentName = null;
|
||||
foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {
|
||||
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
|
||||
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
|
||||
$parent = $qComp;
|
||||
$parentName = $dqlAlias;
|
||||
@@ -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 @@ We will implement a custom Output Walker that allows to specify the
|
||||
|
||||
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 ORM also allows you to handwrite
|
||||
It is worth to mention that Doctrine 2 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
|
||||
@@ -21,7 +21,7 @@ the :doc:`Native Query <../reference/native-sql>` chapter.
|
||||
The DQL Parser has hooks to register functions that can then be
|
||||
used in your DQL queries and transformed into SQL, allowing to
|
||||
extend Doctrines Query capabilities to the vendors strength. This
|
||||
post explains the User-Defined Functions API (UDF) of the Dql
|
||||
post explains the Used-Defined Functions API (UDF) of the Dql
|
||||
Parser and shows some examples to give you some hints how you would
|
||||
extend DQL.
|
||||
|
||||
@@ -53,24 +53,13 @@ DQL query. ``$class`` is a string of a class-name which has to
|
||||
extend ``Doctrine\ORM\Query\Node\FunctionNode``. This is a class
|
||||
that offers all the necessary API and methods to implement a UDF.
|
||||
|
||||
Instead of providing the function class name, you can also provide
|
||||
a callable that returns the function object:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->addCustomStringFunction($name, function () {
|
||||
return new MyCustomFunction();
|
||||
});
|
||||
|
||||
In this post we will implement some MySql specific Date calculation
|
||||
methods, which are quite handy in my opinion:
|
||||
|
||||
Date Diff
|
||||
---------
|
||||
|
||||
`Mysql's DateDiff function <https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_datediff>`_
|
||||
`Mysql's DateDiff function <http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_datediff>`_
|
||||
takes two dates as argument and calculates the difference in days
|
||||
with ``date1-date2``.
|
||||
|
||||
@@ -132,7 +121,7 @@ dql statement.
|
||||
|
||||
The ``ArithmeticPrimary`` method call is the most common
|
||||
denominator of valid EBNF tokens taken from the
|
||||
`DQL EBNF grammar <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
|
||||
`DQL EBNF grammar <http://www.doctrine-project.org/documentation/manual/2_0/en/dql-doctrine-query-language#ebnf>`_
|
||||
that matches our requirements for valid input into the DateDiff Dql
|
||||
function. Picking the right tokens for your methods is a tricky
|
||||
business, but the EBNF grammar is pretty helpful finding it, as is
|
||||
@@ -164,7 +153,7 @@ Date Add
|
||||
|
||||
Often useful it the ability to do some simple date calculations in
|
||||
your DQL query using
|
||||
`MySql's DATE_ADD function <https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add>`_.
|
||||
`MySql's DATE\_ADD function <http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add>`_.
|
||||
|
||||
I'll skip the blah and show the code for this function:
|
||||
|
||||
@@ -240,12 +229,12 @@ functionalities in DQL, we would be excited to see user extensions
|
||||
that add vendor specific function packages, for example more math
|
||||
functions, XML + GIS Support, Hashing functions and so on.
|
||||
|
||||
For ORM we will come with the current set of functions, however for
|
||||
For 2.0 we will come with the current set of functions, however for
|
||||
a future version we will re-evaluate if we can abstract even more
|
||||
vendor sql functions and extend the DQL languages scope.
|
||||
|
||||
Code for this Extension to DQL and other Doctrine Extensions can be
|
||||
found
|
||||
`in the GitHub DoctrineExtensions repository <http://github.com/beberlei/DoctrineExtensions>`_.
|
||||
`in my Github DoctrineExtensions repository <http://github.com/beberlei/DoctrineExtensions>`_.
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ Serializing entity into the session
|
||||
-----------------------------------
|
||||
|
||||
Entities that are serialized into the session normally contain references to
|
||||
other entities as well. Think of the user entity has a reference to their
|
||||
other entities as well. Think of the user entity has a reference to his
|
||||
articles, groups, photos or many other different entities. If you serialize
|
||||
this object into the session then you don't want to serialize the related
|
||||
entities as well. This is why you should call ``EntityManager#detach()`` on this
|
||||
|
||||
@@ -10,11 +10,6 @@ code should look like. We will implement it on a
|
||||
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
for all our domain objects.
|
||||
|
||||
.. note::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
Implementing NotifyPropertyChanged
|
||||
----------------------------------
|
||||
|
||||
@@ -27,8 +22,8 @@ implement the ``NotifyPropertyChanged`` interface from the
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Persistence\NotifyPropertyChanged;
|
||||
use Doctrine\Persistence\PropertyChangedListener;
|
||||
use Doctrine\Common\NotifyPropertyChanged;
|
||||
use Doctrine\Common\PropertyChangedListener;
|
||||
|
||||
abstract class DomainObject implements NotifyPropertyChanged
|
||||
{
|
||||
|
||||
@@ -4,15 +4,15 @@ Implementing Wakeup or Clone
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
|
||||
As explained in the
|
||||
`restrictions for entity classes in the manual <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
|
||||
`restrictions for entity classes in the manual <http://www.doctrine-project.org/documentation/manual/2_0/en/architecture#entities>`_,
|
||||
it is usually not allowed for an entity to implement ``__wakeup``
|
||||
or ``__clone``, because Doctrine makes special use of them.
|
||||
However, it is quite easy to make use of these methods in a safe
|
||||
way by guarding the custom wakeup or clone code with an entity
|
||||
identity check, as demonstrated in the following sections.
|
||||
|
||||
Safely implementing __wakeup
|
||||
----------------------------
|
||||
Safely implementing \_\_wakeup
|
||||
------------------------------
|
||||
|
||||
To safely implement ``__wakeup``, simply enclose your
|
||||
implementation code in an identity check as follows:
|
||||
@@ -37,8 +37,8 @@ implementation code in an identity check as follows:
|
||||
//...
|
||||
}
|
||||
|
||||
Safely implementing __clone
|
||||
---------------------------
|
||||
Safely implementing \_\_clone
|
||||
-----------------------------
|
||||
|
||||
Safely implementing ``__clone`` is pretty much the same:
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
Integrating with CodeIgniter
|
||||
============================
|
||||
|
||||
This is recipe for using Doctrine 2 in your
|
||||
`CodeIgniter <http://www.codeigniter.com>`_ framework.
|
||||
|
||||
.. note::
|
||||
|
||||
This might not work for all CodeIgniter versions and may require
|
||||
slight adjustments.
|
||||
|
||||
|
||||
Here is how to set it up:
|
||||
|
||||
Make a CodeIgniter library that is both a wrapper and a bootstrap
|
||||
for Doctrine 2.
|
||||
|
||||
Setting up the file structure
|
||||
-----------------------------
|
||||
|
||||
Here are the steps:
|
||||
|
||||
|
||||
- Add a php file to your system/application/libraries folder
|
||||
called Doctrine.php. This is going to be your wrapper/bootstrap for
|
||||
the D2 entity manager.
|
||||
- Put the Doctrine folder (the one that contains Common, DBAL, and
|
||||
ORM) inside that same libraries folder.
|
||||
- Your system/application/libraries folder now looks like this:
|
||||
|
||||
system/applications/libraries -Doctrine -Doctrine.php -index.html
|
||||
|
||||
- If you want, open your config/autoload.php file and autoload
|
||||
your Doctrine library.
|
||||
|
||||
<?php $autoload['libraries'] = array('doctrine');
|
||||
|
||||
|
||||
Creating your Doctrine CodeIgniter library
|
||||
------------------------------------------
|
||||
|
||||
Now, here is what your Doctrine.php file should look like.
|
||||
Customize it to your needs.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\ClassLoader,
|
||||
Doctrine\ORM\Configuration,
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\Common\Cache\ArrayCache,
|
||||
Doctrine\DBAL\Logging\EchoSQLLogger;
|
||||
|
||||
class Doctrine {
|
||||
|
||||
public $em = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// load database configuration from CodeIgniter
|
||||
require_once APPPATH.'config/database.php';
|
||||
|
||||
// Set up class loading. You could use different autoloaders, provided by your favorite framework,
|
||||
// if you want to.
|
||||
require_once APPPATH.'libraries/Doctrine/Common/ClassLoader.php';
|
||||
|
||||
$doctrineClassLoader = new ClassLoader('Doctrine', APPPATH.'libraries');
|
||||
$doctrineClassLoader->register();
|
||||
$entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" ));
|
||||
$entitiesClassLoader->register();
|
||||
$proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies');
|
||||
$proxiesClassLoader->register();
|
||||
|
||||
// Set up caches
|
||||
$config = new Configuration;
|
||||
$cache = new ArrayCache;
|
||||
$config->setMetadataCacheImpl($cache);
|
||||
$driverImpl = $config->newDefaultAnnotationDriver(array(APPPATH.'models/Entities'));
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
$config->setQueryCacheImpl($cache);
|
||||
|
||||
$config->setQueryCacheImpl($cache);
|
||||
|
||||
// Proxy configuration
|
||||
$config->setProxyDir(APPPATH.'/models/proxies');
|
||||
$config->setProxyNamespace('Proxies');
|
||||
|
||||
// Set up logger
|
||||
$logger = new EchoSQLLogger;
|
||||
$config->setSQLLogger($logger);
|
||||
|
||||
$config->setAutoGenerateProxyClasses( TRUE );
|
||||
|
||||
// Database connection information
|
||||
$connectionOptions = array(
|
||||
'driver' => 'pdo_mysql',
|
||||
'user' => $db['default']['username'],
|
||||
'password' => $db['default']['password'],
|
||||
'host' => $db['default']['hostname'],
|
||||
'dbname' => $db['default']['database']
|
||||
);
|
||||
|
||||
// Create EntityManager
|
||||
$this->em = EntityManager::create($connectionOptions, $config);
|
||||
}
|
||||
}
|
||||
|
||||
Please note that this is a development configuration; for a
|
||||
production system you'll want to use a real caching system like
|
||||
APC, get rid of EchoSqlLogger, and turn off
|
||||
autoGenerateProxyClasses.
|
||||
|
||||
For more details, consult the
|
||||
`Doctrine 2 Configuration documentation <http://www.doctrine-project.org/documentation/manual/2_0/en/configuration#configuration-options>`_.
|
||||
|
||||
Now to use it
|
||||
-------------
|
||||
|
||||
Whenever you need a reference to the entity manager inside one of
|
||||
your controllers, views, or models you can do this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em = $this->doctrine->em;
|
||||
|
||||
That's all there is to it. Once you get the reference to your
|
||||
EntityManager do your Doctrine 2.0 voodoo as normal.
|
||||
|
||||
Note: If you do not choose to autoload the Doctrine library, you
|
||||
will need to put this line before you get a reference to it:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$this->load->library('doctrine');
|
||||
|
||||
Good luck!
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Mysql Enums
|
||||
===========
|
||||
|
||||
The type system of Doctrine ORM consists of flyweights, which means there is only
|
||||
The type system of Doctrine 2 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 ORM application you will get
|
||||
When using Enums with a non-tweaked Doctrine 2 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
|
||||
@@ -96,9 +96,9 @@ For example for the previous enum type:
|
||||
const STATUS_VISIBLE = 'visible';
|
||||
const STATUS_INVISIBLE = 'invisible';
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
return "ENUM('visible', 'invisible')";
|
||||
return "ENUM('visible', 'invisible') COMMENT '(DC2Type:enumvisibility)'";
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
@@ -118,11 +118,6 @@ For example for the previous enum type:
|
||||
{
|
||||
return self::ENUM_VISIBILITY;
|
||||
}
|
||||
|
||||
public function requiresSQLCommentHint(AbstractPlatform $platform)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
You can register this type with ``Type::addType('enumvisibility', 'MyProject\DBAL\EnumVisibilityType');``.
|
||||
@@ -153,11 +148,11 @@ You can generalize this approach easily to create a base class for enums:
|
||||
protected $name;
|
||||
protected $values = array();
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
$values = array_map(function($val) { return "'".$val."'"; }, $this->values);
|
||||
|
||||
return "ENUM(".implode(", ", $values).")";
|
||||
return "ENUM(".implode(", ", $values).") COMMENT '(DC2Type:".$this->name.")'";
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
@@ -177,11 +172,6 @@ You can generalize this approach easily to create a base class for enums:
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function requiresSQLCommentHint(AbstractPlatform $platform)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
With this base class you can define an enum as easily as:
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
Keeping your Modules independent
|
||||
=================================
|
||||
|
||||
One of the goals of using modules is to create discrete units of functionality
|
||||
.. versionadded:: 2.2
|
||||
|
||||
One of the goals of using modules is to create discreet 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 ORM includes a new utility called the ``ResolveTargetEntityListener``,
|
||||
Doctrine 2.2 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
|
||||
@@ -38,7 +40,6 @@ A Customer entity
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// src/Acme/AppModule/Entity/Customer.php
|
||||
|
||||
namespace Acme\AppModule\Entity;
|
||||
@@ -61,7 +62,6 @@ An Invoice entity
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// src/Acme/InvoiceModule/Entity/Invoice.php
|
||||
|
||||
namespace Acme\InvoiceModule\Entity;
|
||||
@@ -88,7 +88,6 @@ An InvoiceSubjectInterface
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// src/Acme/InvoiceModule/Model/InvoiceSubjectInterface.php
|
||||
|
||||
namespace Acme\InvoiceModule\Model;
|
||||
@@ -117,15 +116,13 @@ the targetEntity resolution will occur reliably:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$evm = new \Doctrine\Common\EventManager;
|
||||
$rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener;
|
||||
$evm = new \Doctrine\Common\EventManager;
|
||||
|
||||
// Adds a target-entity class
|
||||
$rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener;
|
||||
$rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array());
|
||||
|
||||
// Add the ResolveTargetEntityListener
|
||||
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel);
|
||||
$evm->addEventSubscriber($rtel);
|
||||
|
||||
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm);
|
||||
|
||||
|
||||
@@ -39,16 +39,10 @@ appropriate autoloaders.
|
||||
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
|
||||
{
|
||||
$classMetadata = $eventArgs->getClassMetadata();
|
||||
|
||||
if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {
|
||||
$classMetadata->setPrimaryTable([
|
||||
'name' => $this->prefix . $classMetadata->getTableName()
|
||||
]);
|
||||
}
|
||||
|
||||
$classMetadata->setTableName($this->prefix . $classMetadata->getTableName());
|
||||
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
|
||||
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
|
||||
$mappedTableName = $mapping['joinTable']['name'];
|
||||
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY) {
|
||||
$mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name'];
|
||||
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ Strategy-Pattern
|
||||
|
||||
This recipe will give you a short introduction on how to design
|
||||
similar entities without using expensive (i.e. slow) inheritance
|
||||
but with not more than *the well-known strategy pattern* event
|
||||
but with not more than \* the well-known strategy pattern \* event
|
||||
listeners
|
||||
|
||||
Scenario / Problem
|
||||
@@ -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 ORM would do the rest for you, i.e.
|
||||
AbstractPanelType and Doctrine 2 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 ORM)
|
||||
* that is used for this blockitem. (This string (!) value will be persisted by Doctrine 2)
|
||||
*
|
||||
* 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 ORM.
|
||||
* This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine 2.
|
||||
*
|
||||
* @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 ORM
|
||||
Now, the important point is that $strategyClassName is a Doctrine 2
|
||||
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 ORM does not ship with any internal validators, the reason
|
||||
Doctrine 2 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.
|
||||
@@ -25,8 +25,8 @@ the additional benefit of being able to re-use your validation in
|
||||
any other part of your domain.
|
||||
|
||||
Say we have an ``Order`` with several ``OrderLine`` instances. We
|
||||
never want to allow any customer to order for a larger sum than they
|
||||
are allowed to:
|
||||
never want to allow any customer to order for a larger sum than he
|
||||
is allowed to:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -38,7 +38,7 @@ are allowed to:
|
||||
$orderLimit = $this->customer->getOrderLimit();
|
||||
|
||||
$amount = 0;
|
||||
foreach ($this->orderLines as $line) {
|
||||
foreach ($this->orderLines AS $line) {
|
||||
$amount += $line->getAmount();
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ events for one method, this will happen before Beta 1 though.
|
||||
Now validation is performed whenever you call
|
||||
``EntityManager#persist($order)`` or when you call
|
||||
``EntityManager#flush()`` and an order is about to be updated. Any
|
||||
Exception that happens in the lifecycle callbacks will be caught by
|
||||
Exception that happens in the lifecycle callbacks will be cached by
|
||||
the EntityManager and the current transaction is rolled back.
|
||||
|
||||
Of course you can do any type of primitive checks, not null,
|
||||
@@ -134,4 +134,4 @@ instances. This was already discussed in the previous blog post on
|
||||
the Versionable extension, which requires another type of event
|
||||
called "onFlush".
|
||||
|
||||
Further readings: :ref:`reference-events-lifecycle-events`
|
||||
Further readings: :doc:`Lifecycle Events <../reference/events>`
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Working with DateTime Instances
|
||||
===============================
|
||||
|
||||
There are many nitty gritty details when working with PHPs DateTime instances. You have to know their inner
|
||||
There are many nitty gritty details when working with PHPs DateTime instances. You have 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 ORM.
|
||||
interesting pieces of information on how to work with PHP DateTime instances in Doctrine 2.
|
||||
|
||||
DateTime changes are detected by Reference
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -49,16 +49,15 @@ By default Doctrine assumes that you are working with a default timezone. Each D
|
||||
is created by Doctrine will be assigned the timezone that is currently the default, either through
|
||||
the ``date.timezone`` ini setting or by calling ``date_default_timezone_set()``.
|
||||
|
||||
This is very important to handle correctly if your application runs on different servers or is moved from one to another server
|
||||
This is very important to handle correctly if your application runs on different serves or is moved from one to another server
|
||||
(with different timezone settings). You have to make sure that the timezone is the correct one
|
||||
on all this systems.
|
||||
|
||||
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 ORM)
|
||||
If you first come across the requirement to save different you are still optimistic to manage this mess,
|
||||
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2)
|
||||
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 +66,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 ORM. However there is a workaround
|
||||
The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine 2. However there is a workaround
|
||||
that even allows correct date-time handling with timezones:
|
||||
|
||||
1. Always convert any DateTime instance to UTC.
|
||||
@@ -86,71 +85,43 @@ the UTC time at the time of the booking and the timezone the event happened in.
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
use Doctrine\DBAL\Types\DateTimeType;
|
||||
|
||||
class UTCDateTimeType extends DateTimeType
|
||||
{
|
||||
/**
|
||||
* @var \DateTimeZone
|
||||
*/
|
||||
private static $utc;
|
||||
static private $utc = null;
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
if ($value instanceof \DateTime) {
|
||||
$value->setTimezone(self::getUtc());
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::convertToDatabaseValue($value, $platform);
|
||||
|
||||
return $value->format($platform->getDateTimeFormatString(),
|
||||
(self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC'))
|
||||
);
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
if (null === $value || $value instanceof \DateTime) {
|
||||
return $value;
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$converted = \DateTime::createFromFormat(
|
||||
$val = \DateTime::createFromFormat(
|
||||
$platform->getDateTimeFormatString(),
|
||||
$value,
|
||||
self::getUtc()
|
||||
(self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC'))
|
||||
);
|
||||
|
||||
if (! $converted) {
|
||||
throw ConversionException::conversionFailedFormat(
|
||||
$value,
|
||||
$this->getName(),
|
||||
$platform->getDateTimeFormatString()
|
||||
);
|
||||
if (!$val) {
|
||||
throw ConversionException::conversionFailed($value, $this->getName());
|
||||
}
|
||||
|
||||
return $converted;
|
||||
}
|
||||
|
||||
private static function getUtc(): \DateTimeZone
|
||||
{
|
||||
return self::$utc ?: self::$utc = new \DateTimeZone('UTC');
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
This database type makes sure that every DateTime instance is always saved in UTC, relative
|
||||
to the current timezone that the passed DateTime instance has.
|
||||
|
||||
To actually use this new type instead of the default ``datetime`` type, you need to run following
|
||||
code before bootstrapping the ORM:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use DoctrineExtensions\DBAL\Types\UTCDateTimeType;
|
||||
|
||||
Type::overrideType('datetime', UTCDateTimeType::class);
|
||||
Type::overrideType('datetimetz', UTCDateTimeType::class);
|
||||
|
||||
|
||||
To be able to transform these values
|
||||
to the current timezone that the passed DateTime instance has. To be able to transform these values
|
||||
back into their real timezone you have to save the timezone in a separate field of the entity
|
||||
requiring timezoned datetimes:
|
||||
|
||||
|
||||
+27
-32
@@ -13,11 +13,11 @@ If this documentation is not helping to answer questions you have about
|
||||
Doctrine ORM don't panic. You can get help from different sources:
|
||||
|
||||
- There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.
|
||||
- The `Doctrine Mailing List <https://groups.google.com/group/doctrine-user>`_
|
||||
- Slack chat room `#orm <https://www.doctrine-project.org/slack>`_
|
||||
- Report a bug on `GitHub <https://github.com/doctrine/orm/issues>`_.
|
||||
- The `Doctrine Mailing List <http://groups.google.com/group/doctrine-user>`_
|
||||
- Internet Relay Chat (IRC) in #doctrine on Freenode
|
||||
- Report a bug on `JIRA <http://www.doctrine-project.org/jira>`_.
|
||||
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
|
||||
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
|
||||
- On `StackOverflow <http://stackoverflow.com/questions/tagged/doctrine2>`_
|
||||
|
||||
If you need more structure over the different topics you can browse the :doc:`table
|
||||
of contents <toc>`.
|
||||
@@ -65,37 +65,29 @@ Working with Objects
|
||||
Advanced Topics
|
||||
---------------
|
||||
|
||||
* :doc:`Architecture <reference/architecture>`
|
||||
* :doc:`Advanced Configuration <reference/advanced-configuration>`
|
||||
* :doc:`Limitations and known issues <reference/limitations-and-known-issues>`
|
||||
* :doc:`Commandline Tools <reference/tools>`
|
||||
* :doc:`Transactions and Concurrency <reference/transactions-and-concurrency>`
|
||||
* :doc:`Filters <reference/filters>`
|
||||
* :doc:`NamingStrategy <reference/namingstrategy>`
|
||||
* :doc:`Improving Performance <reference/improving-performance>`
|
||||
* :doc:`Caching <reference/caching>`
|
||||
* :doc:`Partial Objects <reference/partial-objects>`
|
||||
* :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
||||
* :doc:`Best Practices <reference/best-practices>`
|
||||
* :doc:`Metadata Drivers <reference/metadata-drivers>`
|
||||
* :doc:`Batch Processing <reference/batch-processing>`
|
||||
* :doc:`Second Level Cache <reference/second-level-cache>`
|
||||
* :doc:`Architecture <reference/architecture>`
|
||||
* :doc:`Advanced Configuration <reference/advanced-configuration>`
|
||||
* :doc:`Limitations and knowns issues <reference/limitations-and-known-issues>`
|
||||
* :doc:`Commandline Tools <reference/tools>`
|
||||
* :doc:`Transactions and Concurrency <reference/transactions-and-concurrency>`
|
||||
* :doc:`Filters <reference/filters>`
|
||||
* :doc:`NamingStrategy <reference/namingstrategy>`
|
||||
* :doc:`Improving Performance <reference/improving-performance>`
|
||||
* :doc:`Caching <reference/caching>`
|
||||
* :doc:`Partial Objects <reference/partial-objects>`
|
||||
* :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
||||
* :doc:`Best Practices <reference/best-practices>`
|
||||
* :doc:`Metadata Drivers <reference/metadata-drivers>`
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
|
||||
* :doc:`Indexed associations <tutorials/working-with-indexed-associations>`
|
||||
* :doc:`Extra Lazy Associations <tutorials/extra-lazy-associations>`
|
||||
* :doc:`Composite Primary Keys <tutorials/composite-primary-keys>`
|
||||
* :doc:`Ordered associations <tutorials/ordered-associations>`
|
||||
* :doc:`Pagination <tutorials/pagination>`
|
||||
* :doc:`Override Field/Association Mappings In Subclasses <tutorials/override-field-association-mappings-in-subclasses>`
|
||||
* :doc:`Embeddables <tutorials/embeddables>`
|
||||
|
||||
Changelogs
|
||||
----------
|
||||
|
||||
* `Upgrade <https://github.com/doctrine/doctrine2/blob/master/UPGRADE.md>`_
|
||||
* :doc:`Indexed associations <tutorials/working-with-indexed-associations>`
|
||||
* :doc:`Extra Lazy Associations <tutorials/extra-lazy-associations>`
|
||||
* :doc:`Composite Primary Keys <tutorials/composite-primary-keys>`
|
||||
* :doc:`Ordered associations <tutorials/ordered-associations>`
|
||||
* :doc:`Pagination <tutorials/pagination>`
|
||||
* :doc:`Override Field/Association Mappings In Subclasses <tutorials/override-field-association-mappings-in-subclasses>`
|
||||
|
||||
Cookbook
|
||||
--------
|
||||
@@ -103,7 +95,7 @@ Cookbook
|
||||
* **Patterns**:
|
||||
:doc:`Aggregate Fields <cookbook/aggregate-fields>` |
|
||||
:doc:`Decorator Pattern <cookbook/decorator-pattern>` |
|
||||
:doc:`Strategy Pattern <cookbook/strategy-cookbook-introduction>`
|
||||
:doc:`Strategy Pattern <cookbook/strategy-cookbook-introduction>`
|
||||
|
||||
* **DQL Extension Points**:
|
||||
:doc:`DQL Custom Walkers <cookbook/dql-custom-walkers>` |
|
||||
@@ -118,6 +110,9 @@ Cookbook
|
||||
:doc:`Entities in the Session <cookbook/entities-in-session>` |
|
||||
:doc:`Keeping your Modules independent <cookbook/resolve-target-entity-listener>`
|
||||
|
||||
* **Integration into Frameworks/Libraries**
|
||||
:doc:`CodeIgniter <cookbook/integrating-with-codeigniter>`
|
||||
|
||||
* **Hidden Gems**
|
||||
:doc:`Prefixing Table Name <cookbook/sql-table-prefixes>`
|
||||
|
||||
|
||||
+113
-113
@@ -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::
|
||||
@@ -101,11 +101,10 @@ Gets or sets the metadata driver implementation that is used by
|
||||
Doctrine to acquire the object-relational metadata for your
|
||||
classes.
|
||||
|
||||
There are currently 5 available implementations:
|
||||
There are currently 4 available implementations:
|
||||
|
||||
|
||||
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\AttributeDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\YamlDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\DriverChain``
|
||||
@@ -153,7 +152,6 @@ The recommended implementations for production are:
|
||||
|
||||
|
||||
- ``Doctrine\Common\Cache\ApcCache``
|
||||
- ``Doctrine\Common\Cache\ApcuCache``
|
||||
- ``Doctrine\Common\Cache\MemcacheCache``
|
||||
- ``Doctrine\Common\Cache\XcacheCache``
|
||||
- ``Doctrine\Common\Cache\RedisCache``
|
||||
@@ -185,7 +183,6 @@ The recommended implementations for production are:
|
||||
|
||||
|
||||
- ``Doctrine\Common\Cache\ApcCache``
|
||||
- ``Doctrine\Common\Cache\ApcuCache``
|
||||
- ``Doctrine\Common\Cache\MemcacheCache``
|
||||
- ``Doctrine\Common\Cache\XcacheCache``
|
||||
- ``Doctrine\Common\Cache\RedisCache``
|
||||
@@ -213,56 +210,17 @@ implementation that logs to the standard output using ``echo`` and
|
||||
Auto-generating Proxy Classes (***OPTIONAL***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Proxy classes can either be generated manually through the Doctrine
|
||||
Console or automatically at runtime by Doctrine. The configuration
|
||||
option that controls this behavior is:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setAutoGenerateProxyClasses($mode);
|
||||
$config->setAutoGenerateProxyClasses($bool);
|
||||
$config->getAutoGenerateProxyClasses();
|
||||
|
||||
Possible values for ``$mode`` are:
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER``
|
||||
|
||||
Never autogenerate a proxy. You will need to generate the proxies
|
||||
manually, for this use the Doctrine Console like so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
When you do this in a development environment,
|
||||
be aware that you may get class/file not found errors if certain proxies
|
||||
are not yet generated. You may also get failing lazy-loads if new
|
||||
methods were added to the entity class that are not yet in the proxy class.
|
||||
In such a case, simply use the Doctrine Console to (re)generate the
|
||||
proxy classes.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS``
|
||||
|
||||
Always generates a new proxy in every request and writes it to disk.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
|
||||
Generate the proxy class when the proxy file does not exist.
|
||||
This strategy causes a file exists call whenever any proxy is
|
||||
used the first time in a request.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL``
|
||||
|
||||
Generate the proxy classes and evaluate them on the fly via eval(),
|
||||
avoiding writing the proxies to disk.
|
||||
This strategy is only sane for development.
|
||||
|
||||
In a production environment, it is highly recommended to use
|
||||
AUTOGENERATE_NEVER to allow for optimal performances. The other
|
||||
options are interesting in development environment.
|
||||
|
||||
``setAutoGenerateProxyClasses`` can accept a boolean
|
||||
value. This is still possible, ``FALSE`` being equivalent to
|
||||
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
|
||||
Gets or sets whether proxy classes should be generated
|
||||
automatically at runtime by Doctrine. If set to ``FALSE``, proxy
|
||||
classes must be generated manually through the doctrine command
|
||||
line task ``generate-proxies``. The strongly recommended value for
|
||||
a production environment is ``FALSE``.
|
||||
|
||||
Development vs Production Configuration
|
||||
---------------------------------------
|
||||
@@ -293,14 +251,14 @@ instance of ``Doctrine\DBAL\Connection``. If an array is passed it
|
||||
is directly passed along to the DBAL Factory
|
||||
``Doctrine\DBAL\DriverManager::getConnection()``. The DBAL
|
||||
configuration is explained in the
|
||||
`DBAL section <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/configuration.html>`_.
|
||||
`DBAL section <./../../../../../dbal/2.0/docs/reference/configuration/en>`_.
|
||||
|
||||
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 ORM,
|
||||
being proxied without that object being aware of it. In Doctrine 2,
|
||||
proxy objects are used to realize several features but mainly for
|
||||
transparent lazy-loading.
|
||||
|
||||
@@ -310,7 +268,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 ORM implements a variant of the proxy pattern where it
|
||||
Doctrine 2 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
|
||||
@@ -364,28 +322,31 @@ transparently initialize itself on first access.
|
||||
Generating Proxy classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In a production environment, it is highly recommended to use
|
||||
``AUTOGENERATE_NEVER`` to allow for optimal performances.
|
||||
However you will be required to generate the proxies manually
|
||||
using the Doctrine Console:
|
||||
Proxy classes can either be generated manually through the Doctrine
|
||||
Console or automatically by Doctrine. The configuration option that
|
||||
controls this behavior is:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setAutoGenerateProxyClasses($bool);
|
||||
$config->getAutoGenerateProxyClasses();
|
||||
|
||||
The default value is ``TRUE`` for convenient development. However,
|
||||
this setting is not optimal for performance and therefore not
|
||||
recommended for a production environment. To eliminate the overhead
|
||||
of proxy class generation during runtime, set this configuration
|
||||
option to ``FALSE``. When you do this in a development environment,
|
||||
note that you may get class/file not found errors if certain proxy
|
||||
classes are not available or failing lazy-loads if new methods were
|
||||
added to the entity class that are not yet in the proxy class. In
|
||||
such a case, simply use the Doctrine Console to (re)generate the
|
||||
proxy classes like so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
The other options are interesting in development environment:
|
||||
|
||||
- ``AUTOGENERATE_ALWAYS`` will require you to create and configure
|
||||
a proxy directory. Proxies will be generated and written to file
|
||||
on each request, so any modification to your code will be acknowledged.
|
||||
|
||||
- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing
|
||||
proxy file. If your code changes, you will need to regenerate the
|
||||
proxies manually.
|
||||
|
||||
- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request,
|
||||
but without writing them to disk.
|
||||
|
||||
Autoloading Proxies
|
||||
-------------------
|
||||
|
||||
@@ -397,7 +358,7 @@ means that you have to register a special autoloader for these classes:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Proxy\Autoloader;
|
||||
use Doctrine\ORM\Proxy\Autoloader;
|
||||
|
||||
$proxyDir = "/path/to/proxies";
|
||||
$proxyNamespace = "MyProxies";
|
||||
@@ -412,7 +373,7 @@ be found.
|
||||
Multiple Metadata Sources
|
||||
-------------------------
|
||||
|
||||
When using different components using Doctrine ORM you may end up
|
||||
When using different components using Doctrine 2 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:
|
||||
|
||||
@@ -1,33 +1,7 @@
|
||||
Annotations Reference
|
||||
=====================
|
||||
|
||||
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 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 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 ORM docblock
|
||||
annotations support namespaces and nested annotations among other
|
||||
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 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 ORM Annotation is given
|
||||
In this chapter a reference of every Doctrine 2 Annotation is given
|
||||
with short explanations on their context and usage.
|
||||
|
||||
Index
|
||||
@@ -35,13 +9,9 @@ Index
|
||||
|
||||
- :ref:`@Column <annref_column>`
|
||||
- :ref:`@ColumnResult <annref_column_result>`
|
||||
- :ref:`@Cache <annref_cache>`
|
||||
- :ref:`@ChangeTrackingPolicy <annref_changetrackingpolicy>`
|
||||
- :ref:`@CustomIdGenerator <annref_customidgenerator>`
|
||||
- :ref:`@DiscriminatorColumn <annref_discriminatorcolumn>`
|
||||
- :ref:`@DiscriminatorMap <annref_discriminatormap>`
|
||||
- :ref:`@Embeddable <annref_embeddable>`
|
||||
- :ref:`@Embedded <annref_embedded>`
|
||||
- :ref:`@Entity <annref_entity>`
|
||||
- :ref:`@EntityResult <annref_entity_result>`
|
||||
- :ref:`@FieldResult <annref_field_result>`
|
||||
@@ -89,7 +59,7 @@ as part of the lifecycle of the instance variables entity-class.
|
||||
Required attributes:
|
||||
|
||||
- **type**: Name of the Doctrine Type which is converted between PHP
|
||||
and Database representation. Default to ``string`` or :ref:`Type from PHP property type <reference-php-mapping-types>`
|
||||
and Database representation.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -99,43 +69,18 @@ Optional attributes:
|
||||
|
||||
- **length**: Used by the "string" type to determine its maximum
|
||||
length in the database. Doctrine does not validate the length of a
|
||||
string value for you.
|
||||
string values for you.
|
||||
|
||||
- **precision**: The precision for a decimal (exact numeric) column
|
||||
(applies only for decimal column), which is the maximum number of
|
||||
digits that are stored for the values.
|
||||
(Applies only for decimal column)
|
||||
|
||||
- **scale**: The scale for a decimal (exact numeric) column (applies
|
||||
only for decimal column), which represents the number of digits
|
||||
to the right of the decimal point and must not be greater than
|
||||
*precision*.
|
||||
- **scale**: The scale for a decimal (exact numeric) column (Applies
|
||||
only for decimal column)
|
||||
|
||||
- **unique**: Boolean value to determine if the value of the column
|
||||
should be unique across all rows of the underlying entities table.
|
||||
|
||||
- **nullable**: Determines if NULL values allowed for this column. If not specified, default value is false.
|
||||
|
||||
- **options**: Array of additional options:
|
||||
|
||||
- ``default``: The default value to set for the column if no value
|
||||
is supplied.
|
||||
|
||||
- ``unsigned``: Boolean value to determine if the column should
|
||||
be capable of representing only non-negative integers
|
||||
(applies only for integer column and might not be supported by
|
||||
all vendors).
|
||||
|
||||
- ``fixed``: Boolean value to determine if the specified length of
|
||||
a string column should be fixed or varying (applies only for
|
||||
string/binary column and might not be supported by all vendors).
|
||||
|
||||
- ``comment``: The comment of the column in the schema (might not
|
||||
be supported by all vendors).
|
||||
|
||||
- ``collation``: The collation of the column (only supported by Drizzle, Mysql, PostgreSQL>=9.1, Sqlite and SQLServer).
|
||||
|
||||
- ``check``: Adds a check constraint type to the column (might not
|
||||
be supported by all vendors).
|
||||
- **nullable**: Determines if NULL values allowed for this column.
|
||||
|
||||
- **columnDefinition**: DDL SQL snippet that starts after the column
|
||||
name and specifies the complete (non-portable!) column definition.
|
||||
@@ -148,12 +93,7 @@ Optional attributes:
|
||||
attribute still handles the conversion between PHP and Database
|
||||
values. If you use this attribute on a column that is used for
|
||||
joins between tables you should also take a look at
|
||||
:ref:`@JoinColumn <annref_joincolumn>`.
|
||||
|
||||
.. note::
|
||||
|
||||
For more detailed information on each attribute, please refer to
|
||||
the DBAL ``Schema-Representation`` documentation.
|
||||
:ref:`@JoinColumn <annref_joincolumn>`.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -164,27 +104,17 @@ Examples:
|
||||
* @Column(type="string", length=32, unique=true, nullable=false)
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
|
||||
/**
|
||||
* @Column(type="string", columnDefinition="CHAR(2) NOT NULL")
|
||||
*/
|
||||
protected $country;
|
||||
|
||||
|
||||
/**
|
||||
* @Column(type="decimal", precision=2, scale=1)
|
||||
*/
|
||||
protected $height;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=2, options={"fixed":true, "comment":"Initial letters of first and last name"})
|
||||
*/
|
||||
protected $initials;
|
||||
|
||||
/**
|
||||
* @Column(type="integer", name="login_count", nullable=false, options={"unsigned":true, "default":0})
|
||||
*/
|
||||
protected $loginCount;
|
||||
|
||||
.. _annref_column_result:
|
||||
|
||||
@ColumnResult
|
||||
@@ -196,24 +126,13 @@ Required attributes:
|
||||
|
||||
- **name**: The name of a column in the SELECT clause of a SQL query
|
||||
|
||||
.. _annref_cache:
|
||||
|
||||
@Cache
|
||||
~~~~~~~~~~~~~~
|
||||
Add caching strategy to a root entity or a collection.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **usage**: One of ``READ_ONLY``, ``READ_WRITE`` or ``NONSTRICT_READ_WRITE``, By default this is ``READ_ONLY``.
|
||||
- **region**: An specific region name
|
||||
|
||||
.. _annref_changetrackingpolicy:
|
||||
|
||||
@ChangeTrackingPolicy
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Change Tracking Policy annotation allows to specify how the
|
||||
Doctrine ORM UnitOfWork should detect changes in properties of
|
||||
Doctrine 2 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
|
||||
@@ -237,43 +156,16 @@ Example:
|
||||
*/
|
||||
class User {}
|
||||
|
||||
.. _annref_customidgenerator:
|
||||
|
||||
@CustomIdGenerator
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This annotations allows you to specify a user-provided class to generate identifiers. This annotation only works when both :ref:`@Id <annref_id>` and :ref:`@GeneratedValue(strategy="CUSTOM") <annref_generatedvalue>` are specified.
|
||||
|
||||
Required attributes:
|
||||
|
||||
- **class**: name of the class which should extend Doctrine\ORM\Id\AbstractIdGenerator
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue(strategy="CUSTOM")
|
||||
* @CustomIdGenerator(class="My\Namespace\MyIdGenerator")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
.. _annref_discriminatorcolumn:
|
||||
|
||||
@DiscriminatorColumn
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This annotation is an optional annotation for the topmost/super
|
||||
This annotation is a required annotation for the topmost/super
|
||||
class of an inheritance hierarchy. It specifies the details of the
|
||||
column which saves the name of the class, which the entity is
|
||||
actually instantiated as.
|
||||
|
||||
If this annotation is not specified, the discriminator column defaults
|
||||
to a string column of length 255 called ``dtype``.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
@@ -292,11 +184,11 @@ Optional attributes:
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The discriminator map is a required annotation on the
|
||||
topmost/super class in an inheritance hierarchy. Its only argument is an
|
||||
array which defines which class should be saved under
|
||||
top-most/super class in an inheritance hierarchy. It takes an array
|
||||
as only argument which defines which class should be saved under
|
||||
which name in the database. Keys are the database value and values
|
||||
are the classes, either as fully- or as unqualified class names
|
||||
depending on whether the classes are in the namespace or not.
|
||||
depending if the classes are in the namespace or not.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -312,74 +204,13 @@ depending on whether the classes are in the namespace or not.
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
.. _annref_embeddable:
|
||||
|
||||
@Embeddable
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The embeddable annotation is required on a class, in order to make it
|
||||
embeddable inside an entity. It works together with the :ref:`@Embedded <annref_embedded>`
|
||||
annotation to establish the relationship between the two classes.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @Embeddable
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
// ...
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @Embedded(class = "Address")
|
||||
*/
|
||||
private $address;
|
||||
|
||||
|
||||
.. _annref_embedded:
|
||||
|
||||
@Embedded
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The embedded annotation is required on an entity's member variable,
|
||||
in order to specify that it is an embedded class.
|
||||
|
||||
Required attributes:
|
||||
|
||||
- **class**: The embeddable class. You can omit this value if you use a PHP property type instead.
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
// ...
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @Embedded(class = "Address")
|
||||
*/
|
||||
private $address;
|
||||
|
||||
/**
|
||||
* @Embeddable
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
// ...
|
||||
|
||||
|
||||
.. _annref_entity:
|
||||
|
||||
@Entity
|
||||
~~~~~~~
|
||||
|
||||
Required annotation to mark a PHP class as an entity. Doctrine manages
|
||||
the persistence of all classes marked as entities.
|
||||
Required annotation to mark a PHP class as Entity. Doctrine manages
|
||||
the persistence of all classes marked as entity.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -388,7 +219,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**: Specifies that this entity is marked as read only and not
|
||||
- **readOnly**: (>= 2.1) 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.
|
||||
|
||||
@@ -451,12 +282,11 @@ conjunction with @Id.
|
||||
If this annotation is not specified with @Id the NONE strategy is
|
||||
used as default.
|
||||
|
||||
Optional attributes:
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **strategy**: Set the name of the identifier generation strategy.
|
||||
Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE.
|
||||
If not specified, default value is AUTO.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -476,7 +306,7 @@ Example:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Annotation which has to be set on the entity-class PHP DocBlock to
|
||||
notify Doctrine that this entity has entity lifecycle callback
|
||||
notify Doctrine that this entity has entity life-cycle callback
|
||||
annotations set on at least one of its methods. Using @PostLoad,
|
||||
@PrePersist, @PostPersist, @PreRemove, @PostRemove, @PreUpdate or
|
||||
@PostUpdate without this marker annotation will make Doctrine
|
||||
@@ -505,7 +335,7 @@ Example:
|
||||
~~~~~~~
|
||||
|
||||
Annotation is used inside the :ref:`@Table <annref_table>` annotation on
|
||||
the entity-class level. It provides a hint to the SchemaTool to
|
||||
the entity-class level. It allows to hint the SchemaTool to
|
||||
generate a database index on the specified table columns. It only
|
||||
has meaning in the SchemaTool schema generation context.
|
||||
|
||||
@@ -513,17 +343,9 @@ Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **options**: Array of platform specific options:
|
||||
|
||||
- ``where``: SQL WHERE condition to be used for partial indexes. It will
|
||||
only have effect on supported platforms.
|
||||
|
||||
Basic example:
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -536,32 +358,6 @@ Basic example:
|
||||
{
|
||||
}
|
||||
|
||||
Basic example using fields:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",indexes={@Index(name="search_idx", fields={"name", "email"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
Example with partial indexes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",indexes={@Index(name="search_idx", columns={"name", "email"}, options={"where": "(((id IS NOT NULL) AND (name IS NULL)) AND (email IS NULL))"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
.. _annref_id:
|
||||
|
||||
@Id
|
||||
@@ -613,7 +409,7 @@ Examples:
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("JOINED")
|
||||
@@ -633,26 +429,31 @@ 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. 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>`.
|
||||
a @ManyToMany. This annotation is not required. If its not
|
||||
specified the attributes *name* and *referencedColumnName* are
|
||||
inferred from the table and primary key names.
|
||||
|
||||
Required attributes:
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **name**: Column name that holds the foreign key identifier for
|
||||
this relation. In the context of @JoinTable it specifies the column
|
||||
name in the join table.
|
||||
- **referencedColumnName**: Name of the primary key identifier that
|
||||
is used for joining of this relation. Defaults to *id*.
|
||||
- **unique**: Determines whether this relation is exclusive between the
|
||||
affected entities and should be enforced as such on the database
|
||||
is used for joining of this relation.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **unique**: Determines if this relation exclusive between the
|
||||
affected entities and should be enforced so on the database
|
||||
constraint level. Defaults to false.
|
||||
- **nullable**: Determine whether the related entity is required, or if
|
||||
- **nullable**: Determine if the related entity is required, or if
|
||||
null is an allowed state for the relation. Defaults to true.
|
||||
- **onDelete**: Cascade Action (Database-level)
|
||||
- **columnDefinition**: DDL SQL snippet that starts after the column
|
||||
name and specifies the complete (non-portable!) column definition.
|
||||
This attribute enables the use of advanced RMDBS features. Using
|
||||
This attribute allows to make use of advanced RMDBS features. Using
|
||||
this attribute on @JoinColumn is necessary if you need slightly
|
||||
different column definitions for joining columns, for example
|
||||
regarding NULL/NOT NULL defaults. However by default a
|
||||
@@ -692,7 +493,7 @@ details of the database join table. If you do not specify
|
||||
@JoinTable on these relations reasonable mapping defaults apply
|
||||
using the affected table and the column names.
|
||||
|
||||
Optional attributes:
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: Database name of the join-table
|
||||
@@ -729,7 +530,6 @@ Required attributes:
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
You can omit this value if you use a PHP property type instead.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
@@ -755,7 +555,7 @@ Example:
|
||||
@ManyToMany
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Defines that the annotated instance variable holds a many-to-many relationship
|
||||
Defines an instance variable holds a many-to-many relationship
|
||||
between two entities. :ref:`@JoinTable <annref_jointable>` is an
|
||||
additional, optional annotation that has reasonable default
|
||||
configuration values using the table and names of the two related
|
||||
@@ -772,9 +572,9 @@ Optional attributes:
|
||||
|
||||
|
||||
- **mappedBy**: This option specifies the property name on the
|
||||
targetEntity that is the owning side of this relation. It is a
|
||||
targetEntity that is the owning side of this relation. Its a
|
||||
required attribute for the inverse side of a relationship.
|
||||
- **inversedBy**: The inversedBy attribute designates the field in the
|
||||
- **inversedBy**: The inversedBy attribute designates the field in the
|
||||
entity that is the inverse side of the relationship.
|
||||
- **cascade**: Cascade Option
|
||||
- **fetch**: One of LAZY, EXTRA_LAZY or EAGER
|
||||
@@ -802,7 +602,7 @@ Example:
|
||||
* )
|
||||
*/
|
||||
private $groups;
|
||||
|
||||
|
||||
/**
|
||||
* Inverse Side
|
||||
*
|
||||
@@ -815,7 +615,7 @@ Example:
|
||||
@MappedSuperclass
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A mapped superclass is an abstract or concrete class that provides
|
||||
An mapped superclass is an abstract or concrete class that provides
|
||||
persistent entity state and mapping information for its subclasses,
|
||||
but which is not itself an entity. This annotation is specified on
|
||||
the Class docblock and has no additional attributes.
|
||||
@@ -827,7 +627,7 @@ The @MappedSuperclass annotation cannot be used in conjunction with
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository.
|
||||
- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository.
|
||||
That will be inherited for all subclasses of that Mapped Superclass.
|
||||
|
||||
Example:
|
||||
@@ -855,11 +655,6 @@ Example:
|
||||
|
||||
@NamedNativeQuery
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
|
||||
|
||||
Is used to specify a native SQL named query.
|
||||
The NamedNativeQuery annotation can be applied to an entity or mapped superclass.
|
||||
|
||||
@@ -933,7 +728,7 @@ Example:
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The @OneToOne annotation works almost exactly as the
|
||||
:ref:`@ManyToOne <annref_manytoone>` with one additional option which can
|
||||
:ref:`@ManyToOne <annref_manytoone>` with one additional option that can
|
||||
be specified. The configuration defaults for
|
||||
:ref:`@JoinColumn <annref_joincolumn>` using the target entity table and
|
||||
primary key column names apply here too.
|
||||
@@ -943,7 +738,6 @@ Required attributes:
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
When typed properties are used it is inherited from PHP type.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
@@ -954,7 +748,7 @@ Optional attributes:
|
||||
- **orphanRemoval**: Boolean that specifies if orphans, inverse
|
||||
OneToOne entities that are not connected to any owning instance,
|
||||
should be removed by Doctrine. Defaults to false.
|
||||
- **inversedBy**: The inversedBy attribute designates the field in the
|
||||
- **inversedBy**: The inversedBy attribute designates the field in the
|
||||
entity that is the inverse side of the relationship.
|
||||
|
||||
Example:
|
||||
@@ -1101,7 +895,7 @@ DocBlock.
|
||||
@SequenceGenerator
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For use with @GeneratedValue(strategy="SEQUENCE") this
|
||||
For the use with @generatedValue(strategy="SEQUENCE") this
|
||||
annotation allows to specify details about the sequence, such as
|
||||
the increment size and initial values of the sequence.
|
||||
|
||||
@@ -1114,10 +908,10 @@ Optional attributes:
|
||||
|
||||
|
||||
- **allocationSize**: Increment the sequence by the allocation size
|
||||
when its fetched. A value larger than 1 allows optimization for
|
||||
when its fetched. A value larger than 1 allows to optimize for
|
||||
scenarios where you create more than one new entity per request.
|
||||
Defaults to 10
|
||||
- **initialValue**: Where the sequence starts, defaults to 1.
|
||||
- **initialValue**: Where does the sequence start, defaults to 1.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1239,7 +1033,7 @@ Example:
|
||||
|
||||
Annotation describes the table an entity is persisted in. It is
|
||||
placed on the entity-class PHP DocBlock and is optional. If it is
|
||||
not specified the table name will default to the entity's
|
||||
not specified the table name will default to the entities
|
||||
unqualified classname.
|
||||
|
||||
Required attributes:
|
||||
@@ -1252,7 +1046,6 @@ Optional attributes:
|
||||
|
||||
- **indexes**: Array of @Index annotations
|
||||
- **uniqueConstraints**: Array of @UniqueConstraint annotations.
|
||||
- **schema**: Name of the schema the table lies in.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1264,7 +1057,6 @@ Example:
|
||||
* @Table(name="user",
|
||||
* uniqueConstraints={@UniqueConstraint(name="user_unique",columns={"username"})},
|
||||
* indexes={@Index(name="user_idx", columns={"email"})}
|
||||
* schema="schema_name"
|
||||
* )
|
||||
*/
|
||||
class User { }
|
||||
@@ -1284,17 +1076,9 @@ Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **options**: Array of platform specific options:
|
||||
|
||||
- ``where``: SQL WHERE condition to be used for partial indexes. It will
|
||||
only have effect on supported platforms.
|
||||
|
||||
Basic example:
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -1307,42 +1091,15 @@ Basic example:
|
||||
{
|
||||
}
|
||||
|
||||
Basic example using fields:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",uniqueConstraints={@UniqueConstraint(name="search_idx", fields={"name", "email"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
Example with partial indexes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",uniqueConstraints={@UniqueConstraint(name="search_idx", columns={"name", "email"}, options={"where": "(((id IS NOT NULL) AND (name IS NULL)) AND (email IS NULL))"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
.. _annref_version:
|
||||
|
||||
@Version
|
||||
~~~~~~~~
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Marker annotation that defines a specified column as version attribute used in
|
||||
an :ref:`optimistic locking <transactions-and-concurrency_optimistic-locking>`
|
||||
scenario. It only works on :ref:`@Column <annref_column>` annotations that have
|
||||
the type ``integer`` or ``datetime``. Combining ``@Version`` with
|
||||
:ref:`@Id <annref_id>` is not supported.
|
||||
Marker annotation that defines a specified column as version
|
||||
attribute used in an optimistic locking scenario. It only works on
|
||||
:ref:`@Column <annref_column>` annotations that have the type integer or
|
||||
datetime. Combining @Version with :ref:`@Id <annref_id>` is not supported.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -2,29 +2,29 @@ Architecture
|
||||
============
|
||||
|
||||
This chapter gives an overview of the overall architecture,
|
||||
terminology and constraints of Doctrine ORM. It is recommended to
|
||||
terminology and constraints of Doctrine 2. It is recommended to
|
||||
read this chapter carefully.
|
||||
|
||||
Using an Object-Relational Mapper
|
||||
---------------------------------
|
||||
|
||||
As the term ORM already hints at, Doctrine ORM aims to simplify the
|
||||
As the term ORM already hints at, Doctrine 2 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 ORM is not suited very
|
||||
that not primarily work with objects Doctrine 2 is not suited very
|
||||
well.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Doctrine ORM requires a minimum of PHP 7.1. For greatly improved
|
||||
Doctrine 2 requires a minimum of PHP 5.3.0. For greatly improved
|
||||
performance it is also recommended that you use APC with PHP.
|
||||
|
||||
Doctrine ORM Packages
|
||||
Doctrine 2 Packages
|
||||
-------------------
|
||||
|
||||
Doctrine ORM is divided into three main packages.
|
||||
Doctrine 2 is divided into three main packages.
|
||||
|
||||
- Common
|
||||
- DBAL (includes Common)
|
||||
@@ -83,7 +83,7 @@ be any regular PHP class observing the following restrictions:
|
||||
- An entity class must not implement ``__wakeup`` or
|
||||
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
|
||||
Also consider implementing
|
||||
`Serializable <http://php.net/manual/en/class.serializable.php>`_
|
||||
`Serializable <http://de3.php.net/manual/en/class.serializable.php>`_
|
||||
instead.
|
||||
- Any two entity classes in a class hierarchy that inherit
|
||||
directly or indirectly from one another must not have a mapped
|
||||
@@ -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
|
||||
functionality provided by Doctrine ORM. The ``EntityManager`` API is
|
||||
The ``EntityManager`` class is a central access point to the ORM
|
||||
functionality provided by Doctrine 2. The ``EntityManager`` API is
|
||||
used to manage the persistence of your objects and to query for
|
||||
persistent objects.
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+416
-252
@@ -1,72 +1,77 @@
|
||||
Basic Mapping
|
||||
=============
|
||||
|
||||
This guide explains the basic mapping of entities and properties.
|
||||
After working through this guide you should know:
|
||||
This chapter explains the basic mapping of objects and properties.
|
||||
Mapping of associations will be covered in the next chapter
|
||||
"Association Mapping".
|
||||
|
||||
- How to create PHP objects that can be saved to the database with Doctrine;
|
||||
- How to configure the mapping between columns on tables and properties on
|
||||
entities;
|
||||
- What Doctrine mapping types are;
|
||||
- Defining primary keys and how identifiers are generated by Doctrine;
|
||||
- How quoting of reserved symbols works in Doctrine.
|
||||
Mapping Drivers
|
||||
---------------
|
||||
|
||||
Mapping of associations will be covered in the next chapter on
|
||||
:doc:`Association Mapping <association-mapping>`.
|
||||
Doctrine provides several different ways for specifying
|
||||
object-relational mapping metadata:
|
||||
|
||||
Guide Assumptions
|
||||
-----------------
|
||||
|
||||
You should have already :doc:`installed and configure <configuration>`
|
||||
Doctrine.
|
||||
- Docblock Annotations
|
||||
- XML
|
||||
- YAML
|
||||
|
||||
Creating Classes for the Database
|
||||
---------------------------------
|
||||
|
||||
Every PHP object that you want to save in the database using Doctrine
|
||||
is called an "Entity". The term "Entity" describes objects
|
||||
that have an identity over many independent requests. This identity is
|
||||
usually achieved by assigning a unique identifier to an entity.
|
||||
In this tutorial the following ``Message`` PHP class will serve as the
|
||||
example Entity:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class Message
|
||||
{
|
||||
private $id;
|
||||
private $text;
|
||||
private $postedAt;
|
||||
}
|
||||
|
||||
Because Doctrine is a generic library, it only knows about your
|
||||
entities because you will describe their existence and structure using
|
||||
mapping metadata, which is configuration that tells Doctrine how your
|
||||
entity should be stored in the database. The documentation will often
|
||||
speak of "mapping something", which means writing the mapping metadata
|
||||
that describes your entity.
|
||||
|
||||
Doctrine provides several different ways to specify object-relational
|
||||
mapping metadata:
|
||||
|
||||
- :doc:`Docblock Annotations <annotations-reference>`
|
||||
- :doc:`XML <xml-mapping>`
|
||||
- :doc:`YAML <yaml-mapping>`
|
||||
- :doc:`PHP code <php-mapping>`
|
||||
|
||||
This manual will usually show mapping metadata via docblock annotations, though
|
||||
many examples also show the equivalent configuration in YAML and XML.
|
||||
This manual usually mentions docblock annotations in all the examples
|
||||
that are spread throughout all chapters, however for many examples
|
||||
alternative YAML and XML examples are given as well. There are dedicated
|
||||
reference chapters for XML and YAML mapping, respectively that explain them
|
||||
in more detail. There is also an Annotation reference chapter.
|
||||
|
||||
.. note::
|
||||
|
||||
All metadata drivers perform equally. Once the metadata of a class has been
|
||||
read from the source (annotations, xml or yaml) it is stored in an instance
|
||||
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are
|
||||
stored in the metadata cache. If you're not using a metadata cache (not
|
||||
recommended!) then the XML driver is the fastest.
|
||||
If you're wondering which mapping driver gives the best
|
||||
performance, the answer is: They all give exactly the same performance.
|
||||
Once the metadata of a class has
|
||||
been read from the source (annotations, xml or yaml) it is stored
|
||||
in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class
|
||||
and these instances are stored in the metadata cache. Therefore at
|
||||
the end of the day all drivers perform equally well. If you're not
|
||||
using a metadata cache (not recommended!) then the XML driver might
|
||||
have a slight edge in performance due to the powerful native XML
|
||||
support in PHP.
|
||||
|
||||
Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
|
||||
Introduction to Docblock Annotations
|
||||
------------------------------------
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
annotations support namespaces and nested annotations among other
|
||||
things. The Doctrine 2 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
|
||||
XML and YAML alternatives and you could easily implement your own
|
||||
favourite mechanism for defining ORM metadata.
|
||||
|
||||
|
||||
Persistent classes
|
||||
------------------
|
||||
|
||||
In order to mark a class for object-relational persistence it needs
|
||||
to be designated as an entity. This can be done through the
|
||||
``@Entity`` marker annotation.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@@ -74,7 +79,7 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class Message
|
||||
class MyPersistentClass
|
||||
{
|
||||
//...
|
||||
}
|
||||
@@ -82,20 +87,20 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Message">
|
||||
<entity name="MyPersistentClass">
|
||||
<!-- ... -->
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
MyPersistentClass:
|
||||
type: entity
|
||||
# ...
|
||||
|
||||
With no additional information, Doctrine expects the entity to be saved
|
||||
into a table with the same name as the class in our case ``Message``.
|
||||
You can change this by configuring information about the table:
|
||||
By default, the entity will be persisted to a table with the same
|
||||
name as the class name. In order to change that, you can use the
|
||||
``@Table`` annotation as follows:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@@ -104,9 +109,9 @@ You can change this by configuring information about the table:
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="message")
|
||||
* @Table(name="my_persistent_class")
|
||||
*/
|
||||
class Message
|
||||
class MyPersistentClass
|
||||
{
|
||||
//...
|
||||
}
|
||||
@@ -114,144 +119,42 @@ You can change this by configuring information about the table:
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Message" table="message">
|
||||
<entity name="MyPersistentClass" table="my_persistent_class">
|
||||
<!-- ... -->
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
MyPersistentClass:
|
||||
type: entity
|
||||
table: message
|
||||
table: my_persistent_class
|
||||
# ...
|
||||
|
||||
Now the class ``Message`` will be saved and fetched from the table ``message``.
|
||||
|
||||
Property Mapping
|
||||
----------------
|
||||
|
||||
The next step after marking a PHP class as an entity is mapping its properties
|
||||
to columns in a table.
|
||||
|
||||
To configure a property use the ``@Column`` docblock annotation. The ``type``
|
||||
attribute specifies the :ref:`Doctrine Mapping Type <reference-mapping-types>`
|
||||
to use for the field. If the type is not specified, ``string`` is used as the
|
||||
default.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class Message
|
||||
{
|
||||
/** @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(length=140) */
|
||||
private $text;
|
||||
/** @Column(type="datetime", name="posted_at") */
|
||||
private $postedAt;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Message">
|
||||
<field name="id" type="integer" />
|
||||
<field name="text" length="140" />
|
||||
<field name="postedAt" column="posted_at" type="datetime" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
type: entity
|
||||
fields:
|
||||
id:
|
||||
type: integer
|
||||
text:
|
||||
length: 140
|
||||
postedAt:
|
||||
type: datetime
|
||||
column: posted_at
|
||||
|
||||
When we don't explicitly specify a column name via the ``name`` option, Doctrine
|
||||
assumes the field name is also the column name. This means that:
|
||||
|
||||
* the ``id`` property will map to the column ``id`` using the type ``integer``;
|
||||
* the ``text`` property will map to the column ``text`` with the default mapping type ``string``;
|
||||
* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type.
|
||||
|
||||
The Column annotation has some more attributes. Here is a complete
|
||||
list:
|
||||
|
||||
- ``type``: (optional, defaults to 'string') The mapping type to
|
||||
use for the column.
|
||||
- ``name``: (optional, defaults to field name) The name of the
|
||||
column in the database.
|
||||
- ``length``: (optional, default 255) The length of the column in
|
||||
the database. (Applies only if a string-valued column is used).
|
||||
- ``unique``: (optional, default FALSE) Whether the column is a
|
||||
unique key.
|
||||
- ``nullable``: (optional, default FALSE) Whether the database
|
||||
column is nullable.
|
||||
- ``precision``: (optional, default 0) The precision for a decimal
|
||||
(exact numeric) column (applies only for decimal column),
|
||||
which is the maximum number of digits that are stored for the values.
|
||||
- ``scale``: (optional, default 0) The scale for a decimal (exact
|
||||
numeric) column (applies only for decimal column), which represents
|
||||
the number of digits to the right of the decimal point and must
|
||||
not be greater than *precision*.
|
||||
- ``columnDefinition``: (optional) Allows to define a custom
|
||||
DDL snippet that is used to create the column. Warning: This normally
|
||||
confuses the SchemaTool to always detect the column as changed.
|
||||
- ``options``: (optional) Key-value pairs of options that get passed
|
||||
to the underlying database platform when generating DDL statements.
|
||||
|
||||
.. _reference-php-mapping-types:
|
||||
|
||||
PHP Types Mapping
|
||||
_________________
|
||||
|
||||
Since version 2.9 Doctrine can determine usable defaults from property types
|
||||
on entity classes. When property type is nullable the default for ``nullable``
|
||||
Column attribute is set to TRUE. Additionally, Doctrine will map PHP types
|
||||
to ``type`` attribute as follows:
|
||||
|
||||
- ``DateInterval``: ``dateinterval``
|
||||
- ``DateTime``: ``datetime``
|
||||
- ``DateTimeImmutable``: ``datetime_immutable``
|
||||
- ``array``: ``json``
|
||||
- ``bool``: ``boolean``
|
||||
- ``float``: ``float``
|
||||
- ``int``: ``integer``
|
||||
- ``string`` or any other type: ``string``
|
||||
|
||||
.. _reference-mapping-types:
|
||||
Now instances of MyPersistentClass will be persisted into a table
|
||||
named ``my_persistent_class``.
|
||||
|
||||
Doctrine Mapping Types
|
||||
----------------------
|
||||
|
||||
The ``type`` option used in the ``@Column`` accepts any of the existing
|
||||
Doctrine types or even your own custom types. A Doctrine type defines
|
||||
the conversion between PHP and SQL types, independent from the database vendor
|
||||
you are using. All Mapping Types that ship with Doctrine are fully portable
|
||||
between the supported database systems.
|
||||
A Doctrine Mapping Type defines the mapping between a PHP type and
|
||||
a SQL type. All Doctrine Mapping Types that ship with Doctrine are
|
||||
fully portable between different RDBMS. You can even write your own
|
||||
custom mapping types that might or might not be portable, which is
|
||||
explained later in this chapter.
|
||||
|
||||
As an example, the Doctrine Mapping Type ``string`` defines the
|
||||
For example, the Doctrine Mapping Type ``string`` defines the
|
||||
mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc.
|
||||
depending on the RDBMS brand). Here is a quick overview of the
|
||||
built-in mapping types:
|
||||
|
||||
|
||||
- ``string``: Type that maps a SQL VARCHAR to a PHP string.
|
||||
- ``integer``: Type that maps a SQL INT to a PHP integer.
|
||||
- ``smallint``: Type that maps a database SMALLINT to a PHP
|
||||
integer.
|
||||
- ``bigint``: Type that maps a database BIGINT to a PHP string.
|
||||
- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
|
||||
- ``boolean``: Type that maps a SQL boolean to a PHP boolean.
|
||||
- ``decimal``: Type that maps a SQL DECIMAL to a PHP string.
|
||||
- ``date``: Type that maps a SQL DATETIME to a PHP DateTime
|
||||
object.
|
||||
@@ -277,19 +180,23 @@ built-in mapping types:
|
||||
varchar but uses a specific type if the platform supports it.
|
||||
- ``blob``: Type that maps a SQL BLOB to a PHP resource stream
|
||||
|
||||
A cookbook article shows how to define :doc:`your own custom mapping types
|
||||
<../cookbook/custom-mapping-types>`.
|
||||
.. note::
|
||||
|
||||
Doctrine Mapping Types are NOT SQL types and NOT PHP
|
||||
types! They are mapping types between 2 types.
|
||||
Additionally Mapping types are *case-sensitive*. For example, using
|
||||
a DateTime column will NOT match the datetime type that ships with
|
||||
Doctrine 2.
|
||||
|
||||
.. note::
|
||||
|
||||
DateTime and Object types are compared by reference, not by value. Doctrine
|
||||
updates this values if the reference changes and therefore behaves as if
|
||||
these objects are immutable value objects.
|
||||
DateTime and Object types are compared by reference, not by value. Doctrine updates this values
|
||||
if the reference changes and therefore behaves as if these objects are immutable value objects.
|
||||
|
||||
.. warning::
|
||||
|
||||
All Date types assume that you are exclusively using the default timezone
|
||||
set by `date_default_timezone_set() <http://php.net/manual/en/function.date-default-timezone-set.php>`_
|
||||
set by `date_default_timezone_set() <http://docs.php.net/manual/en/function.date-default-timezone-set.php>`_
|
||||
or by the php.ini configuration ``date.timezone``. Working with
|
||||
different timezones will cause troubles and unexpected behavior.
|
||||
|
||||
@@ -299,25 +206,242 @@ A cookbook article shows how to define :doc:`your own custom mapping types
|
||||
on working with datetimes that gives hints for implementing
|
||||
multi timezone applications.
|
||||
|
||||
Identifiers / Primary Keys
|
||||
--------------------------
|
||||
|
||||
Every entity class must have an identifier/primary key. You can select
|
||||
the field that serves as the identifier with the ``@Id``
|
||||
annotation.
|
||||
Property Mapping
|
||||
----------------
|
||||
|
||||
After a class has been marked as an entity it can specify mappings
|
||||
for its instance fields. Here we will only look at simple fields
|
||||
that hold scalar values like strings, numbers, etc. Associations to
|
||||
other objects are covered in the chapter "Association Mapping".
|
||||
|
||||
To mark a property for relational persistence the ``@Column``
|
||||
docblock annotation is used. This annotation usually requires at
|
||||
least 1 attribute to be set, the ``type``. The ``type`` attribute
|
||||
specifies the Doctrine Mapping Type to use for the field. If the
|
||||
type is not specified, 'string' is used as the default mapping type
|
||||
since it is the most flexible.
|
||||
|
||||
Example:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class Message
|
||||
/** @Entity */
|
||||
class MyPersistentClass
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
/** @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(length=50) */
|
||||
private $name; // type defaults to string
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyPersistentClass">
|
||||
<field name="id" type="integer" />
|
||||
<field name="name" length="50" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
MyPersistentClass:
|
||||
type: entity
|
||||
fields:
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
length: 50
|
||||
|
||||
In that example we mapped the field ``id`` to the column ``id``
|
||||
using the mapping type ``integer`` and the field ``name`` is mapped
|
||||
to the column ``name`` with the default mapping type ``string``. As
|
||||
you can see, by default the column names are assumed to be the same
|
||||
as the field names. To specify a different name for the column, you
|
||||
can use the ``name`` attribute of the Column annotation as
|
||||
follows:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Column(name="db_name") */
|
||||
private $name;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyPersistentClass">
|
||||
<field name="name" column="db_name" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
MyPersistentClass:
|
||||
type: entity
|
||||
fields:
|
||||
name:
|
||||
length: 50
|
||||
column: db_name
|
||||
|
||||
The Column annotation has some more attributes. Here is a complete
|
||||
list:
|
||||
|
||||
|
||||
- ``type``: (optional, defaults to 'string') The mapping type to
|
||||
use for the column.
|
||||
- ``column``: (optional, defaults to field name) The name of the
|
||||
column in the database.
|
||||
- ``length``: (optional, default 255) The length of the column in
|
||||
the database. (Applies only if a string-valued column is used).
|
||||
- ``unique``: (optional, default FALSE) Whether the column is a
|
||||
unique key.
|
||||
- ``nullable``: (optional, default FALSE) Whether the database
|
||||
column is nullable.
|
||||
- ``precision``: (optional, default 0) The precision for a decimal
|
||||
(exact numeric) column. (Applies only if a decimal column is used.)
|
||||
- ``scale``: (optional, default 0) The scale for a decimal (exact
|
||||
numeric) column. (Applies only if a decimal column is used.)
|
||||
|
||||
.. _reference-basic-mapping-custom-mapping-types:
|
||||
|
||||
Custom Mapping Types
|
||||
--------------------
|
||||
|
||||
Doctrine allows you to create new mapping types. This can come in
|
||||
handy when you're missing a specific mapping type or when you want
|
||||
to replace the existing implementation of a mapping type.
|
||||
|
||||
In order to create a new mapping type you need to subclass
|
||||
``Doctrine\DBAL\Types\Type`` and implement/override the methods as
|
||||
you wish. Here is an example skeleton of such a custom type class:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace My\Project\Types;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
/**
|
||||
* My custom datatype.
|
||||
*/
|
||||
class MyType extends Type
|
||||
{
|
||||
const MYTYPE = 'mytype'; // modify to match your type name
|
||||
|
||||
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
// return the SQL used to create your column type. To create a portable column type, use the $platform.
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is read from the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is written to the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return self::MYTYPE; // modify to match your constant name
|
||||
}
|
||||
}
|
||||
|
||||
Restrictions to keep in mind:
|
||||
|
||||
|
||||
- If the value of the field is *NULL* the method
|
||||
``convertToDatabaseValue()`` is not called.
|
||||
- The ``UnitOfWork`` never passes values to the database convert
|
||||
method that did not change in the request.
|
||||
|
||||
When you have implemented the type you still need to let Doctrine
|
||||
know about it. This can be achieved through the
|
||||
``Doctrine\DBAL\Types\Type#addType($name, $className)``
|
||||
method. See the following example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// in bootstrapping code
|
||||
|
||||
// ...
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
// ...
|
||||
|
||||
// Register my type
|
||||
Type::addType('mytype', 'My\Project\Types\MyType');
|
||||
|
||||
As can be seen above, when registering the custom types in the
|
||||
configuration you specify a unique name for the mapping type and
|
||||
map that to the corresponding fully qualified class name. Now you
|
||||
can use your new type in your mapping like this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyPersistentClass
|
||||
{
|
||||
/** @Column(type="mytype") */
|
||||
private $field;
|
||||
}
|
||||
|
||||
To have Schema-Tool convert the underlying database type of your
|
||||
new "mytype" directly into an instance of ``MyType`` you have to
|
||||
additionally register this mapping with your database platform:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$conn = $em->getConnection();
|
||||
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype');
|
||||
|
||||
Now using Schema-Tool, whenever it detects a column having the
|
||||
``db_mytype`` it will convert it into a ``mytype`` Doctrine Type
|
||||
instance for Schema representation. Keep in mind that you can
|
||||
easily produce clashes this way, each database type can only map to
|
||||
exactly one Doctrine mapping type.
|
||||
|
||||
Custom ColumnDefinition
|
||||
-----------------------
|
||||
|
||||
You can define a custom definition for each column using the "columnDefinition"
|
||||
attribute of ``@Column``. You have to define all the definitions that follow
|
||||
the name of a column here.
|
||||
|
||||
.. note::
|
||||
|
||||
Using columnDefinition will break change-detection in SchemaTool.
|
||||
|
||||
Identifiers / Primary Keys
|
||||
--------------------------
|
||||
|
||||
Every entity class needs an identifier/primary key. You designate
|
||||
the field that serves as the identifier with the ``@Id`` marker
|
||||
annotation. Here is an example:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyPersistentClass
|
||||
{
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
//...
|
||||
}
|
||||
@@ -325,17 +449,60 @@ annotation.
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Message">
|
||||
<id name="id" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
<!-- -->
|
||||
<entity name="MyPersistentClass">
|
||||
<id name="id" type="integer" />
|
||||
<field name="name" length="50" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
MyPersistentClass:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
fields:
|
||||
name:
|
||||
length: 50
|
||||
|
||||
Without doing anything else, the identifier is assumed to be
|
||||
manually assigned. That means your code would need to properly set
|
||||
the identifier property before passing a new entity to
|
||||
``EntityManager#persist($entity)``.
|
||||
|
||||
A common alternative strategy is to use a generated value as the
|
||||
identifier. To do this, you use the ``@GeneratedValue`` annotation
|
||||
like this:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyPersistentClass
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyPersistentClass">
|
||||
<id name="id" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
<field name="name" length="50" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
MyPersistentClass:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
@@ -343,12 +510,15 @@ annotation.
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
# fields here
|
||||
name:
|
||||
length: 50
|
||||
|
||||
In most cases using the automatic generator strategy (``@GeneratedValue``) is
|
||||
what you want. It defaults to the identifier generation mechanism your current
|
||||
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
|
||||
and Oracle and so on.
|
||||
This tells Doctrine to automatically generate a value for the
|
||||
identifier. How this value is generated is specified by the
|
||||
``strategy`` attribute, which is optional and defaults to 'AUTO'. A
|
||||
value of ``AUTO`` tells Doctrine to use the generation strategy
|
||||
that is preferred by the currently used database platform. See
|
||||
below for details.
|
||||
|
||||
Identifier Generation Strategies
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -356,26 +526,24 @@ Identifier Generation Strategies
|
||||
The previous example showed how to use the default identifier
|
||||
generation strategy without knowing the underlying database with
|
||||
the AUTO-detection strategy. It is also possible to specify the
|
||||
identifier generation strategy more explicitly, which allows you to
|
||||
identifier generation strategy more explicitly, which allows to
|
||||
make use of some additional features.
|
||||
|
||||
Here is the list of possible generation strategies:
|
||||
|
||||
|
||||
- ``AUTO`` (default): Tells Doctrine to pick the strategy that is
|
||||
preferred by the used database platform. The preferred strategies
|
||||
are IDENTITY for MySQL, SQLite, MsSQL and SQL Anywhere and SEQUENCE
|
||||
for Oracle and PostgreSQL. This strategy provides full portability.
|
||||
are IDENTITY for MySQL, SQLite and MsSQL and SEQUENCE for Oracle
|
||||
and PostgreSQL. This strategy provides full portability.
|
||||
- ``SEQUENCE``: Tells Doctrine to use a database sequence for ID
|
||||
generation. This strategy does currently not provide full
|
||||
portability. Sequences are supported by Oracle, PostgreSql and
|
||||
SQL Anywhere.
|
||||
portability. Sequences are supported by Oracle and PostgreSql.
|
||||
- ``IDENTITY``: Tells Doctrine to use special identity columns in
|
||||
the database that generate a value on insertion of a row. This
|
||||
strategy does currently not provide full portability and is
|
||||
supported by the following platforms: MySQL/SQLite/SQL Anywhere
|
||||
supported by the following platforms: MySQL/SQLite
|
||||
(AUTO\_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL).
|
||||
- ``UUID``: Tells Doctrine to use the built-in Universally Unique Identifier
|
||||
generator. This strategy provides full portability.
|
||||
- ``TABLE``: Tells Doctrine to use a separate table for ID
|
||||
generation. This strategy provides full portability.
|
||||
***This strategy is not yet implemented!***
|
||||
@@ -383,8 +551,6 @@ Here is the list of possible generation strategies:
|
||||
thus generated) by your code. The assignment must take place before
|
||||
a new entity is passed to ``EntityManager#persist``. NONE is the
|
||||
same as leaving off the @GeneratedValue entirely.
|
||||
- ``CUSTOM``: With this option, you can use the ``@CustomIdGenerator`` annotation.
|
||||
It will allow you to pass a :doc:`class of your own to generate the identifiers.<_annref_customidgenerator>`
|
||||
|
||||
Sequence Generator
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
@@ -398,31 +564,30 @@ besides specifying the sequence's name:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class Message
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue(strategy="SEQUENCE")
|
||||
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
|
||||
* @SequenceGenerator(sequenceName="tablename_seq", initialValue=1, allocationSize=100)
|
||||
*/
|
||||
protected $id = null;
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Message">
|
||||
<entity name="User">
|
||||
<id name="id" type="integer">
|
||||
<generator strategy="SEQUENCE" />
|
||||
<sequence-generator sequence-name="message_seq" allocation-size="100" initial-value="1" />
|
||||
<sequence-generator sequence-name="tablename_seq" allocation-size="100" initial-value="1" />
|
||||
</id>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
MyPersistentClass:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
@@ -430,7 +595,7 @@ besides specifying the sequence's name:
|
||||
generator:
|
||||
strategy: SEQUENCE
|
||||
sequenceGenerator:
|
||||
sequenceName: message_seq
|
||||
sequenceName: tablename_seq
|
||||
allocationSize: 100
|
||||
initialValue: 1
|
||||
|
||||
@@ -442,7 +607,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 ORM would only
|
||||
the above example with ``allocationSize=100`` Doctrine 2 would only
|
||||
need to access the sequence once to generate the identifiers for
|
||||
100 new entities.
|
||||
|
||||
@@ -470,22 +635,25 @@ need to access the sequence once to generate the identifiers for
|
||||
Composite Keys
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
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
|
||||
values yourself before calling ``EntityManager#persist()`` on the entity.
|
||||
Doctrine 2 allows to use composite primary keys. There are however
|
||||
some restrictions opposed to using a single identifier. The use of
|
||||
the ``@GeneratedValue`` annotation is only supported for simple
|
||||
(not composite) primary keys, which means you can only use
|
||||
composite keys if you generate the primary key values yourself
|
||||
before calling ``EntityManager#persist()`` on the entity.
|
||||
|
||||
More details on composite primary keys are discussed in a :doc:`dedicated tutorial
|
||||
<../tutorials/composite-primary-keys>`.
|
||||
To designate a composite primary key / identifier, simply put the
|
||||
@Id marker annotation on all fields that make up the primary key.
|
||||
|
||||
Quoting Reserved Words
|
||||
----------------------
|
||||
|
||||
Sometimes it is necessary to quote a column or table name because of reserved
|
||||
word conflicts. Doctrine does not quote identifiers automatically, because it
|
||||
leads to more problems than it would solve. Quoting tables and column names
|
||||
needs to be done explicitly using ticks in the definition.
|
||||
It may sometimes be necessary to quote a column or table name
|
||||
because it conflicts with a reserved word of the particular RDBMS
|
||||
in use. This is often referred to as "Identifier Quoting". To let
|
||||
Doctrine know that you would like a table or column name to be
|
||||
quoted in all SQL statements, enclose the table or column name in
|
||||
backticks. Here is an example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -498,22 +666,18 @@ according to the used database platform.
|
||||
|
||||
.. warning::
|
||||
|
||||
Identifier Quoting does not work for join column names or discriminator
|
||||
column names unless you are using a custom ``QuoteStrategy``.
|
||||
Identifier Quoting is not supported for join column
|
||||
names or discriminator column names.
|
||||
|
||||
.. _reference-basic-mapping-custom-mapping-types:
|
||||
.. warning::
|
||||
|
||||
For more control over column quoting the ``Doctrine\ORM\Mapping\QuoteStrategy`` interface
|
||||
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()``.
|
||||
Identifier Quoting is a feature that is mainly intended
|
||||
to support legacy database schemas. The use of reserved words and
|
||||
identifier quoting is generally discouraged. Identifier quoting
|
||||
should not be used to enable the use non-standard-characters such
|
||||
as a dash in a hypothetical column ``test-name``. Also Schema-Tool
|
||||
will likely have troubles when quoting is used for case-sensitivity
|
||||
reasons (in Oracle for example).
|
||||
|
||||
The ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name.
|
||||
You can use it with the following code:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\AnsiQuoteStrategy;
|
||||
|
||||
$configuration->setQuoteStrategy(new AnsiQuoteStrategy());
|
||||
|
||||
@@ -16,15 +16,6 @@ especially what the strategies presented here provide help with.
|
||||
operations.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Having an SQL logger enabled when processing batches can have a serious impact on performance and resource usage.
|
||||
To avoid that you should disable it in the DBAL configuration:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConnection()->getConfiguration()->setSQLLogger(null);
|
||||
|
||||
Bulk Inserts
|
||||
------------
|
||||
|
||||
@@ -51,8 +42,6 @@ internally but also mean more work during ``flush``.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
}
|
||||
$em->flush(); //Persist objects that did not make up an entire batch
|
||||
$em->clear();
|
||||
|
||||
Bulk Updates
|
||||
------------
|
||||
@@ -75,7 +64,7 @@ Iterating results
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
An alternative solution for bulk updates is to use the
|
||||
``Query#toIterable()`` facility to iterate over the query results step
|
||||
``Query#iterate()`` 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:
|
||||
@@ -84,16 +73,18 @@ with the batching strategy that was already used for bulk inserts:
|
||||
|
||||
<?php
|
||||
$batchSize = 20;
|
||||
$i = 1;
|
||||
$i = 0;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
foreach ($q->toIterable() as $user) {
|
||||
$iterableResult = $q->iterate();
|
||||
foreach($iterableResult AS $row) {
|
||||
$user = $row[0];
|
||||
$user->increaseCredit();
|
||||
$user->calculateNewBonuses();
|
||||
++$i;
|
||||
if (($i % $batchSize) === 0) {
|
||||
$em->flush(); // Executes all updates.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
@@ -103,12 +94,6 @@ with the batching strategy that was already used for bulk inserts:
|
||||
fetch-join a collection-valued association. The nature of such SQL
|
||||
result sets is not suitable for incremental hydration.
|
||||
|
||||
.. note::
|
||||
|
||||
Results may be fully buffered by the database client/ connection allocating
|
||||
additional memory not visible to the PHP process. For large sets this
|
||||
may easily kill the process for no apparent reason.
|
||||
|
||||
|
||||
Bulk Deletes
|
||||
------------
|
||||
@@ -135,7 +120,7 @@ Iterating results
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
An alternative solution for bulk deletes is to use the
|
||||
``Query#toIterable()`` facility to iterate over the query results step
|
||||
``Query#iterate()`` 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:
|
||||
|
||||
@@ -143,15 +128,16 @@ The following example shows how to do this:
|
||||
|
||||
<?php
|
||||
$batchSize = 20;
|
||||
$i = 1;
|
||||
$i = 0;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
foreach($q->toIterable() as $row) {
|
||||
$em->remove($row);
|
||||
++$i;
|
||||
$iterableResult = $q->iterate();
|
||||
while (($row = $iterableResult->next()) !== false) {
|
||||
$em->remove($row[0]);
|
||||
if (($i % $batchSize) === 0) {
|
||||
$em->flush(); // Executes all deletions.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
@@ -165,18 +151,20 @@ The following example shows how to do this:
|
||||
Iterating Large Results for Data-Processing
|
||||
-------------------------------------------
|
||||
|
||||
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
|
||||
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
|
||||
problems using the following approach:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
|
||||
foreach ($q->toIterable() as $row) {
|
||||
// do stuff with the data in the row
|
||||
|
||||
$iterableResult = $q->iterate();
|
||||
foreach ($iterableResult AS $row) {
|
||||
// do stuff with the data in the row, $row[0] is always the object
|
||||
|
||||
// detach from Doctrine, so that it can be Garbage-Collected immediately
|
||||
$this->_em->detach($row[0]);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,22 @@ design generally refer to best practices when working with Doctrine
|
||||
and do not necessarily reflect best practices for database design
|
||||
in general.
|
||||
|
||||
|
||||
Don't use public properties on entities
|
||||
---------------------------------------
|
||||
|
||||
It is very important that you don't map public properties on
|
||||
entities, but only protected or private ones. The reason for this
|
||||
is simple, whenever you access a public property of a proxy object
|
||||
that hasn't been initialized yet the return value will be null.
|
||||
Doctrine cannot hook into this process and magically make the
|
||||
entity lazy load.
|
||||
|
||||
This can create situations where it is very hard to debug the
|
||||
current failure. We therefore urge you to map only private and
|
||||
protected properties on entities and use getter methods or magic
|
||||
\_\_get() to access them.
|
||||
|
||||
Constrain relationships as much as possible
|
||||
-------------------------------------------
|
||||
|
||||
@@ -54,7 +70,7 @@ Don't use special characters
|
||||
|
||||
Avoid using any non-ASCII characters in class, field, table or
|
||||
column names. Doctrine itself is not unicode-safe in many places
|
||||
and will not be until PHP itself is fully unicode-aware.
|
||||
and will not be until PHP itself is fully unicode-aware (PHP6).
|
||||
|
||||
Don't use identifier quoting
|
||||
----------------------------
|
||||
|
||||
+120
-148
@@ -1,80 +1,71 @@
|
||||
Caching
|
||||
=======
|
||||
|
||||
Doctrine provides cache drivers in the ``doctrine/cache`` package for some
|
||||
of the most popular caching implementations such as APCu, Memcache
|
||||
Doctrine provides cache drivers in the ``Common`` package for some
|
||||
of the most popular caching implementations such as APC, 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
|
||||
testing in a development environment.
|
||||
the data in a PHP array. Obviously, the cache does not live between
|
||||
requests but this is useful for testing in a development
|
||||
environment.
|
||||
|
||||
Cache Drivers
|
||||
-------------
|
||||
|
||||
The cache drivers follow a simple interface that is defined in
|
||||
``Doctrine\Common\Cache\Cache``. All the cache drivers extend a
|
||||
base class ``Doctrine\Common\Cache\CacheProvider`` which implements
|
||||
this interface.
|
||||
base class ``Doctrine\Common\Cache\AbstractCache`` which implements
|
||||
the before mentioned interface.
|
||||
|
||||
The interface defines the following public methods for you to implement:
|
||||
The interface defines the following methods for you to publicly
|
||||
use.
|
||||
|
||||
|
||||
- fetch($id) - Fetches an entry from the cache
|
||||
- contains($id) - Test if an entry exists in the cache
|
||||
- save($id, $data, $lifeTime = false) - Puts data into the cache for x seconds. 0 = infinite time
|
||||
- delete($id) - Deletes a cache entry
|
||||
- fetch($id) - Fetches an entry from the cache.
|
||||
- contains($id) - Test if an entry exists in the cache.
|
||||
- save($id, $data, $lifeTime = false) - Puts data into the cache.
|
||||
- delete($id) - Deletes a cache entry.
|
||||
|
||||
Each driver extends the ``CacheProvider`` class which defines a few
|
||||
Each driver extends the ``AbstractCache`` class which defines a few
|
||||
abstract protected methods that each of the drivers must
|
||||
implement:
|
||||
implement.
|
||||
|
||||
|
||||
- doFetch($id)
|
||||
- doContains($id)
|
||||
- doSave($id, $data, $lifeTime = false)
|
||||
- doDelete($id)
|
||||
- \_doFetch($id)
|
||||
- \_doContains($id)
|
||||
- \_doSave($id, $data, $lifeTime = false)
|
||||
- \_doDelete($id)
|
||||
|
||||
The public methods ``fetch()``, ``contains()`` etc. use the
|
||||
above protected methods which are implemented by the drivers. The
|
||||
The public methods ``fetch()``, ``contains()``, etc. utilize the
|
||||
above protected methods that are implemented by the drivers. The
|
||||
code is organized this way so that the protected methods in the
|
||||
drivers do the raw interaction with the cache implementation and
|
||||
the ``CacheProvider`` can build custom functionality on top of
|
||||
the ``AbstractCache`` can build custom functionality on top of
|
||||
these methods.
|
||||
|
||||
This documentation does not cover every single cache driver included
|
||||
with Doctrine. For an up-to-date-list, see the
|
||||
`cache directory on GitHub <https://github.com/doctrine/cache/tree/2.8.x/lib/Doctrine/Common/Cache>`_.
|
||||
APC
|
||||
~~~
|
||||
|
||||
PhpFileCache
|
||||
~~~~~~~~~~~~
|
||||
In order to use the APC cache driver you must have it compiled and
|
||||
enabled in your php.ini. You can read about APC
|
||||
`in the PHP Documentation <http://us2.php.net/apc>`_. It will give
|
||||
you a little background information about what it is and how you
|
||||
can use it as well as how to install it.
|
||||
|
||||
The preferred cache driver for metadata and query caches is ``PhpFileCache``.
|
||||
This driver serializes cache items and writes them to a file. This allows for
|
||||
opcode caching to be used and provides high performance in most scenarios.
|
||||
|
||||
In order to use the ``PhpFileCache`` driver it must be able to write to
|
||||
a directory.
|
||||
|
||||
Below is an example of how to use the ``PhpFileCache`` driver by itself.
|
||||
Below is a simple example of how you could use the APC cache driver
|
||||
by itself.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
|
||||
The PhpFileCache is not distributed across multiple machines if you are running
|
||||
your application in a distributed setup. This is ok for the metadata and query
|
||||
cache but is not a good approach for the result cache.
|
||||
|
||||
Memcache
|
||||
~~~~~~~~
|
||||
|
||||
In order to use the Memcache cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about Memcache
|
||||
`on the PHP website <http://php.net/memcache>`_. It will
|
||||
` on the PHP website <http://us2.php.net/memcache>`_. It will
|
||||
give you a little background information about what it is and how
|
||||
you can use it as well as how to install it.
|
||||
|
||||
@@ -91,39 +82,32 @@ driver by itself.
|
||||
$cacheDriver->setMemcache($memcache);
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
|
||||
Memcached
|
||||
~~~~~~~~~
|
||||
Xcache
|
||||
~~~~~~
|
||||
|
||||
Memcached is a more recent and complete alternative extension to
|
||||
Memcache.
|
||||
In order to use the Xcache cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about Xcache
|
||||
`here <http://xcache.lighttpd.net/>`_. It will give you a little
|
||||
background information about what it is and how you can use it as
|
||||
well as how to install it.
|
||||
|
||||
In order to use the Memcached cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about Memcached
|
||||
`on the PHP website <http://php.net/memcached>`_. It will
|
||||
give you a little background information about what it is and how
|
||||
you can use it as well as how to install it.
|
||||
|
||||
Below is a simple example of how you could use the Memcached cache
|
||||
Below is a simple example of how you could use the Xcache cache
|
||||
driver by itself.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$memcached = new Memcached();
|
||||
$memcached->addServer('memcache_host', 11211);
|
||||
|
||||
$cacheDriver = new \Doctrine\Common\Cache\MemcachedCache();
|
||||
$cacheDriver->setMemcached($memcached);
|
||||
$cacheDriver = new \Doctrine\Common\Cache\XcacheCache();
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
|
||||
Redis
|
||||
~~~~~
|
||||
|
||||
In order to use the Redis cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about what Redis is
|
||||
and enabled in your php.ini. You can read about what is Redis
|
||||
`from here <http://redis.io/>`_. Also check
|
||||
`A PHP extension for Redis <https://github.com/nicolasff/phpredis/>`_ for how you can use
|
||||
and install the Redis PHP extension.
|
||||
and install Redis PHP extension.
|
||||
|
||||
Below is a simple example of how you could use the Redis cache
|
||||
driver by itself.
|
||||
@@ -142,8 +126,8 @@ Using Cache Drivers
|
||||
-------------------
|
||||
|
||||
In this section we'll describe how you can fully utilize the API of
|
||||
the cache drivers to save data to a cache, check if some cached data
|
||||
exists, fetch the cached data and delete the cached data. We'll use the
|
||||
the cache drivers to save cache, check if some cache exists, fetch
|
||||
the cached data and delete the cached data. We'll use the
|
||||
``ArrayCache`` implementation as our example here.
|
||||
|
||||
.. code-block:: php
|
||||
@@ -154,7 +138,7 @@ exists, fetch the cached data and delete the cached data. We'll use the
|
||||
Saving
|
||||
~~~~~~
|
||||
|
||||
Saving some data to the cache driver is as simple as using the
|
||||
To save some data to the cache driver it is as simple as using the
|
||||
``save()`` method.
|
||||
|
||||
.. code-block:: php
|
||||
@@ -163,7 +147,7 @@ Saving some data to the cache driver is as simple as using the
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
|
||||
The ``save()`` method accepts three arguments which are described
|
||||
below:
|
||||
below.
|
||||
|
||||
|
||||
- ``$id`` - The cache id
|
||||
@@ -186,7 +170,7 @@ object, etc.
|
||||
Checking
|
||||
~~~~~~~~
|
||||
|
||||
Checking whether cached data exists is very simple: just use the
|
||||
Checking whether some cache exists is very simple, just use the
|
||||
``contains()`` method. It accepts a single argument which is the ID
|
||||
of the cache entry.
|
||||
|
||||
@@ -204,7 +188,7 @@ Fetching
|
||||
|
||||
Now if you want to retrieve some cache entry you can use the
|
||||
``fetch()`` method. It also accepts a single argument just like
|
||||
``contains()`` which is again the ID of the cache entry.
|
||||
``contains()`` which is the ID of the cache entry.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -215,8 +199,9 @@ Deleting
|
||||
~~~~~~~~
|
||||
|
||||
As you might guess, deleting is just as easy as saving, checking
|
||||
and fetching. You can delete by an individual ID, or you can
|
||||
delete all entries.
|
||||
and fetching. We have a few ways to delete cache entries. You can
|
||||
delete by an individual ID, regular expression, prefix, suffix or
|
||||
you can delete all entries.
|
||||
|
||||
By Cache ID
|
||||
^^^^^^^^^^^
|
||||
@@ -240,7 +225,7 @@ the ``deleteAll()`` method.
|
||||
Namespaces
|
||||
~~~~~~~~~~
|
||||
|
||||
If you heavily use caching in your application and use it in
|
||||
If you heavily use caching in your application and utilize it in
|
||||
multiple parts of your application, or use it in different
|
||||
applications on the same server you may have issues with cache
|
||||
naming collisions. This can be worked around by using namespaces.
|
||||
@@ -252,14 +237,12 @@ You can set the namespace a cache driver should use by using the
|
||||
<?php
|
||||
$cacheDriver->setNamespace('my_namespace_');
|
||||
|
||||
.. _integrating-with-the-orm:
|
||||
|
||||
Integrating with the ORM
|
||||
------------------------
|
||||
|
||||
The Doctrine ORM package is tightly integrated with the cache
|
||||
drivers to allow you to improve the performance of various aspects of
|
||||
Doctrine by simply making some additional configurations and
|
||||
drivers to allow you to improve performance of various aspects of
|
||||
Doctrine by just simply making some additional configurations and
|
||||
method calls.
|
||||
|
||||
Query Cache
|
||||
@@ -276,11 +259,8 @@ use on your ORM configuration.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setQueryCacheImpl($cacheDriver);
|
||||
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache());
|
||||
|
||||
Result Cache
|
||||
~~~~~~~~~~~~
|
||||
@@ -293,11 +273,7 @@ cache implementation.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setResultCacheImpl($cacheDriver);
|
||||
$config->setResultCacheImpl(new \Doctrine\Common\Cache\ApcCache());
|
||||
|
||||
Now when you're executing DQL queries you can configure them to use
|
||||
the result cache.
|
||||
@@ -306,7 +282,7 @@ the result cache.
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('select u from \Entities\User u');
|
||||
$query->enableResultCache();
|
||||
$query->useResultCache(true);
|
||||
|
||||
You can also configure an individual query to use a different
|
||||
result cache driver.
|
||||
@@ -314,21 +290,18 @@ result cache driver.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$query->setResultCacheDriver($cacheDriver);
|
||||
$query->setResultCacheDriver(new \Doctrine\Common\Cache\ApcCache());
|
||||
|
||||
.. note::
|
||||
|
||||
Setting the result cache driver on the query will
|
||||
automatically enable the result cache for the query. If you want to
|
||||
disable it use ``disableResultCache()``.
|
||||
disable it pass false to ``useResultCache()``.
|
||||
|
||||
::
|
||||
|
||||
<?php
|
||||
$query->disableResultCache();
|
||||
$query->useResultCache(false);
|
||||
|
||||
|
||||
If you want to set the time the cache has to live you can use the
|
||||
@@ -349,12 +322,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 first and second argument to ``enableResultCache()``.
|
||||
the second and third argument to ``useResultCache()``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query->enableResultCache(3600, 'my_custom_id');
|
||||
$query->useResultCache(true, 3600, 'my_custom_id');
|
||||
|
||||
Metadata Cache
|
||||
~~~~~~~~~~~~~~
|
||||
@@ -369,11 +342,7 @@ first.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setMetadataCacheImpl($cacheDriver);
|
||||
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache());
|
||||
|
||||
Now the metadata information will only be parsed once and stored in
|
||||
the cache driver.
|
||||
@@ -381,85 +350,88 @@ the cache driver.
|
||||
Clearing the Cache
|
||||
------------------
|
||||
|
||||
We've already shown you how you can use the API of the
|
||||
We've already shown you previously how you can use the API of the
|
||||
cache drivers to manually delete cache entries. For your
|
||||
convenience we offer command line tasks to help you with
|
||||
convenience we offer a command line task for you to help you with
|
||||
clearing the query, result and metadata cache.
|
||||
|
||||
From the Doctrine command line you can run the following commands:
|
||||
|
||||
To clear the query cache use the ``orm:clear-cache:query`` task.
|
||||
From the Doctrine command line you can run the following command.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:clear-cache:query
|
||||
$ ./doctrine clear-cache
|
||||
|
||||
To clear the metadata cache use the ``orm:clear-cache:metadata`` task.
|
||||
Running this task with no arguments will clear all the cache for
|
||||
all the configured drivers. If you want to be more specific about
|
||||
what you clear you can use the following options.
|
||||
|
||||
To clear the query cache use the ``--query`` option.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:clear-cache:metadata
|
||||
$ ./doctrine clear-cache --query
|
||||
|
||||
To clear the result cache use the ``orm:clear-cache:result`` task.
|
||||
To clear the metadata cache use the ``--metadata`` option.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:clear-cache:result
|
||||
$ ./doctrine clear-cache --metadata
|
||||
|
||||
All these tasks accept a ``--flush`` option to flush the entire
|
||||
contents of the cache instead of invalidating the entries.
|
||||
To clear the result cache use the ``--result`` option.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine clear-cache --result
|
||||
|
||||
When you use the ``--result`` option you can use some other options
|
||||
to be more specific about what queries result sets you want to
|
||||
clear.
|
||||
|
||||
Just like the API of the cache drivers you can clear based on an
|
||||
ID, regular expression, prefix or suffix.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine clear-cache --result --id=cache_id
|
||||
|
||||
Or if you want to clear based on a regular expressions.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine clear-cache --result --regex=users_.*
|
||||
|
||||
Or with a prefix.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine clear-cache --result --prefix=users_
|
||||
|
||||
And finally with a suffix.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine clear-cache --result --suffix=_my_account
|
||||
|
||||
.. note::
|
||||
|
||||
None of these tasks will work with APC, APCu, or XCache drivers
|
||||
because the memory that the cache is stored in is only accessible
|
||||
to the webserver.
|
||||
Using the ``--id``, ``--regex``, etc. options with the
|
||||
``--query`` and ``--metadata`` are not allowed as it is not
|
||||
necessary to be specific about what you clear. You only ever need
|
||||
to completely clear the cache to remove stale entries.
|
||||
|
||||
Cache Chaining
|
||||
--------------
|
||||
|
||||
A common pattern is to use a static cache to store data that is
|
||||
requested many times in a single PHP request. Even though this data
|
||||
may be stored in a fast memory cache, often that cache is over a
|
||||
network link leading to sizable network traffic.
|
||||
|
||||
The ChainCache class allows multiple caches to be registered at once.
|
||||
For example, a per-request ArrayCache can be used first, followed by
|
||||
a (relatively) slower MemcacheCache if the ArrayCache misses.
|
||||
ChainCache automatically handles pushing data up to faster caches in
|
||||
the chain and clearing data in the entire stack when it is deleted.
|
||||
|
||||
A ChainCache takes a simple array of CacheProviders in the order that
|
||||
they should be used.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$arrayCache = new \Doctrine\Common\Cache\ArrayCache();
|
||||
$memcache = new Memcache();
|
||||
$memcache->connect('memcache_host', 11211);
|
||||
$chainCache = new \Doctrine\Common\Cache\ChainCache([
|
||||
$arrayCache,
|
||||
$memcache,
|
||||
]);
|
||||
|
||||
ChainCache itself extends the CacheProvider interface, so it is
|
||||
possible to create chains of chains. While this may seem like an easy
|
||||
way to build a simple high-availability cache, ChainCache does not
|
||||
implement any exception handling so using it as a high-availability
|
||||
mechanism is not recommended.
|
||||
|
||||
Cache Slams
|
||||
-----------
|
||||
|
||||
Something to be careful of when using the cache drivers is
|
||||
"cache slams". Imagine you have a heavily trafficked website with some
|
||||
Something to be careful of when utilizing the cache drivers is
|
||||
cache slams. If you have a heavily trafficked website with some
|
||||
code that checks for the existence of a cache record and if it does
|
||||
not exist it generates the information and saves it to the cache.
|
||||
Now, if 100 requests were issued all at the same time and each one
|
||||
sees the cache does not exist and they all try to insert the same
|
||||
Now if 100 requests were issued all at the same time and each one
|
||||
sees the cache does not exist and they all try and insert the same
|
||||
cache entry it could lock up APC, Xcache, etc. and cause problems.
|
||||
Ways exist to work around this, like pre-populating your cache and
|
||||
not letting your users' requests populate the cache.
|
||||
not letting your users requests populate the cache.
|
||||
|
||||
You can read more about cache slams
|
||||
`in this blog post <http://notmysock.org/blog/php/user-cache-timebomb.html>`_.
|
||||
|
||||
@@ -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 ORM only
|
||||
comparison at commit time. The difference is that Doctrine 2 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
|
||||
@@ -61,11 +61,6 @@ This policy can be configured as follows:
|
||||
Notify
|
||||
~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
This policy is based on the assumption that the entities notify
|
||||
interested listeners of changes to their properties. For that
|
||||
purpose, a class that wants to use this policy needs to implement
|
||||
@@ -76,8 +71,8 @@ follows:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Persistence\NotifyPropertyChanged,
|
||||
Doctrine\Persistence\PropertyChangedListener;
|
||||
use Doctrine\Common\NotifyPropertyChanged,
|
||||
Doctrine\Common\PropertyChangedListener;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
Installation and Configuration
|
||||
==============================
|
||||
|
||||
Doctrine can be installed with `Composer <https://getcomposer.org>`_.
|
||||
Doctrine can be installed with `Composer <http://www.getcomposer.org>`_. For
|
||||
older versions we still have `PEAR packages
|
||||
<http://pear.doctrine-project.org>`_.
|
||||
|
||||
Define the following requirement in your ``composer.json`` file:
|
||||
|
||||
@@ -14,7 +16,8 @@ Define the following requirement in your ``composer.json`` file:
|
||||
}
|
||||
|
||||
Then call ``composer install`` from your command line. If you don't know
|
||||
how Composer works, check out their `Getting Started <https://getcomposer.org/doc/00-intro.md>`_ to set up.
|
||||
how Composer works, check out their `Getting Started
|
||||
<http://getcomposer.org/doc/00-intro.md>`_ to set up.
|
||||
|
||||
Class loading
|
||||
-------------
|
||||
@@ -44,7 +47,7 @@ access point to ORM functionality provided by Doctrine.
|
||||
use Doctrine\ORM\Tools\Setup;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
$paths = array("/path/to/entity-files");
|
||||
$paths = array("/path/to/entities-or-mapping-files");
|
||||
$isDevMode = false;
|
||||
|
||||
// the connection configuration
|
||||
@@ -63,7 +66,6 @@ Or if you prefer XML:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$paths = array("/path/to/xml-mappings");
|
||||
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
|
||||
$entityManager = EntityManager::create($dbParams, $config);
|
||||
|
||||
@@ -72,25 +74,19 @@ Or if you prefer YAML:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$paths = array("/path/to/yml-mappings");
|
||||
$config = Setup::createYAMLMetadataConfiguration($paths, $isDevMode);
|
||||
$entityManager = EntityManager::create($dbParams, $config);
|
||||
|
||||
.. note::
|
||||
If you want to use yml mapping you should add yaml dependency to your `composer.json`:
|
||||
|
||||
::
|
||||
|
||||
"symfony/yaml": "*"
|
||||
|
||||
Inside the ``Setup`` methods several assumptions are made:
|
||||
|
||||
- If `$isDevMode` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated on every request.
|
||||
- If `$isDevMode` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument.
|
||||
- If `$isDevMode` is false, set then proxy classes have to be explicitly created through the command line.
|
||||
- If `$devMode` is true always use an ``ArrayCache`` (in-memory) and regenerate proxies on every request.
|
||||
- If `$devMode` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument.
|
||||
- If `$devMode` is false, set then proxy classes have to be explicitly created
|
||||
through the command line.
|
||||
- If third argument `$proxyDir` is not set, use the systems temporary directory.
|
||||
|
||||
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration <reference/advanced-configuration>` section.
|
||||
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced
|
||||
Configuration <reference/advanced-configuration>` section.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -112,6 +108,8 @@ 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
|
||||
@@ -124,3 +122,19 @@ following content:
|
||||
$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)
|
||||
));
|
||||
|
||||
@@ -2,7 +2,7 @@ Doctrine Query Language
|
||||
===========================
|
||||
|
||||
DQL stands for Doctrine Query Language and is an Object
|
||||
Query Language derivative that is very similar to the Hibernate
|
||||
Query Language derivate that is very similar to the Hibernate
|
||||
Query Language (HQL) or the Java Persistence Query Language (JPQL).
|
||||
|
||||
In essence, DQL provides powerful querying capabilities over your
|
||||
@@ -34,9 +34,9 @@ object model.
|
||||
|
||||
DQL SELECT statements are a very powerful way of retrieving parts
|
||||
of your domain model that are not accessible via associations.
|
||||
Additionally they allow you to retrieve entities and their associations
|
||||
Additionally they allow to retrieve entities and their associations
|
||||
in one single SQL select statement which can make a huge difference
|
||||
in performance compared to using several queries.
|
||||
in performance in contrast to using several queries.
|
||||
|
||||
DQL UPDATE and DELETE statements offer a way to execute bulk
|
||||
changes on the entities of your domain model. This is often
|
||||
@@ -49,6 +49,10 @@ SELECT queries
|
||||
DQL SELECT clause
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The select clause of a DQL query specifies what appears in the
|
||||
query result. The composition of all the expressions in the select
|
||||
clause also influences the nature of the query result.
|
||||
|
||||
Here is an example that selects all users with an age > 20:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -79,58 +83,14 @@ Lets examine the query:
|
||||
The result of this query would be a list of User objects where all
|
||||
users are older than 20.
|
||||
|
||||
Result format
|
||||
~~~~~~~~~~~~~
|
||||
The composition of the expressions in the SELECT clause also
|
||||
influences the nature of the query result. There are three
|
||||
cases:
|
||||
|
||||
**All objects**
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u, p, n FROM Users u...
|
||||
|
||||
In this case, the result will be an array of User objects because of
|
||||
the FROM clause, with children ``p`` and ``n`` hydrated because of
|
||||
their inclusion in the SELECT clause.
|
||||
|
||||
**All scalars**
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u.name, u.address FROM Users u...
|
||||
|
||||
In this case, the result will be an array of arrays. In the example
|
||||
above, each element of the result array would be an array of the
|
||||
scalar name and address values.
|
||||
|
||||
You can select scalars from any entity in the query.
|
||||
|
||||
**Mixed**
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u, p.quantity FROM Users u...
|
||||
|
||||
Here, the result will again be an array of arrays, with each element
|
||||
being an array made up of a User object and the scalar value
|
||||
``p.quantity``.
|
||||
|
||||
Multiple FROM clauses are allowed, which would cause the result
|
||||
array elements to cycle through the classes included in the
|
||||
multiple FROM clauses.
|
||||
|
||||
.. note::
|
||||
|
||||
You cannot select other entities unless you also select the
|
||||
root of the selection (which is the first entity in FROM).
|
||||
|
||||
For example, ``SELECT p,n FROM Users u...`` would be wrong because
|
||||
``u`` is not part of the SELECT
|
||||
|
||||
Doctrine throws an exception if you violate this constraint.
|
||||
|
||||
The SELECT clause allows to specify both class identification
|
||||
variables that signal the hydration of a complete entity class or
|
||||
just fields of the entity using the syntax ``u.name``. Combinations
|
||||
of both are also allowed and it is possible to wrap both fields and
|
||||
identification values into aggregation and DQL functions. Numerical
|
||||
fields can be part of computations using mathematical operations.
|
||||
See the sub-section on `Functions, Operators, Aggregates`_ for
|
||||
more information.
|
||||
|
||||
Joins
|
||||
~~~~~
|
||||
@@ -250,7 +210,7 @@ Retrieve the Username and Name of a CmsUser:
|
||||
$users = $query->getResult(); // array of CmsUser username and name values
|
||||
echo $users[0]['username'];
|
||||
|
||||
Retrieve a ForumUser and its single associated entity:
|
||||
Retrieve a ForumUser and his single associated entity:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -259,7 +219,7 @@ Retrieve a ForumUser and its single associated entity:
|
||||
$users = $query->getResult(); // array of ForumUser objects with the avatar association loaded
|
||||
echo get_class($users[0]->getAvatar());
|
||||
|
||||
Retrieve a CmsUser and fetch join all the phonenumbers it has:
|
||||
Retrieve a CmsUser and fetch join all the phonenumbers he has:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -318,7 +278,7 @@ With Nested Conditions in WHERE Clause:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id');
|
||||
$query = $em->createQuery('SELECT u from ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id');
|
||||
$query->setParameters(array(
|
||||
'name' => 'Bob',
|
||||
'name2' => 'Alice',
|
||||
@@ -342,14 +302,6 @@ With Arithmetic Expression in WHERE clause:
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Retrieve user entities with Arithmetic Expression in ORDER clause, using the ``HIDDEN`` keyword:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u, u.posts_count + u.likes_count AS HIDDEN score FROM CmsUser u ORDER BY score');
|
||||
$users = $query->getResult(); // array of User objects
|
||||
|
||||
Using a LEFT JOIN to hydrate all user-ids and optionally associated
|
||||
article-ids:
|
||||
|
||||
@@ -359,8 +311,7 @@ article-ids:
|
||||
$query = $em->createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a');
|
||||
$results = $query->getResult(); // array of user ids and every article_id for each user
|
||||
|
||||
Restricting a JOIN clause by additional conditions specified by
|
||||
WITH:
|
||||
Restricting a JOIN clause by additional conditions:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -458,6 +409,8 @@ 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
|
||||
@@ -467,36 +420,19 @@ 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)');
|
||||
|
||||
The IDENTITY() DQL function also works for composite primary keys
|
||||
Starting with 2.4, 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 are available,
|
||||
where you can generate an arbitrary join with the following syntax:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM User u JOIN Banlist b WITH u.email = b.email');
|
||||
|
||||
.. note::
|
||||
The differences between WHERE, WITH and HAVING clauses may be
|
||||
confusing.
|
||||
|
||||
- WHERE is applied to the results of an entire query
|
||||
- WITH is applied to a join as an additional condition. For
|
||||
arbitrary joins (SELECT f, b FROM Foo f, Bar b WITH f.id = b.id)
|
||||
the WITH is required, even if it is 1 = 1
|
||||
- HAVING is applied to the results of a query after
|
||||
aggregation (GROUP BY)
|
||||
$query = $em->createQuery('SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1');
|
||||
|
||||
|
||||
Partial Object Syntax
|
||||
@@ -528,12 +464,14 @@ 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.
|
||||
- You can specify any PHP class, it only requires that the constructor of this class matches the ``NEW`` statement.
|
||||
- You can specify any PHP class, it's only require that the constructor of this class matches the ``NEW`` statement.
|
||||
- This approach involves determining exactly which columns you really need,
|
||||
and instantiating a data-transfer object that contains a constructor with those arguments.
|
||||
and instantiating data-transfer object that containing a constructor with those arguments.
|
||||
|
||||
If you want to select data-transfer objects you should create a class:
|
||||
|
||||
@@ -562,8 +500,6 @@ And then use the ``NEW`` DQL keyword :
|
||||
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
Note that you can only pass scalar expressions to the constructor.
|
||||
|
||||
Using INDEX BY
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@@ -603,13 +539,6 @@ then phonenumber-id:
|
||||
...
|
||||
'nameUpper' => string 'JWAGE' (length=5)
|
||||
|
||||
You can also index by a to-one association, which will use the id of
|
||||
the associated entity (the join column) as the key in the result set:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT p, u FROM Participant INDEX BY p.user JOIN p.user u WHERE p.event = 3
|
||||
|
||||
UPDATE queries
|
||||
--------------
|
||||
|
||||
@@ -653,25 +582,12 @@ The same restrictions apply for the reference of related entities.
|
||||
DQL DELETE statements are ported directly into a
|
||||
Database DELETE statement and therefore bypass any events and checks for the
|
||||
version column if they are not explicitly added to the WHERE clause
|
||||
of the query. Additionally Deletes of specified entities are *NOT*
|
||||
of the query. Additionally Deletes of specifies entities are *NOT*
|
||||
cascaded to related entities even if specified in the metadata.
|
||||
|
||||
Comments in queries
|
||||
-------------------
|
||||
|
||||
We can use comments with the SQL syntax of comments.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u FROM MyProject\Model\User u
|
||||
-- my comment
|
||||
WHERE u.age > 20 -- comment at the end of a line
|
||||
|
||||
Functions, Operators, Aggregates
|
||||
--------------------------------
|
||||
It is possible to wrap both fields and identification values into
|
||||
aggregation and DQL functions. Numerical fields can be part of
|
||||
computations using mathematical operations.
|
||||
|
||||
DQL Functions
|
||||
~~~~~~~~~~~~~
|
||||
@@ -700,8 +616,8 @@ clauses:
|
||||
- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim
|
||||
the string by the given trim char, defaults to whitespaces.
|
||||
- UPPER(str) - Return the upper-case of the given string.
|
||||
- DATE_ADD(date, value, unit) - Add the given time to a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
|
||||
- DATE_SUB(date, value, unit) - Subtract the given time from a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
|
||||
- DATE_ADD(date, days, unit) - Add the number of days to a given date. (Supported units are DAY, MONTH)
|
||||
- DATE_SUB(date, days, unit) - Substract the number of days from a given date. (Supported units are DAY, MONTH)
|
||||
- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2.
|
||||
|
||||
Arithmetic operators
|
||||
@@ -784,6 +700,8 @@ classes have to implement the base class :
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$lexer = $parser->getLexer();
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
@@ -909,7 +827,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 ORM
|
||||
of a parent class through a foreign key constraint. Doctrine 2
|
||||
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.
|
||||
@@ -974,7 +892,7 @@ An instance of the ``Doctrine\ORM\Query`` class represents a DQL
|
||||
query. You create a Query instance be calling
|
||||
``EntityManager#createQuery($dql)``, passing the DQL query string.
|
||||
Alternatively you can create an empty ``Query`` instance and invoke
|
||||
``Query#setDQL($dql)`` afterwards. Here are some examples:
|
||||
``Query#setDql($dql)`` afterwards. Here are some examples:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -984,9 +902,9 @@ Alternatively you can create an empty ``Query`` instance and invoke
|
||||
// example1: passing a DQL string
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
|
||||
// example2: using setDQL
|
||||
// example2: using setDql
|
||||
$q = $em->createQuery();
|
||||
$q->setDQL('select u from MyProject\Model\User u');
|
||||
$q->setDql('select u from MyProject\Model\User u');
|
||||
|
||||
Query Result Formats
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1002,12 +920,10 @@ the Query class. Here they are:
|
||||
result is either a plain collection of objects (pure) or an array
|
||||
where the objects are nested in the result rows (mixed).
|
||||
- ``Query#getSingleResult()``: Retrieves a single object. If the
|
||||
result contains more than one object, an ``NonUniqueResultException``
|
||||
is thrown. If the result contains no objects, an ``NoResultException``
|
||||
is thrown. The pure/mixed distinction does not apply.
|
||||
- ``Query#getOneOrNullResult()``: Retrieve a single object. If the
|
||||
result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If no object is found null will be returned.
|
||||
result contains more than one or no object, an exception is thrown. The
|
||||
pure/mixed distinction does not apply.
|
||||
- ``Query#getOneOrNullResult()``: Retrieve a single object. If no
|
||||
object is found null will be returned.
|
||||
- ``Query#getArrayResult()``: Retrieves an array graph (a nested
|
||||
array) that is largely interchangeable with the object graph
|
||||
generated by ``Query#getResult()`` for read-only purposes.
|
||||
@@ -1070,7 +986,7 @@ structure:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$dql = "SELECT u, 'some scalar string', count(g.id) AS num FROM User u JOIN u.groups g GROUP BY u.id";
|
||||
$dql = "SELECT u, 'some scalar string', count(u.groups) AS num FROM User u JOIN u.groups g GROUP BY u.id";
|
||||
|
||||
array
|
||||
[0]
|
||||
@@ -1170,22 +1086,6 @@ Object hydration hydrates the result set into the object graph:
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_OBJECT);
|
||||
|
||||
Sometimes the behavior in the object hydrator can be confusing, which is
|
||||
why we are listing as many of the assumptions here for reference:
|
||||
|
||||
- Objects fetched in a FROM clause are returned as a Set, that means every
|
||||
object is only ever included in the resulting array once. This is the case
|
||||
even when using JOIN or GROUP BY in ways that return the same row for an
|
||||
object multiple times. If the hydrator sees the same object multiple times,
|
||||
then it makes sure it is only returned once.
|
||||
|
||||
- If an object is already in memory from a previous query of any kind, then
|
||||
then the previous object is used, even if the database may contain more
|
||||
recent data. Data from the database is discarded. This even happens if the
|
||||
previous object is still an unloaded proxy.
|
||||
|
||||
This list might be incomplete.
|
||||
|
||||
Array Hydration
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -1229,7 +1129,7 @@ Scalar Hydration:
|
||||
Single Scalar Hydration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a query which returns just a single scalar value you can use
|
||||
If you a query which returns just a single scalar value you can use
|
||||
single scalar hydration:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -1289,7 +1189,7 @@ There are situations when a query you want to execute returns a
|
||||
very large result-set that needs to be processed. All the
|
||||
previously described hydration modes completely load a result-set
|
||||
into memory which might not be feasible with large result sets. See
|
||||
the `Batch Processing <batch-processing.html>`_ section on details how
|
||||
the `Batch Processing <batch-processing>`_ section on details how
|
||||
to iterate large result sets.
|
||||
|
||||
Functions
|
||||
@@ -1377,8 +1277,7 @@ userland:
|
||||
that contain char or binary data. Doctrine has no way of implicitly
|
||||
reloading this data. Partially loaded objects have to be passed to
|
||||
``EntityManager::refresh()`` if they are to be reloaded fully from
|
||||
the database. This query hint is deprecated and will be removed
|
||||
in the future (`Details <https://github.com/doctrine/orm/issues/8471>`_)
|
||||
the database.
|
||||
- Query::HINT\_REFRESH - This query is used internally by
|
||||
``EntityManager::refresh()`` and can be used in userland as well.
|
||||
If you specify this hint and a query returns the data for an entity
|
||||
@@ -1452,7 +1351,7 @@ can mark a many-to-one or one-to-one association as fetched temporarily to batch
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery("SELECT u FROM MyProject\User u");
|
||||
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
|
||||
$query->setFetchMode("MyProject\User", "address", "EAGER");
|
||||
$query->execute();
|
||||
|
||||
Given that there are 10 users and corresponding addresses in the database the executed queries will look something like:
|
||||
@@ -1462,15 +1361,6 @@ Given that there are 10 users and corresponding addresses in the database the ex
|
||||
SELECT * FROM users;
|
||||
SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
|
||||
.. note::
|
||||
Changing the fetch mode during a query mostly makes sense for one-to-one and many-to-one relations. In that case,
|
||||
all the necessary IDs are available after the root entity (``user`` in the above example) has been loaded. So, one
|
||||
query per association can be executed to fetch all the referred-to entities (``address``).
|
||||
|
||||
For one-to-many relations, changing the fetch mode to eager will cause to execute one query **for every root entity
|
||||
loaded**. This gives no improvement over the ``lazy`` fetch mode which will also initialize the associations on
|
||||
a one-by-one basis once they are accessed.
|
||||
|
||||
|
||||
EBNF
|
||||
----
|
||||
@@ -1491,16 +1381,14 @@ Document syntax:
|
||||
e.g. zero or one time
|
||||
- curly brackets {...} are used for repetition, e.g. zero or more
|
||||
times
|
||||
- double quotation marks "..." define a terminal string
|
||||
- a vertical bar \| represents an alternative
|
||||
- double quotation marks "..." define a terminal string a vertical
|
||||
bar \| represents an alternative
|
||||
|
||||
Terminals
|
||||
~~~~~~~~~
|
||||
|
||||
|
||||
- identifier (name, email, ...) must match ``[a-z_][a-z0-9_]*``
|
||||
- fully_qualified_name (Doctrine\Tests\Models\CMS\CmsUser) matches PHP's fully qualified class names
|
||||
- aliased_name (CMS:CmsUser) uses two identifiers, one for the namespace alias and one for the class inside it
|
||||
- identifier (name, email, ...)
|
||||
- string ('foo', 'bar''s house', '%ninja%', ...)
|
||||
- char ('/', '\\', ' ', ...)
|
||||
- integer (-1, 0, 1, 34, ...)
|
||||
@@ -1534,14 +1422,8 @@ Identifiers
|
||||
/* Alias Identification declaration (the "u" of "FROM User u") */
|
||||
AliasIdentificationVariable :: = identifier
|
||||
|
||||
/* identifier that must be a class name (the "User" of "FROM User u"), possibly as a fully qualified class name or namespace-aliased */
|
||||
AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier
|
||||
|
||||
/* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */
|
||||
AliasResultVariable = identifier
|
||||
|
||||
/* ResultVariable identifier usage of mapped field aliases (the "total" of "COUNT(*) AS total") */
|
||||
ResultVariable = identifier
|
||||
/* identifier that must be a class name (the "User" of "FROM User u") */
|
||||
AbstractSchemaName ::= identifier
|
||||
|
||||
/* identifier that must be a field (the "name" of "u.name") */
|
||||
/* This is responsible to know if the field exists in Object, no matter if it's a relation or a simple field */
|
||||
@@ -1553,13 +1435,19 @@ Identifiers
|
||||
/* identifier that must be a single-valued association field (to-one) (the "Group" of "u.Group") */
|
||||
SingleValuedAssociationField ::= FieldIdentificationVariable
|
||||
|
||||
/* identifier that must be an embedded class state field */
|
||||
/* identifier that must be an embedded class state field (for the future) */
|
||||
EmbeddedClassStateField ::= FieldIdentificationVariable
|
||||
|
||||
/* identifier that must be a simple state field (name, email, ...) (the "name" of "u.name") */
|
||||
/* The difference between this and FieldIdentificationVariable is only semantical, because it points to a single field (not mapping to a relation) */
|
||||
SimpleStateField ::= FieldIdentificationVariable
|
||||
|
||||
/* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */
|
||||
AliasResultVariable = identifier
|
||||
|
||||
/* ResultVariable identifier usage of mapped field aliases (the "total" of "COUNT(*) AS total") */
|
||||
ResultVariable = identifier
|
||||
|
||||
Path Expressions
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1609,7 +1497,7 @@ Items
|
||||
.. code-block:: php
|
||||
|
||||
UpdateItem ::= SingleValuedPathExpression "=" NewValue
|
||||
OrderByItem ::= (SimpleArithmeticExpression | SingleValuedPathExpression | ScalarExpression | ResultVariable | FunctionDeclaration) ["ASC" | "DESC"]
|
||||
OrderByItem ::= (SimpleArithmeticExpression | SingleValuedPathExpression | ScalarExpression | ResultVariable) ["ASC" | "DESC"]
|
||||
GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression
|
||||
NewValue ::= SimpleArithmeticExpression | "NULL"
|
||||
|
||||
@@ -1618,24 +1506,22 @@ From, Join and Index by
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
|
||||
SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration
|
||||
IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
|
||||
SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable)
|
||||
JoinVariableDeclaration ::= Join [IndexBy]
|
||||
RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
|
||||
JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
|
||||
Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration | RangeVariableDeclaration) ["WITH" ConditionalExpression]
|
||||
IndexBy ::= "INDEX" "BY" SingleValuedPathExpression
|
||||
Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression ["AS"] AliasIdentificationVariable ["WITH" ConditionalExpression]
|
||||
IndexBy ::= "INDEX" "BY" StateFieldPathExpression
|
||||
|
||||
Select Expressions
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
|
||||
SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
|
||||
SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]
|
||||
PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
|
||||
PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")"
|
||||
NewObjectArg ::= ScalarExpression | "(" Subselect ")"
|
||||
|
||||
Conditional Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1697,7 +1583,7 @@ Scalar and Type Expressions
|
||||
.. code-block:: php
|
||||
|
||||
ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression
|
||||
StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")"
|
||||
StringExpression ::= StringPrimary | "(" Subselect ")"
|
||||
StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
|
||||
BooleanExpression ::= BooleanPrimary | "(" Subselect ")"
|
||||
BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter
|
||||
@@ -1715,7 +1601,8 @@ Aggregate Expressions
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")"
|
||||
AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" |
|
||||
"COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")"
|
||||
|
||||
Case Expressions
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -1745,7 +1632,7 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS
|
||||
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
|
||||
InstanceOfParameter ::= AbstractSchemaName | InputParameter
|
||||
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
|
||||
NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL"
|
||||
NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
|
||||
ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
|
||||
ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
|
||||
|
||||
@@ -1780,6 +1667,6 @@ Functions
|
||||
"TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
|
||||
"LOWER" "(" StringPrimary ")" |
|
||||
"UPPER" "(" StringPrimary ")" |
|
||||
"IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")"
|
||||
"IDENTITY" "(" SingleValuedAssociationPathExpression ")"
|
||||
|
||||
|
||||
|
||||
+95
-117
@@ -1,7 +1,7 @@
|
||||
Events
|
||||
======
|
||||
|
||||
Doctrine ORM features a lightweight event system that is part of the
|
||||
Doctrine 2 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.
|
||||
@@ -20,12 +20,12 @@ manager.
|
||||
$evm = new EventManager();
|
||||
|
||||
Now we can add some event listeners to the ``$evm``. Let's create a
|
||||
``TestEvent`` class to play around with.
|
||||
``EventTest`` class to play around with.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class TestEvent
|
||||
class EventTest
|
||||
{
|
||||
const preFoo = 'preFoo';
|
||||
const postFoo = 'postFoo';
|
||||
@@ -52,15 +52,15 @@ Now we can add some event listeners to the ``$evm``. Let's create a
|
||||
}
|
||||
|
||||
// Create a new instance
|
||||
$test = new TestEvent($evm);
|
||||
$test = new EventTest($evm);
|
||||
|
||||
Events can be dispatched by using the ``dispatchEvent()`` method.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$evm->dispatchEvent(TestEvent::preFoo);
|
||||
$evm->dispatchEvent(TestEvent::postFoo);
|
||||
$evm->dispatchEvent(EventTest::preFoo);
|
||||
$evm->dispatchEvent(EventTest::postFoo);
|
||||
|
||||
You can easily remove a listener with the ``removeEventListener()``
|
||||
method.
|
||||
@@ -70,7 +70,7 @@ method.
|
||||
<?php
|
||||
$evm->removeEventListener(array(self::preFoo, self::postFoo), $this);
|
||||
|
||||
The Doctrine ORM event system also has a simple concept of event
|
||||
The Doctrine 2 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 ORM EventManager are best named
|
||||
Events being used with the Doctrine 2 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:
|
||||
@@ -133,80 +133,65 @@ several reasons:
|
||||
- It is easy to read.
|
||||
- Simplicity.
|
||||
- Each method within an EventSubscriber is named after the
|
||||
corresponding constant's value. If the constant's name and value differ
|
||||
it contradicts the intention of using the constant and makes your code
|
||||
harder to maintain.
|
||||
corresponding constant. If constant name and constant value differ,
|
||||
you MUST use the new value and thus, your code might be subject to
|
||||
codechanges when the value changes. This contradicts the intention
|
||||
of a constant.
|
||||
|
||||
An example for a correct notation can be found in the example
|
||||
``TestEvent`` above.
|
||||
``EventTest`` above.
|
||||
|
||||
.. _reference-events-lifecycle-events:
|
||||
|
||||
Lifecycle Events
|
||||
----------------
|
||||
|
||||
The ``EntityManager`` and ``UnitOfWork`` classes trigger a bunch of
|
||||
events during the life-time of their registered entities.
|
||||
The EntityManager and UnitOfWork 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
|
||||
*initial* persist of an entity
|
||||
- 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.
|
||||
- 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
|
||||
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
|
||||
(annotations/xml/yaml).
|
||||
- preFlush - The preFlush event occurs at the very beginning of a flush
|
||||
operation. This event is not a lifecycle callback.
|
||||
- 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.
|
||||
|
||||
.. warning::
|
||||
|
||||
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#toIterable()`` and ``postLoad`` event
|
||||
handlers.
|
||||
Note that the postLoad event occurs for an entity
|
||||
before any associations have been initialized. Therefore it is not
|
||||
safe to access associations in a postLoad callback or event
|
||||
handler.
|
||||
|
||||
.. warning::
|
||||
|
||||
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.
|
||||
|
||||
You can access the Event constants from the ``Events`` class in the
|
||||
ORM package.
|
||||
@@ -220,19 +205,20 @@ ORM package.
|
||||
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. They receive some kind
|
||||
called when the event is triggered. As of v2.4 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`` instance and other relevant data.
|
||||
The EventArgs instance received by the listener gives access to the entity,
|
||||
EntityManager 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.
|
||||
@@ -247,11 +233,6 @@ a relevant lifecycle event. More than one callback can be defined for each
|
||||
lifecycle event. Lifecycle Callbacks are best used for simple operations
|
||||
specific to a particular entity class's lifecycle.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Note that Licecycle Callbacks are not supported for Embeddables.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -316,7 +297,7 @@ can do it with the following.
|
||||
name:
|
||||
type: string(50)
|
||||
lifecycleCallbacks:
|
||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
|
||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
|
||||
postPersist: [ doStuffOnPostPersist ]
|
||||
|
||||
In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
|
||||
@@ -332,7 +313,7 @@ XML would look something like this:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
/Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="User">
|
||||
|
||||
@@ -368,22 +349,22 @@ defined on your ``User`` model.
|
||||
// ...
|
||||
}
|
||||
|
||||
public function doOtherStuffOnPrePersist()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
public function doStuffOnPostPersist()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
The ``key`` of the lifecycleCallbacks is the name of the method and
|
||||
the value is the event type. The allowed event types are the ones
|
||||
listed in the previous Lifecycle Events section.
|
||||
|
||||
Lifecycle Callbacks Event Argument
|
||||
-----------------------------------
|
||||
|
||||
The triggered event is also given to the lifecycle-callback.
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Since 2.4 the triggered event is given to the lifecycle-callback.
|
||||
|
||||
With the additional argument you have access to the
|
||||
``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
|
||||
@@ -412,9 +393,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`` classes. 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. Please read the
|
||||
*Implementing Event 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
|
||||
@@ -425,7 +406,7 @@ A lifecycle event listener looks like the following:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
class MyEventListener
|
||||
{
|
||||
@@ -441,14 +422,14 @@ A lifecycle event listener looks like the following:
|
||||
}
|
||||
}
|
||||
|
||||
A lifecycle event subscriber may look like this:
|
||||
A lifecycle event subscriber may looks like this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\EventSubscriber;
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
class MyEventSubscriber implements EventSubscriber
|
||||
{
|
||||
@@ -503,16 +484,16 @@ Implementing Event Listeners
|
||||
----------------------------
|
||||
|
||||
This section explains what is and what is not allowed during
|
||||
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
|
||||
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
|
||||
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
|
||||
~~~~~~~~~~
|
||||
@@ -555,9 +536,8 @@ preFlush
|
||||
~~~~~~~~
|
||||
|
||||
``preFlush`` is called at ``EntityManager#flush()`` before
|
||||
anything else. ``EntityManager#flush()`` should not be called inside
|
||||
its listeners, since `preFlush` event is dispatched in it, which would
|
||||
result in infinite loop.
|
||||
anything else. ``EntityManager#flush()`` can be called safely
|
||||
inside its listeners.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -588,8 +568,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
|
||||
@@ -602,23 +582,23 @@ mentioned sets. See this example:
|
||||
$em = $eventArgs->getEntityManager();
|
||||
$uow = $em->getUnitOfWork();
|
||||
|
||||
foreach ($uow->getScheduledEntityInsertions() as $entity) {
|
||||
foreach ($uow->getScheduledEntityInsertions() AS $entity) {
|
||||
|
||||
}
|
||||
|
||||
foreach ($uow->getScheduledEntityUpdates() as $entity) {
|
||||
foreach ($uow->getScheduledEntityUpdates() AS $entity) {
|
||||
|
||||
}
|
||||
|
||||
foreach ($uow->getScheduledEntityDeletions() as $entity) {
|
||||
foreach ($uow->getScheduledEntityDeletions() AS $entity) {
|
||||
|
||||
}
|
||||
|
||||
foreach ($uow->getScheduledCollectionDeletions() as $col) {
|
||||
foreach ($uow->getScheduledCollectionDeletions() AS $col) {
|
||||
|
||||
}
|
||||
|
||||
foreach ($uow->getScheduledCollectionUpdates() as $col) {
|
||||
foreach ($uow->getScheduledCollectionUpdates() AS $col) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -627,13 +607,13 @@ mentioned sets. See this example:
|
||||
The following restrictions apply to the onFlush event:
|
||||
|
||||
|
||||
- If you create and persist a new entity in ``onFlush``, then
|
||||
- If you create and persist a new entity in "onFlush", then
|
||||
calling ``EntityManager#persist()`` is not enough.
|
||||
You have to execute an additional call to
|
||||
``$unitOfWork->computeChangeSet($classMetadata, $entity)``.
|
||||
- Changing primitive fields or associations requires you to
|
||||
explicitly trigger a re-computation of the changeset of the
|
||||
affected entity. This can be done by calling
|
||||
affected entity. This can be done by either calling
|
||||
``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``.
|
||||
|
||||
postFlush
|
||||
@@ -661,8 +641,7 @@ preUpdate
|
||||
|
||||
PreUpdate is the most restrictive to use event, since it is called
|
||||
right before an update statement is called for an entity inside the
|
||||
``EntityManager#flush()`` method. Note that this event is not
|
||||
triggered when the computed changeset is empty.
|
||||
``EntityManager#flush()`` method.
|
||||
|
||||
Changes to associations of the updated entity are never allowed in
|
||||
this event, since Doctrine cannot guarantee to correctly handle
|
||||
@@ -737,7 +716,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.
|
||||
|
||||
@@ -748,7 +727,7 @@ The three post events are called inside ``EntityManager#flush()``.
|
||||
Changes in here are not relevant to the persistence in the
|
||||
database, but you can use these events to alter non-persistable items,
|
||||
like non-mapped fields, logging or even associated classes that are
|
||||
not directly mapped by Doctrine.
|
||||
directly mapped by Doctrine.
|
||||
|
||||
postLoad
|
||||
~~~~~~~~
|
||||
@@ -759,9 +738,11 @@ EntityManager.
|
||||
Entity listeners
|
||||
----------------
|
||||
|
||||
An entity listener is a lifecycle listener class used for an entity.
|
||||
.. versionadded:: 2.4
|
||||
|
||||
- The entity listener's mapping may be applied to an entity class or mapped superclass.
|
||||
An entity listeners is a lifecycle listener classes used for an entity.
|
||||
|
||||
- The entity listeners mapping may be applied to an entity class or mapped superclass.
|
||||
- An entity listener is defined by mapping the entity class with the corresponding mapping.
|
||||
|
||||
.. configuration-block::
|
||||
@@ -819,8 +800,8 @@ An ``Entity Listener`` could be any class, by default it should be a class with
|
||||
}
|
||||
}
|
||||
|
||||
To define a specific event listener method (one that does not follow the naming convention)
|
||||
you need to map the listener method using the event type mapping:
|
||||
To define a specific event listener method
|
||||
you should map the listener method using the event type mapping.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@@ -894,16 +875,13 @@ you need to map the listener method using the event type mapping:
|
||||
preRemove: [preRemoveHandler]
|
||||
# ....
|
||||
|
||||
.. note::
|
||||
|
||||
The order of execution of multiple methods for the same event (e.g. multiple @PrePersist) is not guaranteed.
|
||||
|
||||
|
||||
Entity listeners resolver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Doctrine invokes the listener resolver to get the listener instance.
|
||||
Doctrine invoke the listener resolver to get the listener instance.
|
||||
|
||||
- A resolver allows you register a specific entity listener instance.
|
||||
- An resolver allows you register a specific ``Entity Listener`` instance.
|
||||
- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver``
|
||||
|
||||
Specifying an entity listener instance :
|
||||
@@ -958,9 +936,8 @@ Implementing your own resolver :
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the listener resolver only before instantiating the EntityManager
|
||||
$configurations->setEntityListenerResolver(new MyEntityListenerResolver);
|
||||
EntityManager::create(.., $configurations, ..);
|
||||
// configure the listener resolver.
|
||||
$em->getConfiguration()->setEntityListenerResolver($container->get('my_resolver'));
|
||||
|
||||
Load ClassMetadata Event
|
||||
------------------------
|
||||
@@ -972,12 +949,12 @@ process and manipulate the instance.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$test = new TestEvent();
|
||||
$test = new EventTest();
|
||||
$metadataFactory = $em->getMetadataFactory();
|
||||
$evm = $em->getEventManager();
|
||||
$evm->addEventListener(Events::loadClassMetadata, $test);
|
||||
|
||||
class TestEvent
|
||||
class EventTest
|
||||
{
|
||||
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
|
||||
{
|
||||
@@ -991,3 +968,4 @@ process and manipulate the instance.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+11
-20
@@ -21,6 +21,12 @@ created database tables and columns.
|
||||
Entity Classes
|
||||
--------------
|
||||
|
||||
I access a variable and its null, what is wrong?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If this variable is a public variable then you are violating one of the criteria for entities.
|
||||
All properties have to be protected or private for the proxy object pattern to work.
|
||||
|
||||
How can I add default values to a column?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -80,7 +86,7 @@ You can solve this exception by:
|
||||
How can I filter an association?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You should use DQL queries to query for the filtered set of entities.
|
||||
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.
|
||||
|
||||
I call clear() on a One-To-Many collection but the entities are not deleted
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -98,7 +104,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 ORM.
|
||||
foreign keys as primary keys feature of Doctrine introduced in version 2.1.
|
||||
|
||||
See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`.
|
||||
|
||||
@@ -128,10 +134,10 @@ See the previous question for a solution to this task.
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
Can I use Inheritance with Doctrine ORM?
|
||||
Can I use Inheritance with Doctrine 2?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Yes, you can use Single- or Joined-Table Inheritance in ORM.
|
||||
|
||||
Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2.
|
||||
|
||||
See the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>` for
|
||||
the details.
|
||||
@@ -198,21 +204,6 @@ No, it is not supported to sort by function in DQL. If you need this functionali
|
||||
use a native-query or come up with another solution. As a side note: Sorting with ORDER BY RAND() is painfully slow
|
||||
starting with 1000 rows.
|
||||
|
||||
Is it better to write DQL or to generate it with the query builder?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The purpose of the ``QueryBuilder`` is to generate DQL dynamically,
|
||||
which is useful when you have optional filters, conditional joins, etc.
|
||||
|
||||
But the ``QueryBuilder`` is not an alternative to DQL, it actually generates DQL
|
||||
queries at runtime, which are then interpreted by Doctrine. This means that
|
||||
using the ``QueryBuilder`` to build and run a query is actually always slower
|
||||
than only running the corresponding DQL query.
|
||||
|
||||
So if you only need to generate a query and bind parameters to it,
|
||||
you should use plain DQL, as this is a simpler and much more readable solution.
|
||||
You should only use the ``QueryBuilder`` when you can't achieve what you want to do with a DQL query.
|
||||
|
||||
A Query fails, how can I debug it?
|
||||
----------------------------------
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
Filters
|
||||
=======
|
||||
|
||||
Doctrine ORM features a filter system that allows the developer to add SQL to
|
||||
.. versionadded:: 2.2
|
||||
|
||||
Doctrine 2.2 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).
|
||||
|
||||
@@ -37,7 +39,7 @@ proper quoting of parameters.
|
||||
|
||||
<?php
|
||||
namespace Example;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata,
|
||||
use Doctrine\ORM\Mapping\ClassMetaData,
|
||||
Doctrine\ORM\Query\Filter\SQLFilter;
|
||||
|
||||
class MyLocaleFilter extends SQLFilter
|
||||
@@ -53,9 +55,6 @@ proper quoting of parameters.
|
||||
}
|
||||
}
|
||||
|
||||
If the parameter is an array and should be quoted as a list of values for an IN query
|
||||
this is possible with the alternative ``SQLFilter#setParameterList()`` and
|
||||
``SQLFilter#getParameterList()`` functions.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
@@ -4,7 +4,7 @@ Improving Performance
|
||||
Bytecode Cache
|
||||
--------------
|
||||
|
||||
It is highly recommended to make use of a bytecode cache like OPcache.
|
||||
It is highly recommended to make use of a bytecode cache like APC.
|
||||
A bytecode cache removes the need for parsing PHP code on every
|
||||
request and can greatly improve performance.
|
||||
|
||||
@@ -20,19 +20,12 @@ 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.
|
||||
|
||||
Operating Doctrine without these caches means
|
||||
Metadata and Query cache (preferably with APC or Memcache as the
|
||||
cache driver). 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
|
||||
--------------------------------
|
||||
|
||||
@@ -43,35 +36,11 @@ in scenarios where data is loaded for read-only purposes.
|
||||
Read-Only Entities
|
||||
------------------
|
||||
|
||||
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.
|
||||
During flush on the EntityManager these entities are skipped even if properties
|
||||
changed.
|
||||
|
||||
Read-Only allows to persist new entities of a kind and remove existing ones,
|
||||
they are just not considered for updates.
|
||||
|
||||
See :ref:`annref_entity`
|
||||
|
||||
You can also explicitly mark individual entities read only directly on the
|
||||
UnitOfWork via a call to ``markReadOnly()``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$user = $entityManager->find(User::class, $id);
|
||||
$entityManager->getUnitOfWork()->markReadOnly($user);
|
||||
|
||||
Or you can set all objects that are the result of a query hydration to be
|
||||
marked as read only with the following query hint:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$query = $entityManager->createQuery('SELECT u FROM App\\Entity\\User u');
|
||||
$query->setHint(Query::HINT_READ_ONLY, true);
|
||||
|
||||
$users = $query->getResult();
|
||||
Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping
|
||||
references for details). This means that the entity marked as read only is never considered
|
||||
for updates, which means when you call flush on the EntityManager these entities are skipped
|
||||
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing
|
||||
ones, they are just not considered for updates.
|
||||
|
||||
Extra-Lazy Collections
|
||||
----------------------
|
||||
@@ -83,7 +52,7 @@ for more information on how this fetch mode works.
|
||||
Temporarily change fetch mode in DQL
|
||||
------------------------------------
|
||||
|
||||
See :ref:`dql-temporarily-change-fetch-mode`
|
||||
See :ref:`Doctrine Query Language chapter <dql-temporarily-change-fetch-mode>`
|
||||
|
||||
|
||||
Apply Best Practices
|
||||
@@ -92,9 +61,4 @@ Apply Best Practices
|
||||
A lot of the points mentioned in the Best Practices chapter will
|
||||
also positively affect the performance of Doctrine.
|
||||
|
||||
See :doc:`Best Practices <reference/best-practices>`
|
||||
|
||||
Change Tracking policies
|
||||
------------------------
|
||||
|
||||
See: :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
||||
|
||||
@@ -81,67 +81,45 @@ discriminator column is used.
|
||||
|
||||
Example:
|
||||
|
||||
.. configuration-block::
|
||||
.. code-block:: php
|
||||
|
||||
.. code-block:: php
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
|
||||
*/
|
||||
class Person
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Employee extends Person
|
||||
{
|
||||
// ...
|
||||
}
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
|
||||
*/
|
||||
class Person
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Employee extends Person
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Model\Person:
|
||||
type: entity
|
||||
inheritanceType: SINGLE_TABLE
|
||||
discriminatorColumn:
|
||||
name: discr
|
||||
type: string
|
||||
discriminatorMap:
|
||||
person: Person
|
||||
employee: Employee
|
||||
|
||||
MyProject\Model\Employee:
|
||||
type: entity
|
||||
|
||||
Things to note:
|
||||
|
||||
|
||||
- The @InheritanceType and @DiscriminatorColumn must be specified
|
||||
on the topmost class that is part of the mapped entity hierarchy.
|
||||
- The @InheritanceType, @DiscriminatorColumn and @DiscriminatorMap
|
||||
must be specified on the topmost class that is part of the mapped
|
||||
entity hierarchy.
|
||||
- The @DiscriminatorMap specifies which values of the
|
||||
discriminator column identify a row as being of a certain type. In
|
||||
the case above a value of "person" identifies a row as being of
|
||||
type ``Person`` and "employee" identifies a row as being of type
|
||||
``Employee``.
|
||||
- All entity classes that is part of the mapped entity hierarchy
|
||||
(including the topmost class) should be specified in the
|
||||
@DiscriminatorMap. In the case above Person class included.
|
||||
- The names of the classes in the discriminator map do not need to
|
||||
be fully qualified if the classes are contained in the same
|
||||
namespace as the entity class on which the discriminator map is
|
||||
applied.
|
||||
- If no discriminator map is provided, then the map is generated
|
||||
automatically. The automatically generated discriminator map
|
||||
contains the lowercase short name of each class as key.
|
||||
|
||||
Design-time considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -159,7 +137,7 @@ This strategy is very efficient for querying across all types in
|
||||
the hierarchy or for specific types. No table joins are required,
|
||||
only a WHERE clause listing the type identifiers. In particular,
|
||||
relationships involving types that employ this mapping strategy are
|
||||
very performing.
|
||||
very performant.
|
||||
|
||||
There is a general performance consideration with Single Table
|
||||
Inheritance: If the target-entity of a many-to-one or one-to-one
|
||||
@@ -174,7 +152,7 @@ SQL Schema considerations
|
||||
For Single-Table-Inheritance to work in scenarios where you are
|
||||
using either a legacy database schema or a self-written database
|
||||
schema you have to make sure that all columns that are not in the
|
||||
root entity but in any of the different sub-entities has to allow
|
||||
root entity but in any of the different sub-entities has to allows
|
||||
null values. Columns that have NOT NULL constraints have to be on
|
||||
the root entity of the single-table inheritance hierarchy.
|
||||
|
||||
@@ -185,7 +163,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 ORM
|
||||
of a parent class through a foreign key constraint. Doctrine 2
|
||||
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.
|
||||
@@ -229,9 +207,6 @@ Things to note:
|
||||
be fully qualified if the classes are contained in the same
|
||||
namespace as the entity class on which the discriminator map is
|
||||
applied.
|
||||
- If no discriminator map is provided, then the map is generated
|
||||
automatically. The automatically generated discriminator map
|
||||
contains the lowercase short name of each class as key.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -274,9 +249,6 @@ be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
|
||||
Otherwise Doctrine *CANNOT* create proxy instances
|
||||
of this entity and will *ALWAYS* load the entity eagerly.
|
||||
|
||||
There is also another important performance consideration that it is *NOT POSSIBLE*
|
||||
to query for the base entity without any LEFT JOINs to the sub-types.
|
||||
|
||||
SQL Schema considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -288,19 +260,12 @@ or auto-increment details). Furthermore each child table has to
|
||||
have a foreign key pointing from the id column to the root table id
|
||||
column and cascading on delete.
|
||||
|
||||
.. _inheritence_mapping_overrides:
|
||||
|
||||
Overrides
|
||||
---------
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
Association Override
|
||||
@@ -463,8 +428,6 @@ Things to note:
|
||||
- This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany)
|
||||
- The association type *CANNOT* be changed.
|
||||
- The override could redefine the joinTables or joinColumns depending on the association type.
|
||||
- The override could redefine inversedBy to reference more than one extended entity.
|
||||
- The override could redefine fetch to modify the fetch strategy of the extended entity.
|
||||
|
||||
Attribute Override
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -502,7 +465,7 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
* column=@Column(
|
||||
* name = "guest_id",
|
||||
* type = "integer",
|
||||
* length = 140
|
||||
length = 140
|
||||
* )
|
||||
* ),
|
||||
* @AttributeOverride(name="name",
|
||||
@@ -510,7 +473,7 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
* name = "guest_name",
|
||||
* nullable = false,
|
||||
* unique = true,
|
||||
* length = 240
|
||||
length = 240
|
||||
* )
|
||||
* )
|
||||
* })
|
||||
@@ -592,24 +555,5 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
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 attributes except the type.
|
||||
|
||||
Query the Type
|
||||
--------------
|
||||
|
||||
It may happen that the entities of a special type should be queried. Because there
|
||||
is no direct access to the discriminator column, Doctrine provides the
|
||||
``INSTANCE OF`` construct.
|
||||
|
||||
The following example shows how to use ``INSTANCE OF``. There is a three level hierarchy
|
||||
with a base entity ``NaturalPerson`` which is extended by ``Staff`` which in turn
|
||||
is extended by ``Technician``.
|
||||
|
||||
Querying for the staffs without getting any technicians can be achieved by this DQL:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery("SELECT staff FROM MyProject\Model\Staff staff WHERE staff NOT INSTANCE OF MyProject\Model\Technician");
|
||||
$staffs = $query->getResult();
|
||||
- The column type *CANNOT* be changed. if the column type is not equals you got a ``MappingException``
|
||||
- The override can redefine all the column except the type.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
The installation chapter has moved to :doc:`Installation and Configuration <reference/configuration>`_.
|
||||
The installation chapter has moved to `Installation and Configuration
|
||||
<reference/configuration>`_.
|
||||
|
||||
@@ -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 ORM as well as critical known issues that you should know
|
||||
Doctrine 2 as well as critical known issues that you should know
|
||||
about.
|
||||
|
||||
Current Limitations
|
||||
@@ -63,7 +63,19 @@ Where the ``attribute_name`` column contains the key and
|
||||
``$attributes``.
|
||||
|
||||
The feature request for persistence of primitive value arrays
|
||||
`is described in the DDC-298 ticket <https://github.com/doctrine/orm/issues/3743>`_.
|
||||
`is described in the DDC-298 ticket <http://www.doctrine-project.org/jira/browse/DDC-298>`_.
|
||||
|
||||
Value Objects
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
There is currently no native support value objects in Doctrine
|
||||
other than for ``DateTime`` instances or if you serialize the
|
||||
objects using ``serialize()/deserialize()`` which the DBAL Type
|
||||
"object" supports.
|
||||
|
||||
The feature request for full value-object support
|
||||
`is described in the DDC-93 ticket <http://www.doctrine-project.org/jira/browse/DDC-93>`_.
|
||||
|
||||
|
||||
Cascade Merge with Bi-directional Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -71,8 +83,8 @@ Cascade Merge with Bi-directional Associations
|
||||
There are two bugs now that concern the use of cascade merge in combination with bi-directional associations.
|
||||
Make sure to study the behavior of cascade merge if you are using it:
|
||||
|
||||
- `DDC-875 <https://github.com/doctrine/orm/issues/5398>`_ Merge can sometimes add the same entity twice into a collection
|
||||
- `DDC-763 <https://github.com/doctrine/orm/issues/5277>`_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability"
|
||||
- `DDC-875 <http://www.doctrine-project.org/jira/browse/DDC-875>`_ Merge can sometimes add the same entity twice into a collection
|
||||
- `DDC-763 <http://www.doctrine-project.org/jira/browse/DDC-763>`_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability"
|
||||
|
||||
Custom Persisters
|
||||
~~~~~~~~~~~~~~~~~
|
||||
@@ -83,8 +95,10 @@ Currently there is no way to overwrite the persister implementation
|
||||
for a given entity, however there are several use-cases that can
|
||||
benefit from custom persister implementations:
|
||||
|
||||
- `Add Upsert Support <https://github.com/doctrine/orm/issues/5178>`_
|
||||
- `Evaluate possible ways in which stored-procedures can be used <https://github.com/doctrine/orm/issues/4946>`_
|
||||
|
||||
- `Add Upsert Support <http://www.doctrine-project.org/jira/browse/DDC-668>`_
|
||||
- `Evaluate possible ways in which stored-procedures can be used <http://www.doctrine-project.org/jira/browse/DDC-445>`_
|
||||
- The previous Filter Rules Feature Request
|
||||
|
||||
Persist Keys of Collections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -94,7 +108,7 @@ PHP Arrays are ordered hash-maps and so should be the
|
||||
evaluate a feature that optionally persists and hydrates the keys
|
||||
of a Collection instance.
|
||||
|
||||
`Ticket DDC-213 <https://github.com/doctrine/orm/issues/2817>`_
|
||||
`Ticket DDC-213 <http://www.doctrine-project.org/jira/browse/DDC-213>`_
|
||||
|
||||
Mapping many tables to one entity
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -107,17 +121,18 @@ to the same entity.
|
||||
Behaviors
|
||||
~~~~~~~~~
|
||||
|
||||
Doctrine ORM will **never** include a behavior system like Doctrine 1
|
||||
Doctrine 2 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:
|
||||
|
||||
- `Doctrine2 "Behaviors" in a Nutshell <http://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_
|
||||
- `A re-usable Versionable behavior for Doctrine2 <http://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_
|
||||
- `Write your own ORM on top of Doctrine2 <http://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_
|
||||
- `Doctrine ORM Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
|
||||
- `Doctrine2 "Behaviors" in a Nutshell <http://www.doctrine-project.org/blog/doctrine2-behaviours-nutshell>`_
|
||||
- `A re-usable Versionable behavior for Doctrine2 <http://www.doctrine-project.org/blog/doctrine2-versionable>`_
|
||||
- `Write your own ORM on top of Doctrine2 <http://www.doctrine-project.org/blog/your-own-orm-doctrine2>`_
|
||||
- `Doctrine 2 Behavioral Extensions <http://www.doctrine-project.org/blog/doctrine2-behavioral-extensions>`_
|
||||
- `Doctrator <https://github.com/pablodip/doctrator`>_
|
||||
|
||||
Doctrine ORM has enough hooks and extension points so that **you** can
|
||||
Doctrine 2 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 +141,9 @@ Nested Set
|
||||
~~~~~~~~~~
|
||||
|
||||
NestedSet was offered as a behavior in Doctrine 1 and will not be
|
||||
included in the core of Doctrine ORM. However there are already two
|
||||
included in the core of Doctrine 2. However there are already two
|
||||
extensions out there that offer support for Nested Set with
|
||||
ORM:
|
||||
Doctrine 2:
|
||||
|
||||
|
||||
- `Doctrine2 Hierarchical-Structural Behavior <http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
|
||||
@@ -143,15 +158,17 @@ backwards compatibility issues or where no simple fix exists (yet).
|
||||
We don't plan to add every bug in the tracker there, just those
|
||||
issues that can potentially cause nightmares or pain of any sort.
|
||||
|
||||
See bugs, improvement and feature requests on `Github issues <https://github.com/doctrine/orm/issues>`_.
|
||||
See the Open Bugs on Jira for more details on `bugs, improvement and feature
|
||||
requests
|
||||
<http://www.doctrine-project.org/jira/secure/IssueNavigator.jspa?reset=true&mode=hide&pid=10032&resolution=-1&sorter/field=updated&sorter/order=DESC>`_.
|
||||
|
||||
Identifier Quoting and Legacy Databases
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For compatibility reasons between all the supported vendors and
|
||||
edge case problems Doctrine ORM does **NOT** do automatic identifier
|
||||
edge case problems Doctrine 2 does **NOT** do automatic identifier
|
||||
quoting. This can lead to problems when trying to get
|
||||
legacy-databases to work with Doctrine ORM.
|
||||
legacy-databases to work with Doctrine 2.
|
||||
|
||||
|
||||
- You can quote column-names as described in the
|
||||
@@ -170,34 +187,3 @@ Microsoft SQL Server and Doctrine "datetime"
|
||||
|
||||
Doctrine assumes that you use ``DateTime2`` data-types. If your legacy database contains DateTime
|
||||
datatypes then you have to add your own data-type (see Basic Mapping for an example).
|
||||
|
||||
MySQL with MyISAM tables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Doctrine cannot provide atomic operations when calling ``EntityManager#flush()`` if one
|
||||
of the tables involved uses the storage engine MyISAM. You must use InnoDB or
|
||||
other storage engines that support transactions if you need integrity.
|
||||
|
||||
Entities, Proxies and Reflection
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Using methods for Reflection on entities can be prone to error, when the entity
|
||||
is actually a proxy the following methods will not work correctly:
|
||||
|
||||
- ``new ReflectionClass``
|
||||
- ``new ReflectionObject``
|
||||
- ``get_class()``
|
||||
- ``get_parent_class()``
|
||||
|
||||
This is why ``Doctrine\Common\Util\ClassUtils`` class exists that has similar
|
||||
methods, which resolve the proxy problem beforehand.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
|
||||
$bookProxy = $entityManager->getReference('Acme\Book');
|
||||
|
||||
$reflection = ClassUtils::newReflectionClass($bookProxy);
|
||||
$class = ClassUtils::getClass($bookProxy)¸
|
||||
|
||||
@@ -14,7 +14,6 @@ metadata:
|
||||
|
||||
- **XML files** (XmlDriver)
|
||||
- **Class DocBlock Annotations** (AnnotationDriver)
|
||||
- **Attributes** (AttributeDriver)
|
||||
- **YAML files** (YamlDriver)
|
||||
- **PHP Code in files or static functions** (PhpDriver)
|
||||
|
||||
@@ -36,7 +35,7 @@ an entity.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConfiguration()->setMetadataCacheImpl(new ApcuCache());
|
||||
$em->getConfiguration()->setMetadataCacheImpl(new ApcCache());
|
||||
|
||||
|
||||
If you want to use one of the included core metadata drivers you
|
||||
@@ -148,7 +147,7 @@ ClassMetadata
|
||||
-------------
|
||||
|
||||
The last piece you need to know and understand about metadata in
|
||||
Doctrine ORM is the API of the ``ClassMetadata`` classes. You need to
|
||||
Doctrine 2 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,47 +1,50 @@
|
||||
Implementing a NamingStrategy
|
||||
==============================
|
||||
|
||||
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_``).
|
||||
.. versionadded:: 2.3
|
||||
|
||||
.. warning
|
||||
Using a naming strategy you can provide rules for automatically generating
|
||||
database identifiers, columns and tables names
|
||||
when the table/column name is not given.
|
||||
This feature helps reduce the verbosity of the mapping document,
|
||||
eliminating repetitive noise (eg: ``TABLE_``).
|
||||
|
||||
The naming strategy is always overridden by entity mapping such as the `Table` annotation.
|
||||
|
||||
Configuring a naming strategy
|
||||
-----------------------------
|
||||
The default strategy used by Doctrine is quite minimal.
|
||||
|
||||
By default the ``Doctrine\ORM\Mapping\DefaultNamingStrategy``
|
||||
uses the simple class name and the attribute names to generate tables and columns.
|
||||
uses the simple class name and the attributes names to generate tables and columns
|
||||
|
||||
You can specify a different strategy by calling ``Doctrine\ORM\Configuration#setNamingStrategy()``:
|
||||
You can specify a different strategy by calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$namingStrategy = new MyNamingStrategy();
|
||||
$configuration->setNamingStrategy($namingStrategy);
|
||||
$configuration()->setNamingStrategy($namingStrategy);
|
||||
|
||||
Underscore naming strategy
|
||||
---------------------------
|
||||
|
||||
``\Doctrine\ORM\Mapping\UnderscoreNamingStrategy`` is a built-in strategy.
|
||||
``\Doctrine\ORM\Mapping\UnderscoreNamingStrategy`` is a built-in strategy
|
||||
that might be a useful if you want to use a underlying convention.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$namingStrategy = new \Doctrine\ORM\Mapping\UnderscoreNamingStrategy(CASE_UPPER);
|
||||
$configuration->setNamingStrategy($namingStrategy);
|
||||
$configuration()->setNamingStrategy($namingStrategy);
|
||||
|
||||
Then SomeEntityName will generate the table SOME_ENTITY_NAME when CASE_UPPER
|
||||
or some_entity_name using CASE_LOWER is given.
|
||||
|
||||
For SomeEntityName the strategy will generate the table SOME_ENTITY_NAME with the
|
||||
``CASE_UPPER`` option, or some_entity_name with the ``CASE_LOWER`` option.
|
||||
|
||||
Naming strategy interface
|
||||
-------------------------
|
||||
The interface ``Doctrine\ORM\Mapping\NamingStrategy`` allows you to specify
|
||||
a naming strategy for database tables and columns.
|
||||
a "naming standard" for database tables and columns.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -75,7 +78,7 @@ a naming strategy for database tables and columns.
|
||||
* @param string $propertyName A property
|
||||
* @return string A join column name
|
||||
*/
|
||||
function joinColumnName($propertyName, $className = null);
|
||||
function joinColumnName($propertyName);
|
||||
|
||||
/**
|
||||
* Return a join table name
|
||||
@@ -98,11 +101,10 @@ a naming strategy for database tables and columns.
|
||||
|
||||
Implementing a naming strategy
|
||||
-------------------------------
|
||||
If you have database naming standards, like all table names should be prefixed
|
||||
by the application prefix, all column names should be lower case, you can easily
|
||||
achieve such standards by implementing a naming strategy.
|
||||
|
||||
You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrategy``.
|
||||
If you have database naming standards like all tables names should be prefixed
|
||||
by the application prefix, all column names should be upper case,
|
||||
you can easily achieve such standards by implementing a naming strategy.
|
||||
You need to implements NamingStrategy first. Following is an example
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
@@ -122,7 +124,7 @@ You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrateg
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
public function joinColumnName($propertyName, $className = null)
|
||||
public function joinColumnName($propertyName)
|
||||
{
|
||||
return $propertyName . '_' . $this->referenceColumnName();
|
||||
}
|
||||
@@ -137,3 +139,12 @@ You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrateg
|
||||
($referencedColumnName ?: $this->referenceColumnName()));
|
||||
}
|
||||
}
|
||||
|
||||
Configuring the namingstrategy is easy if.
|
||||
Just set your naming strategy calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` :.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$namingStrategy = new MyAppNamingStrategy();
|
||||
$configuration()->setNamingStrategy($namingStrategy);
|
||||
|
||||
@@ -63,7 +63,7 @@ This has several benefits:
|
||||
- The API is much simpler than the usual ``ResultSetMapping`` API.
|
||||
|
||||
One downside is that the builder API does not yet support entities
|
||||
with inheritance hierarchies.
|
||||
with inheritance hierachies.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -71,7 +71,7 @@ with inheritance hierarchies.
|
||||
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
|
||||
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
|
||||
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
|
||||
"FROM users u INNER JOIN address a ON u.address_id = a.id";
|
||||
|
||||
$rsm = new ResultSetMappingBuilder($entityManager);
|
||||
@@ -80,7 +80,9 @@ with inheritance hierarchies.
|
||||
|
||||
The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well.
|
||||
|
||||
The ``SELECT`` clause can be generated
|
||||
..versionadded:: 2.4
|
||||
|
||||
Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause
|
||||
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
|
||||
@@ -90,7 +92,7 @@ a mapping from DQL alias (key) to SQL alias (value)
|
||||
|
||||
<?php
|
||||
|
||||
$selectClause = $rsm->generateSelectClause(array(
|
||||
$selectClause = $builder->generateSelectClause(array(
|
||||
'u' => 't1',
|
||||
'g' => 't2'
|
||||
));
|
||||
@@ -265,7 +267,7 @@ detail:
|
||||
<?php
|
||||
/**
|
||||
* Adds a meta column (foreign key or discriminator column) to the result set.
|
||||
*
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $columnAlias
|
||||
* @param string $columnName
|
||||
@@ -320,10 +322,10 @@ entity.
|
||||
$rsm->addEntityResult('User', 'u');
|
||||
$rsm->addFieldResult('u', 'id', 'id');
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
The result would look like this:
|
||||
@@ -356,10 +358,10 @@ thus owns the foreign key.
|
||||
$rsm->addFieldResult('u', 'id', 'id');
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
$rsm->addMetaResult('u', 'address_id', 'address_id');
|
||||
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Foreign keys are used by Doctrine for lazy-loading purposes when
|
||||
@@ -385,12 +387,12 @@ associations that are lazy.
|
||||
$rsm->addFieldResult('a', 'address_id', 'id');
|
||||
$rsm->addFieldResult('a', 'street', 'street');
|
||||
$rsm->addFieldResult('a', 'city', 'city');
|
||||
|
||||
|
||||
$sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' .
|
||||
'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?';
|
||||
$query = $this->_em->createNativeQuery($sql, $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
In this case the nested entity ``Address`` is registered with the
|
||||
@@ -420,10 +422,10 @@ to map the hierarchy (both use a discriminator column).
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
$rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column
|
||||
$rsm->setDiscriminatorColumn('u', 'discr');
|
||||
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Note that in the case of Class Table Inheritance, an example as
|
||||
@@ -435,10 +437,6 @@ strategy but with native SQL it is your responsibility.
|
||||
Named Native Query
|
||||
------------------
|
||||
|
||||
.. note::
|
||||
|
||||
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
|
||||
|
||||
You can also map a native query using a named native query mapping.
|
||||
|
||||
To achieve that, you must describe the SQL resultset structure
|
||||
@@ -793,7 +791,7 @@ followed by a dot ("."), followed by the name or the field or property of the pr
|
||||
6:
|
||||
name: address.country
|
||||
column: a_country
|
||||
|
||||
|
||||
|
||||
|
||||
If you retrieve a single entity and if you use the default mapping,
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
Partial Objects
|
||||
===============
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Creating Partial Objects through DQL is deprecated and
|
||||
will be removed in the future, use data transfer object
|
||||
support in DQL instead. (`Details
|
||||
<https://github.com/doctrine/orm/issues/8471>`_)
|
||||
|
||||
A partial object is an object whose state is not fully initialized
|
||||
after being reconstituted from the database and that is
|
||||
disconnected from the rest of its data. The following section will
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
PHP Mapping
|
||||
===========
|
||||
|
||||
Doctrine ORM also allows you to provide the ORM metadata in the form
|
||||
Doctrine 2 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.
|
||||
@@ -27,7 +27,7 @@ to write a mapping file for it using the above configured
|
||||
|
||||
<?php
|
||||
namespace Entities;
|
||||
|
||||
|
||||
class User
|
||||
{
|
||||
private $id;
|
||||
@@ -42,30 +42,16 @@ named ``Entities.User.php`` inside of the
|
||||
|
||||
<?php
|
||||
// /path/to/php/mapping/files/Entities.User.php
|
||||
|
||||
|
||||
$metadata->mapField(array(
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer'
|
||||
));
|
||||
|
||||
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'username',
|
||||
'type' => 'string',
|
||||
'options' => array(
|
||||
'fixed' => true,
|
||||
'comment' => "User's login name"
|
||||
)
|
||||
));
|
||||
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'login_count',
|
||||
'type' => 'integer',
|
||||
'nullable' => false,
|
||||
'options' => array(
|
||||
'unsigned' => true,
|
||||
'default' => 0
|
||||
)
|
||||
'type' => 'string'
|
||||
));
|
||||
|
||||
Now we can easily retrieve the populated ``ClassMetadata`` instance
|
||||
@@ -101,13 +87,13 @@ Now you just need to define a static function named
|
||||
|
||||
<?php
|
||||
namespace Entities;
|
||||
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
|
||||
public static function loadMetadata(ClassMetadata $metadata)
|
||||
{
|
||||
$metadata->mapField(array(
|
||||
@@ -115,7 +101,7 @@ Now you just need to define a static function named
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer'
|
||||
));
|
||||
|
||||
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'username',
|
||||
'type' => 'string'
|
||||
@@ -241,7 +227,6 @@ General Getters
|
||||
|
||||
|
||||
- ``getTableName()``
|
||||
- ``getSchemaName()``
|
||||
- ``getTemporaryIdTableName()``
|
||||
|
||||
Identifier Getters
|
||||
|
||||
@@ -7,20 +7,14 @@ conditionally constructing a DQL query in several steps.
|
||||
It provides a set of classes and methods that is able to
|
||||
programmatically build queries, and also provides a fluent API.
|
||||
This means that you can change between one methodology to the other
|
||||
as you want, or just pick a preferred one.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``QueryBuilder`` is not an abstraction of DQL, but merely a tool to dynamically build it.
|
||||
You should still use plain DQL when you can, as it is simpler and more readable.
|
||||
More about this in the :doc:`FAQ <faq>`_.
|
||||
as you want, and also pick one if you prefer.
|
||||
|
||||
Constructing a new QueryBuilder object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The same way you build a normal Query, you build a ``QueryBuilder``
|
||||
object. Here is an example of how to build a ``QueryBuilder``
|
||||
object:
|
||||
object, just providing the correct method name. Here is an example
|
||||
how to build a ``QueryBuilder`` object:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -30,9 +24,9 @@ object:
|
||||
// example1: creating a QueryBuilder instance
|
||||
$qb = $em->createQueryBuilder();
|
||||
|
||||
An instance of QueryBuilder has several informative methods. One
|
||||
good example is to inspect what type of object the
|
||||
``QueryBuilder`` is.
|
||||
Once you have created an instance of QueryBuilder, it provides a
|
||||
set of useful informative functions that you can use. One good
|
||||
example is to inspect what type of object the ``QueryBuilder`` is.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -86,11 +80,11 @@ Working with QueryBuilder
|
||||
High level API methods
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The most straightforward way to build a dynamic query with the ``QueryBuilder`` is by taking
|
||||
advantage of Helper methods. For all base code, there is a set of
|
||||
useful methods to simplify a programmer's life. To illustrate how
|
||||
to work with them, here is the same example 6 re-written using
|
||||
``QueryBuilder`` helper methods:
|
||||
To simplify even more the way you build a query in Doctrine, we can take
|
||||
advantage of what we call Helper methods. For all base code, there
|
||||
is a set of useful methods to simplify a programmer's life. To
|
||||
illustrate how to work with them, here is the same example 6
|
||||
re-written using ``QueryBuilder`` helper methods:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -103,9 +97,10 @@ to work with them, here is the same example 6 re-written using
|
||||
->orderBy('u.name', 'ASC');
|
||||
|
||||
``QueryBuilder`` helper methods are considered the standard way to
|
||||
use the ``QueryBuilder``. The ``$qb->expr()->*`` methods can help you
|
||||
build conditional expressions dynamically. Here is a converted example 8 to
|
||||
suggested way to build queries with dynamic conditions:
|
||||
build DQL queries. Although it is supported, it should be avoided
|
||||
to use string based queries and greatly encouraged to use
|
||||
``$qb->expr()->*`` methods. Here is a converted example 8 to
|
||||
suggested standard way to build queries:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -118,7 +113,7 @@ suggested way to build queries with dynamic conditions:
|
||||
$qb->expr()->eq('u.id', '?1'),
|
||||
$qb->expr()->like('u.nickname', '?2')
|
||||
))
|
||||
->orderBy('u.surname', 'ASC');
|
||||
->orderBy('u.surname', 'ASC'));
|
||||
|
||||
Here is a complete list of helper methods available in ``QueryBuilder``:
|
||||
|
||||
@@ -132,12 +127,6 @@ Here is a complete list of helper methods available in ``QueryBuilder``:
|
||||
// Example - $qb->select($qb->expr()->select('u', 'p'))
|
||||
public function select($select = null);
|
||||
|
||||
// addSelect does not override previous calls to select
|
||||
//
|
||||
// Example - $qb->select('u');
|
||||
// ->addSelect('p.area_code');
|
||||
public function addSelect($select = null);
|
||||
|
||||
// Example - $qb->delete('User', 'u')
|
||||
public function delete($delete = null, $alias = null);
|
||||
|
||||
@@ -150,23 +139,15 @@ Here is a complete list of helper methods available in ``QueryBuilder``:
|
||||
public function set($key, $value);
|
||||
|
||||
// Example - $qb->from('Phonenumber', 'p')
|
||||
// Example - $qb->from('Phonenumber', 'p', 'p.id')
|
||||
public function from($from, $alias, $indexBy = null);
|
||||
|
||||
// Example - $qb->join('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1'))
|
||||
// Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1')
|
||||
// Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id')
|
||||
public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null);
|
||||
public function from($from, $alias = null);
|
||||
|
||||
// Example - $qb->innerJoin('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1'))
|
||||
// Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1')
|
||||
// Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id')
|
||||
public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null);
|
||||
public function innerJoin($join, $alias = null, $conditionType = null, $condition = null);
|
||||
|
||||
// Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55))
|
||||
// Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55')
|
||||
// Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55', 'p.id')
|
||||
public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null);
|
||||
public function leftJoin($join, $alias = null, $conditionType = null, $condition = null);
|
||||
|
||||
// NOTE: ->where() overrides all previously set conditions
|
||||
//
|
||||
@@ -175,8 +156,6 @@ Here is a complete list of helper methods available in ``QueryBuilder``:
|
||||
// Example - $qb->where('u.firstName = ?1 AND u.surname = ?2')
|
||||
public function where($where);
|
||||
|
||||
// NOTE: ->andWhere() can be used directly, without any ->where() before
|
||||
//
|
||||
// Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0'))
|
||||
public function andWhere($where);
|
||||
|
||||
@@ -227,9 +206,9 @@ allowed. Binding parameters can simply be achieved as follows:
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
$qb->select('u')
|
||||
->from('User', 'u')
|
||||
->from('User u')
|
||||
->where('u.id = ?1')
|
||||
->orderBy('u.name', 'ASC')
|
||||
->orderBy('u.name', 'ASC');
|
||||
->setParameter(1, 100); // Sets ?1 to 100, and thus we will fetch a user with u.id = 100
|
||||
|
||||
You are not forced to enumerate your placeholders as the
|
||||
@@ -241,9 +220,9 @@ alternative syntax is available:
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
$qb->select('u')
|
||||
->from('User', 'u')
|
||||
->from('User u')
|
||||
->where('u.id = :identifier')
|
||||
->orderBy('u.name', 'ASC')
|
||||
->orderBy('u.name', 'ASC');
|
||||
->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100
|
||||
|
||||
Note that numeric placeholders start with a ? followed by a number
|
||||
@@ -255,21 +234,6 @@ 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:
|
||||
@@ -277,17 +241,10 @@ following syntax:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
// Query here...
|
||||
$qb->setParameters(new ArrayCollection([
|
||||
new Parameter('1', 'value for ?1'),
|
||||
new Parameter('2', 'value for ?2')
|
||||
]));
|
||||
$qb->setParameters(array(1 => 'value for ?1', 2 => 'value for ?2'));
|
||||
|
||||
Getting already bound parameters is easy - simply use the above
|
||||
mentioned syntax with "getParameter()" or "getParameters()":
|
||||
@@ -344,7 +301,7 @@ the Query object which can be retrieved from ``EntityManager#createQuery()``.
|
||||
Executing a Query
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The QueryBuilder is a builder object only - it has no means of actually
|
||||
The QueryBuilder is a builder object only, it has no means of actually
|
||||
executing the Query. Additionally a set of parameters such as query hints
|
||||
cannot be set on the QueryBuilder itself. This is why you always have to convert
|
||||
a querybuilder instance into a Query object:
|
||||
@@ -361,7 +318,6 @@ a querybuilder instance into a Query object:
|
||||
|
||||
// Execute Query
|
||||
$result = $query->getResult();
|
||||
$iterableResult = $query->toIterable();
|
||||
$single = $query->getSingleResult();
|
||||
$array = $query->getArrayResult();
|
||||
$scalar = $query->getScalarResult();
|
||||
@@ -380,8 +336,7 @@ set of useful methods to help build expressions:
|
||||
<?php
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
// example8: QueryBuilder port of:
|
||||
// "SELECT u FROM User u WHERE u.id = ? OR u.nickname LIKE ? ORDER BY u.name ASC" using Expr class
|
||||
// example8: QueryBuilder port of: "SELECT u FROM User u WHERE u.id = ? OR u.nickname LIKE ? ORDER BY u.surname DESC" using Expr class
|
||||
$qb->add('select', new Expr\Select(array('u')))
|
||||
->add('from', new Expr\From('User', 'u'))
|
||||
->add('where', $qb->expr()->orX(
|
||||
@@ -478,9 +433,6 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%'))
|
||||
public function like($x, $y); // Returns Expr\Comparison instance
|
||||
|
||||
// Example - $qb->expr()->notLike('u.firstname', $qb->expr()->literal('Gui%'))
|
||||
public function notLike($x, $y); // Returns Expr\Comparison instance
|
||||
|
||||
// Example - $qb->expr()->between('u.id', '1', '10')
|
||||
public function between($val, $x, $y); // Returns Expr\Func
|
||||
|
||||
@@ -493,8 +445,8 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname'))
|
||||
public function concat($x, $y); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->substring('u.firstname', 0, 1)
|
||||
public function substring($x, $from, $len); // Returns Expr\Func
|
||||
// Example - $qb->expr()->substr('u.firstname', 0, 1)
|
||||
public function substr($x, $from, $len); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->lower('u.firstname')
|
||||
public function lower($x); // Returns Expr\Func
|
||||
@@ -520,9 +472,6 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->sqrt('u.currentBalance')
|
||||
public function sqrt($x); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->mod('u.currentBalance', '10')
|
||||
public function mod($x); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->count('u.firstname')
|
||||
public function count($x); // Returns Expr\Func
|
||||
|
||||
@@ -530,32 +479,14 @@ complete list of supported helper methods available:
|
||||
public function countDistinct($x); // Returns Expr\Func
|
||||
}
|
||||
|
||||
Adding a Criteria to a Query
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can also add a :ref:`filtering-collections` to a QueryBuilder by
|
||||
using ``addCriteria``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
// ...
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->orderBy(['firstName', 'ASC']);
|
||||
|
||||
// $qb instanceof QueryBuilder
|
||||
$qb->addCriteria($criteria);
|
||||
// then execute your query like normal
|
||||
|
||||
Low Level API
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Now we will describe the low level method of creating queries.
|
||||
It may be useful to work at this level for optimization purposes,
|
||||
but most of the time it is preferred to work at a higher level of
|
||||
abstraction.
|
||||
Now we have describe the low level (thought of as the
|
||||
hardcore method) of creating queries. It may be useful to work at
|
||||
this level for optimization purposes, but most of the time it is
|
||||
preferred to work at a higher level of abstraction.
|
||||
|
||||
All helper methods in ``QueryBuilder`` actually rely on a single
|
||||
one: ``add()``. This method is responsible of building every piece
|
||||
@@ -579,9 +510,7 @@ of DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and
|
||||
<?php
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
// example6: how to define:
|
||||
// "SELECT u FROM User u WHERE u.id = ? ORDER BY u.name ASC"
|
||||
// using QueryBuilder string support
|
||||
// example6: how to define: "SELECT u FROM User u WHERE u.id = ? ORDER BY u.name ASC" using QueryBuilder string support
|
||||
$qb->add('select', 'u')
|
||||
->add('from', 'User u')
|
||||
->add('where', 'u.id = ?1')
|
||||
@@ -600,10 +529,13 @@ same query of example 6 written using
|
||||
<?php
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
// example7: how to define:
|
||||
// "SELECT u FROM User u WHERE u.id = ? ORDER BY u.name ASC"
|
||||
// using QueryBuilder using Expr\* instances
|
||||
// example7: how to define: "SELECT u FROM User u WHERE u.id = ? ORDER BY u.name ASC" using QueryBuilder using Expr\* instances
|
||||
$qb->add('select', new Expr\Select(array('u')))
|
||||
->add('from', new Expr\From('User', 'u'))
|
||||
->add('where', new Expr\Comparison('u.id', '=', '?1'))
|
||||
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
|
||||
|
||||
Of course this is the hardest way to build a DQL query in Doctrine.
|
||||
To simplify some of these efforts, we introduce what we call as
|
||||
``Expr`` helper class.
|
||||
|
||||
|
||||
@@ -1,736 +0,0 @@
|
||||
The Second Level Cache
|
||||
======================
|
||||
|
||||
.. note::
|
||||
|
||||
The second level cache functionality is marked as experimental for now. It
|
||||
is a very complex feature and we cannot guarantee yet that it works stable
|
||||
in all cases.
|
||||
|
||||
The Second Level Cache is designed to reduce the amount of necessary database access.
|
||||
It sits between your application and the database to avoid the number of database hits as much as possible.
|
||||
|
||||
When turned on, entities will be first searched in cache and if they are not found,
|
||||
a database query will be fired and then the entity result will be stored in a cache provider.
|
||||
|
||||
There are some flavors of caching available, but is better to cache read-only data.
|
||||
|
||||
Be aware that caches are not aware of changes made to the persistent store by another application.
|
||||
They can, however, be configured to regularly expire cached data.
|
||||
|
||||
|
||||
Caching Regions
|
||||
---------------
|
||||
|
||||
Second level cache does not store instances of an entity, instead it caches only entity identifier and values.
|
||||
Each entity class, collection association and query has its region, where values of each instance are stored.
|
||||
|
||||
Caching Regions are specific region into the cache provider that might store entities, collection or queries.
|
||||
Each cache region resides in a specific cache namespace and has its own lifetime configuration.
|
||||
|
||||
Notice that when caching collection and queries only identifiers are stored.
|
||||
The entity values will be stored in its own region
|
||||
|
||||
Something like below for an entity region :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
[
|
||||
'region_name:entity_1_hash' => ['id'=> 1, 'name' => 'FooBar', 'associationName'=>null],
|
||||
'region_name:entity_2_hash' => ['id'=> 2, 'name' => 'Foo', 'associationName'=>['id'=>11]],
|
||||
'region_name:entity_3_hash' => ['id'=> 3, 'name' => 'Bar', 'associationName'=>['id'=>22]]
|
||||
];
|
||||
|
||||
|
||||
If the entity holds a collection that also needs to be cached.
|
||||
An collection region could look something like :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
[
|
||||
'region_name:entity_1_coll_assoc_name_hash' => ['ownerId'=> 1, 'list' => [1, 2, 3]],
|
||||
'region_name:entity_2_coll_assoc_name_hash' => ['ownerId'=> 2, 'list' => [2, 3]],
|
||||
'region_name:entity_3_coll_assoc_name_hash' => ['ownerId'=> 3, 'list' => [2, 4]]
|
||||
];
|
||||
|
||||
A query region might be something like :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
[
|
||||
'region_name:query_1_hash' => ['list' => [1, 2, 3]],
|
||||
'region_name:query_2_hash' => ['list' => [2, 3]],
|
||||
'region_name:query_3_hash' => ['list' => [2, 4]]
|
||||
];
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
The following data structures represents now the cache will looks like, this is not actual cached data.
|
||||
|
||||
|
||||
.. _reference-second-level-cache-regions:
|
||||
|
||||
Cache Regions
|
||||
-------------
|
||||
|
||||
``Doctrine\ORM\Cache\Region\DefaultRegion`` 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``
|
||||
define contracts that should be implemented by a cache provider.
|
||||
|
||||
It allows you to provide your own cache implementation that might take advantage of specific cache driver.
|
||||
|
||||
If you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise.
|
||||
|
||||
|
||||
Cache region
|
||||
~~~~~~~~~~~~
|
||||
|
||||
``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>`_.
|
||||
|
||||
Concurrent cache region
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A ``Doctrine\ORM\Cache\ConcurrentRegion`` is designed to store concurrently managed data region.
|
||||
By default, Doctrine provides a very simple implementation based on file locks ``Doctrine\ORM\Cache\Region\FileLockRegion``.
|
||||
|
||||
If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache 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>`_.
|
||||
|
||||
Timestamp region
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
``Doctrine\ORM\Cache\TimestampRegion``
|
||||
|
||||
Tracks the timestamps of the most recent updates to particular entity.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
|
||||
|
||||
.. _reference-second-level-cache-mode:
|
||||
|
||||
Caching mode
|
||||
------------
|
||||
|
||||
* ``READ_ONLY`` (DEFAULT)
|
||||
|
||||
* Can do reads, inserts and deletes, cannot perform updates or employ any locks.
|
||||
* Useful for data that is read frequently but never updated.
|
||||
* Best performer.
|
||||
* It is Simple.
|
||||
|
||||
* ``NONSTRICT_READ_WRITE``
|
||||
|
||||
* Read Write Cache doesn’t employ any locks but can do reads, inserts, updates and deletes.
|
||||
* Good if the application needs to update data rarely.
|
||||
|
||||
|
||||
* ``READ_WRITE``
|
||||
|
||||
* Read Write cache employs locks before update/delete.
|
||||
* Use if data needs to be updated.
|
||||
* Slowest strategy.
|
||||
* To use it a the cache region implementation must support locking.
|
||||
|
||||
|
||||
Built-in cached persisters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Cached persisters are responsible to access cache regions.
|
||||
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| Cache Usage | Persister |
|
||||
+=======================+===========================================================================================+
|
||||
| READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadOnlyCachedEntityPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadWriteCachedEntityPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Entity\\NonStrictReadWriteCachedEntityPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadOnlyCachedCollectionPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
Doctrine allows you to specify configurations and some points of extension for the second-level-cache
|
||||
|
||||
|
||||
Enable Second Level Cache
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To enable the second-level-cache, you should provide a cache factory.
|
||||
``\Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $config \Doctrine\ORM\Cache\RegionsConfiguration */
|
||||
/* @var $cache \Doctrine\Common\Cache\Cache */
|
||||
|
||||
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($config, $cache);
|
||||
|
||||
// Enable second-level-cache
|
||||
$config->setSecondLevelCacheEnabled();
|
||||
|
||||
// Cache factory
|
||||
$config->getSecondLevelCacheConfiguration()
|
||||
->setCacheFactory($factory);
|
||||
|
||||
|
||||
Cache Factory
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Cache Factory is the main point of extension.
|
||||
|
||||
It allows you to provide a specific implementation of the following components :
|
||||
|
||||
``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>`_.
|
||||
|
||||
Region Lifetime
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
To specify a default lifetime for all regions or specify a different lifetime for a specific region.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $config \Doctrine\ORM\Configuration */
|
||||
/* @var $cacheConfig \Doctrine\ORM\Cache\CacheConfiguration */
|
||||
$cacheConfig = $config->getSecondLevelCacheConfiguration();
|
||||
$regionConfig = $cacheConfig->getRegionsConfiguration();
|
||||
|
||||
// Cache Region lifetime
|
||||
$regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region (in seconds)
|
||||
$regionConfig->setDefaultLifetime(7200); // Default time to live (in seconds)
|
||||
|
||||
|
||||
Cache Log
|
||||
~~~~~~~~~
|
||||
By providing a cache logger you should be able to get information about all cache operations such as hits, misses and puts.
|
||||
|
||||
``\Doctrine\ORM\Cache\Logging\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $config \Doctrine\ORM\Configuration */
|
||||
$logger = new \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger();
|
||||
|
||||
// Cache logger
|
||||
$config->setSecondLevelCacheEnabled(true);
|
||||
$config->getSecondLevelCacheConfiguration()
|
||||
->setCacheLogger($logger);
|
||||
|
||||
|
||||
// Collect cache statistics
|
||||
|
||||
// Get the number of entries successfully retrieved from a specific region.
|
||||
$logger->getRegionHitCount('my_entity_region');
|
||||
|
||||
// Get the number of cached entries *not* found in a specific region.
|
||||
$logger->getRegionMissCount('my_entity_region');
|
||||
|
||||
// Get the number of cacheable entries put in cache.
|
||||
$logger->getRegionPutCount('my_entity_region');
|
||||
|
||||
// Get the total number of put in all regions.
|
||||
$logger->getPutCount();
|
||||
|
||||
// Get the total number of entries successfully retrieved from all regions.
|
||||
$logger->getHitCount();
|
||||
|
||||
// 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 the information you want.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
|
||||
|
||||
|
||||
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`` is an optional value that specifies the name of the second
|
||||
level cache region.
|
||||
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Cache(usage="READ_ONLY", region="my_entity_region")
|
||||
*/
|
||||
class Country
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @Column(unique=true)
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
// other properties and methods
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="Country">
|
||||
<cache usage="READ_ONLY" region="my_entity_region" />
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="IDENTITY"/>
|
||||
</id>
|
||||
<field name="name" type="string" column="name"/>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Country:
|
||||
type: entity
|
||||
cache:
|
||||
usage : READ_ONLY
|
||||
region : my_entity_region
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
id: true
|
||||
generator:
|
||||
strategy: IDENTITY
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
|
||||
|
||||
Association cache definition
|
||||
----------------------------
|
||||
The most common use case is to cache entities. But we can also cache relationships.
|
||||
It caches the primary keys of association and cache each element will be cached into its region.
|
||||
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Cache("NONSTRICT_READ_WRITE")
|
||||
*/
|
||||
class State
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @Column(unique=true)
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @Cache("NONSTRICT_READ_WRITE")
|
||||
* @ManyToOne(targetEntity="Country")
|
||||
* @JoinColumn(name="country_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $country;
|
||||
|
||||
/**
|
||||
* @Cache("NONSTRICT_READ_WRITE")
|
||||
* @OneToMany(targetEntity="City", mappedBy="state")
|
||||
*/
|
||||
protected $cities;
|
||||
|
||||
// other properties and methods
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="State">
|
||||
|
||||
<cache usage="NONSTRICT_READ_WRITE" />
|
||||
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="IDENTITY"/>
|
||||
</id>
|
||||
|
||||
<field name="name" type="string" column="name"/>
|
||||
|
||||
<many-to-one field="country" target-entity="Country">
|
||||
<cache usage="NONSTRICT_READ_WRITE" />
|
||||
|
||||
<join-columns>
|
||||
<join-column name="country_id" referenced-column-name="id"/>
|
||||
</join-columns>
|
||||
</many-to-one>
|
||||
|
||||
<one-to-many field="cities" target-entity="City" mapped-by="state">
|
||||
<cache usage="NONSTRICT_READ_WRITE"/>
|
||||
</one-to-many>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
State:
|
||||
type: entity
|
||||
cache:
|
||||
usage : NONSTRICT_READ_WRITE
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
id: true
|
||||
generator:
|
||||
strategy: IDENTITY
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
|
||||
manyToOne:
|
||||
state:
|
||||
targetEntity: Country
|
||||
joinColumns:
|
||||
country_id:
|
||||
referencedColumnName: id
|
||||
cache:
|
||||
usage : NONSTRICT_READ_WRITE
|
||||
|
||||
oneToMany:
|
||||
cities:
|
||||
targetEntity:City
|
||||
mappedBy: state
|
||||
cache:
|
||||
usage : NONSTRICT_READ_WRITE
|
||||
|
||||
|
||||
> Note: for this to work, the target entity must also be marked as cacheable.
|
||||
|
||||
Cache usage
|
||||
~~~~~~~~~~~
|
||||
|
||||
Basic entity cache
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->persist(new Country($name));
|
||||
$em->flush(); // Hit database to insert the row and put into cache
|
||||
|
||||
$em->clear(); // Clear entity manager
|
||||
|
||||
$country1 = $em->find('Country', 1); // Retrieve item from cache
|
||||
|
||||
$country->setName("New Name");
|
||||
$em->persist($country);
|
||||
$em->flush(); // Hit database to update the row and update cache
|
||||
|
||||
$em->clear(); // Clear entity manager
|
||||
|
||||
$country2 = $em->find('Country', 1); // Retrieve item from cache
|
||||
// Notice that $country1 and $country2 are not the same instance.
|
||||
|
||||
|
||||
Association cache
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Hit database to insert the row and put into cache
|
||||
$em->persist(new State($name, $country));
|
||||
$em->flush();
|
||||
|
||||
// Clear entity manager
|
||||
$em->clear();
|
||||
|
||||
// Retrieve item from cache
|
||||
$state = $em->find('State', 1);
|
||||
|
||||
// Hit database to update the row and update cache entry
|
||||
$state->setName("New Name");
|
||||
$em->persist($state);
|
||||
$em->flush();
|
||||
|
||||
// Create a new collection item
|
||||
$city = new City($name, $state);
|
||||
$state->addCity($city);
|
||||
|
||||
// Hit database to insert new collection item,
|
||||
// put entity and collection cache into cache.
|
||||
$em->persist($city);
|
||||
$em->persist($state);
|
||||
$em->flush();
|
||||
|
||||
// Clear entity manager
|
||||
$em->clear();
|
||||
|
||||
// Retrieve item from cache
|
||||
$state = $em->find('State', 1);
|
||||
|
||||
// Retrieve association from cache
|
||||
$country = $state->getCountry();
|
||||
|
||||
// Retrieve collection from cache
|
||||
$cities = $state->getCities();
|
||||
|
||||
echo $country->getName();
|
||||
echo $state->getName();
|
||||
|
||||
// Retrieve each collection item from cache
|
||||
foreach ($cities as $city) {
|
||||
echo $city->getName();
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Notice that all entities should be marked as cacheable.
|
||||
|
||||
Using the query cache
|
||||
---------------------
|
||||
|
||||
The second level cache stores the entities, associations and collections.
|
||||
The query cache stores the results of the query but as identifiers, entity values are actually stored in the 2nd level cache.
|
||||
|
||||
.. note::
|
||||
|
||||
Query cache should always be used in conjunction with the second-level-cache for those entities which should be cached.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
|
||||
// Execute database query, store query cache and entity cache
|
||||
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
|
||||
$em->clear()
|
||||
|
||||
// Check if query result is valid and load entities from cache
|
||||
$result2 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
|
||||
Cache mode
|
||||
~~~~~~~~~~
|
||||
|
||||
The Cache Mode controls how a particular query interacts with the second-level cache:
|
||||
|
||||
* ``Cache::MODE_GET`` - May read items from the cache, but will not add items.
|
||||
* ``Cache::MODE_PUT`` - Will never read items from the cache, but will add items to the cache as it reads them from the database.
|
||||
* ``Cache::MODE_NORMAL`` - May read items from the cache, and add items to the cache.
|
||||
* ``Cache::MODE_REFRESH`` - The query will never read items from the cache, but will refresh items to the cache as it reads them from the database.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
// Will refresh the query cache and all entities the cache as it reads from the database.
|
||||
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
->setCacheMode(Cache::MODE_GET)
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
|
||||
.. note::
|
||||
|
||||
The the default query cache mode is ```Cache::MODE_NORMAL```
|
||||
|
||||
DELETE / UPDATE queries
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
DQL UPDATE / DELETE statements are ported directly into a database and bypass
|
||||
the second-level cache.
|
||||
Entities that are already cached will NOT be invalidated.
|
||||
However the cached data could be evicted using the cache API or an special query hint.
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT``
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Execute and invalidate
|
||||
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
->setHint(Query::HINT_CACHE_EVICT, true)
|
||||
->execute();
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``all cache entries`` using the cache API
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Execute
|
||||
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
->execute();
|
||||
// Invoke Cache API
|
||||
$em->getCache()->evictEntityRegion('Entity\Country');
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``a specific cache entry`` using the cache API
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Execute
|
||||
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
->execute();
|
||||
// Invoke Cache API
|
||||
$em->getCache()->evictEntity('Entity\Country', 1);
|
||||
|
||||
Using the repository query cache
|
||||
--------------------------------
|
||||
|
||||
As well as ``Query Cache`` all persister queries store only identifier values for an individual query.
|
||||
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.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// load from database and store cache query key hashing the query + parameters + last timestamp cache region..
|
||||
$entities = $em->getRepository('Entity\Country')->findAll();
|
||||
|
||||
// load from query and entities from cache..
|
||||
$entities = $em->getRepository('Entity\Country')->findAll();
|
||||
|
||||
// update the timestamp cache region for Country
|
||||
$em->persist(new Country('zombieland'));
|
||||
$em->flush();
|
||||
$em->clear();
|
||||
|
||||
// Reload from database.
|
||||
// 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
|
||||
---------
|
||||
|
||||
Caches are not aware of changes made by another application.
|
||||
However, you can use the cache API to check / invalidate cache entries.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $cache \Doctrine\ORM\Cache */
|
||||
$cache = $em->getCache();
|
||||
|
||||
$cache->containsEntity('Entity\State', 1) // Check if the cache exists
|
||||
$cache->evictEntity('Entity\State', 1); // Remove an entity from cache
|
||||
$cache->evictEntityRegion('Entity\State'); // Remove all entities from cache
|
||||
|
||||
$cache->containsCollection('Entity\State', 'cities', 1); // Check if the cache exists
|
||||
$cache->evictCollection('Entity\State', 'cities', 1); // Remove an entity collection from cache
|
||||
$cache->evictCollectionRegion('Entity\State', 'cities'); // Remove all collections from cache
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
Composite primary key
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Composite primary key are supported by second level cache,
|
||||
however when one of the keys is an association the cached entity should always be retrieved using the association identifier.
|
||||
For performance reasons the cache API does not extract from composite primary key.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Reference
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="Article", inversedBy="references")
|
||||
* @JoinColumn(name="source_id", referencedColumnName="article_id")
|
||||
*/
|
||||
private $source;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="Article")
|
||||
* @JoinColumn(name="target_id", referencedColumnName="article_id")
|
||||
*/
|
||||
private $target;
|
||||
}
|
||||
|
||||
// Supported
|
||||
/* @var $article Article */
|
||||
$article = $em->find('Article', 1);
|
||||
|
||||
// Supported
|
||||
/* @var $article Article */
|
||||
$article = $em->find('Article', $article);
|
||||
|
||||
// Supported
|
||||
$id = array('source' => 1, 'target' => 2);
|
||||
$reference = $em->find('Reference', $id);
|
||||
|
||||
// NOT Supported
|
||||
$id = array('source' => new Article(1), 'target' => new Article(2));
|
||||
$reference = $em->find('Reference', $id);
|
||||
|
||||
Distributed environments
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some cache driver are not meant to be used in a distributed environment.
|
||||
Load-balancer for distributing workloads across multiple computing resources
|
||||
should be used in conjunction with distributed caching system such as memcached, redis, riak ...
|
||||
|
||||
Caches should be used with care when using a load-balancer if you don't share the cache.
|
||||
While using APC or any file based cache update occurred in a specific machine would not reflect to the cache in other machines.
|
||||
|
||||
|
||||
Paginator
|
||||
~~~~~~~~~
|
||||
|
||||
Count queries generated by ``Doctrine\ORM\Tools\Pagination\Paginator`` are not cached by second-level cache.
|
||||
Although entities and query result are cached, count queries will hit the
|
||||
database every time.
|
||||
@@ -1,151 +0,0 @@
|
||||
Security
|
||||
========
|
||||
|
||||
The Doctrine library is operating very close to your database and as such needs
|
||||
to handle and make assumptions about SQL injection vulnerabilities.
|
||||
|
||||
It is vital that you understand how Doctrine approaches security, because
|
||||
we cannot protect you from SQL injection.
|
||||
|
||||
Please also read the documentation chapter on Security in Doctrine DBAL. This
|
||||
page only handles Security issues in the ORM.
|
||||
|
||||
- `DBAL Security Page <http://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
|
||||
|
||||
If you find a Security bug in Doctrine, please report it on Jira and change the
|
||||
Security Level to "Security Issues". It will be visible to Doctrine Core
|
||||
developers and you only.
|
||||
|
||||
User input and Doctrine ORM
|
||||
---------------------------
|
||||
|
||||
The ORM is much better at protecting against SQL injection than the DBAL alone.
|
||||
You can consider the following APIs to be safe from SQL injection:
|
||||
|
||||
- ``\Doctrine\ORM\EntityManager#find()`` and ``getReference()``.
|
||||
- All values on Objects inserted and updated through ``Doctrine\ORM\EntityManager#persist()``
|
||||
- All find methods on ``Doctrine\ORM\EntityRepository``.
|
||||
- User Input set to DQL Queries or QueryBuilder methods through
|
||||
- ``setParameter()`` or variants
|
||||
- ``setMaxResults()``
|
||||
- ``setFirstResult()``
|
||||
- Queries through the Criteria API on ``Doctrine\ORM\PersistentCollection`` and
|
||||
``Doctrine\ORM\EntityRepository``.
|
||||
|
||||
You are **NOT** safe from SQL injection when using user input with:
|
||||
|
||||
- Expression API of ``Doctrine\ORM\QueryBuilder``
|
||||
- Concatenating user input into DQL SELECT, UPDATE or DELETE statements or
|
||||
Native SQL.
|
||||
|
||||
This means SQL injections can only occur with Doctrine ORM when working with
|
||||
Query Objects of any kind. The safe rule is to always use prepared statement
|
||||
parameters for user objects when using a Query object.
|
||||
|
||||
.. warning::
|
||||
|
||||
Insecure code follows, don't copy paste this.
|
||||
|
||||
The following example shows insecure DQL usage:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
// INSECURE
|
||||
$dql = "SELECT u
|
||||
FROM MyProject\Entity\User u
|
||||
WHERE u.status = '" . $_GET['status'] . "'
|
||||
ORDER BY " . $_GET['orderField'] . " ASC";
|
||||
|
||||
For Doctrine there is absolutely no way to find out which parts of ``$dql`` are
|
||||
from user input and which are not, even if we have our own parsing process
|
||||
this is technically impossible. The correct way is:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
$orderFieldWhitelist = array('email', 'username');
|
||||
$orderField = "email";
|
||||
|
||||
if (in_array($_GET['orderField'], $orderFieldWhitelist)) {
|
||||
$orderField = $_GET['orderField'];
|
||||
}
|
||||
|
||||
$dql = "SELECT u
|
||||
FROM MyProject\Entity\User u
|
||||
WHERE u.status = ?1
|
||||
ORDER BY u." . $orderField . " ASC";
|
||||
|
||||
$query = $entityManager->createQuery($dql);
|
||||
$query->setParameter(1, $_GET['status']);
|
||||
|
||||
|
||||
Preventing Mass Assignment Vulnerabilities
|
||||
------------------------------------------
|
||||
|
||||
ORMs are very convenient for CRUD applications and Doctrine is no exception.
|
||||
However CRUD apps are often vulnerable to mass assignment security problems
|
||||
when implemented naively.
|
||||
|
||||
Doctrine is not vulnerable to this problem out of the box, but you can easily
|
||||
make your entities vulnerable to mass assignment when you add methods of
|
||||
the kind ``updateFromArray()`` or ``updateFromJson()`` to them. A vulnerable
|
||||
entity might look like this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class InsecureEntity
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
/** @Column */
|
||||
private $email;
|
||||
/** @Column(type="boolean") */
|
||||
private $isAdmin;
|
||||
|
||||
public function fromArray(array $userInput)
|
||||
{
|
||||
foreach ($userInput as $key => $value) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Now the possiblity of mass-asignment exists on this entity and can
|
||||
be exploited by attackers to set the "isAdmin" flag to true on any
|
||||
object when you pass the whole request data to this method like:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$entity = new InsecureEntity();
|
||||
$entity->fromArray($_POST);
|
||||
|
||||
$entityManager->persist($entity);
|
||||
$entityManager->flush();
|
||||
|
||||
You can spot this problem in this very simple example easily. However
|
||||
in combination with frameworks and form libraries it might not be
|
||||
so obvious when this issue arises. Be careful to avoid this
|
||||
kind of mistake.
|
||||
|
||||
How to fix this problem? You should always have a whitelist
|
||||
of allowed key to set via mass assignment functions.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function fromArray(array $userInput, $allowedFields = array())
|
||||
{
|
||||
foreach ($userInput as $key => $value) {
|
||||
if (in_array($key, $allowedFields)) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
-39
@@ -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 ORM.
|
||||
administration tasks during the development of a project that uses Doctrine 2.
|
||||
|
||||
Take a look at the :doc:`Installation and Configuration <configuration>`
|
||||
chapter for more information how to setup the console command.
|
||||
@@ -27,34 +27,64 @@ Configuration
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Whenever the ``doctrine`` command line tool is invoked, it can
|
||||
access all Commands that were registered by a developer. There is no
|
||||
access all Commands that were registered by 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``. 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.
|
||||
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.
|
||||
|
||||
Here is an example of a the project-specific ``cli-config.php``:
|
||||
Whenever you invoke the Doctrine binary the current folder is searched for a
|
||||
``cli-config.php`` file. This file contains the project specific configuration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
|
||||
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($conn)
|
||||
));
|
||||
$cli->setHelperSet($helperSet);
|
||||
|
||||
// replace this with the path to your own project bootstrap file.
|
||||
require_once 'bootstrap.php';
|
||||
When dealing with the ORM package, the EntityManagerHelper is
|
||||
required:
|
||||
|
||||
// replace with mechanism to retrieve EntityManager in your app
|
||||
$entityManager = GetEntityManager();
|
||||
.. code-block:: php
|
||||
|
||||
return ConsoleRunner::createHelperSet($entityManager);
|
||||
<?php
|
||||
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
|
||||
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
|
||||
));
|
||||
$cli->setHelperSet($helperSet);
|
||||
|
||||
.. note::
|
||||
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::
|
||||
|
||||
You have to adjust this snippet for your specific application or framework
|
||||
and use their facilities to access the Doctrine EntityManager and
|
||||
@@ -175,7 +205,7 @@ tables of the current model to clean up with orphaned tables.
|
||||
You can also use database introspection to update your schema
|
||||
easily with the ``updateSchema()`` method. It will compare your
|
||||
existing database schema to the passed array of
|
||||
``ClassMetadataInfo`` instances.
|
||||
``ClassMetdataInfo`` instances.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -222,6 +252,15 @@ will output the SQL for the ran operation.
|
||||
Before using the orm:schema-tool commands, remember to configure
|
||||
your cli-config.php properly.
|
||||
|
||||
.. note::
|
||||
|
||||
When using the Annotation Mapping Driver you have to either setup
|
||||
your autoloader in the cli-config.php correctly to find all the
|
||||
entities, or you can use the second argument of the
|
||||
``EntityManagerHelper`` to specify all the paths of your entities
|
||||
(or mapping files), i.e.
|
||||
``new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em, $mappingPaths);``
|
||||
|
||||
Entity Generation
|
||||
-----------------
|
||||
|
||||
@@ -345,8 +384,8 @@ First you need to retrieve the metadata instances with the
|
||||
$em->getConnection()->getSchemaManager()
|
||||
)
|
||||
);
|
||||
|
||||
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
|
||||
|
||||
$cmf = new DisconnectedClassMetadataFactory();
|
||||
$cmf->setEntityManager($em);
|
||||
$metadata = $cmf->getAllMetadata();
|
||||
|
||||
@@ -356,7 +395,6 @@ to yml:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cme = new \Doctrine\ORM\Tools\Export\ClassMetadataExporter();
|
||||
$exporter = $cme->getExporter('yml', '/path/to/export/yml');
|
||||
$exporter->setMetadata($metadata);
|
||||
$exporter->export();
|
||||
@@ -382,7 +420,7 @@ You can also reverse engineer a database using the
|
||||
Runtime vs Development Mapping Validation
|
||||
-----------------------------------------
|
||||
|
||||
For performance reasons Doctrine ORM has to skip some of the
|
||||
For performance reasons Doctrine 2 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.
|
||||
@@ -438,7 +476,7 @@ To include a new command on Doctrine Console, you need to do modify the
|
||||
|
||||
<?php
|
||||
// doctrine.php
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Helper\Application;
|
||||
|
||||
// as before ...
|
||||
|
||||
@@ -469,21 +507,3 @@ defined ones) is possible through the command:
|
||||
new \MyProject\Tools\Console\Commands\AnotherCommand(),
|
||||
new \MyProject\Tools\Console\Commands\OneMoreCommand(),
|
||||
));
|
||||
|
||||
|
||||
Re-use console application
|
||||
--------------------------
|
||||
|
||||
You are also able to retrieve and re-use the default console application.
|
||||
Just call ``ConsoleRunner::createApplication(...)`` with an appropriate
|
||||
HelperSet, like it is described in the configuration section.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
// Retrieve default console application
|
||||
$cli = ConsoleRunner::createApplication($helperSet);
|
||||
|
||||
// Runs console application
|
||||
$cli->run();
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
Transactions and Concurrency
|
||||
============================
|
||||
|
||||
.. _transactions-and-concurrency_transaction-demarcation:
|
||||
|
||||
Transaction Demarcation
|
||||
-----------------------
|
||||
|
||||
@@ -16,20 +14,18 @@ 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 ORM already takes care of proper
|
||||
For the most part, Doctrine 2 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 ORM also allows (and encourages) you to take over
|
||||
However, Doctrine 2 also allows (and encourages) you to take over
|
||||
and control transaction demarcation yourself.
|
||||
|
||||
These are two ways to deal with transactions when using the
|
||||
Doctrine ORM and are now described in more detail.
|
||||
|
||||
.. _transactions-and-concurrency_approach-implicitly:
|
||||
|
||||
Approach 1: Implicitly
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -53,8 +49,6 @@ the DML operations by the Doctrine ORM and is sufficient if all the
|
||||
data manipulation that is part of a unit of work happens through
|
||||
the domain model and thus the ORM.
|
||||
|
||||
.. _transactions-and-concurrency_approach-explicitly:
|
||||
|
||||
Approach 2: Explicitly
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -75,7 +69,8 @@ looks like this:
|
||||
$em->flush();
|
||||
$em->getConnection()->commit();
|
||||
} catch (Exception $e) {
|
||||
$em->getConnection()->rollBack();
|
||||
$em->getConnection()->rollback();
|
||||
$em->close();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -86,12 +81,14 @@ require an active transaction. Such methods will throw a
|
||||
``TransactionRequiredException`` to inform you of that
|
||||
requirement.
|
||||
|
||||
A more convenient alternative for explicit transaction demarcation is the use
|
||||
of provided control abstractions in the form of
|
||||
``Connection#transactional($func)`` and ``EntityManager#transactional($func)``.
|
||||
When used, these control abstractions ensure that you never forget to rollback
|
||||
the transaction, in addition to the obvious code reduction. An example that is
|
||||
functionally equivalent to the previously shown code looks as follows:
|
||||
A more convenient alternative for explicit transaction demarcation
|
||||
is the use of provided control abstractions in the form of
|
||||
``Connection#transactional($func)`` and
|
||||
``EntityManager#transactional($func)``. When used, these control
|
||||
abstractions ensure that you never forget to rollback the
|
||||
transaction or close the ``EntityManager``, apart from the obvious
|
||||
code reduction. An example that is functionally equivalent to the
|
||||
previously shown code looks as follows:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -104,20 +101,11 @@ functionally equivalent to the previously shown code looks as follows:
|
||||
$em->persist($user);
|
||||
});
|
||||
|
||||
.. warning::
|
||||
|
||||
For historical reasons, ``EntityManager#transactional($func)`` will return
|
||||
``true`` whenever the return value of ``$func`` is loosely false.
|
||||
Some examples of this include ``array()``, ``"0"``, ``""``, ``0``, and
|
||||
``null``.
|
||||
|
||||
The difference between ``Connection#transactional($func)`` and
|
||||
``EntityManager#transactional($func)`` is that the latter
|
||||
abstraction flushes the ``EntityManager`` prior to transaction
|
||||
commit and rolls back the transaction when an
|
||||
exception occurs.
|
||||
|
||||
.. _transactions-and-concurrency_exception-handling:
|
||||
commit and also closes the ``EntityManager`` properly when an
|
||||
exception occurs (in addition to rolling back the transaction).
|
||||
|
||||
Exception Handling
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
@@ -149,18 +137,14 @@ knowing that their state is potentially no longer accurate.
|
||||
If you intend to start another unit of work after an exception has
|
||||
occurred you should do that with a new ``EntityManager``.
|
||||
|
||||
.. _transactions-and-concurrency_locking-support:
|
||||
|
||||
Locking Support
|
||||
---------------
|
||||
|
||||
Doctrine ORM offers support for Pessimistic- and Optimistic-locking
|
||||
Doctrine 2 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.
|
||||
|
||||
.. _transactions-and-concurrency_optimistic-locking:
|
||||
|
||||
Optimistic Locking
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -187,68 +171,30 @@ has been modified by someone else already.
|
||||
You designate a version field in an entity as follows. In this
|
||||
example we'll use an integer.
|
||||
|
||||
.. configuration-block::
|
||||
.. code-block:: php
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/** @Version @Column(type="integer") */
|
||||
private $version;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="User">
|
||||
<field name="version" type="integer" version="true" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
fields:
|
||||
version:
|
||||
type: integer
|
||||
version: true
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/** @Version @Column(type="integer") */
|
||||
private $version;
|
||||
// ...
|
||||
}
|
||||
|
||||
Alternatively a datetime type can be used (which maps to a SQL
|
||||
timestamp or datetime):
|
||||
|
||||
.. configuration-block::
|
||||
.. code-block:: php
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/** @Version @Column(type="datetime") */
|
||||
private $version;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="User">
|
||||
<field name="version" type="datetime" version="true" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
fields:
|
||||
version:
|
||||
type: datetime
|
||||
version: true
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/** @Version @Column(type="datetime") */
|
||||
private $version;
|
||||
// ...
|
||||
}
|
||||
|
||||
Version numbers (not timestamps) should however be preferred as
|
||||
they can not potentially conflict in a highly concurrent
|
||||
@@ -362,12 +308,10 @@ And the change headline action (POST Request):
|
||||
|
||||
$post = $em->find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion);
|
||||
|
||||
.. _transactions-and-concurrency_pessimistic-locking:
|
||||
|
||||
Pessimistic Locking
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Doctrine ORM supports Pessimistic Locking at the database level. No
|
||||
Doctrine 2 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 +320,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 ORM will throw an
|
||||
Transaction Demarcation" described above. Doctrine 2 will throw an
|
||||
Exception if you attempt to acquire an pessimistic lock and no
|
||||
transaction is running.
|
||||
|
||||
Doctrine ORM currently supports two pessimistic lock modes:
|
||||
Doctrine 2 currently supports two pessimistic lock modes:
|
||||
|
||||
|
||||
- Pessimistic Write
|
||||
|
||||
@@ -15,10 +15,10 @@ Bidirectional Associations
|
||||
|
||||
The following rules apply to **bidirectional** associations:
|
||||
|
||||
- The inverse side has to have the ``mappedBy`` attribute of the OneToOne,
|
||||
- The inverse side has to use the ``mappedBy`` attribute of the OneToOne,
|
||||
OneToMany, or ManyToMany mapping declaration. The mappedBy
|
||||
attribute contains the name of the association-field on the owning side.
|
||||
- The owning side has to have the ``inversedBy`` attribute of the
|
||||
- The owning side has to use the ``inversedBy`` attribute of the
|
||||
OneToOne, ManyToOne, or ManyToMany mapping declaration.
|
||||
The inversedBy attribute contains the name of the association-field
|
||||
on the inverse-side.
|
||||
@@ -39,7 +39,7 @@ side of the association and these 2 references both represent the
|
||||
same association but can change independently of one another. Of
|
||||
course, in a correct application the semantics of the bidirectional
|
||||
association are properly maintained by the application developer
|
||||
(that's their responsibility). Doctrine needs to know which of these
|
||||
(that's his responsibility). Doctrine needs to know which of these
|
||||
two in-memory references is the one that should be persisted and
|
||||
which not. This is what the owning/inverse concept is mainly used
|
||||
for.
|
||||
|
||||
@@ -134,10 +134,6 @@ optimize the performance of the Flush Operation:
|
||||
explicit strategies of notifying the UnitOfWork what objects/properties
|
||||
changed.
|
||||
|
||||
.. note::
|
||||
|
||||
Flush only a single entity with ``$entityManager->flush($entity)`` is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8459>`_)
|
||||
|
||||
Query Internals
|
||||
---------------
|
||||
@@ -152,16 +148,16 @@ Hydration
|
||||
~~~~~~~~~
|
||||
|
||||
Responsible for creating a final result from a raw database statement and a
|
||||
result-set mapping object. The developer can choose which kind of result they
|
||||
wish to be hydrated. Default result-types include:
|
||||
result-set mapping object. The developer can choose which kind of result he
|
||||
wishes to be hydrated. Default result-types include:
|
||||
|
||||
- SQL to Entities
|
||||
- SQL to structured Arrays
|
||||
- SQL to simple scalar result arrays
|
||||
- SQL to a single result variable
|
||||
|
||||
Hydration to entities and arrays is one of the most complex parts of Doctrine
|
||||
algorithm-wise. It can build results with for example:
|
||||
Hydration to entities and arrays is one of most complex parts of Doctrine
|
||||
algorithm-wise. It can built results with for example:
|
||||
|
||||
- Single table selects
|
||||
- Joins with n:1 or 1:n cardinality, grouping belonging to the same parent.
|
||||
|
||||
@@ -2,25 +2,27 @@ Working with Associations
|
||||
=========================
|
||||
|
||||
Associations between entities are represented just like in regular
|
||||
object-oriented PHP code using references to other objects or
|
||||
collections of objects.
|
||||
object-oriented PHP, with references to other objects or
|
||||
collections of objects. When it comes to persistence, it is
|
||||
important to understand three main things:
|
||||
|
||||
Changes to associations in your code are not synchronized to the
|
||||
database directly, only when calling ``EntityManager#flush()``.
|
||||
|
||||
There are other concepts you should know about when working
|
||||
with associations in Doctrine:
|
||||
|
||||
- The :doc:`concept of owning and inverse sides <unitofwork-associations>`
|
||||
in bidirectional associations.
|
||||
- If an entity is removed from a collection, the association is
|
||||
removed, not the entity itself. A collection of entities always
|
||||
only represents the association to the containing entities, not the
|
||||
entity itself.
|
||||
- When a bidirectional association is updated, Doctrine only checks
|
||||
on one of both sides for these changes. This is called the :doc:`owning side <unitofwork-associations>`
|
||||
of the association.
|
||||
- A property with a reference to many entities has to be instances of the
|
||||
- Collection-valued :ref:`persistent fields <architecture_persistent_fields>` have to be instances of the
|
||||
``Doctrine\Common\Collections\Collection`` interface.
|
||||
|
||||
Changes to associations in your code are not synchronized to the
|
||||
database directly, but upon calling ``EntityManager#flush()``.
|
||||
|
||||
To describe all the concepts of working with associations we
|
||||
introduce a specific set of example entities that show all the
|
||||
different flavors of association management in Doctrine.
|
||||
|
||||
Association Example Entities
|
||||
----------------------------
|
||||
|
||||
@@ -42,7 +44,10 @@ information about its type and if it's the owning or inverse side.
|
||||
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
|
||||
*
|
||||
* @ManyToMany(targetEntity="Comment", inversedBy="userFavorites")
|
||||
* @JoinTable(name="user_favorite_comments")
|
||||
* @JoinTable(name="user_favorite_comments",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="favorite_comment_id", referencedColumnName="id")}
|
||||
* )
|
||||
*/
|
||||
private $favorites;
|
||||
|
||||
@@ -50,7 +55,10 @@ information about its type and if it's the owning or inverse side.
|
||||
* Unidirectional - Many users have marked many comments as read
|
||||
*
|
||||
* @ManyToMany(targetEntity="Comment")
|
||||
* @JoinTable(name="user_read_comments")
|
||||
* @JoinTable(name="user_read_comments",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="comment_id", referencedColumnName="id")}
|
||||
* )
|
||||
*/
|
||||
private $commentsRead;
|
||||
|
||||
@@ -238,14 +246,14 @@ the database permanently.
|
||||
|
||||
Notice how both sides of the bidirectional association are always
|
||||
updated. Unidirectional associations are consequently simpler to
|
||||
handle.
|
||||
|
||||
Also note that if you use type-hinting in your methods, you will
|
||||
have to specify a nullable type, i.e. ``setAddress(?Address $address)``,
|
||||
otherwise ``setAddress(null)`` will fail to remove the association.
|
||||
Another way to deal with this is to provide a special method, like
|
||||
``removeAddress()``. This can also provide better encapsulation as
|
||||
it hides the internal meaning of not having an address.
|
||||
handle. Also note that if you use type-hinting in your methods, i.e.
|
||||
``setAddress(Address $address)``, PHP will only allow null
|
||||
values if ``null`` is set as default value. Otherwise
|
||||
setAddress(null) will fail for removing the association. If you
|
||||
insist on type-hinting a typical way to deal with this is to
|
||||
provide a special method, like ``removeAddress()``. This can also
|
||||
provide better encapsulation as it hides the internal meaning of
|
||||
not having an address.
|
||||
|
||||
When working with collections, keep in mind that a Collection is
|
||||
essentially an ordered map (just like a PHP array). That is why the
|
||||
@@ -396,25 +404,53 @@ There are two approaches to handle this problem in your code:
|
||||
|
||||
1. Ignore updating the inverse side of bidirectional collections,
|
||||
BUT never read from them in requests that changed their state. In
|
||||
the next request Doctrine hydrates the consistent collection state
|
||||
the next Request Doctrine hydrates the consistent collection state
|
||||
again.
|
||||
2. Always keep the bidirectional collections in sync through
|
||||
association management methods. Reads of the Collections directly
|
||||
after changes are consistent then.
|
||||
|
||||
.. _transitive-persistence:
|
||||
|
||||
Transitive persistence / Cascade 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``.
|
||||
Persisting, removing, detaching and merging individual entities can
|
||||
become pretty cumbersome, especially when a highly interweaved object graph
|
||||
is involved. Therefore Doctrine 2 provides a
|
||||
mechanism for transitive persistence through cascading of these
|
||||
operations. Each association to another entity or a collection of
|
||||
entities can be configured to automatically cascade certain
|
||||
operations. By default, no operations are cascaded.
|
||||
|
||||
The main use case for ``cascade: persist`` is to avoid "exposing" associated entities to your PHP application.
|
||||
Continuing with the User-Comment example of this chapter, this is how the creation of a new user and a new
|
||||
comment might look like in your controller (without ``cascade: persist``):
|
||||
The following cascade options exist:
|
||||
|
||||
|
||||
- persist : Cascades persist operations to the associated
|
||||
entities.
|
||||
- remove : Cascades remove operations to the associated entities.
|
||||
- merge : Cascades merge operations to the associated entities.
|
||||
- detach : Cascades detach operations to the associated entities.
|
||||
- all : Cascades persist, remove, merge and detach operations to
|
||||
associated entities.
|
||||
|
||||
.. note::
|
||||
|
||||
Cascade operations are performed in memory. That means collections and related entities
|
||||
are fetched into memory, even if they are still marked as lazy when
|
||||
the cascade operation is about to be performed. However this approach allows
|
||||
entity lifecycle events to be performed for each of these operations.
|
||||
|
||||
However, pulling objects graph into memory on cascade can cause considerable performance
|
||||
overhead, especially when cascading collections are large. Makes sure
|
||||
to weigh the benefits and downsides of each cascade operation that you define.
|
||||
|
||||
To rely on the database level cascade operations for the delete operation instead, you can
|
||||
configure each join column with the **onDelete** option. See the respective
|
||||
mapping driver chapters for more information.
|
||||
|
||||
The following example is an extension to the User-Comment example
|
||||
of this chapter. Suppose in our application a user is created
|
||||
whenever he writes his first comment. In this case we would use the
|
||||
following code:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -424,39 +460,36 @@ comment might look like in your controller (without ``cascade: persist``):
|
||||
$user->addComment($myFirstComment);
|
||||
|
||||
$em->persist($user);
|
||||
$em->persist($myFirstComment); // required, if `cascade: persist` is not set
|
||||
$em->persist($myFirstComment);
|
||||
$em->flush();
|
||||
|
||||
Note that the Comment entity is instantiated right here in the controller.
|
||||
To avoid this, ``cascade: persist`` allows you to "hide" the Comment entity from the controller,
|
||||
only accessing it through the User entity:
|
||||
Even if you *persist* a new User that contains our new Comment this
|
||||
code would fail if you removed the call to
|
||||
``EntityManager#persist($myFirstComment)``. Doctrine 2 does not
|
||||
cascade the persist operation to all nested entities that are new
|
||||
as well.
|
||||
|
||||
More complicated is the deletion of all of a user's comments when he is
|
||||
removed from the system:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// User entity
|
||||
class User
|
||||
{
|
||||
private $id;
|
||||
private $comments;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->id = User::new();
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function comment(string $text, DateTimeInterface $time) : void
|
||||
{
|
||||
$newComment = Comment::create($text, $time);
|
||||
$newComment->setUser($this);
|
||||
$this->comments->add($newComment);
|
||||
}
|
||||
|
||||
// ...
|
||||
$user = $em->find('User', $deleteUserId);
|
||||
|
||||
foreach ($user->getAuthoredComments() AS $comment) {
|
||||
$em->remove($comment);
|
||||
}
|
||||
$em->remove($user);
|
||||
$em->flush();
|
||||
|
||||
If you then set up the cascading to the ``User#commentsAuthored`` property...
|
||||
Without the loop over all the authored comments Doctrine would use
|
||||
an UPDATE statement only to set the foreign key to NULL and only
|
||||
the User would be deleted from the database during the
|
||||
flush()-Operation.
|
||||
|
||||
To have Doctrine handle both cases automatically we can change the
|
||||
``User#commentsAuthored`` property to cascade both the "persist"
|
||||
and the "remove" operation.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -473,51 +506,10 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
|
||||
//...
|
||||
}
|
||||
|
||||
...you can now create a user and an associated comment like this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$user = new User();
|
||||
$user->comment('Lorem ipsum', new DateTime());
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
|
||||
.. note::
|
||||
|
||||
The idea of ``cascade: persist`` is not to save you any lines of code in the controller.
|
||||
If you instantiate the comment object in the controller (i.e. don't set up the user entity as shown above),
|
||||
even with ``cascade: persist`` you still have to call ``$myFirstComment->setUser($user);``.
|
||||
|
||||
Thanks to ``cascade: remove``, you can easily delete a user and all linked comments without having to loop through them:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$user = $em->find('User', $deleteUserId);
|
||||
|
||||
$em->remove($user);
|
||||
$em->flush();
|
||||
|
||||
.. note::
|
||||
|
||||
Cascade operations are performed in memory. That means collections and related entities
|
||||
are fetched into memory (even if they are marked as lazy) when
|
||||
the cascade operation is about to be performed. This approach allows
|
||||
entity lifecycle events to be performed for each of these operations.
|
||||
|
||||
However, pulling object graphs into memory on cascade can cause considerable performance
|
||||
overhead, especially when the cascaded collections are large. Make sure
|
||||
to weigh the benefits and downsides of each cascade operation that you define.
|
||||
|
||||
To rely on the database level cascade operations for the delete operation instead, you can
|
||||
configure each join column with :doc:`the onDelete option <working-with-objects>`.
|
||||
|
||||
Even though automatic cascading is convenient, it should be used
|
||||
with care. Do not blindly apply ``cascade=all`` to all associations as
|
||||
Even though automatic cascading is convenient it should be used
|
||||
with care. Do not blindly apply cascade=all to all associations as
|
||||
it will unnecessarily degrade the performance of your application.
|
||||
For each cascade operation that gets activated, Doctrine also
|
||||
For each cascade operation that gets activated Doctrine also
|
||||
applies that operation to the association, be it single or
|
||||
collection valued.
|
||||
|
||||
@@ -525,20 +517,21 @@ Persistence by Reachability: Cascade Persist
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are additional semantics that apply to the Cascade Persist
|
||||
operation. During each ``flush()`` operation Doctrine detects if there
|
||||
operation. During each flush() operation Doctrine detects if there
|
||||
are new entities in any collection and three possible cases can
|
||||
happen:
|
||||
|
||||
|
||||
1. New entities in a collection marked as ``cascade: persist`` will be
|
||||
1. New entities in a collection marked as cascade persist will be
|
||||
directly persisted by Doctrine.
|
||||
2. New entities in a collection not marked as ``cascade: persist`` will
|
||||
produce an Exception and rollback the ``flush()`` operation.
|
||||
2. New entities in a collection not marked as cascade persist will
|
||||
produce an Exception and rollback the flush() operation.
|
||||
3. Collections without new entities are skipped.
|
||||
|
||||
This concept is called Persistence by Reachability: New entities
|
||||
that are found on already managed entities are automatically
|
||||
persisted as long as the association is defined as ``cascade: persist``.
|
||||
persisted as long as the association is defined as cascade
|
||||
persist.
|
||||
|
||||
Orphan Removal
|
||||
--------------
|
||||
@@ -616,11 +609,11 @@ address reference. When flush is called not only are the references removed
|
||||
but both the old standing data and the one address entity are also deleted
|
||||
from the database.
|
||||
|
||||
.. _filtering-collections:
|
||||
|
||||
Filtering Collections
|
||||
---------------------
|
||||
|
||||
.. filtering-collections:
|
||||
|
||||
Collections have a filtering API that allows to slice parts of data from
|
||||
a collection. If the collection has not been loaded from the database yet,
|
||||
the filtering API can work on the SQL level to make optimized access to
|
||||
@@ -637,7 +630,7 @@ large collections.
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->where(Criteria::expr()->eq("birthday", "1982-02-17"))
|
||||
->orderBy(array("username" => Criteria::ASC))
|
||||
->orderBy(array("username" => "ASC"))
|
||||
->setFirstResult(0)
|
||||
->setMaxResults(20)
|
||||
;
|
||||
@@ -715,14 +708,5 @@ methods:
|
||||
* ``isNull($field)``
|
||||
* ``in($field, array $values)``
|
||||
* ``notIn($field, array $values)``
|
||||
* ``contains($field, $value)``
|
||||
* ``memberOf($value, $field)``
|
||||
* ``startsWith($field, $value)``
|
||||
* ``endsWith($field, $value)``
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
There is a limitation on the compatibility of Criteria comparisons.
|
||||
You have to use scalar values only as the value in a comparison or
|
||||
the behaviour between different backends is not the same.
|
||||
|
||||
@@ -25,13 +25,6 @@ Work that have not yet been persisted are lost.
|
||||
Not calling ``EntityManager#flush()`` will lead to all changes
|
||||
during that request being lost.
|
||||
|
||||
.. note::
|
||||
|
||||
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,
|
||||
any code put in your get/set methods won't be implicitly taken into account.
|
||||
|
||||
Entities and the Identity Map
|
||||
-----------------------------
|
||||
@@ -48,12 +41,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 ORM realizes this and will only
|
||||
but modified in between. Doctrine 2 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,29 +93,28 @@ 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()
|
||||
{
|
||||
|
||||
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
|
||||
a single SELECT statement against the articles table in the database.
|
||||
a single SELECT statement against the user table in the database.
|
||||
You can still access the associated properties author and comments
|
||||
and the associated objects they contain.
|
||||
|
||||
@@ -139,22 +131,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) {
|
||||
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 +166,7 @@ methods along the lines of the ``getName()`` method shown below:
|
||||
{
|
||||
// lazy loading code
|
||||
}
|
||||
|
||||
|
||||
public function getName()
|
||||
{
|
||||
$this->_load();
|
||||
@@ -245,17 +237,11 @@ as follows:
|
||||
persist operation. However, the persist operation is cascaded to
|
||||
entities referenced by X, if the relationships from X to these
|
||||
other entities are mapped with cascade=PERSIST or cascade=ALL (see
|
||||
":ref:`transitive-persistence`").
|
||||
"Transitive Persistence").
|
||||
- If X is a removed entity, it becomes managed.
|
||||
- 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
|
||||
-----------------
|
||||
|
||||
@@ -275,7 +261,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:
|
||||
|
||||
@@ -292,12 +278,12 @@ as follows:
|
||||
- If X is a new entity, it is ignored by the remove operation.
|
||||
However, the remove operation is cascaded to entities referenced by
|
||||
X, if the relationship from X to these other entities is mapped
|
||||
with cascade=REMOVE or cascade=ALL (see ":ref:`transitive-persistence`").
|
||||
with cascade=REMOVE or cascade=ALL (see "Transitive Persistence").
|
||||
- If X is a managed entity, the remove operation causes it to
|
||||
become removed. The remove operation is cascaded to entities
|
||||
referenced by X, if the relationships from X to these other
|
||||
entities is mapped with cascade=REMOVE or cascade=ALL (see
|
||||
":ref:`transitive-persistence`").
|
||||
"Transitive Persistence").
|
||||
- If X is a detached entity, an InvalidArgumentException will be
|
||||
thrown.
|
||||
- If X is a removed entity, it is ignored by the remove operation.
|
||||
@@ -318,10 +304,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 ORM
|
||||
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2
|
||||
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
|
||||
@@ -336,13 +322,6 @@ 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
|
||||
------------------
|
||||
|
||||
@@ -370,14 +349,14 @@ as follows:
|
||||
become detached. The detach operation is cascaded to entities
|
||||
referenced by X, if the relationships from X to these other
|
||||
entities is mapped with cascade=DETACH or cascade=ALL (see
|
||||
":ref:`transitive-persistence`"). Entities which previously referenced X
|
||||
"Transitive Persistence"). Entities which previously referenced X
|
||||
will continue to reference X.
|
||||
- If X is a new or detached entity, it is ignored by the detach
|
||||
operation.
|
||||
- If X is a removed entity, the detach operation is cascaded to
|
||||
entities referenced by X, if the relationships from X to these
|
||||
other entities is mapped with cascade=DETACH or cascade=ALL (see
|
||||
":ref:`transitive-persistence`"). Entities which previously referenced X
|
||||
"Transitive Persistence"). Entities which previously referenced X
|
||||
will continue to reference X.
|
||||
|
||||
There are several situations in which an entity is detached
|
||||
@@ -436,7 +415,8 @@ as follows:
|
||||
- If X is a managed entity, it is ignored by the merge operation,
|
||||
however, the merge operation is cascaded to entities referenced by
|
||||
relationships from X if these relationships have been mapped with
|
||||
the cascade element value MERGE or ALL (see ":ref:`transitive-persistence`").
|
||||
the cascade element value MERGE or ALL (see "Transitive
|
||||
Persistence").
|
||||
- For all entities Y referenced by relationships from X having the
|
||||
cascade element value MERGE or ALL, Y is merged recursively as Y'.
|
||||
For all such Y referenced by X, X' is set to reference Y'. (Note
|
||||
@@ -653,7 +633,7 @@ just created via the "new" operator).
|
||||
Querying
|
||||
--------
|
||||
|
||||
Doctrine ORM provides the following ways, in increasing level of
|
||||
Doctrine 2 provides the following ways, in increasing level of
|
||||
power and flexibility, to query for persistent objects. You should
|
||||
always start with the simplest one that suits your needs.
|
||||
|
||||
@@ -700,13 +680,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'));
|
||||
|
||||
@@ -718,6 +698,8 @@ You can also load by owning side associations through the repository:
|
||||
$number = $em->find('MyProject\Domain\Phonenumber', 1234);
|
||||
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId()));
|
||||
|
||||
Be careful that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself.
|
||||
|
||||
The ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -742,26 +724,21 @@ 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');
|
||||
|
||||
Additionally, you can just count the result of the provided conditions when you don't really need the data:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Check there is no user with nickname
|
||||
$availableNickname = 0 === $em->getRepository('MyProject\Domain\User')->count(['nickname' => 'nonexistent']);
|
||||
|
||||
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.
|
||||
|
||||
See section `Filtering collections` of chapter :doc:`Working with Associations <working-with-associations>`
|
||||
See the :ref:`Working with Associations: Filtering collections
|
||||
<filtering-collections>`.
|
||||
|
||||
By Eager Loading
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -798,7 +775,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();
|
||||
@@ -811,9 +788,7 @@ DQL and its syntax as well as the Doctrine class can be found in
|
||||
:doc:`the dedicated chapter <dql-doctrine-query-language>`.
|
||||
For programmatically building up queries based on conditions that
|
||||
are only known at runtime, Doctrine provides the special
|
||||
``Doctrine\ORM\QueryBuilder`` class. While this a powerful tool,
|
||||
it also brings more complexity to your code compared to plain DQL,
|
||||
so you should only use it when you need it. More information on
|
||||
``Doctrine\ORM\QueryBuilder`` class. More information on
|
||||
constructing queries with a QueryBuilder can be found
|
||||
:doc:`in Query Builder chapter <query-builder>`.
|
||||
|
||||
@@ -843,18 +818,17 @@ in a central location.
|
||||
|
||||
<?php
|
||||
namespace MyDomain\Model;
|
||||
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
|
||||
* @entity(repositoryClass="MyDomain\Model\UserRepository")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
class UserRepository extends EntityRepository
|
||||
{
|
||||
public function getAllAdminUsers()
|
||||
@@ -870,7 +844,7 @@ You can access your repository now by calling:
|
||||
|
||||
<?php
|
||||
// $em instanceof EntityManager
|
||||
|
||||
|
||||
$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,10 @@ form of XML documents.
|
||||
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>`_.
|
||||
The most convenient way to work with
|
||||
`http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd <http://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
|
||||
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
|
||||
@@ -19,10 +21,10 @@ setup for the latest code in trunk.
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
||||
|
||||
...
|
||||
|
||||
|
||||
</doctrine-mapping>
|
||||
|
||||
The XML mapping document of a class is loaded on-demand the first
|
||||
@@ -42,6 +44,8 @@ In order to work, this requires certain conventions:
|
||||
convention and you are not forced to do this. You can change the
|
||||
file extension easily enough.
|
||||
|
||||
-
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -60,16 +64,6 @@ of the constructor, like this:
|
||||
$driver = new \Doctrine\ORM\Mapping\Driver\XmlDriver(array('/path/to/files1', '/path/to/files2'));
|
||||
$config->setMetadataDriverImpl($driver);
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that Doctrine ORM does not modify any settings for ``libxml``,
|
||||
therefore, external XML entities may or may not be enabled or
|
||||
configured correctly.
|
||||
XML mappings are not XXE/XEE attack vectors since they are not
|
||||
related with user input, but it is recommended that you do not
|
||||
use external XML entities in your mapping files to avoid running
|
||||
into unexpected behaviour.
|
||||
|
||||
Simplified XML Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -86,8 +80,8 @@ Configuration of this client works a little bit different:
|
||||
|
||||
<?php
|
||||
$namespaces = array(
|
||||
'/path/to/files1' => 'MyProject\Entities',
|
||||
'/path/to/files2' => 'OtherProject\Entities'
|
||||
'MyProject\Entities' => '/path/to/files1',
|
||||
'OtherProject\Entities' => '/path/to/files2'
|
||||
);
|
||||
$driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver($namespaces);
|
||||
$driver->setGlobalBasename('global'); // global.orm.xml
|
||||
@@ -105,38 +99,38 @@ of several common elements:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\ORM\Mapping\User" table="cms_users">
|
||||
|
||||
|
||||
<indexes>
|
||||
<index name="name_idx" columns="name"/>
|
||||
<index columns="user_email"/>
|
||||
</indexes>
|
||||
|
||||
|
||||
<unique-constraints>
|
||||
<unique-constraint columns="name,user_email" name="search_idx" />
|
||||
</unique-constraints>
|
||||
|
||||
|
||||
<lifecycle-callbacks>
|
||||
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
|
||||
<lifecycle-callback type="prePersist" method="doOtherStuffOnPrePersistToo"/>
|
||||
<lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
|
||||
</lifecycle-callbacks>
|
||||
|
||||
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
<sequence-generator sequence-name="tablename_seq" allocation-size="100" initial-value="1" />
|
||||
</id>
|
||||
|
||||
|
||||
<field name="name" column="name" type="string" length="50" nullable="true" unique="true" />
|
||||
<field name="email" column="user_email" type="string" column-definition="CHAR(32) NOT NULL" />
|
||||
|
||||
|
||||
<one-to-one field="address" target-entity="Address" inversed-by="user">
|
||||
<cascade><cascade-remove /></cascade>
|
||||
<join-column name="address_id" referenced-column-name="id" on-delete="CASCADE" on-update="CASCADE"/>
|
||||
</one-to-one>
|
||||
|
||||
|
||||
<one-to-many field="phonenumbers" target-entity="Phonenumber" mapped-by="user">
|
||||
<cascade>
|
||||
<cascade-persist/>
|
||||
@@ -145,7 +139,7 @@ of several common elements:
|
||||
<order-by-field name="number" direction="ASC" />
|
||||
</order-by>
|
||||
</one-to-many>
|
||||
|
||||
|
||||
<many-to-many field="groups" target-entity="Group">
|
||||
<cascade>
|
||||
<cascade-all/>
|
||||
@@ -159,9 +153,9 @@ of several common elements:
|
||||
</inverse-join-columns>
|
||||
</join-table>
|
||||
</many-to-many>
|
||||
|
||||
|
||||
</entity>
|
||||
|
||||
|
||||
</doctrine-mapping>
|
||||
|
||||
Be aware that class-names specified in the XML files should be
|
||||
@@ -185,7 +179,7 @@ specified as the ``<entity />`` element as a direct child of the
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\User" table="cms_users" schema="schema_name" repository-class="MyProject\UserRepository">
|
||||
<entity name="MyProject\User" table="cms_users" repository-class="MyProject\UserRepository">
|
||||
<!-- definition here -->
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -206,10 +200,9 @@ Optional attributes:
|
||||
- **inheritance-type** - The type of inheritance, defaults to none. A
|
||||
more detailed description follows in the
|
||||
*Defining Inheritance Mappings* section.
|
||||
- **read-only** - Specifies that this entity is marked as read only and not
|
||||
- **read-only** - (>= 2.1) 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** - The schema the table lies in, for platforms that support schemas
|
||||
|
||||
Defining Fields
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -223,18 +216,12 @@ entity. For the ID mapping you have to use the ``<id />`` element.
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="MyProject\User">
|
||||
|
||||
|
||||
<field name="name" type="string" length="50" />
|
||||
<field name="username" type="string" unique="true" />
|
||||
<field name="age" type="integer" nullable="true" />
|
||||
<field name="isActive" column="is_active" type="boolean" />
|
||||
<field name="weight" type="decimal" scale="5" precision="2" />
|
||||
<field name="login_count" type="integer" nullable="false">
|
||||
<options>
|
||||
<option name="comment">The number of times the user has logged in.</option>
|
||||
<option name="default">0</option>
|
||||
</options>
|
||||
</field>
|
||||
</entity>
|
||||
|
||||
Required attributes:
|
||||
@@ -260,38 +247,18 @@ Optional attributes:
|
||||
works on fields with type integer or datetime.
|
||||
- scale - Scale of a decimal type.
|
||||
- precision - Precision of a decimal type.
|
||||
- options - Array of additional options:
|
||||
|
||||
- default - The default value to set for the column if no value
|
||||
is supplied.
|
||||
- unsigned - Boolean value to determine if the column should
|
||||
be capable of representing only non-negative integers
|
||||
(applies only for integer column and might not be supported by
|
||||
all vendors).
|
||||
- fixed - Boolean value to determine if the specified length of
|
||||
a string column should be fixed or varying (applies only for
|
||||
string/binary column and might not be supported by all vendors).
|
||||
- comment - The comment of the column in the schema (might not
|
||||
be supported by all vendors).
|
||||
- customSchemaOptions - Array of additional schema options
|
||||
which are mostly vendor specific.
|
||||
- column-definition - Optional alternative SQL representation for
|
||||
this column. This definition begin after the field-name and has to
|
||||
specify the complete column definition. Using this feature will
|
||||
turn this field dirty for Schema-Tool update commands at all
|
||||
times.
|
||||
|
||||
.. note::
|
||||
|
||||
For more detailed information on each attribute, please refer to
|
||||
the DBAL ``Schema-Representation`` documentation.
|
||||
|
||||
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 ORM. The Id
|
||||
surrogate keys are recommended for use with Doctrine 2. The Id
|
||||
field allows to define properties of the identifier and allows a
|
||||
subset of the ``<field />`` element attributes:
|
||||
|
||||
@@ -319,12 +286,12 @@ Using the simplified definition above Doctrine will use no
|
||||
identifier strategy for this entity. That means you have to
|
||||
manually set the identifier before calling
|
||||
``EntityManager#persist($entity)``. This is the so called
|
||||
``NONE`` strategy.
|
||||
``ASSIGNED`` strategy.
|
||||
|
||||
If you want to switch the identifier generation strategy you have
|
||||
to nest a ``<generator />`` element inside the id-element. This of
|
||||
course only works for surrogate keys. For composite keys you always
|
||||
have to use the ``NONE`` strategy.
|
||||
have to use the ``ASSIGNED`` strategy.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
@@ -448,7 +415,7 @@ using the ``<lifecycle-callbacks />`` element:
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="Doctrine\Tests\ORM\Mapping\User" table="cms_users">
|
||||
|
||||
|
||||
<lifecycle-callbacks>
|
||||
<lifecycle-callback type="prePersist" method="onPrePersist" />
|
||||
</lifecycle-callbacks>
|
||||
@@ -741,12 +708,12 @@ table you can use the ``<indexes />`` and
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="Doctrine\Tests\ORM\Mapping\User" table="cms_users">
|
||||
|
||||
|
||||
<indexes>
|
||||
<index name="name_idx" columns="name"/>
|
||||
<index columns="user_email"/>
|
||||
</indexes>
|
||||
|
||||
|
||||
<unique-constraints>
|
||||
<unique-constraint columns="name,user_email" name="search_idx" />
|
||||
</unique-constraints>
|
||||
@@ -766,7 +733,7 @@ entity relationship. You can define this in XML with the "association-key" attri
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Application\Model\ArticleAttribute">
|
||||
<id name="article" association-key="true" />
|
||||
@@ -775,6 +742,6 @@ entity relationship. You can define this in XML with the "association-key" attri
|
||||
<field name="value" type="string" />
|
||||
|
||||
<many-to-one field="article" target-entity="Article" inversed-by="attributes" />
|
||||
</entity>
|
||||
<entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
YAML Mapping
|
||||
============
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
The YAML mapping driver enables you to provide the ORM metadata in
|
||||
form of YAML documents.
|
||||
|
||||
@@ -76,10 +72,7 @@ of several common elements:
|
||||
# Doctrine.Tests.ORM.Mapping.User.dcm.yml
|
||||
Doctrine\Tests\ORM\Mapping\User:
|
||||
type: entity
|
||||
repositoryClass: Doctrine\Tests\ORM\Mapping\UserRepository
|
||||
table: cms_users
|
||||
schema: schema_name # The schema the table lies in, for platforms that support schemas (Optional, >= 2.5)
|
||||
readOnly: true
|
||||
indexes:
|
||||
name_index:
|
||||
columns: [ name ]
|
||||
@@ -92,28 +85,12 @@ of several common elements:
|
||||
name:
|
||||
type: string
|
||||
length: 50
|
||||
email:
|
||||
type: string
|
||||
length: 32
|
||||
column: user_email
|
||||
unique: true
|
||||
options:
|
||||
fixed: true
|
||||
comment: User's email address
|
||||
loginCount:
|
||||
type: integer
|
||||
column: login_count
|
||||
nullable: false
|
||||
options:
|
||||
unsigned: true
|
||||
default: 0
|
||||
oneToOne:
|
||||
address:
|
||||
targetEntity: Address
|
||||
joinColumn:
|
||||
name: address_id
|
||||
referencedColumnName: id
|
||||
onDelete: CASCADE
|
||||
oneToMany:
|
||||
phonenumbers:
|
||||
targetEntity: Phonenumber
|
||||
@@ -137,22 +114,4 @@ of several common elements:
|
||||
Be aware that class-names specified in the YAML files should be
|
||||
fully qualified.
|
||||
|
||||
Reference
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Unique Constraints
|
||||
------------------
|
||||
|
||||
It is possible to define unique constraints by the following declaration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# ECommerceProduct.orm.yml
|
||||
ECommerceProduct:
|
||||
type: entity
|
||||
fields:
|
||||
# definition of some fields
|
||||
uniqueConstraints:
|
||||
search_idx:
|
||||
columns: [ name, email ]
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
.. toc::
|
||||
|
||||
.. tocheader:: Tutorials
|
||||
|
||||
.. toctree::
|
||||
:depth: 3
|
||||
|
||||
tutorials/getting-started
|
||||
tutorials/getting-started-database
|
||||
tutorials/getting-started-models
|
||||
tutorials/working-with-indexed-associations
|
||||
tutorials/extra-lazy-associations
|
||||
tutorials/composite-primary-keys
|
||||
tutorials/ordered-associations
|
||||
tutorials/override-field-association-mappings-in-subclasses
|
||||
tutorials/pagination
|
||||
tutorials/embeddables
|
||||
|
||||
.. toc::
|
||||
|
||||
.. tocheader:: Reference
|
||||
|
||||
.. toctree::
|
||||
:depth: 3
|
||||
|
||||
reference/architecture
|
||||
reference/configuration
|
||||
reference/faq
|
||||
reference/basic-mapping
|
||||
reference/association-mapping
|
||||
reference/inheritance-mapping
|
||||
reference/working-with-objects
|
||||
reference/working-with-associations
|
||||
reference/events
|
||||
reference/unitofwork
|
||||
reference/unitofwork-associations
|
||||
reference/transactions-and-concurrency
|
||||
reference/batch-processing
|
||||
reference/dql-doctrine-query-language
|
||||
reference/query-builder
|
||||
reference/native-sql
|
||||
reference/change-tracking-policies
|
||||
reference/partial-objects
|
||||
reference/xml-mapping
|
||||
reference/yaml-mapping
|
||||
reference/annotations-reference
|
||||
reference/php-mapping
|
||||
reference/caching
|
||||
reference/improving-performance
|
||||
reference/tools
|
||||
reference/metadata-drivers
|
||||
reference/best-practices
|
||||
reference/limitations-and-known-issues
|
||||
tutorials/pagination
|
||||
reference/filters
|
||||
reference/namingstrategy
|
||||
reference/advanced-configuration
|
||||
reference/second-level-cache
|
||||
reference/security
|
||||
|
||||
.. toc::
|
||||
|
||||
.. tocheader:: Cookbook
|
||||
|
||||
.. toctree::
|
||||
:depth: 3
|
||||
|
||||
cookbook/aggregate-fields
|
||||
cookbook/custom-mapping-types
|
||||
cookbook/decorator-pattern
|
||||
cookbook/dql-custom-walkers
|
||||
cookbook/dql-user-defined-functions
|
||||
cookbook/implementing-arrayaccess-for-domain-objects
|
||||
cookbook/implementing-the-notify-changetracking-policy
|
||||
cookbook/implementing-wakeup-or-clone
|
||||
cookbook/resolve-target-entity-listener
|
||||
cookbook/sql-table-prefixes
|
||||
cookbook/strategy-cookbook-introduction
|
||||
cookbook/validation-of-entities
|
||||
cookbook/working-with-datetime
|
||||
cookbook/mysql-enums
|
||||
cookbook/advanced-field-value-conversion-using-custom-mapping-types
|
||||
cookbook/entities-in-session
|
||||
+6
-8
@@ -16,7 +16,6 @@ Tutorials
|
||||
tutorials/ordered-associations
|
||||
tutorials/override-field-association-mappings-in-subclasses
|
||||
tutorials/pagination.rst
|
||||
tutorials/embeddables.rst
|
||||
|
||||
Reference Guide
|
||||
---------------
|
||||
@@ -26,6 +25,7 @@ Reference Guide
|
||||
:numbered:
|
||||
|
||||
reference/architecture
|
||||
reference/installation
|
||||
reference/configuration.rst
|
||||
reference/faq
|
||||
reference/basic-mapping
|
||||
@@ -53,13 +53,10 @@ Reference Guide
|
||||
reference/metadata-drivers
|
||||
reference/best-practices
|
||||
reference/limitations-and-known-issues
|
||||
tutorials/pagination
|
||||
reference/filters
|
||||
reference/namingstrategy
|
||||
reference/advanced-configuration
|
||||
reference/second-level-cache
|
||||
reference/security
|
||||
|
||||
reference/filters.rst
|
||||
reference/namingstrategy.rst
|
||||
reference/advanced-configuration.rst
|
||||
|
||||
|
||||
Cookbook
|
||||
--------
|
||||
@@ -75,6 +72,7 @@ Cookbook
|
||||
cookbook/implementing-arrayaccess-for-domain-objects
|
||||
cookbook/implementing-the-notify-changetracking-policy
|
||||
cookbook/implementing-wakeup-or-clone
|
||||
cookbook/integrating-with-codeigniter
|
||||
cookbook/resolve-target-entity-listener
|
||||
cookbook/sql-table-prefixes
|
||||
cookbook/strategy-cookbook-introduction
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
Composite and Foreign Keys as Primary Key
|
||||
=========================================
|
||||
|
||||
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
|
||||
.. 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
|
||||
primary keys are supported.
|
||||
|
||||
This tutorial shows how the semantics of composite primary keys work and how they map to the database.
|
||||
@@ -11,13 +13,13 @@ This tutorial shows how the semantics of composite primary keys work and how the
|
||||
General Considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Every entity with a composite key cannot use an id generator other than "NONE". That means
|
||||
Every entity with a composite key cannot use an id generator other than "ASSIGNED". That means
|
||||
the ID fields have to have their values set before you call ``EntityManager#persist($entity)``.
|
||||
|
||||
Primitive Types only
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can have composite keys as long as they only consist of the primitive types
|
||||
Even in version 2.0 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:
|
||||
|
||||
@@ -36,7 +38,7 @@ and year of production as primary keys:
|
||||
/** @Id @Column(type="string") */
|
||||
private $name;
|
||||
/** @Id @Column(type="integer") */
|
||||
private $year;
|
||||
private $year
|
||||
|
||||
public function __construct($name, $year)
|
||||
{
|
||||
@@ -61,7 +63,7 @@ and year of production as primary keys:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="VehicleCatalogue\Model\Car">
|
||||
<id field="name" type="string" />
|
||||
@@ -118,6 +120,10 @@ 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.
|
||||
|
||||
@@ -197,7 +203,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Application\Model\ArticleAttribute">
|
||||
<id name="article" association-key="true" />
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
Separating Concerns using Embeddables
|
||||
-------------------------------------
|
||||
|
||||
Embeddables are classes which are not entities themselves, but are embedded
|
||||
in entities and can also be queried in DQL. You'll mostly want to use them
|
||||
to reduce duplication or separating concerns. Value objects such as date range
|
||||
or address are the primary use case for this feature.
|
||||
|
||||
.. note::
|
||||
|
||||
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
|
||||
the ``User`` class. We will model the ``Address`` class as an embeddable
|
||||
instead of simply adding the respective columns to the ``User`` class.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address") */
|
||||
private $address;
|
||||
}
|
||||
|
||||
/** @Embeddable */
|
||||
class Address
|
||||
{
|
||||
/** @Column(type = "string") */
|
||||
private $street;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private $postalCode;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private $city;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private $country;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" />
|
||||
</entity>
|
||||
|
||||
<embeddable name="Address">
|
||||
<field name="street" type="string" />
|
||||
<field name="postalCode" type="string" />
|
||||
<field name="city" type="string" />
|
||||
<field name="country" type="string" />
|
||||
</embeddable>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
|
||||
Address:
|
||||
type: embeddable
|
||||
fields:
|
||||
street: { type: string }
|
||||
postalCode: { type: string }
|
||||
city: { type: string }
|
||||
country: { type: string }
|
||||
|
||||
In terms of your database schema, Doctrine will automatically inline all
|
||||
columns from the ``Address`` class into the table of the ``User`` class,
|
||||
just as if you had declared them directly there.
|
||||
|
||||
Initializing embeddables
|
||||
------------------------
|
||||
|
||||
In case all fields in the embeddable are ``nullable``, you might want
|
||||
to initialize the embeddable, to avoid getting a null value instead of
|
||||
the embedded object.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->address = new Address();
|
||||
}
|
||||
|
||||
Column Prefixing
|
||||
----------------
|
||||
|
||||
By default, Doctrine names your columns by prefixing them, using the value
|
||||
object name.
|
||||
|
||||
Following the example above, your columns would be named as ``address_street``,
|
||||
``address_postalCode``...
|
||||
|
||||
You can change this behaviour to meet your needs by changing the
|
||||
``columnPrefix`` attribute in the ``@Embedded`` notation.
|
||||
|
||||
The following example shows you how to set your prefix to ``myPrefix_``:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = "myPrefix_") */
|
||||
private $address;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" column-prefix="myPrefix_" />
|
||||
</entity>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
columnPrefix: myPrefix_
|
||||
|
||||
To have Doctrine drop the prefix and use the value object's property name
|
||||
directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML):
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = false) */
|
||||
private $address;
|
||||
}
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
columnPrefix: false
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" use-column-prefix="false" />
|
||||
</entity>
|
||||
|
||||
|
||||
DQL
|
||||
---
|
||||
|
||||
You can also use mapped fields of embedded classes in DQL queries, just
|
||||
as if they were declared in the ``User`` class:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u FROM User u WHERE u.address.city = :myCity
|
||||
|
||||
@@ -5,27 +5,25 @@ 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 ORM if you accessed an association it would always get loaded completely into memory. This
|
||||
In Doctrine 2.0 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.
|
||||
|
||||
Doctrine ORM includes a feature called **Extra Lazy** for associations. Associations
|
||||
With Doctrine 2.1 a feature called **Extra Lazy** is introduced 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)``
|
||||
- ``Collection#count()``
|
||||
- ``Collection#get($key)``
|
||||
- ``Collection#slice($offset, $length = null)``
|
||||
|
||||
For each of the above methods the following semantics apply:
|
||||
For each of this three 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 ORM the following methods do not trigger the collection load:
|
||||
Additionally even with Doctrine 2.0 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
|
||||
@@ -35,15 +33,6 @@ With extra lazy collections you can now not only add entities to large collectio
|
||||
easily using a combination of ``count`` and ``slice``.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
``removeElement`` directly issued DELETE queries to the database from
|
||||
version 2.4.0 to 2.7.0. This circumvents the flush operation and might run
|
||||
outside a transactional boundary if you don't create one yourself. We
|
||||
consider this a critical bug in the assumptio of how the ORM works and
|
||||
reverted ``removeElement`` EXTRA_LAZY behavior in 2.7.1.
|
||||
|
||||
|
||||
Enabling Extra-Lazy Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -74,7 +63,7 @@ switch to extra lazy as shown in these examples:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\Models\CMS\CmsGroup">
|
||||
<!-- ... -->
|
||||
|
||||
@@ -7,7 +7,7 @@ Getting Started: Database First
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you :doc:`Model First <getting-started-models>`, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a Database First, you already have a database schema
|
||||
When you have a :doc:`Database First <getting-started-database>`, you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
.. note::
|
||||
@@ -20,7 +20,8 @@ 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.
|
||||
code-generator for this task and up to Doctrine 2.2, the code generator hasn't
|
||||
been flexible enough to achieve this.
|
||||
|
||||
We spinned off a subproject, Doctrine CodeGenerator, that will fill this gap and
|
||||
allow you to do *Database First* development.
|
||||
|
||||
@@ -5,7 +5,7 @@ Getting Started: Model First
|
||||
|
||||
When you :doc:`Code First <getting-started>`, you
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you Model First, you are modelling your application using tools (for
|
||||
you :doc:`Model First <getting-started-models>`, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a :doc:`Database First <getting-started-database>`, then you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,8 @@ There are use-cases when you'll want to sort collections when they are
|
||||
retrieved from the database. In userland you do this as long as you
|
||||
haven't initially saved an entity with its associations into the
|
||||
database. To retrieve a sorted collection from the database you can
|
||||
use the ``@OrderBy`` annotation with a collection that specifies
|
||||
a DQL snippet that is appended to all queries with this
|
||||
use the ``@OrderBy`` annotation with an collection that specifies
|
||||
an DQL snippet that is appended to all queries with this
|
||||
collection.
|
||||
|
||||
Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you
|
||||
@@ -64,7 +64,7 @@ positional statement. Multiple Fields are separated by a comma (,).
|
||||
The referenced field names have to exist on the ``targetEntity``
|
||||
class of the ``@ManyToMany`` or ``@OneToMany`` annotation.
|
||||
|
||||
The semantics of this feature can be described as follows:
|
||||
The semantics of this feature can be described as follows.
|
||||
|
||||
|
||||
- ``@OrderBy`` acts as an implicit ORDER BY clause for the given
|
||||
@@ -73,7 +73,7 @@ The semantics of this feature can be described as follows:
|
||||
- All collections of the ordered type are always retrieved in an
|
||||
ordered fashion.
|
||||
- To keep the database impact low, these implicit ORDER BY items
|
||||
are only added to a DQL Query if the collection is fetch joined in
|
||||
are only added to an DQL Query if the collection is fetch joined in
|
||||
the DQL query.
|
||||
|
||||
Given our previously defined example, the following would not add
|
||||
|
||||
@@ -58,7 +58,6 @@ which has mapping metadata that is overridden by the annotation above:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Trait class
|
||||
*/
|
||||
@@ -83,10 +82,9 @@ The case for just extending a class would be just the same but:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class ExampleEntityWithOverride extends BaseEntityWithSomeMapping
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
Overriding is also supported via XML and YAML (:ref:`examples <inheritence_mapping_overrides>`).
|
||||
Overriding is also supported via XML and YAML.
|
||||
@@ -1,7 +1,9 @@
|
||||
Pagination
|
||||
==========
|
||||
|
||||
Doctrine ORM ships with a Paginator for DQL queries. It
|
||||
.. versionadded:: 2.2
|
||||
|
||||
Starting with version 2.2 Doctrine ships with a Paginator for DQL queries. It
|
||||
has a very simple API and implements the SPL interfaces ``Countable`` and
|
||||
``IteratorAggregate``.
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
Working with Indexed Associations
|
||||
=================================
|
||||
|
||||
Doctrine ORM collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in
|
||||
.. note::
|
||||
|
||||
This feature is scheduled for version 2.1 of Doctrine and not included in the 2.0.x series.
|
||||
|
||||
Doctrine 2 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. You can index your collections by a value in the related entity.
|
||||
was used. Starting with Doctrine 2.1 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:
|
||||
|
||||
- You have to manage both the key and field if you want to change the index by field value.
|
||||
- On each request the keys are regenerated from the field value, and not from the previous collection key.
|
||||
- Values of the Index-By keys are never considered during persistence. They only exist for accessing purposes.
|
||||
- On each request the keys are regenerated from the field value not from the previous collection key.
|
||||
- Values of the Index-By keys are never considered during persistence, it only exists for accessing purposes.
|
||||
- Fields that are used for the index by feature **HAVE** to be unique in the database. The behavior for multiple entities
|
||||
with the same index-by field value is undefined.
|
||||
|
||||
@@ -103,7 +107,7 @@ The code and mappings for the Market entity looks like this:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\Models\StockExchange\Market">
|
||||
<id name="id" type="integer">
|
||||
@@ -160,6 +164,8 @@ here are the code and mappings for it:
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* For real this column would have to be unique=true. But I want to test behavior of non-unique overrides.
|
||||
*
|
||||
* @Column(type="string", unique=true)
|
||||
*/
|
||||
private $symbol;
|
||||
@@ -189,7 +195,7 @@ here are the code and mappings for it:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\Models\StockExchange\Stock">
|
||||
<id name="id" type="integer">
|
||||
@@ -221,7 +227,7 @@ here are the code and mappings for it:
|
||||
Querying indexed associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now that we defined the stocks collection to be indexed by symbol, we can take a look at some code
|
||||
Now that we defined the stocks collection to be indexed by symbol we can take a look at some code,
|
||||
that makes use of the indexing.
|
||||
|
||||
First we will populate our database with two example stocks traded on a single market:
|
||||
@@ -257,7 +263,7 @@ now query for the market:
|
||||
|
||||
echo $stock->getSymbol(); // will print "AAPL"
|
||||
|
||||
The implementation of ``Market::addStock()``, in combination with ``indexBy``, allows us to access the collection
|
||||
The implementation ``Market::addStock()`` in combination with ``indexBy`` allows to access the collection
|
||||
consistently by the Stock symbol. It does not matter if Stock is managed by Doctrine or not.
|
||||
|
||||
The same applies to DQL queries: The ``indexBy`` configuration acts as implicit "INDEX BY" to a join association.
|
||||
@@ -279,13 +285,14 @@ The same applies to DQL queries: The ``indexBy`` configuration acts as implicit
|
||||
|
||||
echo $stock->getSymbol(); // will print "AAPL"
|
||||
|
||||
If you want to use ``INDEX BY`` explicitly on an indexed association you are free to do so. Additionally,
|
||||
indexed associations also work with the ``Collection::slice()`` functionality, even if the association's fetch mode is
|
||||
If you want to use ``INDEX BY`` explicitly on an indexed association you are free to do so. Additionally
|
||||
indexed associations also work with the ``Collection::slice()`` functionality, no matter if marked as
|
||||
LAZY or EXTRA_LAZY.
|
||||
|
||||
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 `#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.
|
||||
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>`_
|
||||
This feature cannot be implemented for One-To-Many associations, because they are never the owning side.
|
||||
|
||||
|
||||
+64
-124
@@ -5,9 +5,9 @@
|
||||
xmlns:orm="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation><![CDATA[
|
||||
This is the XML Schema for the object/relational
|
||||
<xs:annotation>
|
||||
<xs:documentation><![CDATA[
|
||||
This is the XML Schema for the object/relational
|
||||
mapping file used by the Doctrine ORM.
|
||||
]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
@@ -17,33 +17,32 @@
|
||||
<xs:sequence>
|
||||
<xs:element name="mapped-superclass" type="orm:mapped-superclass" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="entity" type="orm:entity" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="embeddable" type="orm:embeddable" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
|
||||
<xs:complexType name="emptyType">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="cascade-type">
|
||||
<xs:sequence>
|
||||
<xs:element name="cascade-all" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-all" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-detach" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:simpleType name="lifecycle-callback-type">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="prePersist"/>
|
||||
@@ -56,15 +55,7 @@
|
||||
<xs:enumeration value="preFlush"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="cache-usage-type">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="READ_ONLY"/>
|
||||
<xs:enumeration value="READ_WRITE"/>
|
||||
<xs:enumeration value="NONSTRICT_READ_WRITE"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<xs:complexType name="lifecycle-callback">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
@@ -73,7 +64,7 @@
|
||||
<xs:attribute name="method" type="xs:NMTOKEN" use="required" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="lifecycle-callbacks">
|
||||
<xs:sequence>
|
||||
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="1" maxOccurs="unbounded" />
|
||||
@@ -101,7 +92,7 @@
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="result-class" type="orm:fqcn" />
|
||||
<xs:attribute name="result-class" type="xs:string" />
|
||||
<xs:attribute name="result-set-mapping" type="xs:string" />
|
||||
</xs:complexType>
|
||||
|
||||
@@ -117,7 +108,7 @@
|
||||
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="class" type="orm:fqcn"/>
|
||||
<xs:attribute name="class" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="entity-listeners">
|
||||
@@ -139,7 +130,7 @@
|
||||
<xs:sequence>
|
||||
<xs:element name="field-result" type="orm:field-result" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
<xs:attribute name="entity-class" type="orm:fqcn" use="required" />
|
||||
<xs:attribute name="entity-class" type="xs:string" use="required" />
|
||||
<xs:attribute name="discriminator-column" type="xs:string" use="optional" />
|
||||
</xs:complexType>
|
||||
|
||||
@@ -161,14 +152,8 @@
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="cache">
|
||||
<xs:attribute name="usage" type="orm:cache-usage-type" />
|
||||
<xs:attribute name="region" type="xs:string" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="entity">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:sequence>
|
||||
<xs:element name="options" type="orm:options" minOccurs="0" />
|
||||
<xs:element name="indexes" type="orm:indexes" minOccurs="0"/>
|
||||
<xs:element name="unique-constraints" type="orm:unique-constraints" minOccurs="0"/>
|
||||
@@ -181,7 +166,6 @@
|
||||
<xs:element name="sql-result-set-mappings" type="orm:sql-result-set-mappings" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="embedded" type="orm:embedded" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="many-to-one" type="orm:many-to-one" minOccurs="0" maxOccurs="unbounded" />
|
||||
@@ -189,24 +173,17 @@
|
||||
<xs:element name="association-overrides" type="orm:association-overrides" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="attribute-overrides" type="orm:attribute-overrides" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="table" type="orm:tablename" />
|
||||
<xs:attribute name="table" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="schema" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="repository-class" type="orm:fqcn"/>
|
||||
<xs:attribute name="repository-class" type="xs:string"/>
|
||||
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
|
||||
<xs:attribute name="change-tracking-policy" type="orm:change-tracking-policy" />
|
||||
<xs:attribute name="read-only" type="xs:boolean" default="false" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="tablename" id="tablename">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="[a-zA-Z_u01-uff.]+" id="tablename.pattern">
|
||||
</xs:pattern>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<xs:complexType name="option" mixed="true">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="option" type="orm:option"/>
|
||||
@@ -235,16 +212,6 @@
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="embeddable">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="orm:entity">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="change-tracking-policy">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="DEFERRED_IMPLICIT"/>
|
||||
@@ -252,7 +219,7 @@
|
||||
<xs:enumeration value="NOTIFY"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<xs:simpleType name="inheritance-type">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="SINGLE_TABLE"/>
|
||||
@@ -260,33 +227,33 @@
|
||||
<xs:enumeration value="TABLE_PER_CLASS"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="generator-strategy">
|
||||
<xs:restriction base="xs:token">
|
||||
|
||||
<xs:simpleType name="generator-strategy">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="NONE"/>
|
||||
<xs:enumeration value="TABLE"/>
|
||||
<xs:enumeration value="SEQUENCE"/>
|
||||
<xs:enumeration value="IDENTITY"/>
|
||||
<xs:enumeration value="AUTO"/>
|
||||
<xs:enumeration value="UUID"/>
|
||||
<xs:enumeration value="TABLE"/>
|
||||
<xs:enumeration value="SEQUENCE"/>
|
||||
<xs:enumeration value="IDENTITY"/>
|
||||
<xs:enumeration value="AUTO"/>
|
||||
<xs:enumeration value="UUID"/>
|
||||
<xs:enumeration value="CUSTOM" />
|
||||
</xs:restriction>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="fk-action">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="CASCADE"/>
|
||||
<xs:enumeration value="RESTRICT"/>
|
||||
|
||||
<xs:simpleType name="fk-action">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="CASCADE"/>
|
||||
<xs:enumeration value="RESTRICT"/>
|
||||
<xs:enumeration value="SET NULL"/>
|
||||
</xs:restriction>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="fetch-type">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="EAGER"/>
|
||||
|
||||
<xs:simpleType name="fetch-type">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="EAGER"/>
|
||||
<xs:enumeration value="LAZY"/>
|
||||
<xs:enumeration value="EXTRA_LAZY"/>
|
||||
</xs:restriction>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="field">
|
||||
@@ -306,14 +273,7 @@
|
||||
<xs:attribute name="scale" type="xs:integer" use="optional" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="embedded">
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="class" type="orm:fqcn" use="optional" />
|
||||
<xs:attribute name="column-prefix" type="xs:string" use="optional" />
|
||||
<xs:attribute name="use-column-prefix" type="xs:boolean" default="true" use="optional" />
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="discriminator-column">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
@@ -325,18 +285,16 @@
|
||||
<xs:attribute name="column-definition" type="xs:string" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="unique-constraint">
|
||||
<xs:sequence>
|
||||
<xs:element name="options" type="orm:options" minOccurs="0" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="fields" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="unique-constraints">
|
||||
<xs:sequence>
|
||||
<xs:element name="unique-constraint" type="orm:unique-constraint" minOccurs="1" maxOccurs="unbounded"/>
|
||||
@@ -344,19 +302,16 @@
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="index">
|
||||
<xs:sequence>
|
||||
<xs:element name="options" type="orm:options" minOccurs="0" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="required"/>
|
||||
<xs:attribute name="fields" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="flags" type="xs:string" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="indexes">
|
||||
<xs:sequence>
|
||||
<xs:element name="index" type="orm:index" minOccurs="1" maxOccurs="unbounded"/>
|
||||
@@ -364,16 +319,16 @@
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="discriminator-mapping">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="value" type="xs:NMTOKEN" use="required"/>
|
||||
<xs:attribute name="class" type="orm:fqcn" use="required"/>
|
||||
<xs:attribute name="class" type="xs:string" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="discriminator-map">
|
||||
<xs:sequence>
|
||||
<xs:element name="discriminator-mapping" type="orm:discriminator-mapping" minOccurs="1" maxOccurs="unbounded"/>
|
||||
@@ -421,16 +376,9 @@
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="class" type="orm:fqcn" use="required" />
|
||||
<xs:attribute name="class" type="xs:NMTOKEN" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="fqcn" id="fqcn">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="[a-zA-Z_u01-uff][a-zA-Z0-9_u01-uff]+" id="fqcn.pattern">
|
||||
</xs:pattern>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="inverse-join-columns">
|
||||
<xs:sequence>
|
||||
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
|
||||
@@ -448,6 +396,7 @@
|
||||
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="nullable" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="on-delete" type="orm:fk-action" />
|
||||
<xs:attribute name="on-update" type="orm:fk-action" />
|
||||
<xs:attribute name="column-definition" type="xs:string" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
@@ -497,17 +446,16 @@
|
||||
|
||||
<xs:complexType name="many-to-many">
|
||||
<xs:sequence>
|
||||
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
|
||||
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
|
||||
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="index-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
@@ -515,23 +463,21 @@
|
||||
|
||||
<xs:complexType name="one-to-many">
|
||||
<xs:sequence>
|
||||
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
|
||||
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="mapped-by" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="index-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="many-to-one">
|
||||
<xs:sequence>
|
||||
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
|
||||
<xs:choice minOccurs="0" maxOccurs="1">
|
||||
<xs:element name="join-column" type="orm:join-column"/>
|
||||
@@ -540,16 +486,16 @@
|
||||
</xs:choice>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
|
||||
<xs:complexType name="one-to-one">
|
||||
<xs:sequence>
|
||||
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
|
||||
<xs:choice minOccurs="0" maxOccurs="1">
|
||||
<xs:element name="join-column" type="orm:join-column"/>
|
||||
@@ -559,11 +505,11 @@
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" />
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
@@ -578,15 +524,9 @@
|
||||
<xs:sequence>
|
||||
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
|
||||
<xs:element name="join-columns" type="orm:join-columns" minOccurs="0" />
|
||||
<xs:element name="inversed-by" type="orm:inversed-by-override" minOccurs="0" maxOccurs="1" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" use="optional" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="inversed-by-override">
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="attribute-overrides">
|
||||
|
||||
+171
-542
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user