Merge remote-tracking branch 'schmittjoh/ValueObjects'
This commit is contained in:
commit
d4e6618b28
4
.coveralls.yml
Normal file
4
.coveralls.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# for php-coveralls
|
||||||
|
service_name: travis-ci
|
||||||
|
src_dir: lib
|
||||||
|
coverage_clover: build/logs/clover.xml
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ lib/Doctrine/DBAL
|
|||||||
.project
|
.project
|
||||||
.idea
|
.idea
|
||||||
vendor/
|
vendor/
|
||||||
|
composer.lock
|
||||||
|
@ -5,9 +5,6 @@ php:
|
|||||||
- 5.4
|
- 5.4
|
||||||
- 5.5
|
- 5.5
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- php: 5.5
|
|
||||||
env:
|
env:
|
||||||
- DB=mysql
|
- DB=mysql
|
||||||
- DB=pgsql
|
- DB=pgsql
|
||||||
@ -19,6 +16,9 @@ before_script:
|
|||||||
- 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;' -U postgres; fi"
|
||||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -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"
|
- 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-source
|
- composer install --prefer-dist --dev
|
||||||
|
|
||||||
script: phpunit --configuration tests/travis/$DB.travis.xml
|
script: phpunit --configuration tests/travis/$DB.travis.xml
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- php vendor/bin/coveralls -v
|
||||||
|
@ -5,6 +5,11 @@ Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?bra
|
|||||||
2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2)
|
2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2)
|
||||||
2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2)
|
2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2)
|
||||||
|
|
||||||
|
Master: [![Coverage Status](https://coveralls.io/repos/doctrine/doctrine2/badge.png?branch=master)](https://coveralls.io/r/doctrine/doctrine2?branch=master)
|
||||||
|
|
||||||
|
[![Latest Stable Version](https://poser.pugx.org/doctrine/orm/v/stable.png)](https://packagist.org/packages/doctrine/orm) [![Total Downloads](https://poser.pugx.org/doctrine/orm/downloads.png)](https://packagist.org/packages/doctrine/orm)
|
||||||
|
|
||||||
|
|
||||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence
|
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
|
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),
|
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||||
|
47
UPGRADE.md
47
UPGRADE.md
@ -1,5 +1,27 @@
|
|||||||
|
# Upgrade to 2.5
|
||||||
|
|
||||||
|
## BC BREAK: NamingStrategy has 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.
|
||||||
|
|
||||||
|
|
||||||
# Upgrade to 2.4
|
# Upgrade to 2.4
|
||||||
|
|
||||||
|
## BC BREAK: Compatibility Bugfix in PersistentCollection#matching()
|
||||||
|
|
||||||
|
In Doctrine 2.3 it was possible to use the new ``matching($criteria)``
|
||||||
|
functionality by adding constraints for assocations based on ID:
|
||||||
|
|
||||||
|
Criteria::expr()->eq('association', $assocation->getId());
|
||||||
|
|
||||||
|
This functionality does not work on InMemory collections however, because
|
||||||
|
in memory criteria compares object values based on reference.
|
||||||
|
As of 2.4 the above code will throw an exception. You need to change
|
||||||
|
offending code to pass the ``$assocation`` reference directly:
|
||||||
|
|
||||||
|
Criteria::expr()->eq('association', $assocation);
|
||||||
|
|
||||||
## Composer is now the default autoloader
|
## Composer is now the default autoloader
|
||||||
|
|
||||||
The test suite now runs with composer autoloading. Support for PEAR, and tarball autoloading is deprecated.
|
The test suite now runs with composer autoloading. Support for PEAR, and tarball autoloading is deprecated.
|
||||||
@ -11,6 +33,23 @@ Before 2.4 the postFlush and onFlush events were only called when there were
|
|||||||
actually entities that changed. Now these events are called no matter if there
|
actually entities that changed. Now these events are called no matter if there
|
||||||
are entities in the UoW or changes are found.
|
are entities in the UoW or changes are found.
|
||||||
|
|
||||||
|
## Parenthesis are now considered in arithmetic expression
|
||||||
|
|
||||||
|
Before 2.4 parenthesis are not considered in arithmetic primary expression.
|
||||||
|
That's conceptually wrong, since it might result in wrong values. For example:
|
||||||
|
|
||||||
|
The DQL:
|
||||||
|
|
||||||
|
SELECT 100 / ( 2 * 2 ) FROM MyEntity
|
||||||
|
|
||||||
|
Before 2.4 it generates the SQL:
|
||||||
|
|
||||||
|
SELECT 100 / 2 * 2 FROM my_entity
|
||||||
|
|
||||||
|
Now parenthesis are considered, the previous DQL will generate:
|
||||||
|
|
||||||
|
SELECT 100 / (2 * 2) FROM my_entity
|
||||||
|
|
||||||
# Upgrade to 2.3
|
# Upgrade to 2.3
|
||||||
|
|
||||||
## EntityManager#find() not calls EntityRepository#find() anymore
|
## EntityManager#find() not calls EntityRepository#find() anymore
|
||||||
@ -126,7 +165,7 @@ from 2.0 have to configure the annotation driver if they don't use `Configuratio
|
|||||||
$config->setMetadataDriverImpl($driver);
|
$config->setMetadataDriverImpl($driver);
|
||||||
|
|
||||||
|
|
||||||
## Scalar mappings can now be ommitted from DQL result
|
## Scalar mappings can now be omitted from DQL result
|
||||||
|
|
||||||
You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore.
|
You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore.
|
||||||
Example:
|
Example:
|
||||||
@ -307,7 +346,7 @@ them for batch updates like SchemaTool and other commands. However the
|
|||||||
annotations driver being a default driver does not really help that much
|
annotations driver being a default driver does not really help that much
|
||||||
anyways.
|
anyways.
|
||||||
|
|
||||||
Therefore we decided to break backwards compability in this issue and drop
|
Therefore we decided to break backwards compatibility in this issue and drop
|
||||||
the support for Annotations as Default Driver and require our users to
|
the support for Annotations as Default Driver and require our users to
|
||||||
specify the driver explicitly (which allows us to ask for the path to all
|
specify the driver explicitly (which allows us to ask for the path to all
|
||||||
entities).
|
entities).
|
||||||
@ -366,7 +405,7 @@ apologize for the inconvenience.
|
|||||||
## Default Property for Field Mappings
|
## Default Property for Field Mappings
|
||||||
|
|
||||||
The "default" option for database column defaults has been removed. If desired, database column defaults can
|
The "default" option for database column defaults has been removed. If desired, database column defaults can
|
||||||
be implemented by using the columnDefinition attribute of the @Column annotation (or the approriate XML and YAML equivalents).
|
be implemented by using the columnDefinition attribute of the @Column annotation (or the appropriate XML and YAML equivalents).
|
||||||
Prefer PHP default values, if possible.
|
Prefer PHP default values, if possible.
|
||||||
|
|
||||||
## Selecting Partial Objects
|
## Selecting Partial Objects
|
||||||
@ -451,7 +490,7 @@ With new required method AbstractTask::buildDocumentation, its implementation de
|
|||||||
|
|
||||||
* "doctrine schema-tool --drop" now always drops the complete database instead of
|
* "doctrine schema-tool --drop" now always drops the complete database instead of
|
||||||
only those tables defined by the current database model. The previous method had
|
only those tables defined by the current database model. The previous method had
|
||||||
problems when foreign keys of orphaned tables pointed to tables that were schedulded
|
problems when foreign keys of orphaned tables pointed to tables that were scheduled
|
||||||
for deletion.
|
for deletion.
|
||||||
* Use "doctrine schema-tool --update" to get a save incremental update for your
|
* Use "doctrine schema-tool --update" to get a save incremental update for your
|
||||||
database schema without deleting any unused tables, sequences or foreign keys.
|
database schema without deleting any unused tables, sequences or foreign keys.
|
||||||
|
@ -13,32 +13,47 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* This software consists of voluntary contributions made by many individuals
|
* This software consists of voluntary contributions made by many individuals
|
||||||
* and is licensed under the LGPL. For more information, see
|
* and is licensed under the MIT license. For more information, see
|
||||||
* <http://www.doctrine-project.org>.
|
* <http://www.doctrine-project.org>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Helper\HelperSet;
|
||||||
|
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||||
|
|
||||||
(@include_once __DIR__ . '/../vendor/autoload.php') || @include_once __DIR__ . '/../../../autoload.php';
|
(@include_once __DIR__ . '/../vendor/autoload.php') || @include_once __DIR__ . '/../../../autoload.php';
|
||||||
$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php';
|
|
||||||
|
|
||||||
$helperSet = null;
|
$directories = array(getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config');
|
||||||
$commands = array();
|
|
||||||
if (file_exists($configFile)) {
|
$configFile = null;
|
||||||
if ( ! is_readable($configFile)) {
|
foreach ($directories as $directory) {
|
||||||
trigger_error(
|
$configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php';
|
||||||
'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR
|
|
||||||
);
|
if (file_exists($configFile)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
require $configFile;
|
if ( ! file_exists($configFile)) {
|
||||||
|
ConsoleRunner::printCliConfigTemplate();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_readable($configFile)) {
|
||||||
|
echo 'Configuration file [' . $configFile . '] does not have read permission.' . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$commands = array();
|
||||||
|
|
||||||
|
$helperSet = require $configFile;
|
||||||
|
|
||||||
|
if ( ! ($helperSet instanceof HelperSet)) {
|
||||||
foreach ($GLOBALS as $helperSetCandidate) {
|
foreach ($GLOBALS as $helperSetCandidate) {
|
||||||
if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) {
|
if ($helperSetCandidate instanceof HelperSet) {
|
||||||
$helperSet = $helperSetCandidate;
|
$helperSet = $helperSetCandidate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet();
|
|
||||||
|
|
||||||
\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet, $commands);
|
\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet, $commands);
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
# Project Name
|
|
||||||
project.name=DoctrineORM
|
|
||||||
|
|
||||||
# Dependency minimum versions
|
|
||||||
dependencies.common=2.2.0beta1
|
|
||||||
dependencies.dbal=2.2.0beta1
|
|
||||||
dependencies.sfconsole=2.0.0
|
|
||||||
|
|
||||||
# Version class and file
|
# Version class and file
|
||||||
project.version_class = Doctrine\ORM\Version
|
project.version_class = Doctrine\\ORM\\Version
|
||||||
project.version_file = lib/Doctrine/ORM/Version.php
|
project.version_file = lib/Doctrine/ORM/Version.php
|
||||||
|
199
build.xml
199
build.xml
@ -1,114 +1,101 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project name="DoctrineORM" default="build" basedir=".">
|
<project name="DoctrineORM" default="build" basedir=".">
|
||||||
<taskdef classname="phing.tasks.ext.d51PearPkg2Task" name="d51pearpkg2" />
|
|
||||||
<import file="${project.basedir}/lib/vendor/doctrine-build-common/packaging.xml" />
|
|
||||||
|
|
||||||
<property file="build.properties" />
|
<property file="build.properties" />
|
||||||
|
|
||||||
<!--
|
<target name="php">
|
||||||
Fileset for artifacts shared across all distributed packages.
|
<exec executable="which" outputproperty="php_executable">
|
||||||
-->
|
<arg value="php" />
|
||||||
<fileset id="shared-artifacts" dir=".">
|
</exec>
|
||||||
<include name="LICENSE"/>
|
|
||||||
<include name="UPGRADE*" />
|
|
||||||
<include name="doctrine-mapping.xsd" />
|
|
||||||
</fileset>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Fileset for command line scripts
|
|
||||||
-->
|
|
||||||
<fileset id="bin-scripts" dir="./bin">
|
|
||||||
<include name="doctrine"/>
|
|
||||||
<include name="doctrine-pear.php"/>
|
|
||||||
<include name="doctrine.bat"/>
|
|
||||||
</fileset>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Fileset for the sources of the Doctrine Common dependency.
|
|
||||||
-->
|
|
||||||
<fileset id="common-sources" dir="./lib/vendor/doctrine-common/lib">
|
|
||||||
<include name="Doctrine/Common/**"/>
|
|
||||||
</fileset>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Fileset for the sources of the Doctrine DBAL dependency.
|
|
||||||
-->
|
|
||||||
<fileset id="dbal-sources" dir="./lib/vendor/doctrine-dbal/lib">
|
|
||||||
<include name="Doctrine/DBAL/**"/>
|
|
||||||
</fileset>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Fileset for the sources of the Doctrine ORM.
|
|
||||||
-->
|
|
||||||
<fileset id="orm-sources" dir="./lib">
|
|
||||||
<include name="Doctrine/ORM/**"/>
|
|
||||||
</fileset>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Fileset for source of the Symfony YAML and Console components.
|
|
||||||
-->
|
|
||||||
<fileset id="symfony-sources" dir="./lib/vendor">
|
|
||||||
<include name="Symfony/Component/**"/>
|
|
||||||
<exclude name="**/.git/**" />
|
|
||||||
</fileset>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Builds ORM package, preparing it for distribution.
|
|
||||||
-->
|
|
||||||
<target name="copy-files" depends="prepare">
|
|
||||||
<copy todir="${build.dir}/${project.name}-${version}">
|
|
||||||
<fileset refid="shared-artifacts"/>
|
|
||||||
</copy>
|
|
||||||
<copy todir="${build.dir}/${project.name}-${version}">
|
|
||||||
<fileset refid="common-sources"/>
|
|
||||||
<fileset refid="dbal-sources"/>
|
|
||||||
<fileset refid="orm-sources"/>
|
|
||||||
</copy>
|
|
||||||
<copy todir="${build.dir}/${project.name}-${version}/Doctrine">
|
|
||||||
<fileset refid="symfony-sources"/>
|
|
||||||
</copy>
|
|
||||||
<copy todir="${build.dir}/${project.name}-${version}/bin">
|
|
||||||
<fileset refid="bin-scripts"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!--
|
<target name="prepare">
|
||||||
Builds distributable PEAR packages.
|
<mkdir dir="build" />
|
||||||
-->
|
|
||||||
<target name="define-pear-package" depends="copy-files">
|
|
||||||
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/${project.name}-${version}">
|
|
||||||
<name>DoctrineORM</name>
|
|
||||||
<summary>Doctrine Object Relational Mapper</summary>
|
|
||||||
<channel>pear.doctrine-project.org</channel>
|
|
||||||
<description>The Doctrine ORM package is the primary package containing the object relational mapper.</description>
|
|
||||||
<lead user="jwage" name="Jonathan H. Wage" email="jonwage@gmail.com" />
|
|
||||||
<lead user="guilhermeblanco" name="Guilherme Blanco" email="guilhermeblanco@gmail.com" />
|
|
||||||
<lead user="romanb" name="Roman Borschel" email="roman@code-factory.org" />
|
|
||||||
<lead user="beberlei" name="Benjamin Eberlei" email="kontakt@beberlei.de" />
|
|
||||||
<license>LGPL</license>
|
|
||||||
<version release="${pear.version}" api="${pear.version}" />
|
|
||||||
<stability release="${pear.stability}" api="${pear.stability}" />
|
|
||||||
<notes>-</notes>
|
|
||||||
<dependencies>
|
|
||||||
<php minimum_version="5.3.0" />
|
|
||||||
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
|
|
||||||
<package name="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" />
|
|
||||||
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" />
|
|
||||||
<package name="Console" channel="pear.symfony.com" minimum_version="2.0.0" />
|
|
||||||
<package name="Yaml" channel="pear.symfony.com" minimum_version="2.0.0" />
|
|
||||||
</dependencies>
|
|
||||||
<dirroles key="bin">script</dirroles>
|
|
||||||
<ignore>Doctrine/Common/</ignore>
|
|
||||||
<ignore>Doctrine/DBAL/</ignore>
|
|
||||||
<ignore>Symfony/Component/Yaml/</ignore>
|
|
||||||
<ignore>Symfony/Component/Console/</ignore>
|
|
||||||
<release>
|
|
||||||
<install as="doctrine" name="bin/doctrine" />
|
|
||||||
<install as="doctrine.php" name="bin/doctrine-pear.php" />
|
|
||||||
<install as="doctrine.bat" name="bin/doctrine.bat" />
|
|
||||||
</release>
|
|
||||||
<replacement path="bin/doctrine" type="pear-config" from="@php_bin@" to="php_bin" />
|
|
||||||
<replacement path="bin/doctrine.bat" type="pear-config" from="@bin_dir@" to="bin_dir" />
|
|
||||||
</d51pearpkg2>
|
|
||||||
</target>
|
</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>
|
</project>
|
||||||
|
@ -15,9 +15,14 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.2",
|
"php": ">=5.3.2",
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"doctrine/dbal": ">=2.4-dev,<2.5-dev",
|
"doctrine/collections": "~1.1",
|
||||||
|
"doctrine/dbal": ">=2.5-dev,<2.6-dev",
|
||||||
"symfony/console": "2.*"
|
"symfony/console": "2.*"
|
||||||
},
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/yaml": "2.1",
|
||||||
|
"satooshi/php-coveralls": "dev-master"
|
||||||
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||||
},
|
},
|
||||||
@ -27,7 +32,10 @@
|
|||||||
"bin": ["bin/doctrine", "bin/doctrine.php"],
|
"bin": ["bin/doctrine", "bin/doctrine.php"],
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.4.x-dev"
|
"dev-master": "2.5.x-dev"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"archive": {
|
||||||
|
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
507
composer.lock
generated
507
composer.lock
generated
@ -1,507 +0,0 @@
|
|||||||
{
|
|
||||||
"hash": "eff8840dfb1a83e6e1aef32b8031ac7c",
|
|
||||||
"packages": [
|
|
||||||
{
|
|
||||||
"name": "doctrine/annotations",
|
|
||||||
"version": "v1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/annotations.git",
|
|
||||||
"reference": "v1.0"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://github.com/doctrine/annotations/archive/v1.0.zip",
|
|
||||||
"reference": "v1.0",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"doctrine/lexer": "1.*",
|
|
||||||
"php": ">=5.3.2"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"doctrine/cache": "1.*"
|
|
||||||
},
|
|
||||||
"time": "2013-01-12 19:23:32",
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Doctrine\\Common\\Annotations\\": "lib/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com",
|
|
||||||
"homepage": "http://www.jwage.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com",
|
|
||||||
"homepage": "http://www.instaclick.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com",
|
|
||||||
"homepage": "https://github.com/schmittjoh",
|
|
||||||
"role": "Developer of wrapped JMSSerializerBundle"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Docblock Annotations Parser",
|
|
||||||
"homepage": "http://www.doctrine-project.org",
|
|
||||||
"keywords": [
|
|
||||||
"annotations",
|
|
||||||
"docblock",
|
|
||||||
"parser"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "doctrine/cache",
|
|
||||||
"version": "v1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/cache.git",
|
|
||||||
"reference": "v1.0"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://github.com/doctrine/cache/archive/v1.0.zip",
|
|
||||||
"reference": "v1.0",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.3.2"
|
|
||||||
},
|
|
||||||
"time": "2013-01-10 22:43:46",
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Doctrine\\Common\\Cache\\": "lib/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com",
|
|
||||||
"homepage": "http://www.jwage.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com",
|
|
||||||
"homepage": "http://www.instaclick.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com",
|
|
||||||
"homepage": "https://github.com/schmittjoh",
|
|
||||||
"role": "Developer of wrapped JMSSerializerBundle"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Caching library offering an object-oriented API for many cache backends",
|
|
||||||
"homepage": "http://www.doctrine-project.org",
|
|
||||||
"keywords": [
|
|
||||||
"cache",
|
|
||||||
"caching"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "doctrine/collections",
|
|
||||||
"version": "v1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/collections.git",
|
|
||||||
"reference": "v1.0"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://github.com/doctrine/collections/archive/v1.0.zip",
|
|
||||||
"reference": "v1.0",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.3.2"
|
|
||||||
},
|
|
||||||
"time": "2013-01-12 16:36:50",
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Doctrine\\Common\\Collections\\": "lib/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com",
|
|
||||||
"homepage": "http://www.jwage.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com",
|
|
||||||
"homepage": "http://www.instaclick.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com",
|
|
||||||
"homepage": "https://github.com/schmittjoh",
|
|
||||||
"role": "Developer of wrapped JMSSerializerBundle"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Collections Abstraction library",
|
|
||||||
"homepage": "http://www.doctrine-project.org",
|
|
||||||
"keywords": [
|
|
||||||
"array",
|
|
||||||
"collections",
|
|
||||||
"iterator"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "doctrine/common",
|
|
||||||
"version": "dev-master",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/common",
|
|
||||||
"reference": "53859ae1c84ccf1a5aa58c8379c69cd9adedf03a"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/doctrine/common/zipball/53859ae1c84ccf1a5aa58c8379c69cd9adedf03a",
|
|
||||||
"reference": "53859ae1c84ccf1a5aa58c8379c69cd9adedf03a",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"doctrine/annotations": "1.*",
|
|
||||||
"doctrine/cache": "1.*",
|
|
||||||
"doctrine/collections": "1.*",
|
|
||||||
"doctrine/inflector": "1.*",
|
|
||||||
"doctrine/lexer": "1.*",
|
|
||||||
"php": ">=5.3.2"
|
|
||||||
},
|
|
||||||
"time": "2013-01-29 12:48:56",
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "2.4.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Doctrine\\Common\\": "lib/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com",
|
|
||||||
"homepage": "http://www.jwage.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com",
|
|
||||||
"homepage": "http://www.instaclick.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com",
|
|
||||||
"homepage": "https://github.com/schmittjoh",
|
|
||||||
"role": "Developer of wrapped JMSSerializerBundle"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common Library for Doctrine projects",
|
|
||||||
"homepage": "http://www.doctrine-project.org",
|
|
||||||
"keywords": [
|
|
||||||
"annotations",
|
|
||||||
"collections",
|
|
||||||
"eventmanager",
|
|
||||||
"persistence",
|
|
||||||
"spl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "doctrine/dbal",
|
|
||||||
"version": "dev-master",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/dbal",
|
|
||||||
"reference": "eb6ee9a86421ba534d7c5514b190d1d06b30d4b1"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/eb6ee9a86421ba534d7c5514b190d1d06b30d4b1",
|
|
||||||
"reference": "eb6ee9a86421ba534d7c5514b190d1d06b30d4b1",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"doctrine/common": "2.4.x-dev",
|
|
||||||
"php": ">=5.3.2"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"symfony/console": "2.*"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"symfony/console": "For helpful console commands such as SQL execution and import of files."
|
|
||||||
},
|
|
||||||
"time": "2013-02-09 23:28:29",
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "2.4.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Doctrine\\DBAL\\": "lib/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com",
|
|
||||||
"homepage": "http://www.jwage.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com",
|
|
||||||
"homepage": "http://www.instaclick.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Database Abstraction Layer",
|
|
||||||
"homepage": "http://www.doctrine-project.org",
|
|
||||||
"keywords": [
|
|
||||||
"database",
|
|
||||||
"dbal",
|
|
||||||
"persistence",
|
|
||||||
"queryobject"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "doctrine/inflector",
|
|
||||||
"version": "v1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/inflector.git",
|
|
||||||
"reference": "v1.0"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://github.com/doctrine/inflector/archive/v1.0.zip",
|
|
||||||
"reference": "v1.0",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.3.2"
|
|
||||||
},
|
|
||||||
"time": "2013-01-10 21:49:15",
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Doctrine\\Common\\Inflector\\": "lib/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com",
|
|
||||||
"homepage": "http://www.jwage.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com",
|
|
||||||
"homepage": "http://www.instaclick.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com",
|
|
||||||
"homepage": "https://github.com/schmittjoh",
|
|
||||||
"role": "Developer of wrapped JMSSerializerBundle"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common String Manipulations with regard to casing and singular/plural rules.",
|
|
||||||
"homepage": "http://www.doctrine-project.org",
|
|
||||||
"keywords": [
|
|
||||||
"inflection",
|
|
||||||
"pluarlize",
|
|
||||||
"singuarlize",
|
|
||||||
"string"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "doctrine/lexer",
|
|
||||||
"version": "v1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/doctrine/lexer.git",
|
|
||||||
"reference": "v1.0"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://github.com/doctrine/lexer/archive/v1.0.zip",
|
|
||||||
"reference": "v1.0",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.3.2"
|
|
||||||
},
|
|
||||||
"time": "2013-01-12 18:59:04",
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Doctrine\\Common\\Lexer\\": "lib/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com",
|
|
||||||
"homepage": "http://www.instaclick.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com",
|
|
||||||
"homepage": "https://github.com/schmittjoh",
|
|
||||||
"role": "Developer of wrapped JMSSerializerBundle"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
|
|
||||||
"homepage": "http://www.doctrine-project.org",
|
|
||||||
"keywords": [
|
|
||||||
"lexer",
|
|
||||||
"parser"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "symfony/console",
|
|
||||||
"version": "dev-master",
|
|
||||||
"target-dir": "Symfony/Component/Console",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/symfony/Console",
|
|
||||||
"reference": "f65e34d058f0990a724f78e8d091dc0a20e439ac"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/symfony/Console/zipball/f65e34d058f0990a724f78e8d091dc0a20e439ac",
|
|
||||||
"reference": "f65e34d058f0990a724f78e8d091dc0a20e439ac",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.3.3"
|
|
||||||
},
|
|
||||||
"time": "2013-01-31 21:39:01",
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "2.3-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-0": {
|
|
||||||
"Symfony\\Component\\Console\\": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Fabien Potencier",
|
|
||||||
"email": "fabien@symfony.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Symfony Community",
|
|
||||||
"homepage": "http://symfony.com/contributors"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Symfony Console Component",
|
|
||||||
"homepage": "http://symfony.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"packages-dev": null,
|
|
||||||
"aliases": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"minimum-stability": "dev",
|
|
||||||
"stability-flags": [
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
@ -192,3 +192,10 @@ latex_documents = [
|
|||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#latex_use_modindex = True
|
#latex_use_modindex = True
|
||||||
|
|
||||||
|
primary_domain = "dcorm"
|
||||||
|
|
||||||
|
def linkcode_resolve(domain, info):
|
||||||
|
if domain == 'dcorm':
|
||||||
|
return 'http://'
|
||||||
|
return None
|
||||||
|
97
docs/en/cookbook/custom-mapping-types.rst
Normal file
97
docs/en/cookbook/custom-mapping-types.rst
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The following assumptions are applied to mapping types by the ORM:
|
||||||
|
|
||||||
|
- 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');
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -112,7 +112,7 @@ The ``Paginate::count(Query $query)`` looks like:
|
|||||||
}
|
}
|
||||||
|
|
||||||
It clones the query, resets the limit clause first and max results
|
It clones the query, resets the limit clause first and max results
|
||||||
and registers the ``CountSqlWalker`` customer tree walker which
|
and registers the ``CountSqlWalker`` custom tree walker which
|
||||||
will modify the AST to execute a count query. The walkers
|
will modify the AST to execute a count query. The walkers
|
||||||
implementation is:
|
implementation is:
|
||||||
|
|
||||||
|
@ -64,5 +64,5 @@ object or implement the __sleep() magic method on your entity.
|
|||||||
|
|
||||||
When you called detach on your objects they get "unmanaged" with that
|
When you called detach on your objects they get "unmanaged" with that
|
||||||
entity manager. This means you cannot use them as part of write operations
|
entity manager. This means you cannot use them as part of write operations
|
||||||
during ``EntityManagr#flush()`` anymore in this request.
|
during ``EntityManager#flush()`` anymore in this request.
|
||||||
|
|
||||||
|
@ -22,21 +22,21 @@ implement the ``NotifyPropertyChanged`` interface from the
|
|||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
use Doctrine\Common\NotifyPropertyChanged,
|
use Doctrine\Common\NotifyPropertyChanged;
|
||||||
Doctrine\Common\PropertyChangedListener;
|
use Doctrine\Common\PropertyChangedListener;
|
||||||
|
|
||||||
abstract class DomainObject implements NotifyPropertyChanged
|
abstract class DomainObject implements NotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private $_listeners = array();
|
private $listeners = array();
|
||||||
|
|
||||||
public function addPropertyChangedListener(PropertyChangedListener $listener) {
|
public function addPropertyChangedListener(PropertyChangedListener $listener) {
|
||||||
$this->_listeners[] = $listener;
|
$this->listeners[] = $listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Notifies listeners of a change. */
|
/** Notifies listeners of a change. */
|
||||||
protected function _onPropertyChanged($propName, $oldValue, $newValue) {
|
protected function onPropertyChanged($propName, $oldValue, $newValue) {
|
||||||
if ($this->_listeners) {
|
if ($this->listeners) {
|
||||||
foreach ($this->_listeners as $listener) {
|
foreach ($this->listeners as $listener) {
|
||||||
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
|
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ implement the ``NotifyPropertyChanged`` interface from the
|
|||||||
}
|
}
|
||||||
|
|
||||||
Then, in each property setter of concrete, derived domain classes,
|
Then, in each property setter of concrete, derived domain classes,
|
||||||
you need to invoke \_onPropertyChanged as follows to notify
|
you need to invoke onPropertyChanged as follows to notify
|
||||||
listeners:
|
listeners:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -58,7 +58,7 @@ listeners:
|
|||||||
|
|
||||||
public function setData($data) {
|
public function setData($data) {
|
||||||
if ($data != $this->data) { // check: is it actually modified?
|
if ($data != $this->data) { // check: is it actually modified?
|
||||||
$this->_onPropertyChanged('data', $this->data, $data);
|
$this->onPropertyChanged('data', $this->data, $data);
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ A Customer entity
|
|||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
// src/Acme/AppModule/Entity/Customer.php
|
// src/Acme/AppModule/Entity/Customer.php
|
||||||
|
|
||||||
namespace Acme\AppModule\Entity;
|
namespace Acme\AppModule\Entity;
|
||||||
@ -62,6 +63,7 @@ An Invoice entity
|
|||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
// src/Acme/InvoiceModule/Entity/Invoice.php
|
// src/Acme/InvoiceModule/Entity/Invoice.php
|
||||||
|
|
||||||
namespace Acme\InvoiceModule\Entity;
|
namespace Acme\InvoiceModule\Entity;
|
||||||
@ -88,6 +90,7 @@ An InvoiceSubjectInterface
|
|||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
// src/Acme/InvoiceModule/Model/InvoiceSubjectInterface.php
|
// src/Acme/InvoiceModule/Model/InvoiceSubjectInterface.php
|
||||||
|
|
||||||
namespace Acme\InvoiceModule\Model;
|
namespace Acme\InvoiceModule\Model;
|
||||||
@ -112,18 +115,19 @@ An InvoiceSubjectInterface
|
|||||||
|
|
||||||
Next, we need to configure the listener. Add this to the area you set up Doctrine. You
|
Next, we need to configure the listener. Add this to the area you set up Doctrine. You
|
||||||
must set this up in the way outlined below, otherwise you can not be guaranteed that
|
must set this up in the way outlined below, otherwise you can not be guaranteed that
|
||||||
the targetEntity resolution will occur reliably::
|
the targetEntity resolution will occur reliably:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$evm = new \Doctrine\Common\EventManager;
|
<?php
|
||||||
|
$evm = new \Doctrine\Common\EventManager;
|
||||||
$rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener;
|
$rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener;
|
||||||
$rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface',
|
|
||||||
'Acme\\CustomerModule\\Entity\\Customer', array());
|
// Adds a target-entity class
|
||||||
|
$rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array());
|
||||||
|
|
||||||
// Add the ResolveTargetEntityListener
|
// Add the ResolveTargetEntityListener
|
||||||
$evm->addEventSubscriber($rtel);
|
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel);
|
||||||
|
|
||||||
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm);
|
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm);
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ Doctrine ORM don't panic. You can get help from different sources:
|
|||||||
|
|
||||||
- There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.
|
- There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.
|
||||||
- The `Doctrine Mailing List <http://groups.google.com/group/doctrine-user>`_
|
- The `Doctrine Mailing List <http://groups.google.com/group/doctrine-user>`_
|
||||||
- Internet Relay Chat (IRC) in `#doctrine on Freenode <irc://irc.freenode.net/doctrine>`_
|
- Internet Relay Chat (IRC) in #doctrine on Freenode
|
||||||
- Report a bug on `JIRA <http://www.doctrine-project.org/jira>`_.
|
- Report a bug on `JIRA <http://www.doctrine-project.org/jira>`_.
|
||||||
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
|
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
|
||||||
- On `StackOverflow <http://stackoverflow.com/questions/tagged/doctrine2>`_
|
- On `StackOverflow <http://stackoverflow.com/questions/tagged/doctrine2>`_
|
||||||
@ -26,20 +26,10 @@ Getting Started
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
* **Tutorial**:
|
* **Tutorial**:
|
||||||
:doc:`Code First <tutorials/getting-started>` |
|
:doc:`Getting Started with Doctrine <tutorials/getting-started>`
|
||||||
:doc:`Model First <tutorials/getting-started-models>` |
|
|
||||||
:doc:`Database First <tutorials/getting-started-database>`
|
|
||||||
|
|
||||||
* **Introduction**:
|
|
||||||
:doc:`In 10 quick steps <tutorials/in-ten-quick-steps>` |
|
|
||||||
:doc:`Architecture <reference/architecture>`
|
|
||||||
|
|
||||||
* **Setup**:
|
* **Setup**:
|
||||||
:doc:`Installation <reference/installation>` |
|
:doc:`Installation & Configuration <reference/configuration>`
|
||||||
:doc:`Configuration <reference/configuration>` |
|
|
||||||
:doc:`Tools <reference/tools>`
|
|
||||||
|
|
||||||
* :doc:`Limitations and knowns issues <reference/limitations-and-known-issues>`
|
|
||||||
|
|
||||||
Mapping Objects onto a Database
|
Mapping Objects onto a Database
|
||||||
-------------------------------
|
-------------------------------
|
||||||
@ -75,25 +65,29 @@ Working with Objects
|
|||||||
Advanced Topics
|
Advanced Topics
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
* :doc:`Transactions and Concurrency <reference/transactions-and-concurrency>`
|
* :doc:`Architecture <reference/architecture>`
|
||||||
* :doc:`Filters <reference/filters>`
|
* :doc:`Advanced Configuration <reference/advanced-configuration>`
|
||||||
* :doc:`NamingStrategy <reference/namingstrategy>`
|
* :doc:`Limitations and knowns issues <reference/limitations-and-known-issues>`
|
||||||
* :doc:`Improving Performance <reference/improving-performance>`
|
* :doc:`Commandline Tools <reference/tools>`
|
||||||
* :doc:`Caching <reference/caching>`
|
* :doc:`Transactions and Concurrency <reference/transactions-and-concurrency>`
|
||||||
* :doc:`Partial Objects <reference/partial-objects>`
|
* :doc:`Filters <reference/filters>`
|
||||||
* :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
* :doc:`NamingStrategy <reference/namingstrategy>`
|
||||||
* :doc:`Best Practices <reference/best-practices>`
|
* :doc:`Improving Performance <reference/improving-performance>`
|
||||||
* :doc:`Metadata Drivers <reference/metadata-drivers>`
|
* :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
|
Tutorials
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* :doc:`Indexed associations <tutorials/working-with-indexed-associations>`
|
* :doc:`Indexed associations <tutorials/working-with-indexed-associations>`
|
||||||
* :doc:`Extra Lazy Associations <tutorials/extra-lazy-associations>`
|
* :doc:`Extra Lazy Associations <tutorials/extra-lazy-associations>`
|
||||||
* :doc:`Composite Primary Keys <tutorials/composite-primary-keys>`
|
* :doc:`Composite Primary Keys <tutorials/composite-primary-keys>`
|
||||||
* :doc:`Ordered associations <tutorials/ordered-associations>`
|
* :doc:`Ordered associations <tutorials/ordered-associations>`
|
||||||
* :doc:`Pagination <tutorials/pagination>`
|
* :doc:`Pagination <tutorials/pagination>`
|
||||||
* :doc:`Override Field/Association Mappings In Subclasses <tutorials/override-field-association-mappings-in-subclasses>`
|
* :doc:`Override Field/Association Mappings In Subclasses <tutorials/override-field-association-mappings-in-subclasses>`
|
||||||
|
|
||||||
Cookbook
|
Cookbook
|
||||||
--------
|
--------
|
||||||
@ -113,7 +107,8 @@ Cookbook
|
|||||||
:doc:`Using Wakeup Or Clone <cookbook/implementing-wakeup-or-clone>` |
|
:doc:`Using Wakeup Or Clone <cookbook/implementing-wakeup-or-clone>` |
|
||||||
:doc:`Working with DateTime <cookbook/working-with-datetime>` |
|
:doc:`Working with DateTime <cookbook/working-with-datetime>` |
|
||||||
:doc:`Validation <cookbook/validation-of-entities>` |
|
:doc:`Validation <cookbook/validation-of-entities>` |
|
||||||
:doc:`Entities in the Session <cookbook/entities-in-session>`
|
:doc:`Entities in the Session <cookbook/entities-in-session>` |
|
||||||
|
:doc:`Keeping your Modules independent <cookbook/resolve-target-entity-listener>`
|
||||||
|
|
||||||
* **Integration into Frameworks/Libraries**
|
* **Integration into Frameworks/Libraries**
|
||||||
:doc:`CodeIgniter <cookbook/integrating-with-codeigniter>`
|
:doc:`CodeIgniter <cookbook/integrating-with-codeigniter>`
|
||||||
@ -125,3 +120,4 @@ Cookbook
|
|||||||
:doc:`MySQL Enums <cookbook/mysql-enums>`
|
:doc:`MySQL Enums <cookbook/mysql-enums>`
|
||||||
:doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`
|
:doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`
|
||||||
|
|
||||||
|
.. include:: toc.rst
|
||||||
|
468
docs/en/reference/advanced-configuration.rst
Normal file
468
docs/en/reference/advanced-configuration.rst
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
Advanced Configuration
|
||||||
|
======================
|
||||||
|
|
||||||
|
The configuration of the EntityManager requires a
|
||||||
|
``Doctrine\ORM\Configuration`` instance as well as some database
|
||||||
|
connection parameters. This example shows all the potential
|
||||||
|
steps of configuration.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?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');
|
||||||
|
$config->setMetadataDriverImpl($driverImpl);
|
||||||
|
$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::
|
||||||
|
|
||||||
|
Do not use Doctrine without a metadata and query cache!
|
||||||
|
Doctrine is optimized for working with caches. The main
|
||||||
|
parts in Doctrine that are optimized for caching are the metadata
|
||||||
|
mapping information with the metadata cache and the DQL to SQL
|
||||||
|
conversions with the query cache. These 2 caches require only an
|
||||||
|
absolute minimum of memory yet they heavily improve the runtime
|
||||||
|
performance of Doctrine. The recommended cache driver to use with
|
||||||
|
Doctrine is `APC <http://www.php.net/apc>`_. APC provides you with
|
||||||
|
an opcode-cache (which is highly recommended anyway) and a very
|
||||||
|
fast in-memory cache storage that you can use for the metadata and
|
||||||
|
query caches as seen in the previous code snippet.
|
||||||
|
|
||||||
|
Configuration Options
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The following sections describe all the configuration options
|
||||||
|
available on a ``Doctrine\ORM\Configuration`` instance.
|
||||||
|
|
||||||
|
Proxy Directory (***REQUIRED***)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$config->setProxyDir($dir);
|
||||||
|
$config->getProxyDir();
|
||||||
|
|
||||||
|
Gets or sets the directory where Doctrine generates any proxy
|
||||||
|
classes. For a detailed explanation on proxy classes and how they
|
||||||
|
are used in Doctrine, refer to the "Proxy Objects" section further
|
||||||
|
down.
|
||||||
|
|
||||||
|
Proxy Namespace (***REQUIRED***)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$config->setProxyNamespace($namespace);
|
||||||
|
$config->getProxyNamespace();
|
||||||
|
|
||||||
|
Gets or sets the namespace to use for generated proxy classes. For
|
||||||
|
a detailed explanation on proxy classes and how they are used in
|
||||||
|
Doctrine, refer to the "Proxy Objects" section further down.
|
||||||
|
|
||||||
|
Metadata Driver (***REQUIRED***)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$config->setMetadataDriverImpl($driver);
|
||||||
|
$config->getMetadataDriverImpl();
|
||||||
|
|
||||||
|
Gets or sets the metadata driver implementation that is used by
|
||||||
|
Doctrine to acquire the object-relational metadata for your
|
||||||
|
classes.
|
||||||
|
|
||||||
|
There are currently 4 available implementations:
|
||||||
|
|
||||||
|
|
||||||
|
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
|
||||||
|
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
|
||||||
|
- ``Doctrine\ORM\Mapping\Driver\YamlDriver``
|
||||||
|
- ``Doctrine\ORM\Mapping\Driver\DriverChain``
|
||||||
|
|
||||||
|
Throughout the most part of this manual the AnnotationDriver is
|
||||||
|
used in the examples. For information on the usage of the XmlDriver
|
||||||
|
or YamlDriver please refer to the dedicated chapters
|
||||||
|
``XML Mapping`` and ``YAML Mapping``.
|
||||||
|
|
||||||
|
The annotation driver can be configured with a factory method on
|
||||||
|
the ``Doctrine\ORM\Configuration``:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
|
||||||
|
$config->setMetadataDriverImpl($driverImpl);
|
||||||
|
|
||||||
|
The path information to the entities is required for the annotation
|
||||||
|
driver, because otherwise mass-operations on all entities through
|
||||||
|
the console could not work correctly. All of metadata drivers
|
||||||
|
accept either a single directory as a string or an array of
|
||||||
|
directories. With this feature a single driver can support multiple
|
||||||
|
directories of Entities.
|
||||||
|
|
||||||
|
Metadata Cache (***RECOMMENDED***)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$config->setMetadataCacheImpl($cache);
|
||||||
|
$config->getMetadataCacheImpl();
|
||||||
|
|
||||||
|
Gets or sets the cache implementation to use for caching metadata
|
||||||
|
information, that is, all the information you supply via
|
||||||
|
annotations, xml or yaml, so that they do not need to be parsed and
|
||||||
|
loaded from scratch on every single request which is a waste of
|
||||||
|
resources. The cache implementation must implement the
|
||||||
|
``Doctrine\Common\Cache\Cache`` interface.
|
||||||
|
|
||||||
|
Usage of a metadata cache is highly recommended.
|
||||||
|
|
||||||
|
The recommended implementations for production are:
|
||||||
|
|
||||||
|
|
||||||
|
- ``Doctrine\Common\Cache\ApcCache``
|
||||||
|
- ``Doctrine\Common\Cache\MemcacheCache``
|
||||||
|
- ``Doctrine\Common\Cache\XcacheCache``
|
||||||
|
- ``Doctrine\Common\Cache\RedisCache``
|
||||||
|
|
||||||
|
For development you should use the
|
||||||
|
``Doctrine\Common\Cache\ArrayCache`` which only caches data on a
|
||||||
|
per-request basis.
|
||||||
|
|
||||||
|
Query Cache (***RECOMMENDED***)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$config->setQueryCacheImpl($cache);
|
||||||
|
$config->getQueryCacheImpl();
|
||||||
|
|
||||||
|
Gets or sets the cache implementation to use for caching DQL
|
||||||
|
queries, that is, the result of a DQL parsing process that includes
|
||||||
|
the final SQL as well as meta information about how to process the
|
||||||
|
SQL result set of a query. Note that the query cache does not
|
||||||
|
affect query results. You do not get stale data. This is a pure
|
||||||
|
optimization cache without any negative side-effects (except some
|
||||||
|
minimal memory usage in your cache).
|
||||||
|
|
||||||
|
Usage of a query cache is highly recommended.
|
||||||
|
|
||||||
|
The recommended implementations for production are:
|
||||||
|
|
||||||
|
|
||||||
|
- ``Doctrine\Common\Cache\ApcCache``
|
||||||
|
- ``Doctrine\Common\Cache\MemcacheCache``
|
||||||
|
- ``Doctrine\Common\Cache\XcacheCache``
|
||||||
|
- ``Doctrine\Common\Cache\RedisCache``
|
||||||
|
|
||||||
|
For development you should use the
|
||||||
|
``Doctrine\Common\Cache\ArrayCache`` which only caches data on a
|
||||||
|
per-request basis.
|
||||||
|
|
||||||
|
SQL Logger (***Optional***)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$config->setSQLLogger($logger);
|
||||||
|
$config->getSQLLogger();
|
||||||
|
|
||||||
|
Gets or sets the logger to use for logging all SQL statements
|
||||||
|
executed by Doctrine. The logger class must implement the
|
||||||
|
``Doctrine\DBAL\Logging\SQLLogger`` interface. A simple default
|
||||||
|
implementation that logs to the standard output using ``echo`` and
|
||||||
|
``var_dump`` can be found at
|
||||||
|
``Doctrine\DBAL\Logging\EchoSQLLogger``.
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean
|
||||||
|
value. This is still possible, ``FALSE`` being equivalent to
|
||||||
|
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
|
||||||
|
|
||||||
|
Development vs Production Configuration
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
You should code your Doctrine2 bootstrapping with two different
|
||||||
|
runtime models in mind. There are some serious benefits of using
|
||||||
|
APC or Memcache in production. In development however this will
|
||||||
|
frequently give you fatal errors, when you change your entities and
|
||||||
|
the cache still keeps the outdated metadata. That is why we
|
||||||
|
recommend the ``ArrayCache`` for development.
|
||||||
|
|
||||||
|
Furthermore you should have the Auto-generating Proxy Classes
|
||||||
|
option to true in development and to false in production. If this
|
||||||
|
option is set to ``TRUE`` it can seriously hurt your script
|
||||||
|
performance if several proxy classes are re-generated during script
|
||||||
|
execution. Filesystem calls of that magnitude can even slower than
|
||||||
|
all the database queries Doctrine issues. Additionally writing a
|
||||||
|
proxy sets an exclusive file lock which can cause serious
|
||||||
|
performance bottlenecks in systems with regular concurrent
|
||||||
|
requests.
|
||||||
|
|
||||||
|
Connection Options
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The ``$connectionOptions`` passed as the first argument to
|
||||||
|
``EntityManager::create()`` has to be either an array or an
|
||||||
|
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 <./../../../../../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 Doctrine 2,
|
||||||
|
proxy objects are used to realize several features but mainly for
|
||||||
|
transparent lazy-loading.
|
||||||
|
|
||||||
|
Proxy objects with their lazy-loading facilities help to keep the
|
||||||
|
subset of objects that are already in memory connected to the rest
|
||||||
|
of the objects. This is an essential property as without it there
|
||||||
|
would always be fragile partial objects at the outer edges of your
|
||||||
|
object graph.
|
||||||
|
|
||||||
|
Doctrine 2 implements a variant of the proxy pattern where it
|
||||||
|
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
|
||||||
|
the class being proxied. This happens in two situations:
|
||||||
|
|
||||||
|
Reference Proxies
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The method ``EntityManager#getReference($entityName, $identifier)``
|
||||||
|
lets you obtain a reference to an entity for which the identifier
|
||||||
|
is known, without loading that entity from the database. This is
|
||||||
|
useful, for example, as a performance enhancement, when you want to
|
||||||
|
establish an association to an entity for which you have the
|
||||||
|
identifier. You could simply do this:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
|
||||||
|
// $itemId comes from somewhere, probably a request parameter
|
||||||
|
$item = $em->getReference('MyProject\Model\Item', $itemId);
|
||||||
|
$cart->addItem($item);
|
||||||
|
|
||||||
|
Here, we added an Item to a Cart without loading the Item from the
|
||||||
|
database. If you invoke any method on the Item instance, it would
|
||||||
|
fully initialize its state transparently from the database. Here
|
||||||
|
$item is actually an instance of the proxy class that was generated
|
||||||
|
for the Item class but your code does not need to care. In fact it
|
||||||
|
**should not care**. Proxy objects should be transparent to your
|
||||||
|
code.
|
||||||
|
|
||||||
|
Association proxies
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The second most important situation where Doctrine uses proxy
|
||||||
|
objects is when querying for objects. Whenever you query for an
|
||||||
|
object that has a single-valued association to another object that
|
||||||
|
is configured LAZY, without joining that association in the same
|
||||||
|
query, Doctrine puts proxy objects in place where normally the
|
||||||
|
associated object would be. Just like other proxies it will
|
||||||
|
transparently initialize itself on first access.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Joining an association in a DQL or native query
|
||||||
|
essentially means eager loading of that association in that query.
|
||||||
|
This will override the 'fetch' option specified in the mapping for
|
||||||
|
that association, but only for that query.
|
||||||
|
|
||||||
|
|
||||||
|
Generating Proxy classes
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
In a production environment, it is highly recommended to use
|
||||||
|
``AUTOGENERATE_NEVER`` to allow for optimal performances.
|
||||||
|
However you will be required to generate the proxies manually
|
||||||
|
using the Doctrine Console:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$ ./doctrine orm:generate-proxies
|
||||||
|
|
||||||
|
The other options are interesting in development environment:
|
||||||
|
|
||||||
|
- ``AUTOGENERATE_ALWAYS`` will require you to create and configure
|
||||||
|
a proxy directory. Proxies will be generated and written to file
|
||||||
|
on each request, so any modification to your code will be acknowledged.
|
||||||
|
|
||||||
|
- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing
|
||||||
|
proxy file. If your code changes, you will need to regenerate the
|
||||||
|
proxies manually.
|
||||||
|
|
||||||
|
- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request,
|
||||||
|
but without writing them to disk.
|
||||||
|
|
||||||
|
Autoloading Proxies
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
When you deserialize proxy objects from the session or any other storage
|
||||||
|
it is necessary to have an autoloading mechanism in place for these classes.
|
||||||
|
For implementation reasons Proxy class names are not PSR-0 compliant. This
|
||||||
|
means that you have to register a special autoloader for these classes:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\ORM\Proxy\Autoloader;
|
||||||
|
|
||||||
|
$proxyDir = "/path/to/proxies";
|
||||||
|
$proxyNamespace = "MyProxies";
|
||||||
|
|
||||||
|
Autoloader::register($proxyDir, $proxyNamespace);
|
||||||
|
|
||||||
|
If you want to execute additional logic to intercept the proxy file not found
|
||||||
|
state you can pass a closure as the third argument. It will be called with
|
||||||
|
the arguments proxydir, namespace and className when the proxy file could not
|
||||||
|
be found.
|
||||||
|
|
||||||
|
Multiple Metadata Sources
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
When using different components using Doctrine 2 you may end up
|
||||||
|
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:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\ORM\Mapping\Driver\DriverChain;
|
||||||
|
|
||||||
|
$chain = new DriverChain();
|
||||||
|
$chain->addDriver($xmlDriver, 'Doctrine\Tests\Models\Company');
|
||||||
|
$chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping');
|
||||||
|
|
||||||
|
Based on the namespace of the entity the loading of entities is
|
||||||
|
delegated to the appropriate driver. The chain semantics come from
|
||||||
|
the fact that the driver loops through all namespaces and matches
|
||||||
|
the entity class name against the namespace using a
|
||||||
|
``strpos() === 0`` call. This means you need to order the drivers
|
||||||
|
correctly if sub-namespaces use different metadata driver
|
||||||
|
implementations.
|
||||||
|
|
||||||
|
|
||||||
|
Default Repository (***OPTIONAL***)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Specifies the FQCN of a subclass of the EntityRepository.
|
||||||
|
That will be available for all entities without a custom repository class.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$config->setDefaultRepositoryClassName($fqcn);
|
||||||
|
$config->getDefaultRepositoryClassName();
|
||||||
|
|
||||||
|
The default value is ``Doctrine\ORM\EntityRepository``.
|
||||||
|
Any repository class must be a subclass of EntityRepository otherwise you got an ORMException
|
||||||
|
|
||||||
|
Setting up the Console
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Doctrine uses the Symfony Console component for generating the command
|
||||||
|
line interface. You can take a look at the ``vendor/bin/doctrine.php``
|
||||||
|
script and the ``Doctrine\ORM\Tools\Console\ConsoleRunner`` command
|
||||||
|
for inspiration how to setup the cli.
|
||||||
|
|
||||||
|
In general the required code looks like this:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$cli = new Application('Doctrine Command Line Interface', \Doctrine\ORM\Version::VERSION);
|
||||||
|
$cli->setCatchExceptions(true);
|
||||||
|
$cli->setHelperSet($helperSet);
|
||||||
|
Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($cli);
|
||||||
|
$cli->run();
|
||||||
|
|
@ -1,6 +1,32 @@
|
|||||||
Annotations Reference
|
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 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.
|
||||||
|
|
||||||
In this chapter a reference of every Doctrine 2 Annotation is given
|
In this chapter a reference of every Doctrine 2 Annotation is given
|
||||||
with short explanations on their context and usage.
|
with short explanations on their context and usage.
|
||||||
|
|
||||||
@ -21,6 +47,7 @@ Index
|
|||||||
- :ref:`@Id <annref_id>`
|
- :ref:`@Id <annref_id>`
|
||||||
- :ref:`@InheritanceType <annref_inheritancetype>`
|
- :ref:`@InheritanceType <annref_inheritancetype>`
|
||||||
- :ref:`@JoinColumn <annref_joincolumn>`
|
- :ref:`@JoinColumn <annref_joincolumn>`
|
||||||
|
- :ref:`@JoinColumns <annref_joincolumns>`
|
||||||
- :ref:`@JoinTable <annref_jointable>`
|
- :ref:`@JoinTable <annref_jointable>`
|
||||||
- :ref:`@ManyToOne <annref_manytoone>`
|
- :ref:`@ManyToOne <annref_manytoone>`
|
||||||
- :ref:`@ManyToMany <annref_manytomany>`
|
- :ref:`@ManyToMany <annref_manytomany>`
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
Association Mapping
|
Association Mapping
|
||||||
===================
|
===================
|
||||||
|
|
||||||
This chapter introduces association mappings which are used to explain
|
This chapter explains mapping associations between objects.
|
||||||
references between objects and are mapped to a relational database using
|
|
||||||
foreign keys.
|
|
||||||
|
|
||||||
Instead of working with the foreign keys directly you will always work with
|
Instead of working with foreign keys in your code, you will always work with
|
||||||
references to objects:
|
references to objects instead and Doctrine will convert those references
|
||||||
|
to foreign keys internally.
|
||||||
|
|
||||||
- A reference to a single object is represented by a foreign key.
|
- A reference to a single object is represented by a foreign key.
|
||||||
- A collection of objects is represented by many foreign keys pointing to the object holding the collection
|
- A collection of objects is represented by many foreign keys pointing to the object holding the collection
|
||||||
@ -17,15 +16,89 @@ This chapter is split into three different sections.
|
|||||||
- :ref:`association_mapping_defaults` are explained that simplify the use-case examples.
|
- :ref:`association_mapping_defaults` are explained that simplify the use-case examples.
|
||||||
- :ref:`collections` are introduced that contain entities in associations.
|
- :ref:`collections` are introduced that contain entities in associations.
|
||||||
|
|
||||||
To master associations you should also learn about :doc:`owning and inverse sides of associations <unitofwork-associations>`
|
To gain a full understanding of associations you should also read about :doc:`owning and
|
||||||
|
inverse sides of associations <unitofwork-associations>`
|
||||||
|
|
||||||
|
Many-To-One, Unidirectional
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
A many-to-one association is the most common association between objects.
|
||||||
|
|
||||||
|
.. configuration-block::
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
/** @Entity **/
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ManyToOne(targetEntity="Address")
|
||||||
|
* @JoinColumn(name="address_id", referencedColumnName="id")
|
||||||
|
**/
|
||||||
|
private $address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @Entity **/
|
||||||
|
class Address
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<doctrine-mapping>
|
||||||
|
<entity name="User">
|
||||||
|
<many-to-one field="address" target-entity="Address">
|
||||||
|
<join-column name="address_id" referenced-column-name="id" />
|
||||||
|
</many-to-one>
|
||||||
|
</entity>
|
||||||
|
</doctrine-mapping>
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
User:
|
||||||
|
type: entity
|
||||||
|
manyToOne:
|
||||||
|
address:
|
||||||
|
targetEntity: Address
|
||||||
|
joinColumn:
|
||||||
|
name: address_id
|
||||||
|
referencedColumnName: id
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The above ``@JoinColumn`` is optional as it would default
|
||||||
|
to ``address_id`` and ``id`` anyways. You can omit it and let it
|
||||||
|
use the defaults.
|
||||||
|
|
||||||
|
Generated MySQL Schema:
|
||||||
|
|
||||||
|
.. code-block:: sql
|
||||||
|
|
||||||
|
CREATE TABLE User (
|
||||||
|
id INT AUTO_INCREMENT NOT NULL,
|
||||||
|
address_id INT DEFAULT NULL,
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
) ENGINE = InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE Address (
|
||||||
|
id INT AUTO_INCREMENT NOT NULL,
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
) ENGINE = InnoDB;
|
||||||
|
|
||||||
|
ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id);
|
||||||
|
|
||||||
One-To-One, Unidirectional
|
One-To-One, Unidirectional
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
A unidirectional one-to-one association is very common. Here is an
|
Here is an example of a one-to-one association with a ``Product`` entity that
|
||||||
example of a ``Product`` that has one ``Shipping`` object
|
references one ``Shipping`` entity. The ``Shipping`` does not reference back to
|
||||||
associated to it. The ``Shipping`` side does not reference back to
|
the ``Product`` so that the reference is said to be unidirectional, in one
|
||||||
the ``Product`` so it is unidirectional.
|
direction only.
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
|
|
||||||
@ -83,6 +156,7 @@ Generated MySQL Schema:
|
|||||||
CREATE TABLE Product (
|
CREATE TABLE Product (
|
||||||
id INT AUTO_INCREMENT NOT NULL,
|
id INT AUTO_INCREMENT NOT NULL,
|
||||||
shipping_id INT DEFAULT NULL,
|
shipping_id INT DEFAULT NULL,
|
||||||
|
UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipping_id),
|
||||||
PRIMARY KEY(id)
|
PRIMARY KEY(id)
|
||||||
) ENGINE = InnoDB;
|
) ENGINE = InnoDB;
|
||||||
CREATE TABLE Shipping (
|
CREATE TABLE Shipping (
|
||||||
@ -183,7 +257,7 @@ relation, the table ``Cart``.
|
|||||||
One-To-One, Self-referencing
|
One-To-One, Self-referencing
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
You can easily have self referencing one-to-one relationships like
|
You can define a self-referencing one-to-one relationships like
|
||||||
below.
|
below.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -217,6 +291,102 @@ With the generated MySQL Schema:
|
|||||||
) ENGINE = InnoDB;
|
) ENGINE = InnoDB;
|
||||||
ALTER TABLE Student ADD FOREIGN KEY (mentor_id) REFERENCES Student(id);
|
ALTER TABLE Student ADD FOREIGN KEY (mentor_id) REFERENCES Student(id);
|
||||||
|
|
||||||
|
One-To-Many, Bidirectional
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
A one-to-many association has to be bidirectional, unless you are using an
|
||||||
|
additional join-table. This is necessary, because of the foreign key
|
||||||
|
in a one-to-many association being defined on the "many" side. Doctrine
|
||||||
|
needs a many-to-one association that defines the mapping of this
|
||||||
|
foreign key.
|
||||||
|
|
||||||
|
This bidirectional mapping requires the ``mappedBy`` attribute on the
|
||||||
|
``OneToMany`` association and the ``inversedBy`` attribute on the ``ManyToOne``
|
||||||
|
association.
|
||||||
|
|
||||||
|
.. configuration-block::
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
|
||||||
|
/** @Entity **/
|
||||||
|
class Product
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
/**
|
||||||
|
* @OneToMany(targetEntity="Feature", mappedBy="product")
|
||||||
|
**/
|
||||||
|
private $features;
|
||||||
|
// ...
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->features = new ArrayCollection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @Entity **/
|
||||||
|
class Feature
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
/**
|
||||||
|
* @ManyToOne(targetEntity="Product", inversedBy="features")
|
||||||
|
* @JoinColumn(name="product_id", referencedColumnName="id")
|
||||||
|
**/
|
||||||
|
private $product;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<doctrine-mapping>
|
||||||
|
<entity name="Product">
|
||||||
|
<one-to-many field="features" target-entity="Feature" mapped-by="product" />
|
||||||
|
</entity>
|
||||||
|
<entity name="Feature">
|
||||||
|
<many-to-one field="product" target-entity="Product" inversed-by="features">
|
||||||
|
<join-column name="product_id" referenced-column-name="id" />
|
||||||
|
</many-to-one>
|
||||||
|
</entity>
|
||||||
|
</doctrine-mapping>
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
Product:
|
||||||
|
type: entity
|
||||||
|
oneToMany:
|
||||||
|
features:
|
||||||
|
targetEntity: Feature
|
||||||
|
mappedBy: product
|
||||||
|
Feature:
|
||||||
|
type: entity
|
||||||
|
manyToOne:
|
||||||
|
product:
|
||||||
|
targetEntity: Product
|
||||||
|
inversedBy: features
|
||||||
|
joinColumn:
|
||||||
|
name: product_id
|
||||||
|
referencedColumnName: id
|
||||||
|
|
||||||
|
Note that the @JoinColumn is not really necessary in this example,
|
||||||
|
as the defaults would be the same.
|
||||||
|
|
||||||
|
Generated MySQL Schema:
|
||||||
|
|
||||||
|
.. code-block:: sql
|
||||||
|
|
||||||
|
CREATE TABLE Product (
|
||||||
|
id INT AUTO_INCREMENT NOT NULL,
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
) ENGINE = InnoDB;
|
||||||
|
CREATE TABLE Feature (
|
||||||
|
id INT AUTO_INCREMENT NOT NULL,
|
||||||
|
product_id INT DEFAULT NULL,
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
) ENGINE = InnoDB;
|
||||||
|
ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id);
|
||||||
|
|
||||||
One-To-Many, Unidirectional with Join Table
|
One-To-Many, Unidirectional with Join Table
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
@ -225,12 +395,6 @@ join table. From Doctrine's point of view, it is simply mapped as a
|
|||||||
unidirectional many-to-many whereby a unique constraint on one of
|
unidirectional many-to-many whereby a unique constraint on one of
|
||||||
the join columns enforces the one-to-many cardinality.
|
the join columns enforces the one-to-many cardinality.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
One-To-Many uni-directional relations with join-table only
|
|
||||||
work using the @ManyToMany annotation and a unique-constraint.
|
|
||||||
|
|
||||||
|
|
||||||
The following example sets up such a unidirectional one-to-many association:
|
The following example sets up such a unidirectional one-to-many association:
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
@ -325,171 +489,6 @@ Generates the following MySQL Schema:
|
|||||||
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id);
|
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id);
|
||||||
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id);
|
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id);
|
||||||
|
|
||||||
|
|
||||||
Many-To-One, Unidirectional
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
You can easily implement a many-to-one unidirectional association
|
|
||||||
with the following:
|
|
||||||
|
|
||||||
.. configuration-block::
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
/** @Entity **/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ManyToOne(targetEntity="Address")
|
|
||||||
* @JoinColumn(name="address_id", referencedColumnName="id")
|
|
||||||
**/
|
|
||||||
private $address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @Entity **/
|
|
||||||
class Address
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
.. code-block:: xml
|
|
||||||
|
|
||||||
<doctrine-mapping>
|
|
||||||
<entity name="User">
|
|
||||||
<many-to-one field="address" target-entity="Address">
|
|
||||||
<join-column name="address_id" referenced-column-name="id" />
|
|
||||||
</many-to-one>
|
|
||||||
</entity>
|
|
||||||
</doctrine-mapping>
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
User:
|
|
||||||
type: entity
|
|
||||||
manyToOne:
|
|
||||||
address:
|
|
||||||
targetEntity: Address
|
|
||||||
joinColumn:
|
|
||||||
name: address_id
|
|
||||||
referencedColumnName: id
|
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The above ``@JoinColumn`` is optional as it would default
|
|
||||||
to ``address_id`` and ``id`` anyways. You can omit it and let it
|
|
||||||
use the defaults.
|
|
||||||
|
|
||||||
|
|
||||||
Generated MySQL Schema:
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE User (
|
|
||||||
id INT AUTO_INCREMENT NOT NULL,
|
|
||||||
address_id INT DEFAULT NULL,
|
|
||||||
PRIMARY KEY(id)
|
|
||||||
) ENGINE = InnoDB;
|
|
||||||
|
|
||||||
CREATE TABLE Address (
|
|
||||||
id INT AUTO_INCREMENT NOT NULL,
|
|
||||||
PRIMARY KEY(id)
|
|
||||||
) ENGINE = InnoDB;
|
|
||||||
|
|
||||||
ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id);
|
|
||||||
|
|
||||||
One-To-Many, Bidirectional
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Bidirectional one-to-many associations are very common. The
|
|
||||||
following code shows an example with a Product and a Feature
|
|
||||||
class:
|
|
||||||
|
|
||||||
.. configuration-block::
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
/** @Entity **/
|
|
||||||
class Product
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
/**
|
|
||||||
* @OneToMany(targetEntity="Feature", mappedBy="product")
|
|
||||||
**/
|
|
||||||
private $features;
|
|
||||||
// ...
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$this->features = new \Doctrine\Common\Collections\ArrayCollection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @Entity **/
|
|
||||||
class Feature
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
/**
|
|
||||||
* @ManyToOne(targetEntity="Product", inversedBy="features")
|
|
||||||
* @JoinColumn(name="product_id", referencedColumnName="id")
|
|
||||||
**/
|
|
||||||
private $product;
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
.. code-block:: xml
|
|
||||||
|
|
||||||
<doctrine-mapping>
|
|
||||||
<entity name="Product">
|
|
||||||
<one-to-many field="features" target-entity="Feature" mapped-by="product" />
|
|
||||||
</entity>
|
|
||||||
<entity name="Feature">
|
|
||||||
<many-to-one field="product" target-entity="Product" inversed-by="features">
|
|
||||||
<join-column name="product_id" referenced-column-name="id" />
|
|
||||||
</many-to-one>
|
|
||||||
</entity>
|
|
||||||
</doctrine-mapping>
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
Product:
|
|
||||||
type: entity
|
|
||||||
oneToMany:
|
|
||||||
features:
|
|
||||||
targetEntity: Feature
|
|
||||||
mappedBy: product
|
|
||||||
Feature:
|
|
||||||
type: entity
|
|
||||||
manyToOne:
|
|
||||||
product:
|
|
||||||
targetEntity: Product
|
|
||||||
inversedBy: features
|
|
||||||
joinColumn:
|
|
||||||
name: product_id
|
|
||||||
referencedColumnName: id
|
|
||||||
|
|
||||||
|
|
||||||
Note that the @JoinColumn is not really necessary in this example,
|
|
||||||
as the defaults would be the same.
|
|
||||||
|
|
||||||
Generated MySQL Schema:
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE Product (
|
|
||||||
id INT AUTO_INCREMENT NOT NULL,
|
|
||||||
PRIMARY KEY(id)
|
|
||||||
) ENGINE = InnoDB;
|
|
||||||
CREATE TABLE Feature (
|
|
||||||
id INT AUTO_INCREMENT NOT NULL,
|
|
||||||
product_id INT DEFAULT NULL,
|
|
||||||
PRIMARY KEY(id)
|
|
||||||
) ENGINE = InnoDB;
|
|
||||||
ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id);
|
|
||||||
|
|
||||||
One-To-Many, Self-referencing
|
One-To-Many, Self-referencing
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
@ -755,8 +754,8 @@ one is bidirectional.
|
|||||||
The MySQL schema is exactly the same as for the Many-To-Many
|
The MySQL schema is exactly the same as for the Many-To-Many
|
||||||
uni-directional case above.
|
uni-directional case above.
|
||||||
|
|
||||||
Picking Owning and Inverse Side
|
Owning and Inverse Side on a ManyToMany association
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
For Many-To-Many associations you can chose which entity is the
|
For Many-To-Many associations you can chose which entity is the
|
||||||
owning and which the inverse side. There is a very simple semantic
|
owning and which the inverse side. There is a very simple semantic
|
||||||
@ -868,11 +867,9 @@ Generated MySQL Schema:
|
|||||||
Mapping Defaults
|
Mapping Defaults
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
Before we introduce all the association mappings in detail, you
|
The ``@JoinColumn`` and ``@JoinTable`` definitions are usually optional and have
|
||||||
should note that the @JoinColumn and @JoinTable definitions are
|
sensible default values. The defaults for a join column in a
|
||||||
usually optional and have sensible default values. The defaults for
|
one-to-one/many-to-one association is as follows:
|
||||||
a join column in a one-to-one/many-to-one association is as
|
|
||||||
follows:
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -972,8 +969,7 @@ similar defaults. As an example, consider this mapping:
|
|||||||
groups:
|
groups:
|
||||||
targetEntity: Group
|
targetEntity: Group
|
||||||
|
|
||||||
This is essentially the same as the following, more verbose,
|
This is essentially the same as the following, more verbose, mapping:
|
||||||
mapping:
|
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
|
|
||||||
@ -1042,73 +1038,28 @@ minimum.
|
|||||||
Collections
|
Collections
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
In all the examples of many-valued associations in this manual we
|
Unfortunately, PHP arrays, while being great for many things, are missing
|
||||||
will make use of a ``Collection`` interface and a corresponding
|
features that make them suitable for lazy loading in the context of an ORM.
|
||||||
default implementation ``ArrayCollection`` that are defined in the
|
This is why in all the examples of many-valued associations in this manual we
|
||||||
``Doctrine\Common\Collections`` namespace. Why do we need that?
|
will make use of a ``Collection`` interface and its
|
||||||
Doesn't that couple my domain model to Doctrine? Unfortunately, PHP
|
default implementation ``ArrayCollection`` that are both defined in the
|
||||||
arrays, while being great for many things, do not make up for good
|
``Doctrine\Common\Collections`` namespace. A collection implements
|
||||||
collections of business objects, especially not in the context of
|
the PHP interfaces ``ArrayAccess``, ``Traversable`` and ``Countable``.
|
||||||
an ORM. The reason is that plain PHP arrays can not be
|
|
||||||
transparently extended / instrumented in PHP code, which is
|
|
||||||
necessary for a lot of advanced ORM features. The classes /
|
|
||||||
interfaces that come closest to an OO collection are ArrayAccess
|
|
||||||
and ArrayObject but until instances of these types can be used in
|
|
||||||
all places where a plain array can be used (something that may
|
|
||||||
happen in PHP6) their usability is fairly limited. You "can"
|
|
||||||
type-hint on ``ArrayAccess`` instead of ``Collection``, since the
|
|
||||||
Collection interface extends ``ArrayAccess``, but this will
|
|
||||||
severely limit you in the way you can work with the collection,
|
|
||||||
because the ``ArrayAccess`` API is (intentionally) very primitive
|
|
||||||
and more importantly because you can not pass this collection to
|
|
||||||
all the useful PHP array functions, which makes it very hard to
|
|
||||||
work with.
|
|
||||||
|
|
||||||
.. warning::
|
.. note::
|
||||||
|
|
||||||
The Collection interface and ArrayCollection class,
|
The Collection interface and ArrayCollection class,
|
||||||
like everything else in the Doctrine namespace, are neither part of
|
like everything else in the Doctrine namespace, are neither part of
|
||||||
the ORM, nor the DBAL, it is a plain PHP class that has no outside
|
the ORM, nor the DBAL, it is a plain PHP class that has no outside
|
||||||
dependencies apart from dependencies on PHP itself (and the SPL).
|
dependencies apart from dependencies on PHP itself (and the SPL).
|
||||||
Therefore using this class in your domain classes and elsewhere
|
Therefore using this class in your model and elsewhere
|
||||||
does not introduce a coupling to the persistence layer. The
|
does not introduce a coupling to the ORM.
|
||||||
Collection class, like everything else in the Common namespace, is
|
|
||||||
not part of the persistence layer. You could even copy that class
|
|
||||||
over to your project if you want to remove Doctrine from your
|
|
||||||
project and all your domain classes will work the same as before.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Initializing Collections
|
Initializing Collections
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
You have to be careful when using entity fields that contain a
|
You should always initialize the collections of your ``@OneToMany``
|
||||||
collection of related entities. Say we have a User entity that
|
and ``@ManyToMany`` associations in the constructor of your entities:
|
||||||
contains a collection of groups:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
/** @Entity **/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
/** @ManyToMany(targetEntity="Group") **/
|
|
||||||
private $groups;
|
|
||||||
|
|
||||||
public function getGroups()
|
|
||||||
{
|
|
||||||
return $this->groups;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
With this code alone the ``$groups`` field only contains an
|
|
||||||
instance of ``Doctrine\Common\Collections\Collection`` if the user
|
|
||||||
is retrieved from Doctrine, however not after you instantiated a
|
|
||||||
fresh instance of the User. When your user entity is still new
|
|
||||||
``$groups`` will obviously be null.
|
|
||||||
|
|
||||||
This is why we recommend to initialize all collection fields to an
|
|
||||||
empty ``ArrayCollection`` in your entities constructor:
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -1132,13 +1083,12 @@ empty ``ArrayCollection`` in your entities constructor:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Now the following code will work even if the Entity hasn't
|
The following code will then work even if the Entity hasn't
|
||||||
been associated with an EntityManager yet:
|
been associated with an EntityManager yet:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$group = $entityManager->find('Group', $groupId);
|
$group = new Group();
|
||||||
$user = new User();
|
$user = new User();
|
||||||
$user->getGroups()->add($group);
|
$user->getGroups()->add($group);
|
||||||
|
|
||||||
|
@ -1,77 +1,72 @@
|
|||||||
Basic Mapping
|
Basic Mapping
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This chapter explains the basic mapping of objects and properties.
|
This guide explains the basic mapping of entities and properties.
|
||||||
Mapping of associations will be covered in the next chapter
|
After working through this guide you should know:
|
||||||
"Association Mapping".
|
|
||||||
|
|
||||||
Mapping Drivers
|
- 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.
|
||||||
|
|
||||||
Doctrine provides several different ways for specifying
|
Mapping of associations will be covered in the next chapter on
|
||||||
object-relational mapping metadata:
|
:doc:`Association Mapping <association-mapping>`.
|
||||||
|
|
||||||
|
Guide Assumptions
|
||||||
|
-----------------
|
||||||
|
|
||||||
- Docblock Annotations
|
You should have already :doc:`installed and configure <configuration>`
|
||||||
- XML
|
Doctrine.
|
||||||
- YAML
|
|
||||||
|
|
||||||
This manual usually mentions docblock annotations in all the examples
|
Creating Classes for the Database
|
||||||
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
|
Every PHP object that you want to save in the database using Doctrine
|
||||||
in more detail. There is also an Annotation reference chapter.
|
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.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
If you're wondering which mapping driver gives the best
|
All metadata drivers perform equally. Once the metadata of a class has been
|
||||||
performance, the answer is: They all give exactly the same performance.
|
read from the source (annotations, xml or yaml) it is stored in an instance
|
||||||
Once the metadata of a class has
|
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are
|
||||||
been read from the source (annotations, xml or yaml) it is stored
|
stored in the metadata cache. If you're not using a metadata cache (not
|
||||||
in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class
|
recommended!) then the XML driver is the fastest.
|
||||||
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::
|
.. configuration-block::
|
||||||
|
|
||||||
@ -79,7 +74,7 @@ to be designated as an entity. This can be done through the
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
/** @Entity */
|
/** @Entity */
|
||||||
class MyPersistentClass
|
class Message
|
||||||
{
|
{
|
||||||
//...
|
//...
|
||||||
}
|
}
|
||||||
@ -87,20 +82,20 @@ to be designated as an entity. This can be done through the
|
|||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<doctrine-mapping>
|
<doctrine-mapping>
|
||||||
<entity name="MyPersistentClass">
|
<entity name="Message">
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
</entity>
|
</entity>
|
||||||
</doctrine-mapping>
|
</doctrine-mapping>
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
MyPersistentClass:
|
Message:
|
||||||
type: entity
|
type: entity
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
By default, the entity will be persisted to a table with the same
|
With no additional information, Doctrine expects the entity to be saved
|
||||||
name as the class name. In order to change that, you can use the
|
into a table with the same name as the class in our case ``Message``.
|
||||||
``@Table`` annotation as follows:
|
You can change this by configuring information about the table:
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
|
|
||||||
@ -109,9 +104,9 @@ name as the class name. In order to change that, you can use the
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* @Entity
|
* @Entity
|
||||||
* @Table(name="my_persistent_class")
|
* @Table(name="message")
|
||||||
*/
|
*/
|
||||||
class MyPersistentClass
|
class Message
|
||||||
{
|
{
|
||||||
//...
|
//...
|
||||||
}
|
}
|
||||||
@ -119,51 +114,131 @@ name as the class name. In order to change that, you can use the
|
|||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<doctrine-mapping>
|
<doctrine-mapping>
|
||||||
<entity name="MyPersistentClass" table="my_persistent_class">
|
<entity name="Message" table="message">
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
</entity>
|
</entity>
|
||||||
</doctrine-mapping>
|
</doctrine-mapping>
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
MyPersistentClass:
|
Message:
|
||||||
type: entity
|
type: entity
|
||||||
table: my_persistent_class
|
table: message
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
Now instances of MyPersistentClass will be persisted into a table
|
Now the class ``Message`` will be saved and fetched from the table ``message``.
|
||||||
named ``my_persistent_class``.
|
|
||||||
|
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
|
||||||
|
name: 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 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.)
|
||||||
|
- ``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-mapping-types:
|
||||||
|
|
||||||
Doctrine Mapping Types
|
Doctrine Mapping Types
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
A Doctrine Mapping Type defines the mapping between a PHP type and
|
The ``type`` option used in the ``@Column`` accepts any of the existing
|
||||||
an SQL type. All Doctrine Mapping Types that ship with Doctrine are
|
Doctrine types or even your own custom types. A Doctrine type defines
|
||||||
fully portable between different RDBMS. You can even write your own
|
the conversion between PHP and SQL types, independent from the database vendor
|
||||||
custom mapping types that might or might not be portable, which is
|
you are using. All Mapping Types that ship with Doctrine are fully portable
|
||||||
explained later in this chapter.
|
between the supported database systems.
|
||||||
|
|
||||||
For example, the Doctrine Mapping Type ``string`` defines the
|
As an example, the Doctrine Mapping Type ``string`` defines the
|
||||||
mapping from a PHP string to an SQL VARCHAR (or VARCHAR2 etc.
|
mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc.
|
||||||
depending on the RDBMS brand). Here is a quick overview of the
|
depending on the RDBMS brand). Here is a quick overview of the
|
||||||
built-in mapping types:
|
built-in mapping types:
|
||||||
|
|
||||||
|
- ``string``: Type that maps a SQL VARCHAR to a PHP string.
|
||||||
- ``string``: Type that maps an SQL VARCHAR to a PHP string.
|
- ``integer``: Type that maps a SQL INT to a PHP integer.
|
||||||
- ``integer``: Type that maps an SQL INT to a PHP integer.
|
|
||||||
- ``smallint``: Type that maps a database SMALLINT to a PHP
|
- ``smallint``: Type that maps a database SMALLINT to a PHP
|
||||||
integer.
|
integer.
|
||||||
- ``bigint``: Type that maps a database BIGINT to a PHP string.
|
- ``bigint``: Type that maps a database BIGINT to a PHP string.
|
||||||
- ``boolean``: Type that maps an SQL boolean to a PHP boolean.
|
- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
|
||||||
- ``decimal``: Type that maps an SQL DECIMAL to a PHP string.
|
- ``decimal``: Type that maps a SQL DECIMAL to a PHP string.
|
||||||
- ``date``: Type that maps an SQL DATETIME to a PHP DateTime
|
- ``date``: Type that maps a SQL DATETIME to a PHP DateTime
|
||||||
object.
|
object.
|
||||||
- ``time``: Type that maps an SQL TIME to a PHP DateTime object.
|
- ``time``: Type that maps a SQL TIME to a PHP DateTime object.
|
||||||
- ``datetime``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP
|
- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||||
DateTime object.
|
DateTime object.
|
||||||
- ``datetimetz``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP
|
- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||||
DateTime object with timezone.
|
DateTime object with timezone.
|
||||||
- ``text``: Type that maps an SQL CLOB to a PHP string.
|
- ``text``: Type that maps a SQL CLOB to a PHP string.
|
||||||
- ``object``: Type that maps a SQL CLOB to a PHP object using
|
- ``object``: Type that maps a SQL CLOB to a PHP object using
|
||||||
``serialize()`` and ``unserialize()``
|
``serialize()`` and ``unserialize()``
|
||||||
- ``array``: Type that maps a SQL CLOB to a PHP array using
|
- ``array``: Type that maps a SQL CLOB to a PHP array using
|
||||||
@ -178,20 +253,16 @@ built-in mapping types:
|
|||||||
decimal points as separator.
|
decimal points as separator.
|
||||||
- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to
|
- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to
|
||||||
varchar but uses a specific type if the platform supports it.
|
varchar but uses a specific type if the platform supports it.
|
||||||
- ``blob``: Type that maps an SQL BLOB to a PHP resource stream
|
- ``blob``: Type that maps a SQL BLOB to a PHP resource stream
|
||||||
|
|
||||||
|
A cookbook article shows how to define :doc:`your own custom mapping types
|
||||||
|
<../cookbook/custom-mapping-types>`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Doctrine Mapping Types are NOT SQL types and NOT PHP
|
DateTime and Object types are compared by reference, not by value. Doctrine
|
||||||
types! They are mapping types between 2 types.
|
updates this values if the reference changes and therefore behaves as if
|
||||||
Additionally Mapping types are *case-sensitive*. For example, using
|
these objects are immutable value objects.
|
||||||
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.
|
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
@ -206,303 +277,42 @@ built-in mapping types:
|
|||||||
on working with datetimes that gives hints for implementing
|
on working with datetimes that gives hints for implementing
|
||||||
multi timezone applications.
|
multi timezone applications.
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
/** @Entity */
|
|
||||||
class MyPersistentClass
|
|
||||||
{
|
|
||||||
/** @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
|
Identifiers / Primary Keys
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
Every entity class needs an identifier/primary key. You designate
|
Every entity class must have an identifier/primary key. You can select
|
||||||
the field that serves as the identifier with the ``@Id`` marker
|
the field that serves as the identifier with the ``@Id``
|
||||||
annotation. Here is an example:
|
annotation.
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
class MyPersistentClass
|
class Message
|
||||||
{
|
|
||||||
/** @Id @Column(type="integer") */
|
|
||||||
private $id;
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
|
|
||||||
.. code-block:: xml
|
|
||||||
|
|
||||||
<doctrine-mapping>
|
|
||||||
<entity name="MyPersistentClass">
|
|
||||||
<id name="id" type="integer" />
|
|
||||||
<field name="name" length="50" />
|
|
||||||
</entity>
|
|
||||||
</doctrine-mapping>
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
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")
|
* @Id @Column(type="integer")
|
||||||
* @GeneratedValue
|
* @GeneratedValue
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
//...
|
||||||
}
|
}
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<doctrine-mapping>
|
<doctrine-mapping>
|
||||||
<entity name="MyPersistentClass">
|
<entity name="Message">
|
||||||
<id name="id" type="integer">
|
<id name="id" type="integer">
|
||||||
<generator strategy="AUTO" />
|
<generator strategy="AUTO" />
|
||||||
</id>
|
</id>
|
||||||
<field name="name" length="50" />
|
<!-- -->
|
||||||
</entity>
|
</entity>
|
||||||
</doctrine-mapping>
|
</doctrine-mapping>
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
MyPersistentClass:
|
Message:
|
||||||
type: entity
|
type: entity
|
||||||
id:
|
id:
|
||||||
id:
|
id:
|
||||||
@ -510,15 +320,12 @@ like this:
|
|||||||
generator:
|
generator:
|
||||||
strategy: AUTO
|
strategy: AUTO
|
||||||
fields:
|
fields:
|
||||||
name:
|
# fields here
|
||||||
length: 50
|
|
||||||
|
|
||||||
This tells Doctrine to automatically generate a value for the
|
In most cases using the automatic generator strategy (``@GeneratedValue``) is
|
||||||
identifier. How this value is generated is specified by the
|
what you want. It defaults to the identifier generation mechanism your current
|
||||||
``strategy`` attribute, which is optional and defaults to 'AUTO'. A
|
database vendor prefers: AUTO_INCREMENT with MySQL, SERIAL with PostgreSQL,
|
||||||
value of ``AUTO`` tells Doctrine to use the generation strategy
|
Sequences with Oracle and so on.
|
||||||
that is preferred by the currently used database platform. See
|
|
||||||
below for details.
|
|
||||||
|
|
||||||
Identifier Generation Strategies
|
Identifier Generation Strategies
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -526,12 +333,11 @@ Identifier Generation Strategies
|
|||||||
The previous example showed how to use the default identifier
|
The previous example showed how to use the default identifier
|
||||||
generation strategy without knowing the underlying database with
|
generation strategy without knowing the underlying database with
|
||||||
the AUTO-detection strategy. It is also possible to specify the
|
the AUTO-detection strategy. It is also possible to specify the
|
||||||
identifier generation strategy more explicitly, which allows to
|
identifier generation strategy more explicitly, which allows you to
|
||||||
make use of some additional features.
|
make use of some additional features.
|
||||||
|
|
||||||
Here is the list of possible generation strategies:
|
Here is the list of possible generation strategies:
|
||||||
|
|
||||||
|
|
||||||
- ``AUTO`` (default): Tells Doctrine to pick the strategy that is
|
- ``AUTO`` (default): Tells Doctrine to pick the strategy that is
|
||||||
preferred by the used database platform. The preferred strategies
|
preferred by the used database platform. The preferred strategies
|
||||||
are IDENTITY for MySQL, SQLite and MsSQL and SEQUENCE for Oracle
|
are IDENTITY for MySQL, SQLite and MsSQL and SEQUENCE for Oracle
|
||||||
@ -564,30 +370,31 @@ besides specifying the sequence's name:
|
|||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
class User
|
class Message
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @Id
|
* @Id
|
||||||
* @GeneratedValue(strategy="SEQUENCE")
|
* @GeneratedValue(strategy="SEQUENCE")
|
||||||
* @SequenceGenerator(sequenceName="tablename_seq", initialValue=1, allocationSize=100)
|
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
|
||||||
*/
|
*/
|
||||||
protected $id = null;
|
protected $id = null;
|
||||||
|
//...
|
||||||
}
|
}
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<doctrine-mapping>
|
<doctrine-mapping>
|
||||||
<entity name="User">
|
<entity name="Message">
|
||||||
<id name="id" type="integer">
|
<id name="id" type="integer">
|
||||||
<generator strategy="SEQUENCE" />
|
<generator strategy="SEQUENCE" />
|
||||||
<sequence-generator sequence-name="tablename_seq" allocation-size="100" initial-value="1" />
|
<sequence-generator sequence-name="message_seq" allocation-size="100" initial-value="1" />
|
||||||
</id>
|
</id>
|
||||||
</entity>
|
</entity>
|
||||||
</doctrine-mapping>
|
</doctrine-mapping>
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
MyPersistentClass:
|
Message:
|
||||||
type: entity
|
type: entity
|
||||||
id:
|
id:
|
||||||
id:
|
id:
|
||||||
@ -595,7 +402,7 @@ besides specifying the sequence's name:
|
|||||||
generator:
|
generator:
|
||||||
strategy: SEQUENCE
|
strategy: SEQUENCE
|
||||||
sequenceGenerator:
|
sequenceGenerator:
|
||||||
sequenceName: tablename_seq
|
sequenceName: message_seq
|
||||||
allocationSize: 100
|
allocationSize: 100
|
||||||
initialValue: 1
|
initialValue: 1
|
||||||
|
|
||||||
@ -635,25 +442,22 @@ need to access the sequence once to generate the identifiers for
|
|||||||
Composite Keys
|
Composite Keys
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Doctrine 2 allows to use composite primary keys. There are however
|
with Doctrine 2 you can use composite primary keys, using ``@Id`` on more then
|
||||||
some restrictions opposed to using a single identifier. The use of
|
one column. Some restrictions exist opposed to using a single identifier in
|
||||||
the ``@GeneratedValue`` annotation is only supported for simple
|
this case: The use of the ``@GeneratedValue`` annotation is not supported,
|
||||||
(not composite) primary keys, which means you can only use
|
which means you can only use composite keys if you generate the primary key
|
||||||
composite keys if you generate the primary key values yourself
|
values yourself before calling ``EntityManager#persist()`` on the entity.
|
||||||
before calling ``EntityManager#persist()`` on the entity.
|
|
||||||
|
|
||||||
To designate a composite primary key / identifier, simply put the
|
More details on composite primary keys are discussed in a :doc:`dedicated tutorial
|
||||||
@Id marker annotation on all fields that make up the primary key.
|
<../tutorials/composite-primary-keys>`.
|
||||||
|
|
||||||
Quoting Reserved Words
|
Quoting Reserved Words
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
It may sometimes be necessary to quote a column or table name
|
Sometimes it is necessary to quote a column or table name because of reserved
|
||||||
because it conflicts with a reserved word of the particular RDBMS
|
word conflicts. Doctrine does not quote identifiers automatically, because it
|
||||||
in use. This is often referred to as "Identifier Quoting". To let
|
leads to more problems then it would solve. Quoting tables and column names
|
||||||
Doctrine know that you would like a table or column name to be
|
needs to be done explicitly using ticks in the definition.
|
||||||
quoted in all SQL statements, enclose the table or column name in
|
|
||||||
backticks. Here is an example:
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -666,18 +470,26 @@ according to the used database platform.
|
|||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
Identifier Quoting is not supported for join column
|
Identifier Quoting does not work for join column names or discriminator
|
||||||
names or discriminator column names.
|
column names unless you are using a custom ``QuoteStrategy``.
|
||||||
|
|
||||||
.. warning::
|
.. _reference-basic-mapping-custom-mapping-types:
|
||||||
|
|
||||||
Identifier Quoting is a feature that is mainly intended
|
.. versionadded: 2.3
|
||||||
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).
|
|
||||||
|
|
||||||
|
For more control over column quoting the ``Doctrine\ORM\Mapping\QuoteStrategy`` interface
|
||||||
|
was introduced in 2.3. It is invoked for every column, table, alias and other
|
||||||
|
SQL names. You can implement the QuoteStrategy and set it by calling
|
||||||
|
``Doctrine\ORM\Configuration#setQuoteStrategy()``.
|
||||||
|
|
||||||
|
.. versionadded: 2.4
|
||||||
|
|
||||||
|
The ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name.
|
||||||
|
You can use it with the following code:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\ORM\Mapping\AnsiQuoteStrategy;
|
||||||
|
|
||||||
|
$configuration->setQuoteStrategy(new AnsiQuoteStrategy());
|
||||||
|
@ -106,7 +106,7 @@ Redis
|
|||||||
In order to use the Redis cache driver you must have it compiled
|
In order to use the Redis cache driver you must have it compiled
|
||||||
and enabled in your php.ini. You can read about what is Redis
|
and enabled in your php.ini. You can read about what is Redis
|
||||||
`from here <http://redis.io/>`_. Also check
|
`from here <http://redis.io/>`_. Also check
|
||||||
`here <https://github.com/nicolasff/phpredis/>`_ for how you can use
|
`A PHP extension for Redis <https://github.com/nicolasff/phpredis/>`_ for how you can use
|
||||||
and install Redis PHP extension.
|
and install Redis PHP extension.
|
||||||
|
|
||||||
Below is a simple example of how you could use the Redis cache
|
Below is a simple example of how you could use the Redis cache
|
||||||
@ -211,49 +211,6 @@ By Cache ID
|
|||||||
<?php
|
<?php
|
||||||
$cacheDriver->delete('my_array');
|
$cacheDriver->delete('my_array');
|
||||||
|
|
||||||
You can also pass wild cards to the ``delete()`` method and it will
|
|
||||||
return an array of IDs that were matched and deleted.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$deleted = $cacheDriver->delete('users_*');
|
|
||||||
|
|
||||||
By Regular Expression
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
If you need a little more control than wild cards you can use a PHP
|
|
||||||
regular expression to delete cache entries.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$deleted = $cacheDriver->deleteByRegex('/users_.*/');
|
|
||||||
|
|
||||||
By Prefix
|
|
||||||
^^^^^^^^^
|
|
||||||
|
|
||||||
Because regular expressions are kind of slow, if simply deleting by
|
|
||||||
a prefix or suffix is sufficient, it is recommended that you do
|
|
||||||
that instead of using a regular expression because it will be much
|
|
||||||
faster if you have many cache entries.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$deleted = $cacheDriver->deleteByPrefix('users_');
|
|
||||||
|
|
||||||
By Suffix
|
|
||||||
^^^^^^^^^
|
|
||||||
|
|
||||||
Just like we did above with the prefix you can do the same with a
|
|
||||||
suffix.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$deleted = $cacheDriver->deleteBySuffix('_my_account');
|
|
||||||
|
|
||||||
All
|
All
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
@ -265,17 +222,6 @@ the ``deleteAll()`` method.
|
|||||||
<?php
|
<?php
|
||||||
$deleted = $cacheDriver->deleteAll();
|
$deleted = $cacheDriver->deleteAll();
|
||||||
|
|
||||||
Counting
|
|
||||||
~~~~~~~~
|
|
||||||
|
|
||||||
If you want to count how many entries are stored in the cache
|
|
||||||
driver instance you can use the ``count()`` method.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
echo $cacheDriver->count();
|
|
||||||
|
|
||||||
Namespaces
|
Namespaces
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -1,77 +1,36 @@
|
|||||||
Configuration
|
Installation and Configuration
|
||||||
=============
|
==============================
|
||||||
|
|
||||||
Bootstrapping Doctrine is a relatively simple procedure that
|
Doctrine can be installed with `Composer <http://www.getcomposer.org>`_. For
|
||||||
roughly exists of four steps:
|
older versions we still have `PEAR packages
|
||||||
|
<http://pear.doctrine-project.org>`_.
|
||||||
|
|
||||||
- `Installation <reference/installation>`
|
Define the following requirement in your ``composer.json`` file:
|
||||||
- Making sure Doctrine class files can be loaded on demand.
|
|
||||||
- Obtaining an EntityManager instance.
|
::
|
||||||
- Optional: Configuration of the Console Tool
|
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"doctrine/orm": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then call ``composer install`` from your command line. If you don't know
|
||||||
|
how Composer works, check out their `Getting Started
|
||||||
|
<http://getcomposer.org/doc/00-intro.md>`_ to set up.
|
||||||
|
|
||||||
Class loading
|
Class loading
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Composer
|
|
||||||
^^^^^^^^
|
|
||||||
|
|
||||||
Autoloading is taken care of by Composer. You just have to include the composer autoload file in your project:
|
Autoloading is taken care of by Composer. You just have to include the composer autoload file in your project:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
// Include Composer Autoload
|
// bootstrap.php
|
||||||
|
// Include Composer Autoload (relative to project root).
|
||||||
require_once "vendor/autoload.php";
|
require_once "vendor/autoload.php";
|
||||||
|
|
||||||
Skip the rest of this section unless you are using another installation method.
|
|
||||||
|
|
||||||
Lets start with the class loading setup. We need to set up some
|
|
||||||
class loaders (often called "autoloader") so that Doctrine class
|
|
||||||
files are loaded on demand. The Doctrine namespace contains a very
|
|
||||||
fast and minimalistic class loader that can be used for Doctrine
|
|
||||||
and any other libraries where the coding standards ensure that a
|
|
||||||
class's location in the directory tree is reflected by its name and
|
|
||||||
namespace and where there is a common root namespace.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
You are not forced to use the Doctrine class loader to
|
|
||||||
load Doctrine classes. Doctrine does not care how the classes are
|
|
||||||
loaded, if you want to use a different class loader or your own to
|
|
||||||
load Doctrine classes, just do that. Along the same lines, the
|
|
||||||
class loader in the Doctrine namespace is not meant to be only used
|
|
||||||
for Doctrine classes, too. It is a generic class loader that can be
|
|
||||||
used for any classes that follow some basic naming standards as
|
|
||||||
described above.
|
|
||||||
|
|
||||||
|
|
||||||
The following example shows the setup of a ``ClassLoader`` for the
|
|
||||||
different types of Doctrine Installations:
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
This assumes you've created some kind of script to test
|
|
||||||
the following code in. Something like a ``test.php`` file.
|
|
||||||
|
|
||||||
Tarball Download
|
|
||||||
^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// test.php
|
|
||||||
require 'Doctrine/ORM/Tools/Setup.php';
|
|
||||||
|
|
||||||
$lib = "/path/to/doctrine2-orm/lib";
|
|
||||||
Doctrine\ORM\Tools\Setup::registerAutoloadDirectory($lib);
|
|
||||||
|
|
||||||
Additional Symfony Components
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
All three autoloading setups described above also take care of
|
|
||||||
the autoloading of the Symfony Console and YAML component,
|
|
||||||
which are optional dependencies for Doctrine 2.
|
|
||||||
|
|
||||||
Obtaining an EntityManager
|
Obtaining an EntityManager
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
@ -79,20 +38,16 @@ Once you have prepared the class loading, you acquire an
|
|||||||
*EntityManager* instance. The EntityManager class is the primary
|
*EntityManager* instance. The EntityManager class is the primary
|
||||||
access point to ORM functionality provided by Doctrine.
|
access point to ORM functionality provided by Doctrine.
|
||||||
|
|
||||||
Quick Configuration Example
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The above example is a complete setup of the required options for Doctrine.
|
|
||||||
You can have this step of your code much simpler and use one of the predefined
|
|
||||||
setup methods:
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
// bootstrap.php
|
||||||
|
require_once "vendor/autoload.php";
|
||||||
|
|
||||||
use Doctrine\ORM\Tools\Setup;
|
use Doctrine\ORM\Tools\Setup;
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
|
|
||||||
$paths = array("/path/to/entities-or-mapping-files");
|
$paths = array("/path/to/entity-files");
|
||||||
$isDevMode = false;
|
$isDevMode = false;
|
||||||
|
|
||||||
// the connection configuration
|
// the connection configuration
|
||||||
@ -104,456 +59,83 @@ setup methods:
|
|||||||
);
|
);
|
||||||
|
|
||||||
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode);
|
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode);
|
||||||
$em = EntityManager::create($dbParams, $config);
|
$entityManager = EntityManager::create($dbParams, $config);
|
||||||
|
|
||||||
// or if you prefer yaml or xml
|
Or if you prefer XML:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$paths = array("/path/to/xml-mappings");
|
||||||
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
|
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
|
||||||
|
$entityManager = EntityManager::create($dbParams, $config);
|
||||||
|
|
||||||
|
Or if you prefer YAML:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$paths = array("/path/to/yml-mappings");
|
||||||
$config = Setup::createYAMLMetadataConfiguration($paths, $isDevMode);
|
$config = Setup::createYAMLMetadataConfiguration($paths, $isDevMode);
|
||||||
|
$entityManager = EntityManager::create($dbParams, $config);
|
||||||
|
|
||||||
These setup commands make several assumptions:
|
Inside the ``Setup`` methods several assumptions are made:
|
||||||
|
|
||||||
- If `$devMode` is true always use an ``ArrayCache`` and set ``setAutoGenerateProxyClasses(true)``.
|
- If `$isDevMode` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated 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 `$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 `$devMode` is false, set ``setAutoGenerateProxyClasses(false)``
|
- If `$isDevMode` 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 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.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
You can learn more about the connection configuration in the
|
You can learn more about the database connection configuration in the
|
||||||
`Doctrine DBAL connection configuration reference <http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
|
`Doctrine DBAL connection configuration reference <http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
|
||||||
|
|
||||||
Full Configuration Example
|
Setting up the Commandline Tool
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
-------------------------------
|
||||||
|
|
||||||
The configuration of the EntityManager requires a
|
Doctrine ships with a number of command line tools that are very helpful
|
||||||
``Doctrine\ORM\Configuration`` instance as well as some database
|
during development. You can call this command from the Composer binary
|
||||||
connection parameters. This example shows all the potential
|
directory:
|
||||||
steps of configuration.
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ php vendor/bin/doctrine
|
||||||
|
|
||||||
|
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
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
use Doctrine\ORM\EntityManager,
|
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||||
Doctrine\ORM\Configuration;
|
|
||||||
|
|
||||||
// ...
|
// replace with file to your own project bootstrap
|
||||||
|
require_once 'bootstrap.php';
|
||||||
|
|
||||||
if ($applicationMode == "development") {
|
// replace with mechanism to retrieve EntityManager in your app
|
||||||
$cache = new \Doctrine\Common\Cache\ArrayCache;
|
$entityManager = GetEntityManager();
|
||||||
} else {
|
|
||||||
$cache = new \Doctrine\Common\Cache\ApcCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
$config = new Configuration;
|
return ConsoleRunner::createHelperSet($entityManager);
|
||||||
$config->setMetadataCacheImpl($cache);
|
|
||||||
$driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
|
|
||||||
$config->setMetadataDriverImpl($driverImpl);
|
|
||||||
$config->setQueryCacheImpl($cache);
|
|
||||||
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
|
|
||||||
$config->setProxyNamespace('MyProject\Proxies');
|
|
||||||
|
|
||||||
if ($applicationMode == "development") {
|
On Doctrine 2.3 and below:
|
||||||
$config->setAutoGenerateProxyClasses(true);
|
|
||||||
} else {
|
|
||||||
$config->setAutoGenerateProxyClasses(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
$connectionOptions = array(
|
|
||||||
'driver' => 'pdo_sqlite',
|
|
||||||
'path' => 'database.sqlite'
|
|
||||||
);
|
|
||||||
|
|
||||||
$em = EntityManager::create($connectionOptions, $config);
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Do not use Doctrine without a metadata and query cache!
|
|
||||||
Doctrine is optimized for working with caches. The main
|
|
||||||
parts in Doctrine that are optimized for caching are the metadata
|
|
||||||
mapping information with the metadata cache and the DQL to SQL
|
|
||||||
conversions with the query cache. These 2 caches require only an
|
|
||||||
absolute minimum of memory yet they heavily improve the runtime
|
|
||||||
performance of Doctrine. The recommended cache driver to use with
|
|
||||||
Doctrine is `APC <http://www.php.net/apc>`_. APC provides you with
|
|
||||||
an opcode-cache (which is highly recommended anyway) and a very
|
|
||||||
fast in-memory cache storage that you can use for the metadata and
|
|
||||||
query caches as seen in the previous code snippet.
|
|
||||||
|
|
||||||
Configuration Options
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
The following sections describe all the configuration options
|
|
||||||
available on a ``Doctrine\ORM\Configuration`` instance.
|
|
||||||
|
|
||||||
Proxy Directory (***REQUIRED***)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$config->setProxyDir($dir);
|
// cli-config.php
|
||||||
$config->getProxyDir();
|
require_once 'my_bootstrap.php';
|
||||||
|
|
||||||
Gets or sets the directory where Doctrine generates any proxy
|
// Any way to access the EntityManager from your application
|
||||||
classes. For a detailed explanation on proxy classes and how they
|
$em = GetMyEntityManager();
|
||||||
are used in Doctrine, refer to the "Proxy Objects" section further
|
|
||||||
down.
|
|
||||||
|
|
||||||
Proxy Namespace (***REQUIRED***)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$config->setProxyNamespace($namespace);
|
|
||||||
$config->getProxyNamespace();
|
|
||||||
|
|
||||||
Gets or sets the namespace to use for generated proxy classes. For
|
|
||||||
a detailed explanation on proxy classes and how they are used in
|
|
||||||
Doctrine, refer to the "Proxy Objects" section further down.
|
|
||||||
|
|
||||||
Metadata Driver (***REQUIRED***)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$config->setMetadataDriverImpl($driver);
|
|
||||||
$config->getMetadataDriverImpl();
|
|
||||||
|
|
||||||
Gets or sets the metadata driver implementation that is used by
|
|
||||||
Doctrine to acquire the object-relational metadata for your
|
|
||||||
classes.
|
|
||||||
|
|
||||||
There are currently 4 available implementations:
|
|
||||||
|
|
||||||
|
|
||||||
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
|
|
||||||
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
|
|
||||||
- ``Doctrine\ORM\Mapping\Driver\YamlDriver``
|
|
||||||
- ``Doctrine\ORM\Mapping\Driver\DriverChain``
|
|
||||||
|
|
||||||
Throughout the most part of this manual the AnnotationDriver is
|
|
||||||
used in the examples. For information on the usage of the XmlDriver
|
|
||||||
or YamlDriver please refer to the dedicated chapters
|
|
||||||
``XML Mapping`` and ``YAML Mapping``.
|
|
||||||
|
|
||||||
The annotation driver can be configured with a factory method on
|
|
||||||
the ``Doctrine\ORM\Configuration``:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
|
|
||||||
$config->setMetadataDriverImpl($driverImpl);
|
|
||||||
|
|
||||||
The path information to the entities is required for the annotation
|
|
||||||
driver, because otherwise mass-operations on all entities through
|
|
||||||
the console could not work correctly. All of metadata drivers
|
|
||||||
accept either a single directory as a string or an array of
|
|
||||||
directories. With this feature a single driver can support multiple
|
|
||||||
directories of Entities.
|
|
||||||
|
|
||||||
Metadata Cache (***RECOMMENDED***)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$config->setMetadataCacheImpl($cache);
|
|
||||||
$config->getMetadataCacheImpl();
|
|
||||||
|
|
||||||
Gets or sets the cache implementation to use for caching metadata
|
|
||||||
information, that is, all the information you supply via
|
|
||||||
annotations, xml or yaml, so that they do not need to be parsed and
|
|
||||||
loaded from scratch on every single request which is a waste of
|
|
||||||
resources. The cache implementation must implement the
|
|
||||||
``Doctrine\Common\Cache\Cache`` interface.
|
|
||||||
|
|
||||||
Usage of a metadata cache is highly recommended.
|
|
||||||
|
|
||||||
The recommended implementations for production are:
|
|
||||||
|
|
||||||
|
|
||||||
- ``Doctrine\Common\Cache\ApcCache``
|
|
||||||
- ``Doctrine\Common\Cache\MemcacheCache``
|
|
||||||
- ``Doctrine\Common\Cache\XcacheCache``
|
|
||||||
- ``Doctrine\Common\Cache\RedisCache``
|
|
||||||
|
|
||||||
For development you should use the
|
|
||||||
``Doctrine\Common\Cache\ArrayCache`` which only caches data on a
|
|
||||||
per-request basis.
|
|
||||||
|
|
||||||
Query Cache (***RECOMMENDED***)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$config->setQueryCacheImpl($cache);
|
|
||||||
$config->getQueryCacheImpl();
|
|
||||||
|
|
||||||
Gets or sets the cache implementation to use for caching DQL
|
|
||||||
queries, that is, the result of a DQL parsing process that includes
|
|
||||||
the final SQL as well as meta information about how to process the
|
|
||||||
SQL result set of a query. Note that the query cache does not
|
|
||||||
affect query results. You do not get stale data. This is a pure
|
|
||||||
optimization cache without any negative side-effects (except some
|
|
||||||
minimal memory usage in your cache).
|
|
||||||
|
|
||||||
Usage of a query cache is highly recommended.
|
|
||||||
|
|
||||||
The recommended implementations for production are:
|
|
||||||
|
|
||||||
|
|
||||||
- ``Doctrine\Common\Cache\ApcCache``
|
|
||||||
- ``Doctrine\Common\Cache\MemcacheCache``
|
|
||||||
- ``Doctrine\Common\Cache\XcacheCache``
|
|
||||||
- ``Doctrine\Common\Cache\RedisCache``
|
|
||||||
|
|
||||||
For development you should use the
|
|
||||||
``Doctrine\Common\Cache\ArrayCache`` which only caches data on a
|
|
||||||
per-request basis.
|
|
||||||
|
|
||||||
SQL Logger (***Optional***)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$config->setSQLLogger($logger);
|
|
||||||
$config->getSQLLogger();
|
|
||||||
|
|
||||||
Gets or sets the logger to use for logging all SQL statements
|
|
||||||
executed by Doctrine. The logger class must implement the
|
|
||||||
``Doctrine\DBAL\Logging\SQLLogger`` interface. A simple default
|
|
||||||
implementation that logs to the standard output using ``echo`` and
|
|
||||||
``var_dump`` can be found at
|
|
||||||
``Doctrine\DBAL\Logging\EchoSQLLogger``.
|
|
||||||
|
|
||||||
Auto-generating Proxy Classes (***OPTIONAL***)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$config->setAutoGenerateProxyClasses($bool);
|
|
||||||
$config->getAutoGenerateProxyClasses();
|
|
||||||
|
|
||||||
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
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
You should code your Doctrine2 bootstrapping with two different
|
|
||||||
runtime models in mind. There are some serious benefits of using
|
|
||||||
APC or Memcache in production. In development however this will
|
|
||||||
frequently give you fatal errors, when you change your entities and
|
|
||||||
the cache still keeps the outdated metadata. That is why we
|
|
||||||
recommend the ``ArrayCache`` for development.
|
|
||||||
|
|
||||||
Furthermore you should have the Auto-generating Proxy Classes
|
|
||||||
option to true in development and to false in production. If this
|
|
||||||
option is set to ``TRUE`` it can seriously hurt your script
|
|
||||||
performance if several proxy classes are re-generated during script
|
|
||||||
execution. Filesystem calls of that magnitude can even slower than
|
|
||||||
all the database queries Doctrine issues. Additionally writing a
|
|
||||||
proxy sets an exclusive file lock which can cause serious
|
|
||||||
performance bottlenecks in systems with regular concurrent
|
|
||||||
requests.
|
|
||||||
|
|
||||||
Connection Options
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The ``$connectionOptions`` passed as the first argument to
|
|
||||||
``EntityManager::create()`` has to be either an array or an
|
|
||||||
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 <./../../../../../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 Doctrine 2,
|
|
||||||
proxy objects are used to realize several features but mainly for
|
|
||||||
transparent lazy-loading.
|
|
||||||
|
|
||||||
Proxy objects with their lazy-loading facilities help to keep the
|
|
||||||
subset of objects that are already in memory connected to the rest
|
|
||||||
of the objects. This is an essential property as without it there
|
|
||||||
would always be fragile partial objects at the outer edges of your
|
|
||||||
object graph.
|
|
||||||
|
|
||||||
Doctrine 2 implements a variant of the proxy pattern where it
|
|
||||||
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
|
|
||||||
the class being proxied. This happens in two situations:
|
|
||||||
|
|
||||||
Reference Proxies
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The method ``EntityManager#getReference($entityName, $identifier)``
|
|
||||||
lets you obtain a reference to an entity for which the identifier
|
|
||||||
is known, without loading that entity from the database. This is
|
|
||||||
useful, for example, as a performance enhancement, when you want to
|
|
||||||
establish an association to an entity for which you have the
|
|
||||||
identifier. You could simply do this:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
|
|
||||||
// $itemId comes from somewhere, probably a request parameter
|
|
||||||
$item = $em->getReference('MyProject\Model\Item', $itemId);
|
|
||||||
$cart->addItem($item);
|
|
||||||
|
|
||||||
Here, we added an Item to a Cart without loading the Item from the
|
|
||||||
database. If you invoke any method on the Item instance, it would
|
|
||||||
fully initialize its state transparently from the database. Here
|
|
||||||
$item is actually an instance of the proxy class that was generated
|
|
||||||
for the Item class but your code does not need to care. In fact it
|
|
||||||
**should not care**. Proxy objects should be transparent to your
|
|
||||||
code.
|
|
||||||
|
|
||||||
Association proxies
|
|
||||||
~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The second most important situation where Doctrine uses proxy
|
|
||||||
objects is when querying for objects. Whenever you query for an
|
|
||||||
object that has a single-valued association to another object that
|
|
||||||
is configured LAZY, without joining that association in the same
|
|
||||||
query, Doctrine puts proxy objects in place where normally the
|
|
||||||
associated object would be. Just like other proxies it will
|
|
||||||
transparently initialize itself on first access.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Joining an association in a DQL or native query
|
|
||||||
essentially means eager loading of that association in that query.
|
|
||||||
This will override the 'fetch' option specified in the mapping for
|
|
||||||
that association, but only for that query.
|
|
||||||
|
|
||||||
|
|
||||||
Generating Proxy classes
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Autoloading Proxies
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
When you deserialize proxy objects from the session or any other storage
|
|
||||||
it is necessary to have an autoloading mechanism in place for these classes.
|
|
||||||
For implementation reasons Proxy class names are not PSR-0 compliant. This
|
|
||||||
means that you have to register a special autoloader for these classes:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
use Doctrine\ORM\Proxy\Autoloader;
|
|
||||||
|
|
||||||
$proxyDir = "/path/to/proxies";
|
|
||||||
$proxyNamespace = "MyProxies";
|
|
||||||
|
|
||||||
Autoloader::register($proxyDir, $proxyNamespace);
|
|
||||||
|
|
||||||
If you want to execute additional logic to intercept the proxy file not found
|
|
||||||
state you can pass a closure as the third argument. It will be called with
|
|
||||||
the arguments proxydir, namespace and className when the proxy file could not
|
|
||||||
be found.
|
|
||||||
|
|
||||||
Multiple Metadata Sources
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
When using different components using Doctrine 2 you may end up
|
|
||||||
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:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
use Doctrine\ORM\Mapping\Driver\DriverChain;
|
|
||||||
|
|
||||||
$chain = new DriverChain();
|
|
||||||
$chain->addDriver($xmlDriver, 'Doctrine\Tests\Models\Company');
|
|
||||||
$chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping');
|
|
||||||
|
|
||||||
Based on the namespace of the entity the loading of entities is
|
|
||||||
delegated to the appropriate driver. The chain semantics come from
|
|
||||||
the fact that the driver loops through all namespaces and matches
|
|
||||||
the entity class name against the namespace using a
|
|
||||||
``strpos() === 0`` call. This means you need to order the drivers
|
|
||||||
correctly if sub-namespaces use different metadata driver
|
|
||||||
implementations.
|
|
||||||
|
|
||||||
|
|
||||||
Default Repository (***OPTIONAL***)
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Specifies the FQCN of a subclass of the EntityRepository.
|
|
||||||
That will be available for all entities without a custom repository class.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$config->setDefaultRepositoryClassName($fqcn);
|
|
||||||
$config->getDefaultRepositoryClassName();
|
|
||||||
|
|
||||||
The default value is ``Doctrine\ORM\EntityRepository``.
|
|
||||||
Any repository class must be a subclass of EntityRepository otherwise you got an ORMException
|
|
||||||
|
|
||||||
Setting up the Console
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Doctrine uses the Symfony Console component for generating the command
|
|
||||||
line interface. You can take a look at the ``bin/doctrine.php``
|
|
||||||
script and the ``Doctrine\ORM\Tools\Console\ConsoleRunner`` command
|
|
||||||
for inspiration how to setup the cli.
|
|
||||||
|
|
||||||
If you installed Doctrine 2 through Composer, then the Doctrine command is
|
|
||||||
available to you in the bin-dir, by default at ``vendor/bin/doctrine-orm``.
|
|
||||||
|
|
||||||
In general the required code looks like this:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$cli = new Application('Doctrine Command Line Interface', \Doctrine\ORM\Version::VERSION);
|
|
||||||
$cli->setCatchExceptions(true);
|
|
||||||
$cli->setHelperSet($helperSet);
|
|
||||||
Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($cli);
|
|
||||||
$cli->run();
|
|
||||||
|
|
||||||
|
$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)
|
||||||
|
));
|
||||||
|
@ -89,9 +89,8 @@ 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
|
of both are also allowed and it is possible to wrap both fields and
|
||||||
identification values into aggregation and DQL functions. Numerical
|
identification values into aggregation and DQL functions. Numerical
|
||||||
fields can be part of computations using mathematical operations.
|
fields can be part of computations using mathematical operations.
|
||||||
See the sub-section on
|
See the sub-section on `Functions, Operators, Aggregates`_ for
|
||||||
`DQL Functions, Aggregates and Operations <#dqlfn>`_ on more
|
more information.
|
||||||
information.
|
|
||||||
|
|
||||||
Joins
|
Joins
|
||||||
~~~~~
|
~~~~~
|
||||||
@ -428,13 +427,23 @@ Get all users visible on a given website that have chosen certain gender:
|
|||||||
<?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)');
|
$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)');
|
||||||
|
|
||||||
IDENTITY() DQL Function when the association has a composite primary key:
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
|
Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?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');
|
$query = $em->createQuery('SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1');
|
||||||
|
|
||||||
|
Joins between entities without associations were not possible until version
|
||||||
|
2.4, where you can generate an arbitrary join with the following syntax:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
|
||||||
|
|
||||||
Partial Object Syntax
|
Partial Object Syntax
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -464,14 +473,15 @@ You use the partial syntax when joining as well:
|
|||||||
"NEW" Operator Syntax
|
"NEW" Operator Syntax
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Using the ``NEW`` operator you can construct DTOs from queries.
|
.. 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.
|
- When using ``SELECT NEW`` you don't need to specify a mapped entity.
|
||||||
- You can specify any PHP class, it's only require that you have a matching constructor in your class.
|
- 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,
|
- This approach involves determining exactly which columns you really need,
|
||||||
and instantiating data-transfer object that containing 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:
|
If you want to select data-transfer objects you should create a class:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -910,7 +920,7 @@ Query Result Formats
|
|||||||
|
|
||||||
The format in which the result of a DQL SELECT query is returned
|
The format in which the result of a DQL SELECT query is returned
|
||||||
can be influenced by a so-called ``hydration mode``. A hydration
|
can be influenced by a so-called ``hydration mode``. A hydration
|
||||||
mode specifies a particular way in which an SQL result set is
|
mode specifies a particular way in which a SQL result set is
|
||||||
transformed. Each hydration mode has its own dedicated method on
|
transformed. Each hydration mode has its own dedicated method on
|
||||||
the Query class. Here they are:
|
the Query class. Here they are:
|
||||||
|
|
||||||
@ -1188,7 +1198,7 @@ There are situations when a query you want to execute returns a
|
|||||||
very large result-set that needs to be processed. All the
|
very large result-set that needs to be processed. All the
|
||||||
previously described hydration modes completely load a result-set
|
previously described hydration modes completely load a result-set
|
||||||
into memory which might not be feasible with large result sets. See
|
into memory which might not be feasible with large result sets. See
|
||||||
the `Batch Processing <batch-processing>`_ section on details how
|
the `Batch Processing <batch-processing.html>`_ section on details how
|
||||||
to iterate large result sets.
|
to iterate large result sets.
|
||||||
|
|
||||||
Functions
|
Functions
|
||||||
@ -1291,7 +1301,7 @@ userland:
|
|||||||
Query Cache (DQL Query Only)
|
Query Cache (DQL Query Only)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Parsing a DQL query and converting it into an SQL query against the
|
Parsing a DQL query and converting it into a SQL query against the
|
||||||
underlying database platform obviously has some overhead in
|
underlying database platform obviously has some overhead in
|
||||||
contrast to directly executing Native SQL queries. That is why
|
contrast to directly executing Native SQL queries. That is why
|
||||||
there is a dedicated Query Cache for caching the DQL parser
|
there is a dedicated Query Cache for caching the DQL parser
|
||||||
@ -1582,7 +1592,7 @@ Scalar and Type Expressions
|
|||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression
|
ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression
|
||||||
StringExpression ::= StringPrimary | "(" Subselect ")"
|
StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")"
|
||||||
StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
|
StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
|
||||||
BooleanExpression ::= BooleanPrimary | "(" Subselect ")"
|
BooleanExpression ::= BooleanPrimary | "(" Subselect ")"
|
||||||
BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter
|
BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter
|
||||||
@ -1631,7 +1641,7 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS
|
|||||||
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
|
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
|
||||||
InstanceOfParameter ::= AbstractSchemaName | InputParameter
|
InstanceOfParameter ::= AbstractSchemaName | InputParameter
|
||||||
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
|
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
|
||||||
NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
|
NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL"
|
||||||
ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
|
ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
|
||||||
ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
|
ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@ Events
|
|||||||
======
|
======
|
||||||
|
|
||||||
Doctrine 2 features a lightweight event system that is part of the
|
Doctrine 2 features a lightweight event system that is part of the
|
||||||
Common package.
|
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.
|
||||||
|
|
||||||
The Event System
|
The Event System
|
||||||
----------------
|
----------------
|
||||||
@ -18,12 +20,12 @@ manager.
|
|||||||
$evm = new EventManager();
|
$evm = new EventManager();
|
||||||
|
|
||||||
Now we can add some event listeners to the ``$evm``. Let's create a
|
Now we can add some event listeners to the ``$evm``. Let's create a
|
||||||
``EventTest`` class to play around with.
|
``TestEvent`` class to play around with.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
class EventTest
|
class TestEvent
|
||||||
{
|
{
|
||||||
const preFoo = 'preFoo';
|
const preFoo = 'preFoo';
|
||||||
const postFoo = 'postFoo';
|
const postFoo = 'postFoo';
|
||||||
@ -50,15 +52,15 @@ Now we can add some event listeners to the ``$evm``. Let's create a
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new instance
|
// Create a new instance
|
||||||
$test = new EventTest($evm);
|
$test = new TestEvent($evm);
|
||||||
|
|
||||||
Events can be dispatched by using the ``dispatchEvent()`` method.
|
Events can be dispatched by using the ``dispatchEvent()`` method.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$evm->dispatchEvent(EventTest::preFoo);
|
$evm->dispatchEvent(TestEvent::preFoo);
|
||||||
$evm->dispatchEvent(EventTest::postFoo);
|
$evm->dispatchEvent(TestEvent::postFoo);
|
||||||
|
|
||||||
You can easily remove a listener with the ``removeEventListener()``
|
You can easily remove a listener with the ``removeEventListener()``
|
||||||
method.
|
method.
|
||||||
@ -95,7 +97,13 @@ array of events it should be subscribed to.
|
|||||||
$eventSubscriber = new TestEventSubscriber();
|
$eventSubscriber = new TestEventSubscriber();
|
||||||
$evm->addEventSubscriber($eventSubscriber);
|
$evm->addEventSubscriber($eventSubscriber);
|
||||||
|
|
||||||
Now when you dispatch an event any event subscribers will be
|
.. note::
|
||||||
|
|
||||||
|
The array to return in the ``getSubscribedEvents`` method is a simple array
|
||||||
|
with the values being the event names. The subscriber must have a method
|
||||||
|
that is named exactly like the event.
|
||||||
|
|
||||||
|
Now when you dispatch an event, any event subscribers will be
|
||||||
notified for that event.
|
notified for that event.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -125,13 +133,14 @@ several reasons:
|
|||||||
- It is easy to read.
|
- It is easy to read.
|
||||||
- Simplicity.
|
- Simplicity.
|
||||||
- Each method within an EventSubscriber is named after the
|
- Each method within an EventSubscriber is named after the
|
||||||
corresponding constant. If constant name and constant value differ,
|
corresponding constant's value. If the constant's name and value differ
|
||||||
you MUST use the new value and thus, your code might be subject to
|
it contradicts the intention of using the constant and makes your code
|
||||||
codechanges when the value changes. This contradicts the intention
|
harder to maintain.
|
||||||
of a constant.
|
|
||||||
|
|
||||||
An example for a correct notation can be found in the example
|
An example for a correct notation can be found in the example
|
||||||
``EventTest`` above.
|
``TestEvent`` above.
|
||||||
|
|
||||||
|
.. _reference-events-lifecycle-events:
|
||||||
|
|
||||||
Lifecycle Events
|
Lifecycle Events
|
||||||
----------------
|
----------------
|
||||||
@ -149,7 +158,7 @@ the life-time of their registered entities.
|
|||||||
- prePersist - The prePersist event occurs for a given entity
|
- prePersist - The prePersist event occurs for a given entity
|
||||||
before the respective EntityManager persist operation for that
|
before the respective EntityManager persist operation for that
|
||||||
entity is executed. It should be noted that this event is only triggered on
|
entity is executed. It should be noted that this event is only triggered on
|
||||||
*initial* persist of an entity
|
*initial* persist of an entity (i.e. it does not trigger on future updates).
|
||||||
- postPersist - The postPersist event occurs for an entity after
|
- postPersist - The postPersist event occurs for an entity after
|
||||||
the entity has been made persistent. It will be invoked after the
|
the entity has been made persistent. It will be invoked after the
|
||||||
database insert operations. Generated primary key values are
|
database insert operations. Generated primary key values are
|
||||||
@ -163,7 +172,7 @@ the life-time of their registered entities.
|
|||||||
database or after the refresh operation has been applied to it.
|
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
|
mapping metadata for a class has been loaded from a mapping source
|
||||||
(annotations/xml/yaml).
|
(annotations/xml/yaml). This event is not a lifecycle callback.
|
||||||
- preFlush - The preFlush event occurs at the very beginning of a flush
|
- preFlush - The preFlush event occurs at the very beginning of a flush
|
||||||
operation. This event is not a lifecycle callback.
|
operation. This event is not a lifecycle callback.
|
||||||
- onFlush - The onFlush event occurs after the change-sets of all
|
- onFlush - The onFlush event occurs after the change-sets of all
|
||||||
@ -173,7 +182,7 @@ the life-time of their registered entities.
|
|||||||
event is not a lifecycle callback.
|
event is not a lifecycle callback.
|
||||||
- onClear - The onClear event occurs when the EntityManager#clear() operation is
|
- onClear - The onClear event occurs when the EntityManager#clear() operation is
|
||||||
invoked, after all references to entities have been removed from the unit of
|
invoked, after all references to entities have been removed from the unit of
|
||||||
work.
|
work. This event is not a lifecycle callback.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
@ -195,12 +204,14 @@ ORM package.
|
|||||||
These can be hooked into by two different types of event
|
These can be hooked into by two different types of event
|
||||||
listeners:
|
listeners:
|
||||||
|
|
||||||
|
|
||||||
- Lifecycle Callbacks are methods on the entity classes that are
|
- Lifecycle Callbacks are methods on the entity classes that are
|
||||||
called when the event is triggered. They receives some kind of ``EventArgs``.
|
called when the event is triggered. As of v2.4 they receive some kind
|
||||||
- Lifecycle Event Listeners are classes with specific callback
|
of ``EventArgs`` instance.
|
||||||
methods that receives some kind of ``EventArgs`` instance which
|
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
||||||
give access to the entity, EntityManager or other relevant data.
|
methods that receives some kind of ``EventArgs`` instance.
|
||||||
|
|
||||||
|
The EventArgs instance received by the listener gives access to the entity,
|
||||||
|
EntityManager and other relevant data.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -214,10 +225,11 @@ listeners:
|
|||||||
Lifecycle Callbacks
|
Lifecycle Callbacks
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
A lifecycle event is a regular event with the additional feature of
|
Lifecycle Callbacks are defined on an entity class. They allow you to
|
||||||
providing a mechanism to register direct callbacks inside the
|
trigger callbacks whenever an instance of that entity class experiences
|
||||||
corresponding entity classes that are executed when the lifecycle
|
a relevant lifecycle event. More than one callback can be defined for each
|
||||||
event occurs.
|
lifecycle event. Lifecycle Callbacks are best used for simple operations
|
||||||
|
specific to a particular entity class's lifecycle.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -267,8 +279,9 @@ event occurs.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Note that when using annotations you have to apply the
|
Note that the methods set as lifecycle callbacks need to be public and,
|
||||||
@HasLifecycleCallbacks marker annotation on the entity class.
|
when using these annotations, you have to apply the
|
||||||
|
``@HasLifecycleCallbacks`` marker annotation on the entity class.
|
||||||
|
|
||||||
If you want to register lifecycle callbacks from YAML or XML you
|
If you want to register lifecycle callbacks from YAML or XML you
|
||||||
can do it with the following.
|
can do it with the following.
|
||||||
@ -282,9 +295,13 @@ can do it with the following.
|
|||||||
name:
|
name:
|
||||||
type: string(50)
|
type: string(50)
|
||||||
lifecycleCallbacks:
|
lifecycleCallbacks:
|
||||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
|
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
|
||||||
postPersist: [ doStuffOnPostPersist ]
|
postPersist: [ doStuffOnPostPersist ]
|
||||||
|
|
||||||
|
In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
|
||||||
|
are triggering on and the value is the method (or methods) to call. The allowed
|
||||||
|
event types are the ones listed in the previous Lifecycle Events section.
|
||||||
|
|
||||||
XML would look something like this:
|
XML would look something like this:
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
@ -307,9 +324,14 @@ XML would look something like this:
|
|||||||
|
|
||||||
</doctrine-mapping>
|
</doctrine-mapping>
|
||||||
|
|
||||||
You just need to make sure a public ``doStuffOnPrePersist()`` and
|
In XML the ``type`` of the lifecycle-callback entry is the event that you
|
||||||
``doStuffOnPostPersist()`` method is defined on your ``User``
|
are triggering on and the ``method`` is the method to call. The allowed event
|
||||||
model.
|
types are the ones listed in the previous Lifecycle Events section.
|
||||||
|
|
||||||
|
When using YAML or XML you need to remember to create public methods to match the
|
||||||
|
callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``,
|
||||||
|
``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be
|
||||||
|
defined on your ``User`` model.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -325,15 +347,17 @@ model.
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function doOtherStuffOnPrePersist()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
public function doStuffOnPostPersist()
|
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
|
Lifecycle Callbacks Event Argument
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
@ -360,18 +384,80 @@ With the additional argument you have access to the
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Listening to Lifecycle Events
|
Listening and subscribing to Lifecycle Events
|
||||||
-----------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
Lifecycle event listeners are much more powerful than the simple
|
Lifecycle event listeners are much more powerful than the simple
|
||||||
lifecycle callbacks that are defined on the entity classes. They
|
lifecycle callbacks that are defined on the entity classes. They
|
||||||
allow to implement re-usable behaviors between different entity
|
sit at a level above the entities and allow you to implement re-usable
|
||||||
classes, yet require much more detailed knowledge about the inner
|
behaviors across different entity classes.
|
||||||
|
|
||||||
|
Note that they require much more detailed knowledge about the inner
|
||||||
workings of the EntityManager and UnitOfWork. Please read the
|
workings of the EntityManager and UnitOfWork. Please read the
|
||||||
*Implementing Event Listeners* section carefully if you are trying
|
*Implementing Event Listeners* section carefully if you are trying
|
||||||
to write your own listener.
|
to write your own listener.
|
||||||
|
|
||||||
To register an event listener you have to hook it into the
|
For event subscribers, there are no surprises. They declare the
|
||||||
|
lifecycle events in their ``getSubscribedEvents`` method and provide
|
||||||
|
public methods that expect the relevant arguments.
|
||||||
|
|
||||||
|
A lifecycle event listener looks like the following:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||||
|
|
||||||
|
class MyEventListener
|
||||||
|
{
|
||||||
|
public function preUpdate(LifecycleEventArgs $args)
|
||||||
|
{
|
||||||
|
$entity = $args->getObject();
|
||||||
|
$entityManager = $args->getObjectManager();
|
||||||
|
|
||||||
|
// perhaps you only want to act on some "Product" entity
|
||||||
|
if ($entity instanceof Product) {
|
||||||
|
// do something with the Product
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
A lifecycle event subscriber may looks like this:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\ORM\Events;
|
||||||
|
use Doctrine\Common\EventSubscriber;
|
||||||
|
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||||
|
|
||||||
|
class MyEventSubscriber implements EventSubscriber
|
||||||
|
{
|
||||||
|
public function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
Events::postUpdate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postUpdate(LifecycleEventArgs $args)
|
||||||
|
{
|
||||||
|
$entity = $args->getObject();
|
||||||
|
$entityManager = $args->getObjectManager();
|
||||||
|
|
||||||
|
// perhaps you only want to act on some "Product" entity
|
||||||
|
if ($entity instanceof Product) {
|
||||||
|
// do something with the Product
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Lifecycle events are triggered for all entities. It is the responsibility
|
||||||
|
of the listeners and subscribers to check if the entity is of a type
|
||||||
|
it wants to handle.
|
||||||
|
|
||||||
|
To register an event listener or subscriber, you have to hook it into the
|
||||||
EventManager that is passed to the EntityManager factory:
|
EventManager that is passed to the EntityManager factory:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -406,8 +492,8 @@ data and lost updates/persists/removes.
|
|||||||
|
|
||||||
For the described events that are also lifecycle callback events
|
For the described events that are also lifecycle callback events
|
||||||
the restrictions apply as well, with the additional restriction
|
the restrictions apply as well, with the additional restriction
|
||||||
that you do not have access to the EntityManager or UnitOfWork APIs
|
that (prior to version 2.4) you do not have access to the
|
||||||
inside these events.
|
EntityManager or UnitOfWork APIs inside these events.
|
||||||
|
|
||||||
prePersist
|
prePersist
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
@ -431,10 +517,10 @@ The following restrictions apply to ``prePersist``:
|
|||||||
- If you are using a PrePersist Identity Generator such as
|
- If you are using a PrePersist Identity Generator such as
|
||||||
sequences the ID value will *NOT* be available within any
|
sequences the ID value will *NOT* be available within any
|
||||||
PrePersist events.
|
PrePersist events.
|
||||||
- Doctrine will not recognize changes made to relations in a pre
|
- Doctrine will not recognize changes made to relations in a prePersist
|
||||||
persist event called by "reachability" through a cascade persist
|
event called by "reachability" through a cascade persist unless you
|
||||||
unless you use the internal ``UnitOfWork`` API. We do not recommend
|
use the internal ``UnitOfWork`` API. We do not recommend such
|
||||||
such operations in the persistence by reachability context, so do
|
operations in the persistence by reachability context, so do
|
||||||
this at your own risk and possibly supported by unit-tests.
|
this at your own risk and possibly supported by unit-tests.
|
||||||
|
|
||||||
preRemove
|
preRemove
|
||||||
@ -523,20 +609,20 @@ mentioned sets. See this example:
|
|||||||
The following restrictions apply to the onFlush event:
|
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.
|
calling ``EntityManager#persist()`` is not enough.
|
||||||
You have to execute an additional call to
|
You have to execute an additional call to
|
||||||
``$unitOfWork->computeChangeSet($classMetadata, $entity)``.
|
``$unitOfWork->computeChangeSet($classMetadata, $entity)``.
|
||||||
- Changing primitive fields or associations requires you to
|
- Changing primitive fields or associations requires you to
|
||||||
explicitly trigger a re-computation of the changeset of the
|
explicitly trigger a re-computation of the changeset of the
|
||||||
affected entity. This can be done by either calling
|
affected entity. This can be done by calling
|
||||||
``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``.
|
``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``.
|
||||||
|
|
||||||
postFlush
|
postFlush
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
``postFlush`` is called at the end of ``EntityManager#flush()``. ``EntityManager#flush()`` can be
|
``postFlush`` is called at the end of ``EntityManager#flush()``.
|
||||||
called safely inside its listeners.
|
``EntityManager#flush()`` can **NOT** be called safely inside its listeners.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -629,7 +715,8 @@ Restrictions for this event:
|
|||||||
recognized by the flush operation anymore.
|
recognized by the flush operation anymore.
|
||||||
- Changes to fields of the passed entities are not recognized by
|
- Changes to fields of the passed entities are not recognized by
|
||||||
the flush operation anymore, use the computed change-set passed to
|
the flush operation anymore, use the computed change-set passed to
|
||||||
the event to modify primitive field values.
|
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
|
- 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
|
API are strongly discouraged and don't work as expected outside the
|
||||||
@ -655,9 +742,9 @@ Entity listeners
|
|||||||
|
|
||||||
.. versionadded:: 2.4
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
An entity listeners is a lifecycle listener classes used for an entity.
|
An entity listener is a lifecycle listener class used for an entity.
|
||||||
|
|
||||||
- The entity listeners mapping may be applied to an entity class or mapped superclass.
|
- The entity listener's 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.
|
- An entity listener is defined by mapping the entity class with the corresponding mapping.
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
@ -699,9 +786,10 @@ An ``Entity Listener`` could be any class, by default it should be a class with
|
|||||||
|
|
||||||
- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity
|
- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity
|
||||||
- An entity listener method receives two arguments, the entity instance and the lifecycle event.
|
- An entity listener method receives two arguments, the entity instance and the lifecycle event.
|
||||||
- A callback method could be defined by naming convention or specifying a method mapping.
|
- The callback method can be defined by naming convention or specifying a method mapping.
|
||||||
- When the listener mapping is not given the parser will lookup for methods that match with the naming convention.
|
- When a listener mapping is not given the parser will use the naming convention to look for a matching method,
|
||||||
- When the listener mapping is given the parser won't lookup for any naming convention.
|
e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event.
|
||||||
|
- When a listener mapping is given the parser will not look for any methods using the naming convention.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -714,8 +802,8 @@ An ``Entity Listener`` could be any class, by default it should be a class with
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
To define a specific event listener method
|
To define a specific event listener method (one that does not follow the naming convention)
|
||||||
you should map the listener method using the event type mapping.
|
you need to map the listener method using the event type mapping:
|
||||||
|
|
||||||
.. configuration-block::
|
.. configuration-block::
|
||||||
|
|
||||||
@ -793,9 +881,9 @@ you should map the listener method using the event type mapping.
|
|||||||
|
|
||||||
Entity listeners resolver
|
Entity listeners resolver
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Doctrine invoke the listener resolver to get the listener instance.
|
Doctrine invokes the listener resolver to get the listener instance.
|
||||||
|
|
||||||
- An resolver allows you register a specific ``Entity Listener`` instance.
|
- A 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``
|
- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver``
|
||||||
|
|
||||||
Specifying an entity listener instance :
|
Specifying an entity listener instance :
|
||||||
@ -863,12 +951,12 @@ process and manipulate the instance.
|
|||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$test = new EventTest();
|
$test = new TestEvent();
|
||||||
$metadataFactory = $em->getMetadataFactory();
|
$metadataFactory = $em->getMetadataFactory();
|
||||||
$evm = $em->getEventManager();
|
$evm = $em->getEventManager();
|
||||||
$evm->addEventListener(Events::loadClassMetadata, $test);
|
$evm->addEventListener(Events::loadClassMetadata, $test);
|
||||||
|
|
||||||
class EventTest
|
class TestEvent
|
||||||
{
|
{
|
||||||
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
|
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
|
||||||
{
|
{
|
||||||
|
@ -143,11 +143,11 @@ See the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>
|
|||||||
the details.
|
the details.
|
||||||
|
|
||||||
Why does Doctrine not create proxy objects for my inheritance hierarchy?
|
Why does Doctrine not create proxy objects for my inheritance hierarchy?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you set a many-to-one or one-to-one association target-entity to any parent class of
|
If you set a many-to-one or one-to-one association target-entity to any parent class of
|
||||||
an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of.
|
an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of.
|
||||||
To find this out it has to execute an SQL query to look this information up in the database.
|
To find this out it has to execute a SQL query to look this information up in the database.
|
||||||
|
|
||||||
EntityGenerator
|
EntityGenerator
|
||||||
---------------
|
---------------
|
||||||
@ -162,7 +162,7 @@ is supposed to kick-start you, but not towards 100%.
|
|||||||
Why does the EntityGenerator not generate inheritance correctly?
|
Why does the EntityGenerator not generate inheritance correctly?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierachy.
|
Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy.
|
||||||
This is why the generation of inherited entities does not fully work. You have to adjust some additional
|
This is why the generation of inherited entities does not fully work. You have to adjust some additional
|
||||||
code to get this one working correctly.
|
code to get this one working correctly.
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ 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
|
the conditional clauses of queries, regardless the place where the SQL is
|
||||||
generated (e.g. from a DQL query, or by loading associated entities).
|
generated (e.g. from a DQL query, or by loading associated entities).
|
||||||
|
|
||||||
The filter functionality works on SQL level. Whether an SQL query is generated
|
The filter functionality works on SQL level. Whether a SQL query is generated
|
||||||
in a Persister, during lazy loading, in extra lazy collections or from DQL.
|
in a Persister, during lazy loading, in extra lazy collections or from DQL.
|
||||||
Each time the system iterates over all the enabled filters, adding a new SQL
|
Each time the system iterates over all the enabled filters, adding a new SQL
|
||||||
part as a filter returns.
|
part as a filter returns.
|
||||||
|
@ -260,6 +260,7 @@ or auto-increment details). Furthermore each child table has to
|
|||||||
have a foreign key pointing from the id column to the root table id
|
have a foreign key pointing from the id column to the root table id
|
||||||
column and cascading on delete.
|
column and cascading on delete.
|
||||||
|
|
||||||
|
.. _inheritence_mapping_overrides:
|
||||||
|
|
||||||
Overrides
|
Overrides
|
||||||
---------
|
---------
|
||||||
|
@ -1,18 +1,5 @@
|
|||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
Doctrine was installable in many different ways, however `Composer <http://www.getcomposer.org>`_ turned out to be one of the best things for PHP in a long time.
|
The installation chapter has moved to `Installation and Configuration
|
||||||
This is why we moved all installation to use Composer only.
|
<reference/configuration>`_.
|
||||||
|
|
||||||
Define the following requirement in your ``composer.json`` file:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
{
|
|
||||||
"require": {
|
|
||||||
"doctrine/orm": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Then run the composer command and you are done. Continue with the
|
|
||||||
:doc:`Configuration <configuration>`.
|
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
Native SQL
|
Native SQL
|
||||||
==========
|
==========
|
||||||
|
|
||||||
A ``NativeQuery`` lets you execute native SELECT SQL statements, mapping the results
|
With ``NativeQuery`` you can execute native SELECT SQL statements
|
||||||
according to your specifications. Such a specification that
|
and map the results to Doctrine entities or any other result format
|
||||||
describes how an SQL result set is mapped to a Doctrine result is
|
supported by Doctrine.
|
||||||
represented by a ``ResultSetMapping``. It describes how each column
|
|
||||||
of the database result should be mapped by Doctrine in terms of the
|
|
||||||
object graph. This allows you to map arbitrary SQL code to objects,
|
|
||||||
such as highly vendor-optimized SQL or stored-procedures.
|
|
||||||
|
|
||||||
Because writing ``ResultSetMapping`` is not so simple, there is a convenience
|
In order to make this mapping possible, you need to describe
|
||||||
wrapper around it called a ``ResultSetMappingBuilder``. The last section
|
to Doctrine what columns in the result map to which entity property.
|
||||||
of this chapter describes its usage.
|
This description is represented by a ``ResultSetMapping`` object.
|
||||||
|
|
||||||
|
With this feature you can map arbitrary SQL code to objects, such as highly
|
||||||
|
vendor-optimized SQL or stored-procedures.
|
||||||
|
|
||||||
|
Writing ``ResultSetMapping`` from scratch is complex, but there is a convenience
|
||||||
|
wrapper around it called a ``ResultSetMappingBuilder``. It can generate
|
||||||
|
the mappings for you based on Entities and even generates the ``SELECT``
|
||||||
|
clause based on this information for you.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -25,14 +29,75 @@ The NativeQuery class
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
To create a ``NativeQuery`` you use the method
|
To create a ``NativeQuery`` you use the method
|
||||||
``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As
|
``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As you can see in
|
||||||
you can see in the signature of this method, it expects 2
|
the signature of this method, it expects 2 ingredients: The SQL you want to
|
||||||
ingredients: The SQL you want to execute and the
|
execute and the ``ResultSetMapping`` that describes how the results will be
|
||||||
``ResultSetMapping`` that describes how the results will be
|
|
||||||
mapped.
|
mapped.
|
||||||
|
|
||||||
Once you obtained an instance of a ``NativeQuery``, you can bind
|
Once you obtained an instance of a ``NativeQuery``, you can bind parameters to
|
||||||
parameters to it and finally execute it.
|
it with the same API that ``Query`` has and execute it.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\ORM\Query\ResultSetMapping;
|
||||||
|
|
||||||
|
$rsm = new ResultSetMapping();
|
||||||
|
// build rsm here
|
||||||
|
|
||||||
|
$query = $entityManager->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
|
||||||
|
$query->setParameter(1, 'romanb');
|
||||||
|
|
||||||
|
$users = $query->getResult();
|
||||||
|
|
||||||
|
ResultSetMappingBuilder
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
An easy start into ResultSet mapping is the ``ResultSetMappingBuilder`` object.
|
||||||
|
This has several benefits:
|
||||||
|
|
||||||
|
- The builder takes care of automatically updating your ``ResultSetMapping``
|
||||||
|
when the fields or associations change on the metadata of an entity.
|
||||||
|
- You can generate the required ``SELECT`` expression for a builder
|
||||||
|
by converting it to a string.
|
||||||
|
- The API is much simpler than the usual ``ResultSetMapping`` API.
|
||||||
|
|
||||||
|
One downside is that the builder API does not yet support entities
|
||||||
|
with inheritance hierachies.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||||
|
|
||||||
|
$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);
|
||||||
|
$rsm->addRootEntityFromClassMetadata('MyProject\User', 'u');
|
||||||
|
$rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', 'u', 'address', array('id' => 'address_id'));
|
||||||
|
|
||||||
|
The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well.
|
||||||
|
|
||||||
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
|
Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause
|
||||||
|
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
|
||||||
|
a mapping from DQL alias (key) to SQL alias (value)
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$selectClause = $builder->generateSelectClause(array(
|
||||||
|
'u' => 't1',
|
||||||
|
'g' => 't2'
|
||||||
|
));
|
||||||
|
$sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id";
|
||||||
|
|
||||||
|
|
||||||
The ResultSetMapping
|
The ResultSetMapping
|
||||||
--------------------
|
--------------------
|
||||||
@ -136,7 +201,7 @@ joined entity result.
|
|||||||
Field results
|
Field results
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
A field result describes the mapping of a single column in an SQL
|
A field result describes the mapping of a single column in a SQL
|
||||||
result set to a field in an entity. As such, field results are
|
result set to a field in an entity. As such, field results are
|
||||||
inherently bound to entity results. You add a field result through
|
inherently bound to entity results. You add a field result through
|
||||||
``ResultSetMapping#addFieldResult()``. Again, let's examine the
|
``ResultSetMapping#addFieldResult()``. Again, let's examine the
|
||||||
@ -165,7 +230,7 @@ column should be set.
|
|||||||
Scalar results
|
Scalar results
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A scalar result describes the mapping of a single column in an SQL
|
A scalar result describes the mapping of a single column in a SQL
|
||||||
result set to a scalar value in the Doctrine result. Scalar results
|
result set to a scalar value in the Doctrine result. Scalar results
|
||||||
are typically used for aggregate values but any column in the SQL
|
are typically used for aggregate values but any column in the SQL
|
||||||
result set can be mapped as a scalar value. To add a scalar result
|
result set can be mapped as a scalar value. To add a scalar result
|
||||||
@ -190,7 +255,7 @@ of the column will be placed in the transformed Doctrine result.
|
|||||||
Meta results
|
Meta results
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
A meta result describes a single column in an SQL result set that
|
A meta result describes a single column in a SQL result set that
|
||||||
is either a foreign key or a discriminator column. These columns
|
is either a foreign key or a discriminator column. These columns
|
||||||
are essential for Doctrine to properly construct objects out of SQL
|
are essential for Doctrine to properly construct objects out of SQL
|
||||||
result sets. To add a column as a meta result use
|
result sets. To add a column as a meta result use
|
||||||
@ -203,18 +268,21 @@ detail:
|
|||||||
/**
|
/**
|
||||||
* Adds a meta column (foreign key or discriminator column) to the result set.
|
* Adds a meta column (foreign key or discriminator column) to the result set.
|
||||||
*
|
*
|
||||||
* @param string $alias
|
* @param string $alias
|
||||||
* @param string $columnAlias
|
* @param string $columnAlias
|
||||||
* @param string $columnName
|
* @param string $columnName
|
||||||
|
* @param boolean $isIdentifierColumn
|
||||||
*/
|
*/
|
||||||
public function addMetaResult($alias, $columnAlias, $columnName)
|
public function addMetaResult($alias, $columnAlias, $columnName, $isIdentifierColumn = false)
|
||||||
|
|
||||||
The first parameter is the alias of the entity result to which the
|
The first parameter is the alias of the entity result to which the
|
||||||
meta column belongs. A meta result column (foreign key or
|
meta column belongs. A meta result column (foreign key or
|
||||||
discriminator column) always belongs to to an entity result. The
|
discriminator column) always belongs to an entity result. The
|
||||||
second parameter is the column alias/name of the column in the SQL
|
second parameter is the column alias/name of the column in the SQL
|
||||||
result set and the third parameter is the column name used in the
|
result set and the third parameter is the column name used in the
|
||||||
mapping.
|
mapping.
|
||||||
|
The fourth parameter should be set to true in case the primary key
|
||||||
|
of the entity is the foreign key you're adding.
|
||||||
|
|
||||||
Discriminator Column
|
Discriminator Column
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -366,35 +434,6 @@ are actually a subtype of User. When using DQL, Doctrine
|
|||||||
automatically includes the necessary joins for this mapping
|
automatically includes the necessary joins for this mapping
|
||||||
strategy but with native SQL it is your responsibility.
|
strategy but with native SQL it is your responsibility.
|
||||||
|
|
||||||
ResultSetMappingBuilder
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
There are some downsides with Native SQL queries. The primary one is that you have to adjust all result set mapping
|
|
||||||
definitions if names of columns change. In DQL this is detected dynamically when the Query is regenerated with
|
|
||||||
the current metadata.
|
|
||||||
|
|
||||||
To avoid this hassle you can use the ``ResultSetMappingBuilder`` class. It allows to add all columns of an entity
|
|
||||||
to a result set mapping. To avoid clashes you can optionally rename specific columns when you are doing the same
|
|
||||||
in your sQL statement:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
|
||||||
|
|
||||||
$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);
|
|
||||||
$rsm->addRootEntityFromClassMetadata('MyProject\User', 'u');
|
|
||||||
$rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', 'u', 'address', array('id' => 'address_id'));
|
|
||||||
|
|
||||||
For entities with more columns the builder is very convenient to use. It extends the ``ResultSetMapping`` class
|
|
||||||
and as such has all the functionality of it as well. Currently the ``ResultSetMappingBuilder`` does not support
|
|
||||||
entities with inheritance.
|
|
||||||
|
|
||||||
|
|
||||||
Named Native Query
|
Named Native Query
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -543,12 +582,12 @@ it represents the name of a defined @SqlResultSetMapping.
|
|||||||
|
|
||||||
Things to note:
|
Things to note:
|
||||||
- The resultset mapping declares the entities retrieved by this native query.
|
- The resultset mapping declares the entities retrieved by this native query.
|
||||||
- Each field of the entity is bound to an SQL alias (or column name).
|
- Each field of the entity is bound to a SQL alias (or column name).
|
||||||
- All fields of the entity including the ones of subclasses
|
- All fields of the entity including the ones of subclasses
|
||||||
and the foreign key columns of related entities have to be present in the SQL query.
|
and the foreign key columns of related entities have to be present in the SQL query.
|
||||||
- Field definitions are optional provided that they map to the same
|
- Field definitions are optional provided that they map to the same
|
||||||
column name as the one declared on the class property.
|
column name as the one declared on the class property.
|
||||||
- ``__CLASS__`` is a alias for the mapped class
|
- ``__CLASS__`` is an alias for the mapped class
|
||||||
|
|
||||||
|
|
||||||
In the above example,
|
In the above example,
|
||||||
@ -564,7 +603,6 @@ Let's now see an implicit declaration of the property / column.
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
namespace MyProject\Model;
|
namespace MyProject\Model;
|
||||||
<?php
|
|
||||||
/**
|
/**
|
||||||
* @NamedNativeQueries({
|
* @NamedNativeQueries({
|
||||||
* @NamedNativeQuery(
|
* @NamedNativeQuery(
|
||||||
@ -648,7 +686,6 @@ followed by a dot ("."), followed by the name or the field or property of the pr
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
namespace MyProject\Model;
|
namespace MyProject\Model;
|
||||||
<?php
|
|
||||||
/**
|
/**
|
||||||
* @NamedNativeQueries({
|
* @NamedNativeQueries({
|
||||||
* @NamedNativeQuery(
|
* @NamedNativeQuery(
|
||||||
@ -766,7 +803,6 @@ you can use the resultClass attribute instead of resultSetMapping:
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
namespace MyProject\Model;
|
namespace MyProject\Model;
|
||||||
<?php
|
|
||||||
/**
|
/**
|
||||||
* @NamedNativeQueries({
|
* @NamedNativeQueries({
|
||||||
* @NamedNativeQuery(
|
* @NamedNativeQuery(
|
||||||
@ -814,7 +850,6 @@ You actually can even mix, entities and scalar returns in the same native query
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
namespace MyProject\Model;
|
namespace MyProject\Model;
|
||||||
<?php
|
|
||||||
/**
|
/**
|
||||||
* @NamedNativeQueries({
|
* @NamedNativeQueries({
|
||||||
* @NamedNativeQuery(
|
* @NamedNativeQuery(
|
||||||
|
@ -208,7 +208,7 @@ allowed. Binding parameters can simply be achieved as follows:
|
|||||||
$qb->select('u')
|
$qb->select('u')
|
||||||
->from('User u')
|
->from('User u')
|
||||||
->where('u.id = ?1')
|
->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
|
->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
|
You are not forced to enumerate your placeholders as the
|
||||||
@ -222,7 +222,7 @@ alternative syntax is available:
|
|||||||
$qb->select('u')
|
$qb->select('u')
|
||||||
->from('User u')
|
->from('User u')
|
||||||
->where('u.id = :identifier')
|
->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
|
->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
|
Note that numeric placeholders start with a ? followed by a number
|
||||||
@ -433,6 +433,9 @@ complete list of supported helper methods available:
|
|||||||
// Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%'))
|
// Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%'))
|
||||||
public function like($x, $y); // Returns Expr\Comparison instance
|
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')
|
// Example - $qb->expr()->between('u.id', '1', '10')
|
||||||
public function between($val, $x, $y); // Returns Expr\Func
|
public function between($val, $x, $y); // Returns Expr\Func
|
||||||
|
|
||||||
@ -445,8 +448,8 @@ complete list of supported helper methods available:
|
|||||||
// Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname'))
|
// Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname'))
|
||||||
public function concat($x, $y); // Returns Expr\Func
|
public function concat($x, $y); // Returns Expr\Func
|
||||||
|
|
||||||
// Example - $qb->expr()->substr('u.firstname', 0, 1)
|
// Example - $qb->expr()->substring('u.firstname', 0, 1)
|
||||||
public function substr($x, $from, $len); // Returns Expr\Func
|
public function substring($x, $from, $len); // Returns Expr\Func
|
||||||
|
|
||||||
// Example - $qb->expr()->lower('u.firstname')
|
// Example - $qb->expr()->lower('u.firstname')
|
||||||
public function lower($x); // Returns Expr\Func
|
public function lower($x); // Returns Expr\Func
|
||||||
|
@ -4,30 +4,29 @@ Tools
|
|||||||
Doctrine Console
|
Doctrine Console
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The Doctrine Console is a Command Line Interface tool for
|
The Doctrine Console is a Command Line Interface tool for simplifying common
|
||||||
simplifying common tasks during the development of a project that
|
administration tasks during the development of a project that uses Doctrine 2.
|
||||||
uses Doctrine 2.
|
|
||||||
|
|
||||||
Take a look at the :doc:`Configuration <configuration>` for more
|
Take a look at the :doc:`Installation and Configuration <configuration>`
|
||||||
information how to setup the console command.
|
chapter for more information how to setup the console command.
|
||||||
|
|
||||||
Getting Help
|
Display Help Information
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Type ``php vendor/bin/doctrine-orm`` on the command line and you should see an
|
Type ``php vendor/bin/doctrine`` on the command line and you should see an
|
||||||
overview of the available commands or use the --help flag to get
|
overview of the available commands or use the --help flag to get
|
||||||
information on the available commands. If you want to know more
|
information on the available commands. If you want to know more
|
||||||
about the use of generate entities for example, you can call:
|
about the use of generate entities for example, you can call:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$> php vendor/bin/doctrine-orm orm:generate-entities --help
|
$> php vendor/bin/doctrine orm:generate-entities --help
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
Whenever the ``doctrine-orm`` command line tool is invoked, it can
|
Whenever the ``doctrine`` command line tool is invoked, it can
|
||||||
access all Commands that were registered by developer. There is no
|
access all Commands that were registered by developer. There is no
|
||||||
auto-detection mechanism at work. The Doctrine binary
|
auto-detection mechanism at work. The Doctrine binary
|
||||||
already registers all the commands that currently ship with
|
already registers all the commands that currently ship with
|
||||||
@ -132,6 +131,20 @@ The following Commands are currently available:
|
|||||||
update the database schema of EntityManager Storage Connection or
|
update the database schema of EntityManager Storage Connection or
|
||||||
generate the SQL output.
|
generate the SQL output.
|
||||||
|
|
||||||
|
For these commands are also available aliases:
|
||||||
|
|
||||||
|
|
||||||
|
- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``.
|
||||||
|
- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``.
|
||||||
|
- ``orm:generate:entities`` is alias for ``orm:generate-entities``.
|
||||||
|
- ``orm:generate:proxies`` is alias for ``orm:generate-proxies``.
|
||||||
|
- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Console also supports auto completion, for example, instead of
|
||||||
|
``orm:clear-cache:query`` you can use just ``o:c:q``.
|
||||||
|
|
||||||
Database Schema Generation
|
Database Schema Generation
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
@ -70,7 +70,6 @@ looks like this:
|
|||||||
$em->getConnection()->commit();
|
$em->getConnection()->commit();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$em->getConnection()->rollback();
|
$em->getConnection()->rollback();
|
||||||
$em->close();
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,14 +80,12 @@ require an active transaction. Such methods will throw a
|
|||||||
``TransactionRequiredException`` to inform you of that
|
``TransactionRequiredException`` to inform you of that
|
||||||
requirement.
|
requirement.
|
||||||
|
|
||||||
A more convenient alternative for explicit transaction demarcation
|
A more convenient alternative for explicit transaction demarcation is the use
|
||||||
is the use of provided control abstractions in the form of
|
of provided control abstractions in the form of
|
||||||
``Connection#transactional($func)`` and
|
``Connection#transactional($func)`` and ``EntityManager#transactional($func)``.
|
||||||
``EntityManager#transactional($func)``. When used, these control
|
When used, these control abstractions ensure that you never forget to rollback
|
||||||
abstractions ensure that you never forget to rollback the
|
the transaction, in addition to the obvious code reduction. An example that is
|
||||||
transaction or close the ``EntityManager``, apart from the obvious
|
functionally equivalent to the previously shown code looks as follows:
|
||||||
code reduction. An example that is functionally equivalent to the
|
|
||||||
previously shown code looks as follows:
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -104,8 +101,8 @@ previously shown code looks as follows:
|
|||||||
The difference between ``Connection#transactional($func)`` and
|
The difference between ``Connection#transactional($func)`` and
|
||||||
``EntityManager#transactional($func)`` is that the latter
|
``EntityManager#transactional($func)`` is that the latter
|
||||||
abstraction flushes the ``EntityManager`` prior to transaction
|
abstraction flushes the ``EntityManager`` prior to transaction
|
||||||
commit and also closes the ``EntityManager`` properly when an
|
commit and rolls back the transaction when an
|
||||||
exception occurs (in addition to rolling back the transaction).
|
exception occurs.
|
||||||
|
|
||||||
Exception Handling
|
Exception Handling
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
@ -182,7 +179,7 @@ example we'll use an integer.
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
Alternatively a datetime type can be used (which maps to an SQL
|
Alternatively a datetime type can be used (which maps to a SQL
|
||||||
timestamp or datetime):
|
timestamp or datetime):
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
@ -117,7 +117,7 @@ that consume new memory.
|
|||||||
Now whenever you call ``EntityManager#flush`` Doctrine will iterate over the
|
Now whenever you call ``EntityManager#flush`` Doctrine will iterate over the
|
||||||
Identity Map and for each object compares the original property and association
|
Identity Map and for each object compares the original property and association
|
||||||
values with the values that are currently set on the object. If changes are
|
values with the values that are currently set on the object. If changes are
|
||||||
detected then the object is queued for an SQL UPDATE operation. Only the fields
|
detected then the object is queued for a SQL UPDATE operation. Only the fields
|
||||||
that actually changed are updated.
|
that actually changed are updated.
|
||||||
|
|
||||||
This process has an obvious performance impact. The larger the size of the
|
This process has an obvious performance impact. The larger the size of the
|
||||||
@ -157,7 +157,7 @@ wishes to be hydrated. Default result-types include:
|
|||||||
- SQL to a single result variable
|
- SQL to a single result variable
|
||||||
|
|
||||||
Hydration to entities and arrays is one of most complex parts of Doctrine
|
Hydration to entities and arrays is one of most complex parts of Doctrine
|
||||||
algorithm-wise. It can built results with for example:
|
algorithm-wise. It can build results with for example:
|
||||||
|
|
||||||
- Single table selects
|
- Single table selects
|
||||||
- Joins with n:1 or 1:n cardinality, grouping belonging to the same parent.
|
- Joins with n:1 or 1:n cardinality, grouping belonging to the same parent.
|
||||||
|
@ -2,27 +2,25 @@ Working with Associations
|
|||||||
=========================
|
=========================
|
||||||
|
|
||||||
Associations between entities are represented just like in regular
|
Associations between entities are represented just like in regular
|
||||||
object-oriented PHP, with references to other objects or
|
object-oriented PHP code using references to other objects or
|
||||||
collections of objects. When it comes to persistence, it is
|
collections of objects.
|
||||||
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
|
- If an entity is removed from a collection, the association is
|
||||||
removed, not the entity itself. A collection of entities always
|
removed, not the entity itself. A collection of entities always
|
||||||
only represents the association to the containing entities, not the
|
only represents the association to the containing entities, not the
|
||||||
entity itself.
|
entity itself.
|
||||||
- Collection-valued :ref:`persistent fields <architecture_persistent_fields>` have to be instances of the
|
- When a bidirectional assocation 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
|
||||||
``Doctrine\Common\Collections\Collection`` interface.
|
``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
|
Association Example Entities
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
@ -44,10 +42,6 @@ information about its type and if it's the owning or inverse side.
|
|||||||
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
|
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
|
||||||
*
|
*
|
||||||
* @ManyToMany(targetEntity="Comment", inversedBy="userFavorites")
|
* @ManyToMany(targetEntity="Comment", inversedBy="userFavorites")
|
||||||
* @JoinTable(name="user_favorite_comments",
|
|
||||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
|
||||||
* inverseJoinColumns={@JoinColumn(name="favorite_comment_id", referencedColumnName="id")}
|
|
||||||
* )
|
|
||||||
*/
|
*/
|
||||||
private $favorites;
|
private $favorites;
|
||||||
|
|
||||||
@ -55,10 +49,6 @@ information about its type and if it's the owning or inverse side.
|
|||||||
* Unidirectional - Many users have marked many comments as read
|
* Unidirectional - Many users have marked many comments as read
|
||||||
*
|
*
|
||||||
* @ManyToMany(targetEntity="Comment")
|
* @ManyToMany(targetEntity="Comment")
|
||||||
* @JoinTable(name="user_read_comments",
|
|
||||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
|
||||||
* inverseJoinColumns={@JoinColumn(name="comment_id", referencedColumnName="id")}
|
|
||||||
* )
|
|
||||||
*/
|
*/
|
||||||
private $commentsRead;
|
private $commentsRead;
|
||||||
|
|
||||||
@ -93,7 +83,7 @@ information about its type and if it's the owning or inverse side.
|
|||||||
/**
|
/**
|
||||||
* Bidirectional - Many Comments are authored by one user (OWNING SIDE)
|
* Bidirectional - Many Comments are authored by one user (OWNING SIDE)
|
||||||
*
|
*
|
||||||
* @ManyToOne(targetEntity="User", inversedBy="authoredComments")
|
* @ManyToOne(targetEntity="User", inversedBy="commentsAuthored")
|
||||||
*/
|
*/
|
||||||
private $author;
|
private $author;
|
||||||
}
|
}
|
||||||
@ -474,6 +464,7 @@ removed from the system:
|
|||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
$user = $em->find('User', $deleteUserId);
|
$user = $em->find('User', $deleteUserId);
|
||||||
|
|
||||||
foreach ($user->getAuthoredComments() AS $comment) {
|
foreach ($user->getAuthoredComments() AS $comment) {
|
||||||
@ -630,7 +621,7 @@ large collections.
|
|||||||
|
|
||||||
$criteria = Criteria::create()
|
$criteria = Criteria::create()
|
||||||
->where(Criteria::expr()->eq("birthday", "1982-02-17"))
|
->where(Criteria::expr()->eq("birthday", "1982-02-17"))
|
||||||
->orderBy(array("username" => "ASC"))
|
->orderBy(array("username" => Criteria::ASC))
|
||||||
->setFirstResult(0)
|
->setFirstResult(0)
|
||||||
->setMaxResults(20)
|
->setMaxResults(20)
|
||||||
;
|
;
|
||||||
@ -689,7 +680,7 @@ interchangeably, independent of in-memory or sql-backed collections.
|
|||||||
*/
|
*/
|
||||||
public function setMaxResults($maxResults);
|
public function setMaxResults($maxResults);
|
||||||
public function getOrderings();
|
public function getOrderings();
|
||||||
public function getWhereExpresion();
|
public function getWhereExpression();
|
||||||
public function getFirstResult();
|
public function getFirstResult();
|
||||||
public function getMaxResults();
|
public function getMaxResults();
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,6 @@ In order to work, this requires certain conventions:
|
|||||||
convention and you are not forced to do this. You can change the
|
convention and you are not forced to do this. You can change the
|
||||||
file extension easily enough.
|
file extension easily enough.
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
@ -47,9 +45,9 @@ Simplified YAML Driver
|
|||||||
The Symfony project sponsored a driver that simplifies usage of the YAML Driver.
|
The Symfony project sponsored a driver that simplifies usage of the YAML Driver.
|
||||||
The changes between the original driver are:
|
The changes between the original driver are:
|
||||||
|
|
||||||
1. File Extension is .orm.yml
|
- File Extension is .orm.yml
|
||||||
2. Filenames are shortened, "MyProject\Entities\User" will become User.orm.yml
|
- Filenames are shortened, "MyProject\\Entities\\User" will become User.orm.yml
|
||||||
3. You can add a global file and add multiple entities in this file.
|
- You can add a global file and add multiple entities in this file.
|
||||||
|
|
||||||
Configuration of this client works a little bit different:
|
Configuration of this client works a little bit different:
|
||||||
|
|
||||||
@ -116,4 +114,22 @@ of several common elements:
|
|||||||
Be aware that class-names specified in the YAML files should be
|
Be aware that class-names specified in the YAML files should be
|
||||||
fully qualified.
|
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 ]
|
||||||
|
|
||||||
|
@ -8,12 +8,14 @@ Tutorials
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
tutorials/getting-started
|
tutorials/getting-started
|
||||||
|
tutorials/getting-started-database
|
||||||
|
tutorials/getting-started-models
|
||||||
tutorials/working-with-indexed-associations
|
tutorials/working-with-indexed-associations
|
||||||
tutorials/extra-lazy-associations
|
tutorials/extra-lazy-associations
|
||||||
tutorials/composite-primary-keys
|
tutorials/composite-primary-keys
|
||||||
tutorials/ordered-associations
|
tutorials/ordered-associations
|
||||||
tutorials/in-ten-quick-steps
|
|
||||||
tutorials/override-field-association-mappings-in-subclasses
|
tutorials/override-field-association-mappings-in-subclasses
|
||||||
|
tutorials/pagination.rst
|
||||||
|
|
||||||
Reference Guide
|
Reference Guide
|
||||||
---------------
|
---------------
|
||||||
@ -22,9 +24,9 @@ Reference Guide
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
:numbered:
|
:numbered:
|
||||||
|
|
||||||
reference/introduction
|
|
||||||
reference/architecture
|
reference/architecture
|
||||||
reference/configuration
|
reference/installation
|
||||||
|
reference/configuration.rst
|
||||||
reference/faq
|
reference/faq
|
||||||
reference/basic-mapping
|
reference/basic-mapping
|
||||||
reference/association-mapping
|
reference/association-mapping
|
||||||
@ -51,9 +53,9 @@ Reference Guide
|
|||||||
reference/metadata-drivers
|
reference/metadata-drivers
|
||||||
reference/best-practices
|
reference/best-practices
|
||||||
reference/limitations-and-known-issues
|
reference/limitations-and-known-issues
|
||||||
tutorials/pagination.rst
|
|
||||||
reference/filters.rst
|
reference/filters.rst
|
||||||
reference/namingstrategy.rst
|
reference/namingstrategy.rst
|
||||||
|
reference/advanced-configuration.rst
|
||||||
|
|
||||||
|
|
||||||
Cookbook
|
Cookbook
|
||||||
@ -63,6 +65,7 @@ Cookbook
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
cookbook/aggregate-fields
|
cookbook/aggregate-fields
|
||||||
|
cookbook/custom-mapping-types
|
||||||
cookbook/decorator-pattern
|
cookbook/decorator-pattern
|
||||||
cookbook/dql-custom-walkers
|
cookbook/dql-custom-walkers
|
||||||
cookbook/dql-user-defined-functions
|
cookbook/dql-user-defined-functions
|
||||||
@ -70,6 +73,7 @@ Cookbook
|
|||||||
cookbook/implementing-the-notify-changetracking-policy
|
cookbook/implementing-the-notify-changetracking-policy
|
||||||
cookbook/implementing-wakeup-or-clone
|
cookbook/implementing-wakeup-or-clone
|
||||||
cookbook/integrating-with-codeigniter
|
cookbook/integrating-with-codeigniter
|
||||||
|
cookbook/resolve-target-entity-listener
|
||||||
cookbook/sql-table-prefixes
|
cookbook/sql-table-prefixes
|
||||||
cookbook/strategy-cookbook-introduction
|
cookbook/strategy-cookbook-introduction
|
||||||
cookbook/validation-of-entities
|
cookbook/validation-of-entities
|
||||||
|
@ -19,7 +19,7 @@ the ID fields have to have their values set before you call ``EntityManager#pers
|
|||||||
Primitive Types only
|
Primitive Types only
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Even in version 2.0 you can have composite keys as long as they only consist of the primative 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
|
``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name
|
||||||
and year of production as primary keys:
|
and year of production as primary keys:
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ of one or many parent entities.
|
|||||||
|
|
||||||
- Dynamic Attributes of an Entity (for example Article). Each Article has many
|
- Dynamic Attributes of an Entity (for example Article). Each Article has many
|
||||||
attributes with primary key "article_id" and "attribute_name".
|
attributes with primary key "article_id" and "attribute_name".
|
||||||
- Address object of a Person, the primary key of the adress is "user_id". This is not a case of a composite primary
|
- Address object of a Person, the primary key of the address is "user_id". This is not a case of a composite primary
|
||||||
key, but the identity is derived through a foreign entity and a foreign key.
|
key, but the identity is derived through a foreign entity and a foreign key.
|
||||||
- Join Tables with metadata can be modelled as Entity, for example connections between two articles
|
- Join Tables with metadata can be modelled as Entity, for example connections between two articles
|
||||||
with a little description and a score.
|
with a little description and a score.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,296 +0,0 @@
|
|||||||
Doctrine explained in 10 quick steps
|
|
||||||
====================================
|
|
||||||
|
|
||||||
You can follow this tutorial step by step yourself and end up with a simple
|
|
||||||
Doctrine application. It assumed that you installed Doctrine via Composer.
|
|
||||||
For more information take a look at the :doc:`Installation help
|
|
||||||
<../reference/introduction>`.
|
|
||||||
|
|
||||||
1. Allows you to map PHP Objects to database tables
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
class Post
|
|
||||||
{
|
|
||||||
protected $id;
|
|
||||||
protected $title;
|
|
||||||
protected $body;
|
|
||||||
}
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
mysql> CREATE TABLE Post (id INT AUTO_INCREMENT PRIMARY KEY, title
|
|
||||||
VARCHAR(255), body TEXT);
|
|
||||||
|
|
||||||
mysql> DESCRIBE Post;
|
|
||||||
+-------+--------------+------+-----+---------+----------------+
|
|
||||||
| Field | Type | Null | Key | Default | Extra |
|
|
||||||
+-------+--------------+------+-----+---------+----------------+
|
|
||||||
| id | int(11) | NO | PRI | NULL | auto_increment |
|
|
||||||
| title | varchar(255) | YES | | NULL | |
|
|
||||||
| body | text | YES | | NULL | |
|
|
||||||
+-------+--------------+------+-----+---------+----------------+
|
|
||||||
|
|
||||||
.. tip::
|
|
||||||
|
|
||||||
Objects mapped with Doctrine are called Entities. They don't need to extend
|
|
||||||
a base class and even allow constructors with required parameters.
|
|
||||||
|
|
||||||
You are responsible for implementing getters, setters and constructors of
|
|
||||||
your entities yourself. This gives you full freedom to design your business
|
|
||||||
objects as you wish.
|
|
||||||
|
|
||||||
2. Using Annotations, XML or YAML for Metadata Mapping
|
|
||||||
------------------------------------------------------
|
|
||||||
|
|
||||||
.. configuration-block::
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
/** @Entity **/
|
|
||||||
class Post
|
|
||||||
{
|
|
||||||
/** @Id @GeneratedValue @Column(type="integer") **/
|
|
||||||
protected $id;
|
|
||||||
/** @Column(type="string") **/
|
|
||||||
protected $title;
|
|
||||||
/** @Column(type="text") **/
|
|
||||||
protected $body;
|
|
||||||
}
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
Post:
|
|
||||||
type: entity
|
|
||||||
id:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
generator:
|
|
||||||
strategy: AUTO
|
|
||||||
fields:
|
|
||||||
title:
|
|
||||||
type: string
|
|
||||||
body:
|
|
||||||
type: text
|
|
||||||
|
|
||||||
.. code-block:: xml
|
|
||||||
|
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<doctrine-mapping>
|
|
||||||
<entity name="Post">
|
|
||||||
<id name="id type="integer">
|
|
||||||
<generator strategy="AUTO" />
|
|
||||||
</id>
|
|
||||||
<field name="title" type="string" />
|
|
||||||
<field name="body" type="text" />
|
|
||||||
</entity>
|
|
||||||
</doctrine-mapping>
|
|
||||||
|
|
||||||
|
|
||||||
3. Object References map to Foreign keys
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
/** @Entity **/
|
|
||||||
class Post
|
|
||||||
{
|
|
||||||
// .. previous code
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ManyToOne(targetEntity="User")
|
|
||||||
**/
|
|
||||||
protected $author;
|
|
||||||
|
|
||||||
public function __construct(User $user)
|
|
||||||
{
|
|
||||||
$this->author = $user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @Entity **/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
/** @Id @GeneratedValue @Column(type="integer") **/
|
|
||||||
protected $id;
|
|
||||||
/** @Column(type="string") **/
|
|
||||||
protected $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = new User();
|
|
||||||
$post = new Post($user);
|
|
||||||
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
mysql> CREATE TABLE Post (id INT AUTO_INCREMENT PRIMARY KEY, title
|
|
||||||
VARCHAR(255), body TEXT, author_id INT);
|
|
||||||
|
|
||||||
mysql> CREATE TABLE User (id INT AUTO_INCREMENT PRIMARY KEY, name
|
|
||||||
VARCHAR(255));
|
|
||||||
|
|
||||||
mysql> ALTER TABLE Post ADD FOREIGN KEY (author_id) REFERENCES User (id);
|
|
||||||
|
|
||||||
mysql> DESCRIBE Post;
|
|
||||||
+-----------+--------------+------+-----+---------+----------------+
|
|
||||||
| Field | Type | Null | Key | Default | Extra |
|
|
||||||
+-----------+--------------+------+-----+---------+----------------+
|
|
||||||
| id | int(11) | NO | PRI | NULL | auto_increment |
|
|
||||||
| title | varchar(255) | YES | | NULL | |
|
|
||||||
| body | text | YES | | NULL | |
|
|
||||||
| author_id | int(11) | YES | MUL | NULL | |
|
|
||||||
+-----------+--------------+------+-----+---------+----------------+
|
|
||||||
|
|
||||||
.. tip::
|
|
||||||
|
|
||||||
This means you don't have to mess with foreign keys yourself, just use
|
|
||||||
references to connect objects with each other and let Doctrine handle the
|
|
||||||
rest.
|
|
||||||
|
|
||||||
4. Collections handle sets of objects references
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
|
||||||
|
|
||||||
class Post
|
|
||||||
{
|
|
||||||
// .. previous code
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OneToMany(targetEntity="Comment", mappedBy="post",
|
|
||||||
* cascade={"persist"})
|
|
||||||
**/
|
|
||||||
protected $comments;
|
|
||||||
|
|
||||||
public function __construct(User $author)
|
|
||||||
{
|
|
||||||
$this->author = $author;
|
|
||||||
$this->comments = new ArrayCollection();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addComment($text)
|
|
||||||
{
|
|
||||||
$this->comments[] = new Comment($this, $text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @Entity **/
|
|
||||||
class Comment
|
|
||||||
{
|
|
||||||
/** @Id @GeneratedValue @Column(type="integer") **/
|
|
||||||
protected $id;
|
|
||||||
/** @Column(type="text") **/
|
|
||||||
protected $comment;
|
|
||||||
/**
|
|
||||||
* @ManyToOne(targetEntity="Post", inversedBy="comments")
|
|
||||||
**/
|
|
||||||
protected $post;
|
|
||||||
|
|
||||||
public function __construct(Post $post, $text)
|
|
||||||
{
|
|
||||||
$this->post = $post;
|
|
||||||
$this->comment = $text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$post->addComment("First..");
|
|
||||||
$post->addComment("Second!");
|
|
||||||
|
|
||||||
5. Easy to setup for the default configuration case
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
use Doctrine\ORM\Tools\Setup;
|
|
||||||
use Doctrine\ORM\EntityManager;
|
|
||||||
|
|
||||||
require_once "vendor/autoload.php";
|
|
||||||
|
|
||||||
$dbParams = array(
|
|
||||||
'driver' => 'pdo_mysql',
|
|
||||||
'user' => 'root',
|
|
||||||
'password' => '',
|
|
||||||
'dbname' => 'tests'
|
|
||||||
);
|
|
||||||
$path = 'path/to/entities';
|
|
||||||
$config = Setup::createAnnotationMetadataConfiguration($path, true);
|
|
||||||
$entityManager = EntityManager::create($dbParams, $config);
|
|
||||||
|
|
||||||
|
|
||||||
6. The EntityManager needs to know about your new objects
|
|
||||||
---------------------------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$entityManager->persist($user);
|
|
||||||
$entityManager->persist($post);
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
This does not lead to INSERT/UPDATE statements yet. You need to call
|
|
||||||
EntityManager#flush()
|
|
||||||
|
|
||||||
|
|
||||||
7. EntityManager#flush() batches SQL INSERT/UPDATE/DELETE statements
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$entityManager->flush();
|
|
||||||
|
|
||||||
.. tip::
|
|
||||||
|
|
||||||
Batching all write-operations against the database allows Doctrine to wrap all
|
|
||||||
statements into a single transaction and benefit from other performance
|
|
||||||
optimizations such as prepared statement re-use.
|
|
||||||
|
|
||||||
8. You can fetch objects from the database through the EntityManager
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$post = $entityManager->find("Post", $id);
|
|
||||||
|
|
||||||
9. ..or through a Repository
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$authorRepository = $entityManager->getRepository("Author");
|
|
||||||
$author = $authorRepository->find($authorId);
|
|
||||||
|
|
||||||
$postRepository = $entityManager->getRepository("Post");
|
|
||||||
$post = $postRepository->findOneBy(array("title" => "Hello World!"));
|
|
||||||
|
|
||||||
$posts = $repository->findBy(
|
|
||||||
array("author" => $author),
|
|
||||||
array("title" => "ASC")
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
10. Or complex finder scenarios with the Doctrine Query Language
|
|
||||||
----------------------------------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// all posts and their comment count
|
|
||||||
$dql = "SELECT p, count(c.id) AS comments " .
|
|
||||||
"FROM Post p JOIN p.comments GROUP BY p";
|
|
||||||
$results = $entityManager->createQuery($dql)->getResult();
|
|
@ -12,32 +12,51 @@ collection.
|
|||||||
Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you
|
Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you
|
||||||
can specify the ``@OrderBy`` in the following way:
|
can specify the ``@OrderBy`` in the following way:
|
||||||
|
|
||||||
.. code-block:: php
|
.. configuration-block::
|
||||||
|
|
||||||
<?php
|
.. code-block:: php
|
||||||
/** @Entity **/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
|
|
||||||
/**
|
<?php
|
||||||
* @ManyToMany(targetEntity="Group")
|
/** @Entity **/
|
||||||
* @OrderBy({"name" = "ASC"})
|
class User
|
||||||
**/
|
{
|
||||||
private $groups;
|
// ...
|
||||||
}
|
|
||||||
|
|
||||||
.. code-block:: xml
|
/**
|
||||||
|
* @ManyToMany(targetEntity="Group")
|
||||||
|
* @OrderBy({"name" = "ASC"})
|
||||||
|
**/
|
||||||
|
private $groups;
|
||||||
|
}
|
||||||
|
|
||||||
<doctrine-mapping>
|
.. code-block:: xml
|
||||||
<entity name="User">
|
|
||||||
<many-to-many field="groups" target-entity="Group">
|
<doctrine-mapping>
|
||||||
<order-by>
|
<entity name="User">
|
||||||
<order-by-field name="name" direction="ASC" />
|
<many-to-many field="groups" target-entity="Group">
|
||||||
</order-by>
|
<order-by>
|
||||||
</many-to-many>
|
<order-by-field name="name" direction="ASC" />
|
||||||
</entity>
|
</order-by>
|
||||||
</doctrine-mapping>
|
</many-to-many>
|
||||||
|
</entity>
|
||||||
|
</doctrine-mapping>
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
User:
|
||||||
|
type: entity
|
||||||
|
manyToMany:
|
||||||
|
groups:
|
||||||
|
orderBy: { 'name': 'ASC' }
|
||||||
|
targetEntity: Group
|
||||||
|
joinTable:
|
||||||
|
name: users_groups
|
||||||
|
joinColumns:
|
||||||
|
user_id:
|
||||||
|
referencedColumnName: id
|
||||||
|
inverseJoinColumns:
|
||||||
|
group_id:
|
||||||
|
referencedColumnName: id
|
||||||
|
|
||||||
The DQL Snippet in OrderBy is only allowed to consist of
|
The DQL Snippet in OrderBy is only allowed to consist of
|
||||||
unqualified, unquoted field names and of an optional ASC/DESC
|
unqualified, unquoted field names and of an optional ASC/DESC
|
||||||
|
@ -87,4 +87,4 @@ The case for just extending a class would be just the same but:
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
Overriding is also supported via XML and YAML.
|
Overriding is also supported via XML and YAML (:ref:`examples <inheritence_mapping_overrides>`).
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
<xs:element name="cascade-merge" 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-remove" type="orm:emptyType" minOccurs="0"/>
|
||||||
<xs:element name="cascade-refresh" 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:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:anyAttribute namespace="##other"/>
|
<xs:anyAttribute namespace="##other"/>
|
||||||
@ -86,8 +87,11 @@
|
|||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="named-native-query">
|
<xs:complexType name="named-native-query">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="query" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
|
</xs:sequence>
|
||||||
<xs:attribute name="name" type="xs:string" use="required" />
|
<xs:attribute name="name" type="xs:string" use="required" />
|
||||||
<xs:attribute name="query" type="xs:string" use="required"/>
|
|
||||||
<xs:attribute name="result-class" type="xs:string" />
|
<xs:attribute name="result-class" type="xs:string" />
|
||||||
<xs:attribute name="result-set-mapping" type="xs:string" />
|
<xs:attribute name="result-set-mapping" type="xs:string" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
@ -95,14 +99,14 @@
|
|||||||
<xs:complexType name="named-native-queries">
|
<xs:complexType name="named-native-queries">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="named-native-query" type="orm:named-native-query" minOccurs="1" maxOccurs="unbounded" />
|
<xs:element name="named-native-query" type="orm:named-native-query" minOccurs="1" maxOccurs="unbounded" />
|
||||||
<xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="entity-listener">
|
<xs:complexType name="entity-listener">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="0" maxOccurs="unbounded"/>
|
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
<xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="class" type="xs:string"/>
|
<xs:attribute name="class" type="xs:string"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
@ -127,20 +131,24 @@
|
|||||||
<xs:element name="field-result" type="orm:field-result" minOccurs="0" maxOccurs="unbounded" />
|
<xs:element name="field-result" type="orm:field-result" minOccurs="0" maxOccurs="unbounded" />
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="entity-class" type="xs:string" use="required" />
|
<xs:attribute name="entity-class" type="xs:string" use="required" />
|
||||||
|
<xs:attribute name="discriminator-column" type="xs:string" use="optional" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="sql-result-set-mapping">
|
<xs:complexType name="sql-result-set-mapping">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="entity-result" type="orm:entity-result" minOccurs="0" maxOccurs="unbounded" />
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:element name="column-result" type="orm:column-result" minOccurs="0" maxOccurs="unbounded" />
|
<xs:element name="entity-result" type="orm:entity-result"/>
|
||||||
<xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
|
<xs:element name="column-result" type="orm:column-result"/>
|
||||||
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
|
</xs:choice>
|
||||||
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
|
<xs:attribute name="name" type="xs:string" use="required" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="sql-result-set-mappings">
|
<xs:complexType name="sql-result-set-mappings">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="sql-result-set-mapping" type="orm:sql-result-set-mapping" minOccurs="1" maxOccurs="unbounded" />
|
<xs:element name="sql-result-set-mapping" type="orm:sql-result-set-mapping" minOccurs="1" maxOccurs="unbounded" />
|
||||||
<xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
|
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
@ -155,6 +163,7 @@
|
|||||||
<xs:element name="entity-listeners" type="orm:entity-listeners" minOccurs="0" maxOccurs="1" />
|
<xs:element name="entity-listeners" type="orm:entity-listeners" minOccurs="0" maxOccurs="1" />
|
||||||
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
|
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
|
||||||
<xs:element name="named-native-queries" type="orm:named-native-queries" minOccurs="0" maxOccurs="1" />
|
<xs:element name="named-native-queries" type="orm:named-native-queries" minOccurs="0" maxOccurs="1" />
|
||||||
|
<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="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
|
||||||
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
|
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
|
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
@ -221,6 +230,7 @@
|
|||||||
|
|
||||||
<xs:simpleType name="generator-strategy">
|
<xs:simpleType name="generator-strategy">
|
||||||
<xs:restriction base="xs:token">
|
<xs:restriction base="xs:token">
|
||||||
|
<xs:enumeration value="NONE"/>
|
||||||
<xs:enumeration value="TABLE"/>
|
<xs:enumeration value="TABLE"/>
|
||||||
<xs:enumeration value="SEQUENCE"/>
|
<xs:enumeration value="SEQUENCE"/>
|
||||||
<xs:enumeration value="IDENTITY"/>
|
<xs:enumeration value="IDENTITY"/>
|
||||||
@ -345,6 +355,7 @@
|
|||||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||||
<xs:attribute name="type" type="xs:NMTOKEN" />
|
<xs:attribute name="type" type="xs:NMTOKEN" />
|
||||||
<xs:attribute name="column" type="xs:NMTOKEN" />
|
<xs:attribute name="column" type="xs:NMTOKEN" />
|
||||||
|
<xs:attribute name="length" type="xs:NMTOKEN" />
|
||||||
<xs:attribute name="association-key" type="xs:boolean" default="false" />
|
<xs:attribute name="association-key" type="xs:boolean" default="false" />
|
||||||
<xs:attribute name="column-definition" type="xs:string" />
|
<xs:attribute name="column-definition" type="xs:string" />
|
||||||
<xs:anyAttribute namespace="##other"/>
|
<xs:anyAttribute namespace="##other"/>
|
||||||
@ -379,7 +390,7 @@
|
|||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
<xs:attribute name="name" type="xs:NMTOKEN" use="optional" />
|
||||||
<xs:attribute name="referenced-column-name" type="xs:NMTOKEN" use="optional" default="id" />
|
<xs:attribute name="referenced-column-name" type="xs:NMTOKEN" use="optional" default="id" />
|
||||||
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
||||||
<xs:attribute name="nullable" type="xs:boolean" default="true" />
|
<xs:attribute name="nullable" type="xs:boolean" default="true" />
|
||||||
@ -504,7 +515,7 @@
|
|||||||
<xs:complexType name="association-overrides">
|
<xs:complexType name="association-overrides">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="association-override" type="orm:association-override" minOccurs="1" maxOccurs="unbounded" />
|
<xs:element name="association-override" type="orm:association-override" minOccurs="1" maxOccurs="unbounded" />
|
||||||
<xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
@ -520,16 +531,33 @@
|
|||||||
<xs:complexType name="attribute-overrides">
|
<xs:complexType name="attribute-overrides">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="attribute-override" type="orm:attribute-override" minOccurs="1" maxOccurs="unbounded" />
|
<xs:element name="attribute-override" type="orm:attribute-override" minOccurs="1" maxOccurs="unbounded" />
|
||||||
<xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="attribute-override">
|
<xs:complexType name="attribute-override">
|
||||||
<xs:sequence>
|
<xs:sequence>
|
||||||
<xs:element name="field" type="orm:field" minOccurs="1" />
|
<xs:element name="field" type="orm:attribute-override-field" minOccurs="1" />
|
||||||
<xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="attribute-override-field">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="options" type="orm:options" minOccurs="0" />
|
||||||
|
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
|
||||||
|
<xs:attribute name="column" type="xs:NMTOKEN" />
|
||||||
|
<xs:attribute name="length" type="xs:NMTOKEN" />
|
||||||
|
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
||||||
|
<xs:attribute name="nullable" type="xs:boolean" default="false" />
|
||||||
|
<xs:attribute name="version" type="xs:boolean" />
|
||||||
|
<xs:attribute name="column-definition" type="xs:string" />
|
||||||
|
<xs:attribute name="precision" type="xs:integer" use="optional" />
|
||||||
|
<xs:attribute name="scale" type="xs:integer" use="optional" />
|
||||||
|
<xs:anyAttribute namespace="##other"/>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
</xs:schema>
|
</xs:schema>
|
||||||
|
@ -284,6 +284,10 @@ abstract class AbstractQuery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($value instanceof Mapping\ClassMetadata) {
|
||||||
|
return $value->name;
|
||||||
|
}
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +309,7 @@ abstract class AbstractQuery
|
|||||||
/**
|
/**
|
||||||
* Allows to translate entity namespaces to full qualified names.
|
* Allows to translate entity namespaces to full qualified names.
|
||||||
*
|
*
|
||||||
* @param EntityManager $em
|
* @param Query\ResultSetMapping $rsm
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@ -386,7 +390,7 @@ abstract class AbstractQuery
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a cache driver to be used for caching result sets and implictly enables caching.
|
* Defines a cache driver to be used for caching result sets and implicitly enables caching.
|
||||||
*
|
*
|
||||||
* @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver
|
* @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver
|
||||||
*
|
*
|
||||||
@ -605,7 +609,12 @@ abstract class AbstractQuery
|
|||||||
*/
|
*/
|
||||||
public function getOneOrNullResult($hydrationMode = null)
|
public function getOneOrNullResult($hydrationMode = null)
|
||||||
{
|
{
|
||||||
$result = $this->execute(null, $hydrationMode);
|
try {
|
||||||
|
$result = $this->execute(null, $hydrationMode);
|
||||||
|
} catch (NoResultException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
|
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
|
||||||
return null;
|
return null;
|
||||||
@ -697,6 +706,18 @@ abstract class AbstractQuery
|
|||||||
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
|
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the query has a hint
|
||||||
|
*
|
||||||
|
* @param string $name The name of the hint
|
||||||
|
*
|
||||||
|
* @return bool False if the query does not have any hint
|
||||||
|
*/
|
||||||
|
public function hasHint($name)
|
||||||
|
{
|
||||||
|
return isset($this->_hints[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the key value map of query hints that are currently set.
|
* Return the key value map of query hints that are currently set.
|
||||||
*
|
*
|
||||||
@ -783,7 +804,7 @@ abstract class AbstractQuery
|
|||||||
return $stmt;
|
return $stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
$data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll(
|
||||||
$stmt, $this->_resultSetMapping, $this->_hints
|
$stmt, $this->_resultSetMapping, $this->_hints
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -19,20 +19,22 @@
|
|||||||
|
|
||||||
namespace Doctrine\ORM;
|
namespace Doctrine\ORM;
|
||||||
|
|
||||||
use Doctrine\Common\Cache\Cache;
|
|
||||||
use Doctrine\Common\Cache\ArrayCache;
|
|
||||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
use Doctrine\Common\Annotations\AnnotationReader;
|
||||||
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
|
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||||
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
|
|
||||||
use Doctrine\ORM\Mapping\QuoteStrategy;
|
|
||||||
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
|
|
||||||
use Doctrine\ORM\Mapping\NamingStrategy;
|
|
||||||
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
|
|
||||||
use Doctrine\ORM\Mapping\EntityListenerResolver;
|
|
||||||
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
|
|
||||||
use Doctrine\Common\Annotations\SimpleAnnotationReader;
|
|
||||||
use Doctrine\Common\Annotations\CachedReader;
|
use Doctrine\Common\Annotations\CachedReader;
|
||||||
|
use Doctrine\Common\Annotations\SimpleAnnotationReader;
|
||||||
|
use Doctrine\Common\Cache\ArrayCache;
|
||||||
|
use Doctrine\Common\Cache\Cache;
|
||||||
|
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
|
||||||
|
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
|
||||||
|
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
|
||||||
|
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
|
||||||
|
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
|
||||||
|
use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||||
|
use Doctrine\ORM\Mapping\NamingStrategy;
|
||||||
|
use Doctrine\ORM\Mapping\QuoteStrategy;
|
||||||
|
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
|
||||||
|
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration container for all configuration options of Doctrine.
|
* Configuration container for all configuration options of Doctrine.
|
||||||
@ -88,7 +90,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
|||||||
* Sets a boolean flag that indicates whether proxy classes should always be regenerated
|
* Sets a boolean flag that indicates whether proxy classes should always be regenerated
|
||||||
* during each script execution.
|
* during each script execution.
|
||||||
*
|
*
|
||||||
* @param boolean $bool
|
* @param boolean|int $bool Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@ -779,4 +781,28 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
|||||||
|
|
||||||
return $this->_attributes['entityListenerResolver'];
|
return $this->_attributes['entityListenerResolver'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the entity repository factory.
|
||||||
|
*
|
||||||
|
* @since 2.4
|
||||||
|
* @param \Doctrine\ORM\Repository\RepositoryFactory $repositoryFactory
|
||||||
|
*/
|
||||||
|
public function setRepositoryFactory(RepositoryFactory $repositoryFactory)
|
||||||
|
{
|
||||||
|
$this->_attributes['repositoryFactory'] = $repositoryFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the entity repository factory.
|
||||||
|
*
|
||||||
|
* @since 2.4
|
||||||
|
* @return \Doctrine\ORM\Repository\RepositoryFactory
|
||||||
|
*/
|
||||||
|
public function getRepositoryFactory()
|
||||||
|
{
|
||||||
|
return isset($this->_attributes['repositoryFactory'])
|
||||||
|
? $this->_attributes['repositoryFactory']
|
||||||
|
: new DefaultRepositoryFactory();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
271
lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php
Normal file
271
lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Decorator;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\LockMode;
|
||||||
|
use Doctrine\ORM\Query\ResultSetMapping;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\Common\Persistence\ObjectManagerDecorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for EntityManager decorators
|
||||||
|
*
|
||||||
|
* @since 2.4
|
||||||
|
* @author Lars Strojny <lars@strojny.net
|
||||||
|
*/
|
||||||
|
abstract class EntityManagerDecorator extends ObjectManagerDecorator implements EntityManagerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var EntityManagerInterface
|
||||||
|
*/
|
||||||
|
protected $wrapped;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param EntityManagerInterface $wrapped
|
||||||
|
*/
|
||||||
|
public function __construct(EntityManagerInterface $wrapped)
|
||||||
|
{
|
||||||
|
$this->wrapped = $wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getConnection()
|
||||||
|
{
|
||||||
|
return $this->wrapped->getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getExpressionBuilder()
|
||||||
|
{
|
||||||
|
return $this->wrapped->getExpressionBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function beginTransaction()
|
||||||
|
{
|
||||||
|
return $this->wrapped->beginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function transactional($func)
|
||||||
|
{
|
||||||
|
return $this->wrapped->transactional($func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
return $this->wrapped->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function rollback()
|
||||||
|
{
|
||||||
|
return $this->wrapped->rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createQuery($dql = '')
|
||||||
|
{
|
||||||
|
return $this->wrapped->createQuery($dql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createNamedQuery($name)
|
||||||
|
{
|
||||||
|
return $this->wrapped->createNamedQuery($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createNativeQuery($sql, ResultSetMapping $rsm)
|
||||||
|
{
|
||||||
|
return $this->wrapped->createNativeQuery($sql, $rsm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createNamedNativeQuery($name)
|
||||||
|
{
|
||||||
|
return $this->wrapped->createNamedNativeQuery($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createQueryBuilder()
|
||||||
|
{
|
||||||
|
return $this->wrapped->createQueryBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getReference($entityName, $id)
|
||||||
|
{
|
||||||
|
return $this->wrapped->getReference($entityName, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getPartialReference($entityName, $identifier)
|
||||||
|
{
|
||||||
|
return $this->wrapped->getPartialReference($entityName, $identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
return $this->wrapped->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function copy($entity, $deep = false)
|
||||||
|
{
|
||||||
|
return $this->wrapped->copy($entity, $deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function lock($entity, $lockMode, $lockVersion = null)
|
||||||
|
{
|
||||||
|
return $this->wrapped->lock($entity, $lockMode, $lockVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function find($entityName, $id, $lockMode = LockMode::NONE, $lockVersion = null)
|
||||||
|
{
|
||||||
|
return $this->wrapped->find($entityName, $id, $lockMode, $lockVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function flush($entity = null)
|
||||||
|
{
|
||||||
|
return $this->wrapped->flush($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEventManager()
|
||||||
|
{
|
||||||
|
return $this->wrapped->getEventManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getConfiguration()
|
||||||
|
{
|
||||||
|
return $this->wrapped->getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isOpen()
|
||||||
|
{
|
||||||
|
return $this->wrapped->isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getUnitOfWork()
|
||||||
|
{
|
||||||
|
return $this->wrapped->getUnitOfWork();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getHydrator($hydrationMode)
|
||||||
|
{
|
||||||
|
return $this->wrapped->getHydrator($hydrationMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function newHydrator($hydrationMode)
|
||||||
|
{
|
||||||
|
return $this->wrapped->newHydrator($hydrationMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getProxyFactory()
|
||||||
|
{
|
||||||
|
return $this->wrapped->getProxyFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFilters()
|
||||||
|
{
|
||||||
|
return $this->wrapped->getFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isFiltersStateClean()
|
||||||
|
{
|
||||||
|
return $this->wrapped->isFiltersStateClean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function hasFilters()
|
||||||
|
{
|
||||||
|
return $this->wrapped->hasFilters();
|
||||||
|
}
|
||||||
|
}
|
@ -21,11 +21,8 @@ namespace Doctrine\ORM;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Doctrine\Common\EventManager;
|
use Doctrine\Common\EventManager;
|
||||||
use Doctrine\Common\Persistence\ObjectManager;
|
|
||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use Doctrine\DBAL\LockMode;
|
use Doctrine\DBAL\LockMode;
|
||||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
|
||||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
|
||||||
use Doctrine\ORM\Query\ResultSetMapping;
|
use Doctrine\ORM\Query\ResultSetMapping;
|
||||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||||
use Doctrine\ORM\Query\FilterCollection;
|
use Doctrine\ORM\Query\FilterCollection;
|
||||||
@ -34,13 +31,35 @@ use Doctrine\Common\Util\ClassUtils;
|
|||||||
/**
|
/**
|
||||||
* The EntityManager is the central access point to ORM functionality.
|
* The EntityManager is the central access point to ORM functionality.
|
||||||
*
|
*
|
||||||
|
* It is a facade to all different ORM subsystems such as UnitOfWork,
|
||||||
|
* Query Language and Repository API. Instantiation is done through
|
||||||
|
* the static create() method. The quickest way to obtain a fully
|
||||||
|
* configured EntityManager is:
|
||||||
|
*
|
||||||
|
* use Doctrine\ORM\Tools\Setup;
|
||||||
|
* use Doctrine\ORM\EntityManager;
|
||||||
|
*
|
||||||
|
* $paths = array('/path/to/entity/mapping/files');
|
||||||
|
*
|
||||||
|
* $config = Setup::createAnnotationMetadataConfiguration($paths);
|
||||||
|
* $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
|
||||||
|
* $entityManager = EntityManager::create($dbParams, $config);
|
||||||
|
*
|
||||||
|
* For more information see
|
||||||
|
* {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
|
||||||
|
*
|
||||||
|
* You should never attempt to inherit from the EntityManager: Inheritance
|
||||||
|
* is not a valid extension point for the EntityManager. Instead you
|
||||||
|
* should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
|
||||||
|
* and wrap your entity manager in a decorator.
|
||||||
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||||
* @author Jonathan Wage <jonwage@gmail.com>
|
* @author Jonathan Wage <jonwage@gmail.com>
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
*/
|
*/
|
||||||
class EntityManager implements ObjectManager
|
/* final */class EntityManager implements EntityManagerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The used Configuration.
|
* The used Configuration.
|
||||||
@ -63,13 +82,6 @@ class EntityManager implements ObjectManager
|
|||||||
*/
|
*/
|
||||||
private $metadataFactory;
|
private $metadataFactory;
|
||||||
|
|
||||||
/**
|
|
||||||
* The EntityRepository instances.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $repositories = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The UnitOfWork used to coordinate object-level transactions.
|
* The UnitOfWork used to coordinate object-level transactions.
|
||||||
*
|
*
|
||||||
@ -84,13 +96,6 @@ class EntityManager implements ObjectManager
|
|||||||
*/
|
*/
|
||||||
private $eventManager;
|
private $eventManager;
|
||||||
|
|
||||||
/**
|
|
||||||
* The maintained (cached) hydrators. One instance per type.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $hydrators = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The proxy factory used to create dynamic proxies.
|
* The proxy factory used to create dynamic proxies.
|
||||||
*
|
*
|
||||||
@ -98,6 +103,13 @@ class EntityManager implements ObjectManager
|
|||||||
*/
|
*/
|
||||||
private $proxyFactory;
|
private $proxyFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository factory used to create dynamic repositories.
|
||||||
|
*
|
||||||
|
* @var \Doctrine\ORM\Repository\RepositoryFactory
|
||||||
|
*/
|
||||||
|
private $repositoryFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The expression builder instance used to generate query expressions.
|
* The expression builder instance used to generate query expressions.
|
||||||
*
|
*
|
||||||
@ -129,9 +141,9 @@ class EntityManager implements ObjectManager
|
|||||||
*/
|
*/
|
||||||
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
|
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
|
||||||
{
|
{
|
||||||
$this->conn = $conn;
|
$this->conn = $conn;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->eventManager = $eventManager;
|
$this->eventManager = $eventManager;
|
||||||
|
|
||||||
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
|
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
|
||||||
|
|
||||||
@ -139,8 +151,9 @@ class EntityManager implements ObjectManager
|
|||||||
$this->metadataFactory->setEntityManager($this);
|
$this->metadataFactory->setEntityManager($this);
|
||||||
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
|
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
|
||||||
|
|
||||||
$this->unitOfWork = new UnitOfWork($this);
|
$this->repositoryFactory = $config->getRepositoryFactory();
|
||||||
$this->proxyFactory = new ProxyFactory(
|
$this->unitOfWork = new UnitOfWork($this);
|
||||||
|
$this->proxyFactory = new ProxyFactory(
|
||||||
$this,
|
$this,
|
||||||
$config->getProxyDir(),
|
$config->getProxyDir(),
|
||||||
$config->getProxyNamespace(),
|
$config->getProxyNamespace(),
|
||||||
@ -286,7 +299,7 @@ class EntityManager implements ObjectManager
|
|||||||
*
|
*
|
||||||
* @return \Doctrine\ORM\Query
|
* @return \Doctrine\ORM\Query
|
||||||
*/
|
*/
|
||||||
public function createQuery($dql = "")
|
public function createQuery($dql = '')
|
||||||
{
|
{
|
||||||
$query = new Query($this);
|
$query = new Query($this);
|
||||||
|
|
||||||
@ -736,28 +749,11 @@ class EntityManager implements ObjectManager
|
|||||||
*
|
*
|
||||||
* @param string $entityName The name of the entity.
|
* @param string $entityName The name of the entity.
|
||||||
*
|
*
|
||||||
* @return EntityRepository The repository class.
|
* @return \Doctrine\ORM\EntityRepository The repository class.
|
||||||
*/
|
*/
|
||||||
public function getRepository($entityName)
|
public function getRepository($entityName)
|
||||||
{
|
{
|
||||||
$entityName = ltrim($entityName, '\\');
|
return $this->repositoryFactory->getRepository($this, $entityName);
|
||||||
|
|
||||||
if (isset($this->repositories[$entityName])) {
|
|
||||||
return $this->repositories[$entityName];
|
|
||||||
}
|
|
||||||
|
|
||||||
$metadata = $this->getClassMetadata($entityName);
|
|
||||||
$repositoryClassName = $metadata->customRepositoryClassName;
|
|
||||||
|
|
||||||
if ($repositoryClassName === null) {
|
|
||||||
$repositoryClassName = $this->config->getDefaultRepositoryClassName();
|
|
||||||
}
|
|
||||||
|
|
||||||
$repository = new $repositoryClassName($this, $metadata);
|
|
||||||
|
|
||||||
$this->repositories[$entityName] = $repository;
|
|
||||||
|
|
||||||
return $repository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -834,17 +830,15 @@ class EntityManager implements ObjectManager
|
|||||||
* This method caches the hydrator instances which is used for all queries that don't
|
* This method caches the hydrator instances which is used for all queries that don't
|
||||||
* selectively iterate over the result.
|
* selectively iterate over the result.
|
||||||
*
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
* @param int $hydrationMode
|
* @param int $hydrationMode
|
||||||
*
|
*
|
||||||
* @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
* @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||||
*/
|
*/
|
||||||
public function getHydrator($hydrationMode)
|
public function getHydrator($hydrationMode)
|
||||||
{
|
{
|
||||||
if ( ! isset($this->hydrators[$hydrationMode])) {
|
return $this->newHydrator($hydrationMode);
|
||||||
$this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->hydrators[$hydrationMode];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
60
lib/Doctrine/ORM/EntityManagerInterface.php
Normal file
60
lib/Doctrine/ORM/EntityManagerInterface.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION); HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE); ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM;
|
||||||
|
|
||||||
|
use Doctrine\Common\Persistence\ObjectManager;
|
||||||
|
use Doctrine\DBAL\LockMode;
|
||||||
|
use Doctrine\ORM\Query\ResultSetMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EntityManager interface
|
||||||
|
*
|
||||||
|
* @since 2.4
|
||||||
|
* @author Lars Strojny <lars@strojny.net
|
||||||
|
*/
|
||||||
|
interface EntityManagerInterface extends ObjectManager
|
||||||
|
{
|
||||||
|
public function getConnection();
|
||||||
|
public function getExpressionBuilder();
|
||||||
|
public function beginTransaction();
|
||||||
|
public function transactional($func);
|
||||||
|
public function commit();
|
||||||
|
public function rollback();
|
||||||
|
public function createQuery($dql = '');
|
||||||
|
public function createNamedQuery($name);
|
||||||
|
public function createNativeQuery($sql, ResultSetMapping $rsm);
|
||||||
|
public function createNamedNativeQuery($name);
|
||||||
|
public function createQueryBuilder();
|
||||||
|
public function getReference($entityName, $id);
|
||||||
|
public function getPartialReference($entityName, $identifier);
|
||||||
|
public function close();
|
||||||
|
public function copy($entity, $deep = false);
|
||||||
|
public function lock($entity, $lockMode, $lockVersion = null);
|
||||||
|
public function getEventManager();
|
||||||
|
public function getConfiguration();
|
||||||
|
public function isOpen();
|
||||||
|
public function getUnitOfWork();
|
||||||
|
public function getHydrator($hydrationMode);
|
||||||
|
public function newHydrator($hydrationMode);
|
||||||
|
public function getProxyFactory();
|
||||||
|
public function getFilters();
|
||||||
|
public function isFiltersStateClean();
|
||||||
|
public function hasFilters();
|
||||||
|
}
|
@ -75,14 +75,15 @@ class EntityRepository implements ObjectRepository, Selectable
|
|||||||
* Creates a new QueryBuilder instance that is prepopulated for this entity name.
|
* Creates a new QueryBuilder instance that is prepopulated for this entity name.
|
||||||
*
|
*
|
||||||
* @param string $alias
|
* @param string $alias
|
||||||
|
* @param string $indexBy The index for the from.
|
||||||
*
|
*
|
||||||
* @return QueryBuilder
|
* @return QueryBuilder
|
||||||
*/
|
*/
|
||||||
public function createQueryBuilder($alias)
|
public function createQueryBuilder($alias, $indexBy = null)
|
||||||
{
|
{
|
||||||
return $this->_em->createQueryBuilder()
|
return $this->_em->createQueryBuilder()
|
||||||
->select($alias)
|
->select($alias)
|
||||||
->from($this->_entityName, $alias);
|
->from($this->_entityName, $alias, $indexBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,7 +91,7 @@ class ListenersInvoker
|
|||||||
*
|
*
|
||||||
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
|
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
|
||||||
* @param string $eventName The entity lifecycle event.
|
* @param string $eventName The entity lifecycle event.
|
||||||
* @param object $entity The Entity on which the event occured.
|
* @param object $entity The Entity on which the event occurred.
|
||||||
* @param \Doctrine\Common\EventArgs $event The Event args.
|
* @param \Doctrine\Common\EventArgs $event The Event args.
|
||||||
* @param integer $invoke Bitmask to invoke listeners.
|
* @param integer $invoke Bitmask to invoke listeners.
|
||||||
*/
|
*/
|
||||||
|
@ -24,7 +24,7 @@ use Doctrine\ORM\EntityManager;
|
|||||||
/**
|
/**
|
||||||
* Provides event arguments for the onClear event.
|
* Provides event arguments for the onClear event.
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.org
|
* @link www.doctrine-project.org
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Roman Borschel <roman@code-factory.de>
|
* @author Roman Borschel <roman@code-factory.de>
|
||||||
|
@ -25,7 +25,7 @@ use Doctrine\ORM\EntityManager;
|
|||||||
/**
|
/**
|
||||||
* Provides event arguments for the preFlush event.
|
* Provides event arguments for the preFlush event.
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.org
|
* @link www.doctrine-project.org
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Roman Borschel <roman@code-factory.de>
|
* @author Roman Borschel <roman@code-factory.de>
|
||||||
@ -38,9 +38,6 @@ class OnFlushEventArgs extends EventArgs
|
|||||||
*/
|
*/
|
||||||
private $em;
|
private $em;
|
||||||
|
|
||||||
//private $entitiesToPersist = array();
|
|
||||||
//private $entitiesToRemove = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
@ -61,25 +58,4 @@ class OnFlushEventArgs extends EventArgs
|
|||||||
return $this->em;
|
return $this->em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
public function addEntityToPersist($entity)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addEntityToRemove($entity)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addEntityToUpdate($entity)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEntitiesToPersist()
|
|
||||||
{
|
|
||||||
return $this->_entitiesToPersist;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ use Doctrine\Common\EventArgs;
|
|||||||
/**
|
/**
|
||||||
* Provides event arguments for the postFlush event.
|
* Provides event arguments for the postFlush event.
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.org
|
* @link www.doctrine-project.org
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Daniel Freudenberger <df@rebuy.de>
|
* @author Daniel Freudenberger <df@rebuy.de>
|
||||||
|
@ -25,7 +25,7 @@ use Doctrine\ORM\EntityManager;
|
|||||||
/**
|
/**
|
||||||
* Provides event arguments for the preFlush event.
|
* Provides event arguments for the preFlush event.
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.com
|
* @link www.doctrine-project.com
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Roman Borschel <roman@code-factory.de>
|
* @author Roman Borschel <roman@code-factory.de>
|
||||||
|
@ -122,7 +122,7 @@ final class Events
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
|
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||||
* but before any changes to managed entites have been calculated. This event is
|
* but before any changes to managed entities have been calculated. This event is
|
||||||
* always raised right after EntityManager#flush() call.
|
* always raised right after EntityManager#flush() call.
|
||||||
*/
|
*/
|
||||||
const preFlush = 'preFlush';
|
const preFlush = 'preFlush';
|
||||||
|
@ -23,7 +23,7 @@ use Doctrine\ORM\EntityManager;
|
|||||||
use Doctrine\ORM\ORMException;
|
use Doctrine\ORM\ORMException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special generator for application-assigned identifiers (doesnt really generate anything).
|
* Special generator for application-assigned identifiers (doesn't really generate anything).
|
||||||
*
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
|
@ -38,9 +38,9 @@ class BigIntegerIdentityGenerator extends AbstractIdGenerator
|
|||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param string|null $seqName The name of the sequence to pass to lastInsertId()
|
* @param string|null $sequenceName The name of the sequence to pass to lastInsertId()
|
||||||
* to obtain the last generated identifier within the current
|
* to obtain the last generated identifier within the current
|
||||||
* database session/connection, if any.
|
* database session/connection, if any.
|
||||||
*/
|
*/
|
||||||
public function __construct($sequenceName = null)
|
public function __construct($sequenceName = null)
|
||||||
{
|
{
|
||||||
|
@ -38,9 +38,9 @@ class IdentityGenerator extends AbstractIdGenerator
|
|||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param string|null $seqName The name of the sequence to pass to lastInsertId()
|
* @param string|null $sequenceName The name of the sequence to pass to lastInsertId()
|
||||||
* to obtain the last generated identifier within the current
|
* to obtain the last generated identifier within the current
|
||||||
* database session/connection, if any.
|
* database session/connection, if any.
|
||||||
*/
|
*/
|
||||||
public function __construct($sequenceName = null)
|
public function __construct($sequenceName = null)
|
||||||
{
|
{
|
||||||
|
@ -168,7 +168,7 @@ abstract class AbstractHydrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excutes one-time preparation tasks, once each time hydration is started
|
* Executes one-time preparation tasks, once each time hydration is started
|
||||||
* through {@link hydrateAll} or {@link iterate()}.
|
* through {@link hydrateAll} or {@link iterate()}.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
@ -178,7 +178,7 @@ abstract class AbstractHydrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excutes one-time cleanup tasks at the end of a hydration that was initiated
|
* Executes one-time cleanup tasks at the end of a hydration that was initiated
|
||||||
* through {@link hydrateAll} or {@link iterate()}.
|
* through {@link hydrateAll} or {@link iterate()}.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
@ -223,7 +223,7 @@ abstract class AbstractHydrator
|
|||||||
* Puts the elements of a result row into a new array, grouped by the dql alias
|
* Puts the elements of a result row into a new array, grouped by the dql alias
|
||||||
* they belong to. The column names in the result set are mapped to their
|
* they belong to. The column names in the result set are mapped to their
|
||||||
* field names during this procedure as well as any necessary conversions on
|
* field names during this procedure as well as any necessary conversions on
|
||||||
* the values applied. Scalar values are kept in a specfic key 'scalars'.
|
* the values applied. Scalar values are kept in a specific key 'scalars'.
|
||||||
*
|
*
|
||||||
* @param array $data SQL Result Row.
|
* @param array $data SQL Result Row.
|
||||||
* @param array &$cache Cache for column to field result information.
|
* @param array &$cache Cache for column to field result information.
|
||||||
@ -321,7 +321,7 @@ abstract class AbstractHydrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// in an inheritance hierarchy the same field could be defined several times.
|
// in an inheritance hierarchy the same field could be defined several times.
|
||||||
// We overwrite this value so long we dont have a non-null value, that value we keep.
|
// We overwrite this value so long we don't have a non-null value, that value we keep.
|
||||||
// Per definition it cannot be that a field is defined several times and has several values.
|
// Per definition it cannot be that a field is defined several times and has several values.
|
||||||
if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
|
if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -146,6 +146,7 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
$baseElement =& $this->_resultPointers[$parent];
|
$baseElement =& $this->_resultPointers[$parent];
|
||||||
} else {
|
} else {
|
||||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +168,7 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
|
|
||||||
if ( ! $indexExists || ! $indexIsValid) {
|
if ( ! $indexExists || ! $indexIsValid) {
|
||||||
$element = $data;
|
$element = $data;
|
||||||
|
|
||||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||||
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
|
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
|
||||||
} else {
|
} else {
|
||||||
@ -183,7 +185,10 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
} else {
|
} else {
|
||||||
$oneToOne = true;
|
$oneToOne = true;
|
||||||
|
|
||||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
|
if (
|
||||||
|
( ! isset($nonemptyComponents[$dqlAlias])) &&
|
||||||
|
( ! isset($baseElement[$relationAlias]))
|
||||||
|
) {
|
||||||
$baseElement[$relationAlias] = null;
|
$baseElement[$relationAlias] = null;
|
||||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||||
$baseElement[$relationAlias] = $data;
|
$baseElement[$relationAlias] = $data;
|
||||||
@ -192,10 +197,9 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
|
|
||||||
$coll =& $baseElement[$relationAlias];
|
$coll =& $baseElement[$relationAlias];
|
||||||
|
|
||||||
if ($coll !== null) {
|
if (is_array($coll)) {
|
||||||
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
|
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// It's a root result element
|
// It's a root result element
|
||||||
|
|
||||||
@ -204,22 +208,21 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
|
|
||||||
// if this row has a NULL value for the root result id then make it a null result.
|
// if this row has a NULL value for the root result id then make it a null result.
|
||||||
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
|
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
|
||||||
if ($this->_rsm->isMixed) {
|
$result[] = $this->_rsm->isMixed
|
||||||
$result[] = array($entityKey => null);
|
? array($entityKey => null)
|
||||||
} else {
|
: null;
|
||||||
$result[] = null;
|
|
||||||
}
|
|
||||||
$resultKey = $this->_resultCounter;
|
$resultKey = $this->_resultCounter;
|
||||||
++$this->_resultCounter;
|
++$this->_resultCounter;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an existing element
|
// Check for an existing element
|
||||||
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||||
$element = $rowData[$dqlAlias];
|
$element = $this->_rsm->isMixed
|
||||||
if ($this->_rsm->isMixed) {
|
? array($entityKey => $rowData[$dqlAlias])
|
||||||
$element = array($entityKey => $element);
|
: $rowData[$dqlAlias];
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||||
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
|
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
|
||||||
@ -227,6 +230,7 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
} else {
|
} else {
|
||||||
$resultKey = $this->_resultCounter;
|
$resultKey = $this->_resultCounter;
|
||||||
$result[] = $element;
|
$result[] = $element;
|
||||||
|
|
||||||
++$this->_resultCounter;
|
++$this->_resultCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,11 +238,13 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
} else {
|
} else {
|
||||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||||
$resultKey = $index;
|
$resultKey = $index;
|
||||||
|
|
||||||
/*if ($this->_rsm->isMixed) {
|
/*if ($this->_rsm->isMixed) {
|
||||||
$result[] =& $result[$index];
|
$result[] =& $result[$index];
|
||||||
++$this->_resultCounter;
|
++$this->_resultCounter;
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateResultPointer($result, $index, $dqlAlias, false);
|
$this->updateResultPointer($result, $index, $dqlAlias, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,11 +253,9 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
if (isset($scalars)) {
|
if (isset($scalars)) {
|
||||||
if ( ! isset($resultKey) ) {
|
if ( ! isset($resultKey) ) {
|
||||||
// this only ever happens when no object is fetched (scalar result only)
|
// this only ever happens when no object is fetched (scalar result only)
|
||||||
if (isset($this->_rsm->indexByMap['scalars'])) {
|
$resultKey = isset($this->_rsm->indexByMap['scalars'])
|
||||||
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
|
? $row[$this->_rsm->indexByMap['scalars']]
|
||||||
} else {
|
: $this->_resultCounter - 1;
|
||||||
$resultKey = $this->_resultCounter - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($scalars as $name => $value) {
|
foreach ($scalars as $name => $value) {
|
||||||
@ -279,6 +283,12 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($oneToOne) {
|
||||||
|
$this->_resultPointers[$dqlAlias] =& $coll;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($index !== false) {
|
if ($index !== false) {
|
||||||
$this->_resultPointers[$dqlAlias] =& $coll[$index];
|
$this->_resultPointers[$dqlAlias] =& $coll[$index];
|
||||||
|
|
||||||
@ -289,12 +299,6 @@ class ArrayHydrator extends AbstractHydrator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($oneToOne) {
|
|
||||||
$this->_resultPointers[$dqlAlias] =& $coll;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
end($coll);
|
end($coll);
|
||||||
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class HydrationException extends \Doctrine\ORM\ORMException
|
|||||||
public static function emptyDiscriminatorValue($dqlAlias)
|
public static function emptyDiscriminatorValue($dqlAlias)
|
||||||
{
|
{
|
||||||
return new self("The DQL alias '" . $dqlAlias . "' contains an entity ".
|
return new self("The DQL alias '" . $dqlAlias . "' contains an entity ".
|
||||||
"of an inheritance hierachy with an empty discriminator value. This means " .
|
"of an inheritance hierarchy with an empty discriminator value. This means " .
|
||||||
"that the database contains inconsistent data with an empty " .
|
"that the database contains inconsistent data with an empty " .
|
||||||
"discriminator value in a table row."
|
"discriminator value in a table row."
|
||||||
);
|
);
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
namespace Doctrine\ORM\Internal\Hydration;
|
namespace Doctrine\ORM\Internal\Hydration;
|
||||||
|
|
||||||
|
use Doctrine\ORM\UnitOfWork;
|
||||||
use PDO;
|
use PDO;
|
||||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
use Doctrine\ORM\PersistentCollection;
|
use Doctrine\ORM\PersistentCollection;
|
||||||
@ -94,8 +95,8 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
|
|
||||||
$this->resultCounter = 0;
|
$this->resultCounter = 0;
|
||||||
|
|
||||||
if ( ! isset($this->_hints['deferEagerLoad'])) {
|
if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
|
||||||
$this->_hints['deferEagerLoad'] = true;
|
$this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||||
@ -152,7 +153,7 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
*/
|
*/
|
||||||
protected function cleanup()
|
protected function cleanup()
|
||||||
{
|
{
|
||||||
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
|
$eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true;
|
||||||
|
|
||||||
parent::cleanup();
|
parent::cleanup();
|
||||||
|
|
||||||
|
@ -114,9 +114,8 @@ class SimpleObjectHydrator extends AbstractHydrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert field to a valid PHP value
|
// Convert field to a valid PHP value
|
||||||
if (isset($cache[$column]['field'])) {
|
if (isset($cache[$column]['type'])) {
|
||||||
$type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
|
$value = Type::getType($cache[$column]['type'])->convertToPHPValue($value, $this->_platform);
|
||||||
$value = $type->convertToPHPValue($value, $this->_platform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
|
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
|
||||||
@ -145,41 +144,36 @@ class SimpleObjectHydrator extends AbstractHydrator
|
|||||||
*/
|
*/
|
||||||
protected function hydrateColumnInfo($entityName, $column)
|
protected function hydrateColumnInfo($entityName, $column)
|
||||||
{
|
{
|
||||||
switch (true) {
|
|
||||||
case (isset($this->_rsm->fieldMappings[$column])):
|
|
||||||
$class = isset($this->declaringClasses[$column])
|
|
||||||
? $this->declaringClasses[$column]
|
|
||||||
: $this->class;
|
|
||||||
|
|
||||||
// If class is not part of the inheritance, ignore
|
if (isset($this->_rsm->fieldMappings[$column])) {
|
||||||
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
|
$name = $this->_rsm->fieldMappings[$column];
|
||||||
return null;
|
$class = isset($this->declaringClasses[$column])
|
||||||
}
|
? $this->declaringClasses[$column]
|
||||||
|
: $this->class;
|
||||||
|
|
||||||
return array(
|
// If class is not part of the inheritance, ignore
|
||||||
'class' => $class,
|
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
|
||||||
'name' => $this->_rsm->fieldMappings[$column],
|
return null;
|
||||||
'field' => true,
|
}
|
||||||
);
|
|
||||||
|
|
||||||
case (isset($this->_rsm->relationMap[$column])):
|
return array(
|
||||||
$class = isset($this->_rsm->relationMap[$column])
|
'name' => $name,
|
||||||
? $this->_rsm->relationMap[$column]
|
'type' => $class->fieldMappings[$name]['type']
|
||||||
: $this->class;
|
);
|
||||||
|
|
||||||
// If class is not self referencing, ignore
|
|
||||||
if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Decide what to do with associations. It seems original code is incomplete.
|
|
||||||
// One solution is to load the association, but it might require extra efforts.
|
|
||||||
return array('name' => $column);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return array(
|
|
||||||
'name' => $this->_rsm->metaMappings[$column]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($this->_rsm->metaMappings[$column])) {
|
||||||
|
return array(
|
||||||
|
'name' => $this->_rsm->metaMappings[$column],
|
||||||
|
'type' => (isset($this->_rsm->typeMappings[$column]) ? $this->_rsm->typeMappings[$column] : null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ObjectHydrator should be used instead of SimpleObjectHydrator
|
||||||
|
if (isset($this->_rsm->relationMap[$column])) {
|
||||||
|
throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class SingleScalarHydrator extends AbstractHydrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($numRows > 1 || count($data[key($data)]) > 1) {
|
if ($numRows > 1 || count($data[key($data)]) > 1) {
|
||||||
throw new NonUniqueResultException();
|
throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cache = array();
|
$cache = array();
|
||||||
|
97
lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php
Normal file
97
lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Mapping;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
|
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ANSI compliant quote strategy, this strategy does not apply any quote.
|
||||||
|
* To use this strategy all mapped tables and columns should be ANSI compliant.
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||||
|
*/
|
||||||
|
class AnsiQuoteStrategy implements QuoteStrategy
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform)
|
||||||
|
{
|
||||||
|
return $class->fieldMappings[$fieldName]['columnName'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getTableName(ClassMetadata $class, AbstractPlatform $platform)
|
||||||
|
{
|
||||||
|
return $class->table['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform)
|
||||||
|
{
|
||||||
|
return $definition['sequenceName'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform)
|
||||||
|
{
|
||||||
|
return $joinColumn['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform)
|
||||||
|
{
|
||||||
|
return $joinColumn['referencedColumnName'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform)
|
||||||
|
{
|
||||||
|
return $association['joinTable']['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform)
|
||||||
|
{
|
||||||
|
return $class->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null)
|
||||||
|
{
|
||||||
|
return $platform->getSQLResultCasing($columnName . $counter);
|
||||||
|
}
|
||||||
|
}
|
@ -200,7 +200,7 @@ class AssociationBuilder
|
|||||||
} else if ($this->type == ClassMetadata::ONE_TO_ONE) {
|
} else if ($this->type == ClassMetadata::ONE_TO_ONE) {
|
||||||
$cm->mapOneToOne($mapping);
|
$cm->mapOneToOne($mapping);
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException("Type should be a ToOne Assocation here");
|
throw new \InvalidArgumentException("Type should be a ToOne Association here");
|
||||||
}
|
}
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
|||||||
/**
|
/**
|
||||||
* Builder Object for ClassMetadata
|
* Builder Object for ClassMetadata
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.com
|
* @link www.doctrine-project.com
|
||||||
* @since 2.2
|
* @since 2.2
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
@ -163,7 +163,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets class as root of a joined table inheritance hierachy.
|
* Sets class as root of a joined table inheritance hierarchy.
|
||||||
*
|
*
|
||||||
* @return ClassMetadataBuilder
|
* @return ClassMetadataBuilder
|
||||||
*/
|
*/
|
||||||
@ -175,7 +175,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets class as root of a single table inheritance hierachy.
|
* Sets class as root of a single table inheritance hierarchy.
|
||||||
*
|
*
|
||||||
* @return ClassMetadataBuilder
|
* @return ClassMetadataBuilder
|
||||||
*/
|
*/
|
||||||
@ -207,7 +207,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a subclass to this inheritance hierachy.
|
* Adds a subclass to this inheritance hierarchy.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $class
|
* @param string $class
|
||||||
@ -319,7 +319,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a ManyToOne Assocation Builder.
|
* Creates a ManyToOne Association Builder.
|
||||||
*
|
*
|
||||||
* Note: This method does not add the association, you have to call build() on the AssociationBuilder.
|
* Note: This method does not add the association, you have to call build() on the AssociationBuilder.
|
||||||
*
|
*
|
||||||
@ -361,7 +361,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds simple inverse one-to-one assocation.
|
* Adds simple inverse one-to-one association.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $targetEntity
|
* @param string $targetEntity
|
||||||
@ -378,7 +378,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds simple owning one-to-one assocation.
|
* Adds simple owning one-to-one association.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $targetEntity
|
* @param string $targetEntity
|
||||||
@ -398,7 +398,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a ManyToMany Assocation Builder.
|
* Creates a ManyToMany Association Builder.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $targetEntity
|
* @param string $targetEntity
|
||||||
@ -418,7 +418,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a simple owning many to many assocation.
|
* Adds a simple owning many to many association.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $targetEntity
|
* @param string $targetEntity
|
||||||
@ -438,7 +438,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a simple inverse many to many assocation.
|
* Adds a simple inverse many to many association.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $targetEntity
|
* @param string $targetEntity
|
||||||
@ -455,7 +455,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a one to many assocation builder.
|
* Creates a one to many association builder.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $targetEntity
|
* @param string $targetEntity
|
||||||
@ -475,7 +475,7 @@ class ClassMetadataBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds simple OneToMany assocation.
|
* Adds simple OneToMany association.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $targetEntity
|
* @param string $targetEntity
|
||||||
|
@ -22,7 +22,7 @@ namespace Doctrine\ORM\Mapping\Builder;
|
|||||||
/**
|
/**
|
||||||
* Field Builder
|
* Field Builder
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.com
|
* @link www.doctrine-project.com
|
||||||
* @since 2.2
|
* @since 2.2
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
|
@ -22,7 +22,7 @@ namespace Doctrine\ORM\Mapping\Builder;
|
|||||||
/**
|
/**
|
||||||
* ManyToMany Association Builder
|
* ManyToMany Association Builder
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.com
|
* @link www.doctrine-project.com
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
|
@ -22,7 +22,7 @@ namespace Doctrine\ORM\Mapping\Builder;
|
|||||||
/**
|
/**
|
||||||
* OneToMany Association Builder
|
* OneToMany Association Builder
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.com
|
* @link www.doctrine-project.com
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
|
@ -103,6 +103,10 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
|||||||
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
|
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
|
||||||
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
|
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
|
||||||
|
|
||||||
|
if ( ! empty($parent->customGeneratorDefinition)) {
|
||||||
|
$class->setCustomGeneratorDefinition($parent->customGeneratorDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
if ($parent->isMappedSuperclass) {
|
if ($parent->isMappedSuperclass) {
|
||||||
$class->setCustomRepositoryClass($parent->customRepositoryClassName);
|
$class->setCustomRepositoryClass($parent->customRepositoryClassName);
|
||||||
}
|
}
|
||||||
@ -136,6 +140,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
|||||||
$this->completeIdGeneratorMapping($class);
|
$this->completeIdGeneratorMapping($class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($class->embeddedClasses as $property => $embeddableClass) {
|
||||||
|
$embeddableMetadata = $this->getMetadataFor($embeddableClass);
|
||||||
|
$class->inlineEmbeddable($property, $embeddableMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
if ($parent && $parent->isInheritanceTypeSingleTable()) {
|
if ($parent && $parent->isInheritanceTypeSingleTable()) {
|
||||||
$class->setPrimaryTable($parent->table);
|
$class->setPrimaryTable($parent->table);
|
||||||
}
|
}
|
||||||
@ -171,7 +180,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
|||||||
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->wakeupReflection($class, $this->getReflectionService());
|
|
||||||
$this->validateRuntimeMetadata($class, $parent);
|
$this->validateRuntimeMetadata($class, $parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +201,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
$class->validateIdentifier();
|
$class->validateIdentifier();
|
||||||
$class->validateAssocations();
|
$class->validateAssociations();
|
||||||
$class->validateLifecycleCallbacks($this->getReflectionService());
|
$class->validateLifecycleCallbacks($this->getReflectionService());
|
||||||
|
|
||||||
// verify inheritance
|
// verify inheritance
|
||||||
@ -319,7 +327,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
|||||||
foreach ($parentClass->associationMappings as $field => $mapping) {
|
foreach ($parentClass->associationMappings as $field => $mapping) {
|
||||||
if ($parentClass->isMappedSuperclass) {
|
if ($parentClass->isMappedSuperclass) {
|
||||||
if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
|
if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
|
||||||
throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field);
|
throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field);
|
||||||
}
|
}
|
||||||
$mapping['sourceEntity'] = $subClass->name;
|
$mapping['sourceEntity'] = $subClass->name;
|
||||||
}
|
}
|
||||||
@ -447,7 +455,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
|||||||
$sequenceName = null;
|
$sequenceName = null;
|
||||||
$fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null;
|
$fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null;
|
||||||
|
|
||||||
if ($this->targetPlatform instanceof Platforms\PostgreSQLPlatform) {
|
if ($this->targetPlatform instanceof Platforms\PostgreSqlPlatform) {
|
||||||
$columnName = $class->getSingleIdentifierColumnName();
|
$columnName = $class->getSingleIdentifierColumnName();
|
||||||
$quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
|
$quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
|
||||||
$sequenceName = $class->getTableName() . '_' . $columnName . '_seq';
|
$sequenceName = $class->getTableName() . '_' . $columnName . '_seq';
|
||||||
|
@ -246,6 +246,13 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
*/
|
*/
|
||||||
public $isMappedSuperclass = false;
|
public $isMappedSuperclass = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* READ-ONLY: Wheather this class describes the mapping of an embeddable class.
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
public $isEmbeddedClass = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* READ-ONLY: The names of the parent classes (ancestors).
|
* READ-ONLY: The names of the parent classes (ancestors).
|
||||||
*
|
*
|
||||||
@ -260,6 +267,13 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
*/
|
*/
|
||||||
public $subClasses = array();
|
public $subClasses = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* READ-ONLY: The names of all embedded classes based on properties.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $embeddedClasses = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* READ-ONLY: The named queries allowed to be called directly from Repository.
|
* READ-ONLY: The named queries allowed to be called directly from Repository.
|
||||||
*
|
*
|
||||||
@ -358,7 +372,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
* - <b>scale</b> (integer, optional, schema-only)
|
* - <b>scale</b> (integer, optional, schema-only)
|
||||||
* The scale of a decimal column. Only valid if the column type is decimal.
|
* The scale of a decimal column. Only valid if the column type is decimal.
|
||||||
*
|
*
|
||||||
[* - <b>'unique'] (string, optional, schema-only)</b>
|
* - <b>'unique'</b> (string, optional, schema-only)
|
||||||
* Whether a unique constraint should be generated for the column.
|
* Whether a unique constraint should be generated for the column.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
@ -509,7 +523,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
public $isIdentifierComposite = false;
|
public $isIdentifierComposite = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association.
|
* READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association.
|
||||||
*
|
*
|
||||||
* This flag is necessary because some code blocks require special treatment of this cases.
|
* This flag is necessary because some code blocks require special treatment of this cases.
|
||||||
*
|
*
|
||||||
@ -631,7 +645,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the ReflectionPropertys of the mapped class.
|
* Gets the ReflectionProperties of the mapped class.
|
||||||
*
|
*
|
||||||
* @return array An array of ReflectionProperty instances.
|
* @return array An array of ReflectionProperty instances.
|
||||||
*/
|
*/
|
||||||
@ -831,6 +845,10 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
$serialized[] = 'lifecycleCallbacks';
|
$serialized[] = 'lifecycleCallbacks';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->entityListeners) {
|
||||||
|
$serialized[] = 'entityListeners';
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->namedQueries) {
|
if ($this->namedQueries) {
|
||||||
$serialized[] = 'namedQueries';
|
$serialized[] = 'namedQueries';
|
||||||
}
|
}
|
||||||
@ -880,6 +898,15 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
$this->reflClass = $reflService->getClass($this->name);
|
$this->reflClass = $reflService->getClass($this->name);
|
||||||
|
|
||||||
foreach ($this->fieldMappings as $field => $mapping) {
|
foreach ($this->fieldMappings as $field => $mapping) {
|
||||||
|
if (isset($mapping['declaredField'])) {
|
||||||
|
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
|
||||||
|
$reflService->getAccessibleProperty($this->name, $mapping['declaredField']),
|
||||||
|
$reflService->getAccessibleProperty($this->embeddedClasses[$mapping['declaredField']], $mapping['originalField']),
|
||||||
|
$this->embeddedClasses[$mapping['declaredField']]
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$this->reflFields[$field] = isset($mapping['declared'])
|
$this->reflFields[$field] = isset($mapping['declared'])
|
||||||
? $reflService->getAccessibleProperty($mapping['declared'], $field)
|
? $reflService->getAccessibleProperty($mapping['declared'], $field)
|
||||||
: $reflService->getAccessibleProperty($this->name, $field);
|
: $reflService->getAccessibleProperty($this->name, $field);
|
||||||
@ -921,8 +948,12 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
*/
|
*/
|
||||||
public function validateIdentifier()
|
public function validateIdentifier()
|
||||||
{
|
{
|
||||||
|
if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify & complete identifier mapping
|
// Verify & complete identifier mapping
|
||||||
if ( ! $this->identifier && ! $this->isMappedSuperclass) {
|
if ( ! $this->identifier) {
|
||||||
throw MappingException::identifierRequired($this->name);
|
throw MappingException::identifierRequired($this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -938,7 +969,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
*
|
*
|
||||||
* @throws MappingException
|
* @throws MappingException
|
||||||
*/
|
*/
|
||||||
public function validateAssocations()
|
public function validateAssociations()
|
||||||
{
|
{
|
||||||
foreach ($this->associationMappings as $mapping) {
|
foreach ($this->associationMappings as $mapping) {
|
||||||
if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
|
if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
|
||||||
@ -1377,7 +1408,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
|
if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
|
||||||
throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
|
throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch mode. Default fetch mode to LAZY, if not set.
|
// Fetch mode. Default fetch mode to LAZY, if not set.
|
||||||
@ -1437,7 +1468,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
$uniqueContraintColumns = array();
|
$uniqueConstraintColumns = array();
|
||||||
foreach ($mapping['joinColumns'] as &$joinColumn) {
|
foreach ($mapping['joinColumns'] as &$joinColumn) {
|
||||||
if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
|
if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
|
||||||
if (count($mapping['joinColumns']) == 1) {
|
if (count($mapping['joinColumns']) == 1) {
|
||||||
@ -1445,7 +1476,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
$joinColumn['unique'] = true;
|
$joinColumn['unique'] = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$uniqueContraintColumns[] = $joinColumn['name'];
|
$uniqueConstraintColumns[] = $joinColumn['name'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1472,12 +1503,12 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
? $joinColumn['fieldName'] : $joinColumn['name'];
|
? $joinColumn['fieldName'] : $joinColumn['name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($uniqueContraintColumns) {
|
if ($uniqueConstraintColumns) {
|
||||||
if ( ! $this->table) {
|
if ( ! $this->table) {
|
||||||
throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
|
throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
|
||||||
}
|
}
|
||||||
$this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
|
$this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
|
||||||
'columns' => $uniqueContraintColumns
|
'columns' => $uniqueConstraintColumns
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1487,8 +1518,12 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
|
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
|
||||||
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
|
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
|
||||||
|
|
||||||
|
if ($mapping['orphanRemoval']) {
|
||||||
|
unset($mapping['unique']);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
|
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
|
||||||
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
|
throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $mapping;
|
return $mapping;
|
||||||
@ -1543,15 +1578,18 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
$mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
|
$mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity']
|
||||||
|
&& (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])));
|
||||||
|
|
||||||
if ( ! isset($mapping['joinTable']['joinColumns'])) {
|
if ( ! isset($mapping['joinTable']['joinColumns'])) {
|
||||||
$mapping['joinTable']['joinColumns'] = array(array(
|
$mapping['joinTable']['joinColumns'] = array(array(
|
||||||
'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity']),
|
'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null),
|
||||||
'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
|
'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
|
||||||
'onDelete' => 'CASCADE'));
|
'onDelete' => 'CASCADE'));
|
||||||
}
|
}
|
||||||
if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
|
if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
|
||||||
$mapping['joinTable']['inverseJoinColumns'] = array(array(
|
$mapping['joinTable']['inverseJoinColumns'] = array(array(
|
||||||
'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity']),
|
'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null),
|
||||||
'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
|
'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
|
||||||
'onDelete' => 'CASCADE'));
|
'onDelete' => 'CASCADE'));
|
||||||
}
|
}
|
||||||
@ -2051,7 +2089,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this entity is the root in any entity-inheritance-hierachy.
|
* Checks if this entity is the root in any entity-inheritance-hierarchy.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -2151,9 +2189,8 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
public function mapField(array $mapping)
|
public function mapField(array $mapping)
|
||||||
{
|
{
|
||||||
$this->_validateAndCompleteFieldMapping($mapping);
|
$this->_validateAndCompleteFieldMapping($mapping);
|
||||||
if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
|
$this->assertFieldNotMapped($mapping['fieldName']);
|
||||||
throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
|
|
||||||
}
|
|
||||||
$this->fieldMappings[$mapping['fieldName']] = $mapping;
|
$this->fieldMappings[$mapping['fieldName']] = $mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2401,9 +2438,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
{
|
{
|
||||||
$sourceFieldName = $assocMapping['fieldName'];
|
$sourceFieldName = $assocMapping['fieldName'];
|
||||||
|
|
||||||
if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
|
$this->assertFieldNotMapped($sourceFieldName);
|
||||||
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->associationMappings[$sourceFieldName] = $assocMapping;
|
$this->associationMappings[$sourceFieldName] = $assocMapping;
|
||||||
}
|
}
|
||||||
@ -2427,7 +2462,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
* @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
|
* @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
|
||||||
*
|
*
|
||||||
* @param string $lifecycleEvent The lifecycle event.
|
* @param string $lifecycleEvent The lifecycle event.
|
||||||
* @param object $entity The Entity on which the event occured.
|
* @param object $entity The Entity on which the event occurred.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@ -2779,8 +2814,12 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
*/
|
*/
|
||||||
public function setSequenceGeneratorDefinition(array $definition)
|
public function setSequenceGeneratorDefinition(array $definition)
|
||||||
{
|
{
|
||||||
if (isset($definition['name']) && $definition['name'] == '`') {
|
if ( ! isset($definition['sequenceName'])) {
|
||||||
$definition['name'] = trim($definition['name'], '`');
|
throw MappingException::missingSequenceName($this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($definition['sequenceName'][0] == '`') {
|
||||||
|
$definition['sequenceName'] = trim($definition['sequenceName'], '`');
|
||||||
$definition['quoted'] = true;
|
$definition['quoted'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3015,4 +3054,63 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
|
|
||||||
return $className;
|
return $className;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getMetadataValue($name) {
|
||||||
|
|
||||||
|
if (isset($this->$name)) {
|
||||||
|
return $this->$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map Embedded Class
|
||||||
|
*
|
||||||
|
* @array $mapping
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function mapEmbedded(array $mapping)
|
||||||
|
{
|
||||||
|
$this->assertFieldNotMapped($mapping['fieldName']);
|
||||||
|
|
||||||
|
$this->embeddedClasses[$mapping['fieldName']] = $this->fullyQualifiedClassName($mapping['class']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline the embeddable class
|
||||||
|
*
|
||||||
|
* @param string $property
|
||||||
|
* @param ClassMetadataInfo $embeddable
|
||||||
|
*/
|
||||||
|
public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
|
||||||
|
{
|
||||||
|
foreach ($embeddable->fieldMappings as $fieldMapping) {
|
||||||
|
$fieldMapping['declaredField'] = $property;
|
||||||
|
$fieldMapping['originalField'] = $fieldMapping['fieldName'];
|
||||||
|
$fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName'];
|
||||||
|
$fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName($property, $fieldMapping['columnName'], $this->reflClass->name, $embeddable->reflClass->name);
|
||||||
|
|
||||||
|
$this->mapField($fieldMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $fieldName
|
||||||
|
* @throws MappingException
|
||||||
|
*/
|
||||||
|
private function assertFieldNotMapped($fieldName)
|
||||||
|
{
|
||||||
|
if (isset($this->fieldMappings[$fieldName]) ||
|
||||||
|
isset($this->associationMappings[$fieldName]) ||
|
||||||
|
isset($this->embeddedClasses[$fieldName])) {
|
||||||
|
|
||||||
|
throw MappingException::duplicateFieldMapping($this->name, $fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
namespace Doctrine\ORM\Mapping;
|
namespace Doctrine\ORM\Mapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default DefaultEntityListene
|
* The default DefaultEntityListener
|
||||||
*
|
*
|
||||||
* @since 2.4
|
* @since 2.4
|
||||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||||
|
@ -50,6 +50,14 @@ class DefaultNamingStrategy implements NamingStrategy
|
|||||||
return $propertyName;
|
return $propertyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null)
|
||||||
|
{
|
||||||
|
return $propertyName.'_'.$embeddedColumnName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -127,12 +127,15 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
|||||||
*/
|
*/
|
||||||
public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null)
|
public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null)
|
||||||
{
|
{
|
||||||
// Trim the column alias to the maximum identifier length of the platform.
|
// 1 ) Concatenate column name and counter
|
||||||
// If the alias is to long, characters are cut off from the beginning.
|
// 2 ) Trim the column alias to the maximum identifier length of the platform.
|
||||||
// And strip non alphanumeric characters
|
// If the alias is to long, characters are cut off from the beginning.
|
||||||
|
// 3 ) Strip non alphanumeric characters
|
||||||
|
// 4 ) Prefix with "_" if the result its numeric
|
||||||
$columnName = $columnName . $counter;
|
$columnName = $columnName . $counter;
|
||||||
$columnName = substr($columnName, -$platform->getMaxIdentifierLength());
|
$columnName = substr($columnName, -$platform->getMaxIdentifierLength());
|
||||||
$columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName);
|
$columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName);
|
||||||
|
$columnName = is_numeric($columnName) ? '_' . $columnName : $columnName;
|
||||||
|
|
||||||
return $platform->getSQLResultCasing($columnName);
|
return $platform->getSQLResultCasing($columnName);
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,8 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
|||||||
$mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
|
$mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
|
||||||
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
|
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
|
||||||
$metadata->isMappedSuperclass = true;
|
$metadata->isMappedSuperclass = true;
|
||||||
|
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\Embeddable'])) {
|
||||||
|
$metadata->isEmbeddedClass = true;
|
||||||
} else {
|
} else {
|
||||||
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
|
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
|
||||||
}
|
}
|
||||||
@ -247,7 +249,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
|||||||
$mapping = array();
|
$mapping = array();
|
||||||
$mapping['fieldName'] = $property->getName();
|
$mapping['fieldName'] = $property->getName();
|
||||||
|
|
||||||
// Check for JoinColummn/JoinColumns annotations
|
// Check for JoinColumn/JoinColumns annotations
|
||||||
$joinColumns = array();
|
$joinColumns = array();
|
||||||
|
|
||||||
if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) {
|
if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) {
|
||||||
@ -364,6 +366,9 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
$metadata->mapManyToMany($mapping);
|
$metadata->mapManyToMany($mapping);
|
||||||
|
} else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Embedded')) {
|
||||||
|
$mapping['class'] = $embeddedAnnot->class;
|
||||||
|
$metadata->mapEmbedded($mapping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +380,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
|||||||
$override = array();
|
$override = array();
|
||||||
$fieldName = $associationOverride->name;
|
$fieldName = $associationOverride->name;
|
||||||
|
|
||||||
// Check for JoinColummn/JoinColumns annotations
|
// Check for JoinColumn/JoinColumns annotations
|
||||||
if ($associationOverride->joinColumns) {
|
if ($associationOverride->joinColumns) {
|
||||||
$joinColumns = array();
|
$joinColumns = array();
|
||||||
foreach ($associationOverride->joinColumns as $joinColumn) {
|
foreach ($associationOverride->joinColumns as $joinColumn) {
|
||||||
@ -533,6 +538,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
|||||||
/**
|
/**
|
||||||
* Parse the given JoinColumn as array
|
* Parse the given JoinColumn as array
|
||||||
*
|
*
|
||||||
|
* @param JoinColumn $joinColumn
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function joinColumnToArray(JoinColumn $joinColumn)
|
private function joinColumnToArray(JoinColumn $joinColumn)
|
||||||
|
@ -19,12 +19,15 @@
|
|||||||
|
|
||||||
namespace Doctrine\ORM\Mapping\Driver;
|
namespace Doctrine\ORM\Mapping\Driver;
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
|
||||||
use Doctrine\DBAL\Schema\SchemaException;
|
|
||||||
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
|
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
|
||||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
|
||||||
use Doctrine\Common\Util\Inflector;
|
use Doctrine\Common\Util\Inflector;
|
||||||
|
use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||||
|
use Doctrine\DBAL\Schema\SchemaException;
|
||||||
|
use Doctrine\DBAL\Schema\Table;
|
||||||
|
use Doctrine\DBAL\Schema\Column;
|
||||||
|
use Doctrine\DBAL\Types\Type;
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||||
use Doctrine\ORM\Mapping\MappingException;
|
use Doctrine\ORM\Mapping\MappingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,248 +87,15 @@ class DatabaseDriver implements MappingDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets tables manually instead of relying on the reverse engeneering capabilities of SchemaManager.
|
* Set the namespace for the generated entities.
|
||||||
*
|
*
|
||||||
* @param array $entityTables
|
* @param string $namespace
|
||||||
* @param array $manyToManyTables
|
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function setTables($entityTables, $manyToManyTables)
|
public function setNamespace($namespace)
|
||||||
{
|
{
|
||||||
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
|
$this->namespace = $namespace;
|
||||||
foreach ($entityTables as $table) {
|
|
||||||
$className = $this->getClassNameForTable($table->getName());
|
|
||||||
$this->classToTableNames[$className] = $table->getName();
|
|
||||||
$this->tables[$table->getName()] = $table;
|
|
||||||
}
|
|
||||||
foreach ($manyToManyTables as $table) {
|
|
||||||
$this->manyToManyTables[$table->getName()] = $table;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @throws \Doctrine\ORM\Mapping\MappingException
|
|
||||||
*/
|
|
||||||
private function reverseEngineerMappingFromDatabase()
|
|
||||||
{
|
|
||||||
if ($this->tables !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tables = array();
|
|
||||||
|
|
||||||
foreach ($this->_sm->listTableNames() as $tableName) {
|
|
||||||
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
|
|
||||||
foreach ($tables as $tableName => $table) {
|
|
||||||
/* @var $table \Doctrine\DBAL\Schema\Table */
|
|
||||||
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
|
||||||
$foreignKeys = $table->getForeignKeys();
|
|
||||||
} else {
|
|
||||||
$foreignKeys = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$allForeignKeyColumns = array();
|
|
||||||
foreach ($foreignKeys as $foreignKey) {
|
|
||||||
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! $table->hasPrimaryKey()) {
|
|
||||||
throw new MappingException(
|
|
||||||
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
|
|
||||||
"support reverse engineering from tables that don't have a primary key."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$pkColumns = $table->getPrimaryKey()->getColumns();
|
|
||||||
sort($pkColumns);
|
|
||||||
sort($allForeignKeyColumns);
|
|
||||||
|
|
||||||
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
|
|
||||||
$this->manyToManyTables[$tableName] = $table;
|
|
||||||
} else {
|
|
||||||
// lower-casing is necessary because of Oracle Uppercase Tablenames,
|
|
||||||
// assumption is lower-case + underscore separated.
|
|
||||||
$className = $this->getClassNameForTable($tableName);
|
|
||||||
$this->tables[$tableName] = $table;
|
|
||||||
$this->classToTableNames[$className] = $tableName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function loadMetadataForClass($className, ClassMetadata $metadata)
|
|
||||||
{
|
|
||||||
$this->reverseEngineerMappingFromDatabase();
|
|
||||||
|
|
||||||
if (!isset($this->classToTableNames[$className])) {
|
|
||||||
throw new \InvalidArgumentException("Unknown class " . $className);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tableName = $this->classToTableNames[$className];
|
|
||||||
|
|
||||||
$metadata->name = $className;
|
|
||||||
$metadata->table['name'] = $tableName;
|
|
||||||
|
|
||||||
$columns = $this->tables[$tableName]->getColumns();
|
|
||||||
$indexes = $this->tables[$tableName]->getIndexes();
|
|
||||||
try {
|
|
||||||
$primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns();
|
|
||||||
} catch(SchemaException $e) {
|
|
||||||
$primaryKeyColumns = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
|
||||||
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
|
|
||||||
} else {
|
|
||||||
$foreignKeys = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$allForeignKeyColumns = array();
|
|
||||||
foreach ($foreignKeys as $foreignKey) {
|
|
||||||
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
|
|
||||||
}
|
|
||||||
|
|
||||||
$ids = array();
|
|
||||||
$fieldMappings = array();
|
|
||||||
foreach ($columns as $column) {
|
|
||||||
$fieldMapping = array();
|
|
||||||
|
|
||||||
if (in_array($column->getName(), $allForeignKeyColumns)) {
|
|
||||||
continue;
|
|
||||||
} else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
|
|
||||||
$fieldMapping['id'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
|
|
||||||
$fieldMapping['columnName'] = $column->getName();
|
|
||||||
$fieldMapping['type'] = strtolower((string) $column->getType());
|
|
||||||
|
|
||||||
if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) {
|
|
||||||
$fieldMapping['length'] = $column->getLength();
|
|
||||||
$fieldMapping['fixed'] = $column->getFixed();
|
|
||||||
} else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) {
|
|
||||||
$fieldMapping['unsigned'] = $column->getUnsigned();
|
|
||||||
}
|
|
||||||
$fieldMapping['nullable'] = $column->getNotNull() ? false : true;
|
|
||||||
|
|
||||||
if (isset($fieldMapping['id'])) {
|
|
||||||
$ids[] = $fieldMapping;
|
|
||||||
} else {
|
|
||||||
$fieldMappings[] = $fieldMapping;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ids) {
|
|
||||||
if (count($ids) == 1) {
|
|
||||||
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($ids as $id) {
|
|
||||||
$metadata->mapField($id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($fieldMappings as $fieldMapping) {
|
|
||||||
$metadata->mapField($fieldMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->manyToManyTables as $manyTable) {
|
|
||||||
foreach ($manyTable->getForeignKeys() as $foreignKey) {
|
|
||||||
// foreign key maps to the table of the current entity, many to many association probably exists
|
|
||||||
if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) {
|
|
||||||
$myFk = $foreignKey;
|
|
||||||
$otherFk = null;
|
|
||||||
foreach ($manyTable->getForeignKeys() as $foreignKey) {
|
|
||||||
if ($foreignKey != $myFk) {
|
|
||||||
$otherFk = $foreignKey;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$otherFk) {
|
|
||||||
// the definition of this many to many table does not contain
|
|
||||||
// enough foreign key information to continue reverse engeneering.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$localColumn = current($myFk->getColumns());
|
|
||||||
$associationMapping = array();
|
|
||||||
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
|
|
||||||
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
|
|
||||||
if (current($manyTable->getColumns())->getName() == $localColumn) {
|
|
||||||
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
|
||||||
$associationMapping['joinTable'] = array(
|
|
||||||
'name' => strtolower($manyTable->getName()),
|
|
||||||
'joinColumns' => array(),
|
|
||||||
'inverseJoinColumns' => array(),
|
|
||||||
);
|
|
||||||
|
|
||||||
$fkCols = $myFk->getForeignColumns();
|
|
||||||
$cols = $myFk->getColumns();
|
|
||||||
for ($i = 0; $i < count($cols); $i++) {
|
|
||||||
$associationMapping['joinTable']['joinColumns'][] = array(
|
|
||||||
'name' => $cols[$i],
|
|
||||||
'referencedColumnName' => $fkCols[$i],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$fkCols = $otherFk->getForeignColumns();
|
|
||||||
$cols = $otherFk->getColumns();
|
|
||||||
for ($i = 0; $i < count($cols); $i++) {
|
|
||||||
$associationMapping['joinTable']['inverseJoinColumns'][] = array(
|
|
||||||
'name' => $cols[$i],
|
|
||||||
'referencedColumnName' => $fkCols[$i],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
|
||||||
}
|
|
||||||
$metadata->mapManyToMany($associationMapping);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($foreignKeys as $foreignKey) {
|
|
||||||
$foreignTable = $foreignKey->getForeignTableName();
|
|
||||||
$cols = $foreignKey->getColumns();
|
|
||||||
$fkCols = $foreignKey->getForeignColumns();
|
|
||||||
|
|
||||||
$localColumn = current($cols);
|
|
||||||
$associationMapping = array();
|
|
||||||
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
|
|
||||||
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
|
|
||||||
|
|
||||||
if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) {
|
|
||||||
$associationMapping['fieldName'] = $associationMapping['fieldName'] . "2";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) {
|
|
||||||
$associationMapping['id'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < count($cols); $i++) {
|
|
||||||
$associationMapping['joinColumns'][] = array(
|
|
||||||
'name' => $cols[$i],
|
|
||||||
'referencedColumnName' => $fkCols[$i],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Here we need to check if $cols are the same as $primaryKeyColums
|
|
||||||
if (!array_diff($cols,$primaryKeyColumns)) {
|
|
||||||
$metadata->mapOneToOne($associationMapping);
|
|
||||||
} else {
|
|
||||||
$metadata->mapManyToOne($associationMapping);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -373,6 +143,376 @@ class DatabaseDriver implements MappingDriver
|
|||||||
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
|
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.
|
||||||
|
*
|
||||||
|
* @param array $entityTables
|
||||||
|
* @param array $manyToManyTables
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setTables($entityTables, $manyToManyTables)
|
||||||
|
{
|
||||||
|
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
|
||||||
|
|
||||||
|
foreach ($entityTables as $table) {
|
||||||
|
$className = $this->getClassNameForTable($table->getName());
|
||||||
|
|
||||||
|
$this->classToTableNames[$className] = $table->getName();
|
||||||
|
$this->tables[$table->getName()] = $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($manyToManyTables as $table) {
|
||||||
|
$this->manyToManyTables[$table->getName()] = $table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function loadMetadataForClass($className, ClassMetadata $metadata)
|
||||||
|
{
|
||||||
|
$this->reverseEngineerMappingFromDatabase();
|
||||||
|
|
||||||
|
if ( ! isset($this->classToTableNames[$className])) {
|
||||||
|
throw new \InvalidArgumentException("Unknown class " . $className);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tableName = $this->classToTableNames[$className];
|
||||||
|
|
||||||
|
$metadata->name = $className;
|
||||||
|
$metadata->table['name'] = $tableName;
|
||||||
|
|
||||||
|
$this->buildIndexes($metadata);
|
||||||
|
$this->buildFieldMappings($metadata);
|
||||||
|
$this->buildToOneAssociationMappings($metadata);
|
||||||
|
|
||||||
|
foreach ($this->manyToManyTables as $manyTable) {
|
||||||
|
foreach ($manyTable->getForeignKeys() as $foreignKey) {
|
||||||
|
// foreign key maps to the table of the current entity, many to many association probably exists
|
||||||
|
if ( ! (strtolower($tableName) === strtolower($foreignKey->getForeignTableName()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$myFk = $foreignKey;
|
||||||
|
$otherFk = null;
|
||||||
|
|
||||||
|
foreach ($manyTable->getForeignKeys() as $foreignKey) {
|
||||||
|
if ($foreignKey != $myFk) {
|
||||||
|
$otherFk = $foreignKey;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $otherFk) {
|
||||||
|
// the definition of this many to many table does not contain
|
||||||
|
// enough foreign key information to continue reverse engineering.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$localColumn = current($myFk->getColumns());
|
||||||
|
|
||||||
|
$associationMapping = array();
|
||||||
|
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
|
||||||
|
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
|
||||||
|
|
||||||
|
if (current($manyTable->getColumns())->getName() == $localColumn) {
|
||||||
|
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
||||||
|
$associationMapping['joinTable'] = array(
|
||||||
|
'name' => strtolower($manyTable->getName()),
|
||||||
|
'joinColumns' => array(),
|
||||||
|
'inverseJoinColumns' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$fkCols = $myFk->getForeignColumns();
|
||||||
|
$cols = $myFk->getColumns();
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($cols); $i++) {
|
||||||
|
$associationMapping['joinTable']['joinColumns'][] = array(
|
||||||
|
'name' => $cols[$i],
|
||||||
|
'referencedColumnName' => $fkCols[$i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fkCols = $otherFk->getForeignColumns();
|
||||||
|
$cols = $otherFk->getColumns();
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($cols); $i++) {
|
||||||
|
$associationMapping['joinTable']['inverseJoinColumns'][] = array(
|
||||||
|
'name' => $cols[$i],
|
||||||
|
'referencedColumnName' => $fkCols[$i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata->mapManyToMany($associationMapping);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws \Doctrine\ORM\Mapping\MappingException
|
||||||
|
*/
|
||||||
|
private function reverseEngineerMappingFromDatabase()
|
||||||
|
{
|
||||||
|
if ($this->tables !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tables = array();
|
||||||
|
|
||||||
|
foreach ($this->_sm->listTableNames() as $tableName) {
|
||||||
|
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
|
||||||
|
|
||||||
|
foreach ($tables as $tableName => $table) {
|
||||||
|
$foreignKeys = ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints())
|
||||||
|
? $table->getForeignKeys()
|
||||||
|
: array();
|
||||||
|
|
||||||
|
$allForeignKeyColumns = array();
|
||||||
|
|
||||||
|
foreach ($foreignKeys as $foreignKey) {
|
||||||
|
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $table->hasPrimaryKey()) {
|
||||||
|
throw new MappingException(
|
||||||
|
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
|
||||||
|
"support reverse engineering from tables that don't have a primary key."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pkColumns = $table->getPrimaryKey()->getColumns();
|
||||||
|
|
||||||
|
sort($pkColumns);
|
||||||
|
sort($allForeignKeyColumns);
|
||||||
|
|
||||||
|
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
|
||||||
|
$this->manyToManyTables[$tableName] = $table;
|
||||||
|
} else {
|
||||||
|
// lower-casing is necessary because of Oracle Uppercase Tablenames,
|
||||||
|
// assumption is lower-case + underscore separated.
|
||||||
|
$className = $this->getClassNameForTable($tableName);
|
||||||
|
|
||||||
|
$this->tables[$tableName] = $table;
|
||||||
|
$this->classToTableNames[$className] = $tableName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build indexes from a class metadata.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata
|
||||||
|
*/
|
||||||
|
private function buildIndexes(ClassMetadataInfo $metadata)
|
||||||
|
{
|
||||||
|
$tableName = $metadata->table['name'];
|
||||||
|
$indexes = $this->tables[$tableName]->getIndexes();
|
||||||
|
|
||||||
|
foreach($indexes as $index){
|
||||||
|
if ($index->isPrimary()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$indexName = $index->getName();
|
||||||
|
$indexColumns = $index->getColumns();
|
||||||
|
$constraintType = $index->isUnique()
|
||||||
|
? 'uniqueConstraints'
|
||||||
|
: 'indexes';
|
||||||
|
|
||||||
|
$metadata->table[$constraintType][$indexName]['columns'] = $indexColumns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build field mapping from class metadata.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata
|
||||||
|
*/
|
||||||
|
private function buildFieldMappings(ClassMetadataInfo $metadata)
|
||||||
|
{
|
||||||
|
$tableName = $metadata->table['name'];
|
||||||
|
$columns = $this->tables[$tableName]->getColumns();
|
||||||
|
$primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]);
|
||||||
|
$foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]);
|
||||||
|
$allForeignKeys = array();
|
||||||
|
|
||||||
|
foreach ($foreignKeys as $foreignKey) {
|
||||||
|
$allForeignKeys = array_merge($allForeignKeys, $foreignKey->getLocalColumns());
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
$fieldMappings = array();
|
||||||
|
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
if (in_array($column->getName(), $allForeignKeys)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldMapping = $this->buildFieldMapping($tableName, $column);
|
||||||
|
|
||||||
|
if ($primaryKeys && in_array($column->getName(), $primaryKeys)) {
|
||||||
|
$fieldMapping['id'] = true;
|
||||||
|
$ids[] = $fieldMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldMappings[] = $fieldMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check for the columns here, because we might have associations as id as well.
|
||||||
|
if ($ids && count($primaryKeys) == 1) {
|
||||||
|
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($fieldMappings as $fieldMapping) {
|
||||||
|
$metadata->mapField($fieldMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build field mapping from a schema column definition
|
||||||
|
*
|
||||||
|
* @param string $tableName
|
||||||
|
* @param \Doctrine\DBAL\Schema\Column $column
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function buildFieldMapping($tableName, Column $column)
|
||||||
|
{
|
||||||
|
$fieldMapping = array(
|
||||||
|
'fieldName' => $this->getFieldNameForColumn($tableName, $column->getName(), false),
|
||||||
|
'columnName' => $column->getName(),
|
||||||
|
'type' => strtolower((string) $column->getType()),
|
||||||
|
'nullable' => ( ! $column->getNotNull()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Type specific elements
|
||||||
|
switch ($fieldMapping['type']) {
|
||||||
|
case Type::TARRAY:
|
||||||
|
case Type::BLOB:
|
||||||
|
case Type::GUID:
|
||||||
|
case Type::JSON_ARRAY:
|
||||||
|
case Type::OBJECT:
|
||||||
|
case Type::SIMPLE_ARRAY:
|
||||||
|
case Type::STRING:
|
||||||
|
case Type::TEXT:
|
||||||
|
$fieldMapping['length'] = $column->getLength();
|
||||||
|
$fieldMapping['fixed'] = $column->getFixed();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::DECIMAL:
|
||||||
|
case Type::FLOAT:
|
||||||
|
$fieldMapping['precision'] = $column->getPrecision();
|
||||||
|
$fieldMapping['scale'] = $column->getScale();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::INTEGER:
|
||||||
|
case Type::BIGINT:
|
||||||
|
case Type::SMALLINT:
|
||||||
|
$fieldMapping['unsigned'] = $column->getUnsigned();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment
|
||||||
|
if (($comment = $column->getComment()) !== null) {
|
||||||
|
$fieldMapping['comment'] = $comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default
|
||||||
|
if (($default = $column->getDefault()) !== null) {
|
||||||
|
$fieldMapping['default'] = $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fieldMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build to one (one to one, many to one) association mapping from class metadata.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata
|
||||||
|
*/
|
||||||
|
private function buildToOneAssociationMappings(ClassMetadataInfo $metadata)
|
||||||
|
{
|
||||||
|
$tableName = $metadata->table['name'];
|
||||||
|
$primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]);
|
||||||
|
$foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]);
|
||||||
|
|
||||||
|
foreach ($foreignKeys as $foreignKey) {
|
||||||
|
$foreignTableName = $foreignKey->getForeignTableName();
|
||||||
|
$fkColumns = $foreignKey->getColumns();
|
||||||
|
$fkForeignColumns = $foreignKey->getForeignColumns();
|
||||||
|
$localColumn = current($fkColumns);
|
||||||
|
$associationMapping = array(
|
||||||
|
'fieldName' => $this->getFieldNameForColumn($tableName, $localColumn, true),
|
||||||
|
'targetEntity' => $this->getClassNameForTable($foreignTableName),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) {
|
||||||
|
$associationMapping['fieldName'] .= '2'; // "foo" => "foo2"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($primaryKeys && in_array($localColumn, $primaryKeys)) {
|
||||||
|
$associationMapping['id'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($fkColumns); $i++) {
|
||||||
|
$associationMapping['joinColumns'][] = array(
|
||||||
|
'name' => $fkColumns[$i],
|
||||||
|
'referencedColumnName' => $fkForeignColumns[$i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we need to check if $fkColumns are the same as $primaryKeys
|
||||||
|
if ( ! array_diff($fkColumns, $primaryKeys)) {
|
||||||
|
$metadata->mapOneToOne($associationMapping);
|
||||||
|
} else {
|
||||||
|
$metadata->mapManyToOne($associationMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreive schema table definition foreign keys.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Schema\Table $table
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getTableForeignKeys(Table $table)
|
||||||
|
{
|
||||||
|
return ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints())
|
||||||
|
? $table->getForeignKeys()
|
||||||
|
: array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreive schema table definition primary keys.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Schema\Table $table
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getTablePrimaryKeys(Table $table)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $table->getPrimaryKey()->getColumns();
|
||||||
|
} catch(SchemaException $e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mapped class name for a table if it exists. Otherwise return "classified" version.
|
* Returns the mapped class name for a table if it exists. Otherwise return "classified" version.
|
||||||
*
|
*
|
||||||
@ -412,16 +552,4 @@ class DatabaseDriver implements MappingDriver
|
|||||||
}
|
}
|
||||||
return Inflector::camelize($columnName);
|
return Inflector::camelize($columnName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the namespace for the generated entities.
|
|
||||||
*
|
|
||||||
* @param string $namespace
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setNamespace($namespace)
|
|
||||||
{
|
|
||||||
$this->namespace = $namespace;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
require_once __DIR__.'/../Annotation.php';
|
require_once __DIR__.'/../Annotation.php';
|
||||||
require_once __DIR__.'/../Entity.php';
|
require_once __DIR__.'/../Entity.php';
|
||||||
|
require_once __DIR__.'/../Embeddable.php';
|
||||||
|
require_once __DIR__.'/../Embedded.php';
|
||||||
require_once __DIR__.'/../MappedSuperclass.php';
|
require_once __DIR__.'/../MappedSuperclass.php';
|
||||||
require_once __DIR__.'/../InheritanceType.php';
|
require_once __DIR__.'/../InheritanceType.php';
|
||||||
require_once __DIR__.'/../DiscriminatorColumn.php';
|
require_once __DIR__.'/../DiscriminatorColumn.php';
|
||||||
|
@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping\MappingException;
|
|||||||
/**
|
/**
|
||||||
* XmlDriver is a metadata driver that enables mapping through XML files.
|
* XmlDriver is a metadata driver that enables mapping through XML files.
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.org
|
* @link www.doctrine-project.org
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
@ -224,7 +224,7 @@ class XmlDriver extends FileDriver
|
|||||||
$metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children());
|
$metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The mapping assignement is done in 2 times as a bug might occurs on some php/xml lib versions
|
// The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions
|
||||||
// The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception
|
// The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception
|
||||||
$mappings = array();
|
$mappings = array();
|
||||||
// Evaluate <field ...> mappings
|
// Evaluate <field ...> mappings
|
||||||
@ -234,6 +234,7 @@ class XmlDriver extends FileDriver
|
|||||||
|
|
||||||
if (isset($mapping['version'])) {
|
if (isset($mapping['version'])) {
|
||||||
$metadata->setVersionMapping($mapping);
|
$metadata->setVersionMapping($mapping);
|
||||||
|
unset($mapping['version']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$metadata->mapField($mapping);
|
$metadata->mapField($mapping);
|
||||||
@ -686,7 +687,7 @@ class XmlDriver extends FileDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($fieldMapping['version']) && $fieldMapping['version']) {
|
if (isset($fieldMapping['version']) && $fieldMapping['version']) {
|
||||||
$mapping['version'] = $fieldMapping['version'];
|
$mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($fieldMapping['column-definition'])) {
|
if (isset($fieldMapping['column-definition'])) {
|
||||||
|
@ -264,6 +264,10 @@ class YamlDriver extends FileDriver
|
|||||||
$mapping['columnDefinition'] = $idElement['columnDefinition'];
|
$mapping['columnDefinition'] = $idElement['columnDefinition'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($idElement['options'])) {
|
||||||
|
$mapping['options'] = $idElement['options'];
|
||||||
|
}
|
||||||
|
|
||||||
$metadata->mapField($mapping);
|
$metadata->mapField($mapping);
|
||||||
|
|
||||||
if (isset($idElement['generator'])) {
|
if (isset($idElement['generator'])) {
|
||||||
@ -300,12 +304,14 @@ class YamlDriver extends FileDriver
|
|||||||
|
|
||||||
if (isset($mapping['version'])) {
|
if (isset($mapping['version'])) {
|
||||||
$metadata->setVersionMapping($mapping);
|
$metadata->setVersionMapping($mapping);
|
||||||
|
unset($mapping['version']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$metadata->mapField($mapping);
|
$metadata->mapField($mapping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Evaluate oneToOne relationships
|
// Evaluate oneToOne relationships
|
||||||
if (isset($element['oneToOne'])) {
|
if (isset($element['oneToOne'])) {
|
||||||
foreach ($element['oneToOne'] as $name => $oneToOneElement) {
|
foreach ($element['oneToOne'] as $name => $oneToOneElement) {
|
||||||
|
@ -17,8 +17,12 @@
|
|||||||
* <http://www.doctrine-project.org>.
|
* <http://www.doctrine-project.org>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Doctrine\ORM\Persisters;
|
namespace Doctrine\ORM\Mapping;
|
||||||
|
|
||||||
class UnionSubclassPersister extends BasicEntityPersister
|
/**
|
||||||
|
* @Annotation
|
||||||
|
* @Target("CLASS")
|
||||||
|
*/
|
||||||
|
final class Embeddable implements Annotation
|
||||||
{
|
{
|
||||||
}
|
}
|
32
lib/Doctrine/ORM/Mapping/Embedded.php
Normal file
32
lib/Doctrine/ORM/Mapping/Embedded.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Mapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Annotation
|
||||||
|
* @Target("PROPERTY")
|
||||||
|
*/
|
||||||
|
final class Embedded implements Annotation
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $class;
|
||||||
|
}
|
@ -49,7 +49,7 @@ interface EntityListenerResolver
|
|||||||
/**
|
/**
|
||||||
* Register a entity listener instance.
|
* Register a entity listener instance.
|
||||||
*
|
*
|
||||||
* @return object An entity listener
|
* @param object $object An entity listener
|
||||||
*/
|
*/
|
||||||
function register($object);
|
function register($object);
|
||||||
}
|
}
|
@ -529,7 +529,7 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
*/
|
*/
|
||||||
public static function cannotVersionIdField($className, $fieldName)
|
public static function cannotVersionIdField($className, $fieldName)
|
||||||
{
|
{
|
||||||
return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported.");
|
return new self("Setting Id field '$fieldName' as versionable in entity class '$className' is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -561,7 +561,7 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
*
|
*
|
||||||
* @return MappingException
|
* @return MappingException
|
||||||
*/
|
*/
|
||||||
public static function illegalToManyAssocationOnMappedSuperclass($className, $field)
|
public static function illegalToManyAssociationOnMappedSuperclass($className, $field)
|
||||||
{
|
{
|
||||||
return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'.");
|
return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'.");
|
||||||
}
|
}
|
||||||
@ -632,7 +632,7 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
*
|
*
|
||||||
* @return MappingException
|
* @return MappingException
|
||||||
*/
|
*/
|
||||||
public static function illegalInverseIdentifierAssocation($className, $field)
|
public static function illegalInverseIdentifierAssociation($className, $field)
|
||||||
{
|
{
|
||||||
return new self("An inverse association is not allowed to be identifier in '$className#$field'.");
|
return new self("An inverse association is not allowed to be identifier in '$className#$field'.");
|
||||||
}
|
}
|
||||||
@ -643,7 +643,7 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
*
|
*
|
||||||
* @return MappingException
|
* @return MappingException
|
||||||
*/
|
*/
|
||||||
public static function illegalToManyIdentifierAssoaction($className, $field)
|
public static function illegalToManyIdentifierAssociation($className, $field)
|
||||||
{
|
{
|
||||||
return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'.");
|
return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'.");
|
||||||
}
|
}
|
||||||
@ -668,8 +668,8 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
{
|
{
|
||||||
return new self(
|
return new self(
|
||||||
"Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " .
|
"Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " .
|
||||||
"to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " .
|
"to be properly mapped in the inheritance hierarchy. Alternatively you can make '".$className."' an abstract class " .
|
||||||
"to avoid this exception from occuring."
|
"to avoid this exception from occurring."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,8 +685,8 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param string $listenerName
|
||||||
* @param string $className
|
* @param string $className
|
||||||
* @param string $methodName
|
|
||||||
*
|
*
|
||||||
* @return \Doctrine\ORM\Mapping\MappingException
|
* @return \Doctrine\ORM\Mapping\MappingException
|
||||||
*/
|
*/
|
||||||
@ -757,4 +757,16 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
$cascades
|
$cascades
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $className
|
||||||
|
*
|
||||||
|
* @return MappingException
|
||||||
|
*/
|
||||||
|
public static function missingSequenceName($className)
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,16 @@ interface NamingStrategy
|
|||||||
*/
|
*/
|
||||||
function propertyToColumnName($propertyName, $className = null);
|
function propertyToColumnName($propertyName, $className = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a column name for an embedded property.
|
||||||
|
*
|
||||||
|
* @param string $propertyName
|
||||||
|
* @param string $embeddedColumnName
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default reference column name.
|
* Returns the default reference column name.
|
||||||
*
|
*
|
||||||
|
66
lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php
Normal file
66
lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Mapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acts as a proxy to a nested Property structure, making it look like
|
||||||
|
* just a single scalar property.
|
||||||
|
*
|
||||||
|
* This way value objects "just work" without UnitOfWork, Persisters or Hydrators
|
||||||
|
* needing any changes.
|
||||||
|
*
|
||||||
|
* TODO: Move this class into Common\Reflection
|
||||||
|
*/
|
||||||
|
class ReflectionEmbeddedProperty
|
||||||
|
{
|
||||||
|
private $parentProperty;
|
||||||
|
private $childProperty;
|
||||||
|
private $class;
|
||||||
|
|
||||||
|
public function __construct($parentProperty, $childProperty, $class)
|
||||||
|
{
|
||||||
|
$this->parentProperty = $parentProperty;
|
||||||
|
$this->childProperty = $childProperty;
|
||||||
|
$this->class = $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValue($object)
|
||||||
|
{
|
||||||
|
$embeddedObject = $this->parentProperty->getValue($object);
|
||||||
|
|
||||||
|
if ($embeddedObject === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->childProperty->getValue($embeddedObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValue($object, $value)
|
||||||
|
{
|
||||||
|
$embeddedObject = $this->parentProperty->getValue($object);
|
||||||
|
|
||||||
|
if ($embeddedObject === null) {
|
||||||
|
$embeddedObject = new $this->class; // TODO
|
||||||
|
$this->parentProperty->setValue($object, $embeddedObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->childProperty->setValue($embeddedObject, $value);
|
||||||
|
}
|
||||||
|
}
|
@ -87,6 +87,14 @@ class UnderscoreNamingStrategy implements NamingStrategy
|
|||||||
return $this->underscore($propertyName);
|
return $this->underscore($propertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null)
|
||||||
|
{
|
||||||
|
return $this->underscore($propertyName).'_'.$embeddedColumnName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -176,7 +176,7 @@ class ORMInvalidArgumentException extends \InvalidArgumentException
|
|||||||
public static function invalidCompositeIdentifier()
|
public static function invalidCompositeIdentifier()
|
||||||
{
|
{
|
||||||
return new self("Binding an entity with a composite primary key to a query is not supported. " .
|
return new self("Binding an entity with a composite primary key to a query is not supported. " .
|
||||||
"You should split the parameter into the explicit fields and bind them seperately.");
|
"You should split the parameter into the explicit fields and bind them separately.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,8 +71,10 @@ class OptimisticLockException extends ORMException
|
|||||||
*
|
*
|
||||||
* @return OptimisticLockException
|
* @return OptimisticLockException
|
||||||
*/
|
*/
|
||||||
public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion)
|
public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion)
|
||||||
{
|
{
|
||||||
|
$expectedLockVersion = ($expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion;
|
||||||
|
$actualLockVersion = ($actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion;
|
||||||
return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity);
|
return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,6 +517,18 @@ final class PersistentCollection implements Collection, Selectable
|
|||||||
*/
|
*/
|
||||||
public function get($key)
|
public function get($key)
|
||||||
{
|
{
|
||||||
|
if ( ! $this->initialized
|
||||||
|
&& $this->association['type'] === Mapping\ClassMetadataInfo::ONE_TO_MANY
|
||||||
|
&& $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY
|
||||||
|
&& isset($this->association['indexBy'])
|
||||||
|
) {
|
||||||
|
if (!$this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->association['indexBy'])) {
|
||||||
|
return $this->em->find($this->typeClass->name, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key);
|
||||||
|
}
|
||||||
|
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->get($key);
|
return $this->coll->get($key);
|
||||||
@ -745,6 +757,8 @@ final class PersistentCollection implements Collection, Selectable
|
|||||||
*/
|
*/
|
||||||
public function key()
|
public function key()
|
||||||
{
|
{
|
||||||
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->key();
|
return $this->coll->key();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,6 +767,8 @@ final class PersistentCollection implements Collection, Selectable
|
|||||||
*/
|
*/
|
||||||
public function current()
|
public function current()
|
||||||
{
|
{
|
||||||
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->current();
|
return $this->coll->current();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -761,6 +777,8 @@ final class PersistentCollection implements Collection, Selectable
|
|||||||
*/
|
*/
|
||||||
public function next()
|
public function next()
|
||||||
{
|
{
|
||||||
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->next();
|
return $this->coll->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,27 +856,20 @@ final class PersistentCollection implements Collection, Selectable
|
|||||||
*/
|
*/
|
||||||
public function matching(Criteria $criteria)
|
public function matching(Criteria $criteria)
|
||||||
{
|
{
|
||||||
|
if ($this->isDirty) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->initialized) {
|
if ($this->initialized) {
|
||||||
return $this->coll->matching($criteria);
|
return $this->coll->matching($criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->association['type'] !== ClassMetadata::ONE_TO_MANY) {
|
if ($this->association['type'] !== ClassMetadata::ONE_TO_MANY) {
|
||||||
throw new \RuntimeException("Matching Criteria on PersistentCollection only works on OneToMany assocations at the moment.");
|
throw new \RuntimeException("Matching Criteria on PersistentCollection only works on OneToMany associations at the moment.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are NEW objects we have to check if any of them matches the criteria
|
|
||||||
$newObjects = array();
|
|
||||||
|
|
||||||
if ($this->isDirty) {
|
|
||||||
$newObjects = $this->coll->matching($criteria)->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $this->em
|
|
||||||
->getClassMetadata(get_class($this->owner))
|
|
||||||
->getSingleIdReflectionProperty()
|
|
||||||
->getValue($this->owner);
|
|
||||||
$builder = Criteria::expr();
|
$builder = Criteria::expr();
|
||||||
$ownerExpression = $builder->eq($this->backRefFieldName, $id);
|
$ownerExpression = $builder->eq($this->backRefFieldName, $this->owner);
|
||||||
$expression = $criteria->getWhereExpression();
|
$expression = $criteria->getWhereExpression();
|
||||||
$expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression;
|
$expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression;
|
||||||
|
|
||||||
@ -866,6 +877,6 @@ final class PersistentCollection implements Collection, Selectable
|
|||||||
|
|
||||||
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->association['targetEntity']);
|
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->association['targetEntity']);
|
||||||
|
|
||||||
return new ArrayCollection(array_merge($persister->loadCriteria($criteria), $newObjects));
|
return new ArrayCollection($persister->loadCriteria($criteria));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ abstract class AbstractCollectionPersister
|
|||||||
*
|
*
|
||||||
* @param \Doctrine\ORM\PersistentCollection $coll
|
* @param \Doctrine\ORM\PersistentCollection $coll
|
||||||
*
|
*
|
||||||
* @return void
|
* @return string
|
||||||
*/
|
*/
|
||||||
abstract protected function getDeleteSQL(PersistentCollection $coll);
|
abstract protected function getDeleteSQL(PersistentCollection $coll);
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ abstract class AbstractCollectionPersister
|
|||||||
*
|
*
|
||||||
* @param \Doctrine\ORM\PersistentCollection $coll
|
* @param \Doctrine\ORM\PersistentCollection $coll
|
||||||
*
|
*
|
||||||
* @return void
|
* @return array
|
||||||
*/
|
*/
|
||||||
abstract protected function getDeleteSQLParameters(PersistentCollection $coll);
|
abstract protected function getDeleteSQLParameters(PersistentCollection $coll);
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ use Doctrine\Common\Collections\Criteria;
|
|||||||
use Doctrine\Common\Collections\Expr\Comparison;
|
use Doctrine\Common\Collections\Expr\Comparison;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A BasicEntityPersiter maps an entity to a single table in a relational database.
|
* A BasicEntityPersister maps an entity to a single table in a relational database.
|
||||||
*
|
*
|
||||||
* A persister is always responsible for a single entity type.
|
* A persister is always responsible for a single entity type.
|
||||||
*
|
*
|
||||||
@ -84,15 +84,16 @@ class BasicEntityPersister
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
static private $comparisonMap = array(
|
static private $comparisonMap = array(
|
||||||
Comparison::EQ => '= %s',
|
Comparison::EQ => '= %s',
|
||||||
Comparison::IS => 'IS %s',
|
Comparison::IS => '= %s',
|
||||||
Comparison::NEQ => '!= %s',
|
Comparison::NEQ => '!= %s',
|
||||||
Comparison::GT => '> %s',
|
Comparison::GT => '> %s',
|
||||||
Comparison::GTE => '>= %s',
|
Comparison::GTE => '>= %s',
|
||||||
Comparison::LT => '< %s',
|
Comparison::LT => '< %s',
|
||||||
Comparison::LTE => '<= %s',
|
Comparison::LTE => '<= %s',
|
||||||
Comparison::IN => 'IN (%s)',
|
Comparison::IN => 'IN (%s)',
|
||||||
Comparison::NIN => 'NOT IN (%s)',
|
Comparison::NIN => 'NOT IN (%s)',
|
||||||
|
Comparison::CONTAINS => 'LIKE %s',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,8 +178,8 @@ class BasicEntityPersister
|
|||||||
protected $selectColumnListSql;
|
protected $selectColumnListSql;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one
|
* The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one
|
||||||
* associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations.
|
* associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
@ -463,7 +464,9 @@ class BasicEntityPersister
|
|||||||
$params[] = $this->class->reflFields[$versionField]->getValue($entity);
|
$params[] = $this->class->reflFields[$versionField]->getValue($entity);
|
||||||
|
|
||||||
switch ($versionFieldType) {
|
switch ($versionFieldType) {
|
||||||
|
case Type::SMALLINT:
|
||||||
case Type::INTEGER:
|
case Type::INTEGER:
|
||||||
|
case Type::BIGINT:
|
||||||
$set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
|
$set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -499,7 +502,7 @@ class BasicEntityPersister
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Todo this only covers scenarios with no inheritance or of the same level. Is there something
|
// @Todo this only covers scenarios with no inheritance or of the same level. Is there something
|
||||||
// like self-referential relationship between different levels of an inheritance hierachy? I hope not!
|
// like self-referential relationship between different levels of an inheritance hierarchy? I hope not!
|
||||||
$selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']);
|
$selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']);
|
||||||
$class = $this->class;
|
$class = $this->class;
|
||||||
$association = $mapping;
|
$association = $mapping;
|
||||||
@ -559,13 +562,35 @@ class BasicEntityPersister
|
|||||||
*/
|
*/
|
||||||
public function delete($entity)
|
public function delete($entity)
|
||||||
{
|
{
|
||||||
|
$class = $this->class;
|
||||||
|
$em = $this->em;
|
||||||
|
|
||||||
$identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
|
$identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
|
||||||
$tableName = $this->quoteStrategy->getTableName($this->class, $this->platform);
|
$tableName = $this->quoteStrategy->getTableName($class, $this->platform);
|
||||||
$idColumns = $this->quoteStrategy->getIdentifierColumnNames($this->class, $this->platform);
|
$idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform);
|
||||||
$id = array_combine($idColumns, $identifier);
|
$id = array_combine($idColumns, $identifier);
|
||||||
|
$types = array_map(function ($identifier) use ($class, $em) {
|
||||||
|
|
||||||
|
if (isset($class->fieldMappings[$identifier])) {
|
||||||
|
return $class->fieldMappings[$identifier]['type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetMapping = $em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']);
|
||||||
|
|
||||||
|
if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) {
|
||||||
|
return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) {
|
||||||
|
return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ORMException::unrecognizedField($targetMapping->identifier[0]);
|
||||||
|
|
||||||
|
}, $class->identifier);
|
||||||
|
|
||||||
$this->deleteJoinTableRecords($identifier);
|
$this->deleteJoinTableRecords($identifier);
|
||||||
$this->conn->delete($tableName, $id);
|
$this->conn->delete($tableName, $id, $types);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -851,7 +876,7 @@ class BasicEntityPersister
|
|||||||
$stmt = $this->conn->executeQuery($query, $params, $types);
|
$stmt = $this->conn->executeQuery($query, $params, $types);
|
||||||
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
||||||
|
|
||||||
return $hydrator->hydrateAll($stmt, $this->rsm, array('deferEagerLoads' => true));
|
return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -908,7 +933,7 @@ class BasicEntityPersister
|
|||||||
|
|
||||||
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
||||||
|
|
||||||
return $hydrator->hydrateAll($stmt, $this->rsm, array('deferEagerLoads' => true));
|
return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -939,7 +964,7 @@ class BasicEntityPersister
|
|||||||
private function loadArrayFromStatement($assoc, $stmt)
|
private function loadArrayFromStatement($assoc, $stmt)
|
||||||
{
|
{
|
||||||
$rsm = $this->rsm;
|
$rsm = $this->rsm;
|
||||||
$hints = array('deferEagerLoads' => true);
|
$hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true);
|
||||||
|
|
||||||
if (isset($assoc['indexBy'])) {
|
if (isset($assoc['indexBy'])) {
|
||||||
$rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed.
|
$rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed.
|
||||||
@ -962,8 +987,8 @@ class BasicEntityPersister
|
|||||||
{
|
{
|
||||||
$rsm = $this->rsm;
|
$rsm = $this->rsm;
|
||||||
$hints = array(
|
$hints = array(
|
||||||
'deferEagerLoads' => true,
|
UnitOfWork::HINT_DEFEREAGERLOAD => true,
|
||||||
'collection' => $coll
|
'collection' => $coll
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isset($assoc['indexBy'])) {
|
if (isset($assoc['indexBy'])) {
|
||||||
@ -1311,16 +1336,22 @@ class BasicEntityPersister
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$columnList = array();
|
$columnList = array();
|
||||||
|
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||||
|
|
||||||
foreach ($assoc['joinColumns'] as $joinColumn) {
|
foreach ($assoc['joinColumns'] as $joinColumn) {
|
||||||
|
$type = null;
|
||||||
|
$isIdentifier = isset($assoc['id']) && $assoc['id'] === true;
|
||||||
$quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
|
$quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
|
||||||
$resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
|
$resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
|
||||||
$columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
|
$columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
|
||||||
. '.' . $quotedColumn . ' AS ' . $resultColumnName;
|
. '.' . $quotedColumn . ' AS ' . $resultColumnName;
|
||||||
|
|
||||||
$this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, isset($assoc['id']) && $assoc['id'] === true);
|
if (isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) {
|
||||||
|
$type = $targetClass->fieldMappings[$targetClass->fieldNames[$joinColumn['referencedColumnName']]]['type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode(', ', $columnList);
|
return implode(', ', $columnList);
|
||||||
@ -1560,7 +1591,7 @@ class BasicEntityPersister
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$visitor = new SqlExpressionVisitor($this);
|
$visitor = new SqlExpressionVisitor($this, $this->class);
|
||||||
|
|
||||||
return $visitor->dispatch($expression);
|
return $visitor->dispatch($expression);
|
||||||
}
|
}
|
||||||
@ -1585,6 +1616,14 @@ class BasicEntityPersister
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($comparison !== null) {
|
if ($comparison !== null) {
|
||||||
|
|
||||||
|
// special case null value handling
|
||||||
|
if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null) {
|
||||||
|
return $condition . ' IS NULL';
|
||||||
|
} else if ($comparison === Comparison::NEQ && $value === null) {
|
||||||
|
return $condition . ' IS NOT NULL';
|
||||||
|
}
|
||||||
|
|
||||||
return $condition . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);
|
return $condition . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1847,16 +1886,7 @@ class BasicEntityPersister
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
|
return $this->em->getUnitOfWork()->getSingleIdentifierValue($value);
|
||||||
$idValues = $this->em->getUnitOfWork()->getEntityIdentifier($value);
|
|
||||||
|
|
||||||
return reset($idValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
$class = $this->em->getClassMetadata(get_class($value));
|
|
||||||
$idValues = $class->getIdentifierValues($value);
|
|
||||||
|
|
||||||
return reset($idValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,6 +184,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
|||||||
// Execute inserts on subtables.
|
// Execute inserts on subtables.
|
||||||
// The order doesn't matter because all child tables link to the root table via FK.
|
// The order doesn't matter because all child tables link to the root table via FK.
|
||||||
foreach ($subTableStmts as $tableName => $stmt) {
|
foreach ($subTableStmts as $tableName => $stmt) {
|
||||||
|
/** @var \Doctrine\DBAL\Statement $stmt */
|
||||||
$paramIndex = 1;
|
$paramIndex = 1;
|
||||||
$data = isset($insertData[$tableName])
|
$data = isset($insertData[$tableName])
|
||||||
? $insertData[$tableName]
|
? $insertData[$tableName]
|
||||||
@ -196,7 +197,9 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($data as $columnName => $value) {
|
foreach ($data as $columnName => $value) {
|
||||||
$stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]);
|
if (!is_array($id) || !isset($id[$columnName])) {
|
||||||
|
$stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
@ -302,31 +305,31 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
|||||||
|
|
||||||
// INNER JOIN parent tables
|
// INNER JOIN parent tables
|
||||||
foreach ($this->class->parentClasses as $parentClassName) {
|
foreach ($this->class->parentClasses as $parentClassName) {
|
||||||
$contitions = array();
|
$conditions = array();
|
||||||
$parentClass = $this->em->getClassMetadata($parentClassName);
|
$parentClass = $this->em->getClassMetadata($parentClassName);
|
||||||
$tableAlias = $this->getSQLTableAlias($parentClassName);
|
$tableAlias = $this->getSQLTableAlias($parentClassName);
|
||||||
$joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';
|
$joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';
|
||||||
|
|
||||||
|
|
||||||
foreach ($identifierColumn as $idColumn) {
|
foreach ($identifierColumn as $idColumn) {
|
||||||
$contitions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
$conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
$joinSql .= implode(' AND ', $contitions);
|
$joinSql .= implode(' AND ', $conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// OUTER JOIN sub tables
|
// OUTER JOIN sub tables
|
||||||
foreach ($this->class->subClasses as $subClassName) {
|
foreach ($this->class->subClasses as $subClassName) {
|
||||||
$contitions = array();
|
$conditions = array();
|
||||||
$subClass = $this->em->getClassMetadata($subClassName);
|
$subClass = $this->em->getClassMetadata($subClassName);
|
||||||
$tableAlias = $this->getSQLTableAlias($subClassName);
|
$tableAlias = $this->getSQLTableAlias($subClassName);
|
||||||
$joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';
|
$joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';
|
||||||
|
|
||||||
foreach ($identifierColumn as $idColumn) {
|
foreach ($identifierColumn as $idColumn) {
|
||||||
$contitions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
$conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
$joinSql .= implode(' AND ', $contitions);
|
$joinSql .= implode(' AND ', $conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) {
|
if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) {
|
||||||
|
@ -190,22 +190,22 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
*/
|
*/
|
||||||
protected function getDeleteSQLParameters(PersistentCollection $coll)
|
protected function getDeleteSQLParameters(PersistentCollection $coll)
|
||||||
{
|
{
|
||||||
$identifier = $this->uow->getEntityIdentifier($coll->getOwner());
|
|
||||||
$mapping = $coll->getMapping();
|
$mapping = $coll->getMapping();
|
||||||
$params = array();
|
$identifier = $this->uow->getEntityIdentifier($coll->getOwner());
|
||||||
|
|
||||||
// Optimization for single column identifier
|
// Optimization for single column identifier
|
||||||
if (count($mapping['relationToSourceKeyColumns']) === 1) {
|
if (count($mapping['relationToSourceKeyColumns']) === 1) {
|
||||||
$params[] = array_pop($identifier);
|
return array(reset($identifier));
|
||||||
|
|
||||||
return $params;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Composite identifier
|
// Composite identifier
|
||||||
$sourceClass = $this->em->getClassMetadata(get_class($coll->getOwner()));
|
$sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
|
||||||
|
$params = array();
|
||||||
|
|
||||||
foreach ($mapping['relationToSourceKeyColumns'] as $srcColumn) {
|
foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
|
||||||
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
|
$params[] = isset($sourceClass->fieldNames[$refColumnName])
|
||||||
|
? $identifier[$sourceClass->fieldNames[$refColumnName]]
|
||||||
|
: $identifier[$sourceClass->getFieldForColumn($columnName)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $params;
|
return $params;
|
||||||
@ -235,7 +235,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
foreach ($joinColumns as $joinColumn) {
|
foreach ($joinColumns as $joinColumn) {
|
||||||
$columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
|
$columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
|
||||||
$referencedName = $joinColumn['referencedColumnName'];
|
$referencedName = $joinColumn['referencedColumnName'];
|
||||||
$conditions[] = $columnName . ' = ?';
|
$conditions[] = 't.' . $columnName . ' = ?';
|
||||||
$params[] = ($class->containsForeignIdentifier)
|
$params[] = ($class->containsForeignIdentifier)
|
||||||
? $id[$class->getFieldForColumn($referencedName)]
|
? $id[$class->getFieldForColumn($referencedName)]
|
||||||
: $id[$class->fieldNames[$referencedName]];
|
: $id[$class->fieldNames[$referencedName]];
|
||||||
@ -361,12 +361,13 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
$params = array();
|
$params = array();
|
||||||
|
|
||||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||||
$whereClauses[] = $joinTableColumn . ' = ?';
|
$whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
|
||||||
|
|
||||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||||
$params[] = ($targetClass->containsForeignIdentifier)
|
$params[] = ($targetClass->containsForeignIdentifier)
|
||||||
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
|
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
|
||||||
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,9 +378,12 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($addFilters) {
|
if ($addFilters) {
|
||||||
|
$quotedJoinTable .= ' t';
|
||||||
|
|
||||||
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
|
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
|
||||||
|
|
||||||
if ($filterSql) {
|
if ($filterSql) {
|
||||||
$quotedJoinTable .= ' t ' . $joinTargetEntitySQL;
|
$quotedJoinTable .= ' ' . $joinTargetEntitySQL;
|
||||||
$whereClauses[] = $filterSql;
|
$whereClauses[] = $filterSql;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,24 @@ use Doctrine\ORM\UnitOfWork;
|
|||||||
*/
|
*/
|
||||||
class OneToManyPersister extends AbstractCollectionPersister
|
class OneToManyPersister extends AbstractCollectionPersister
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
public function get(PersistentCollection $coll, $index)
|
||||||
|
{
|
||||||
|
$mapping = $coll->getMapping();
|
||||||
|
$uow = $this->em->getUnitOfWork();
|
||||||
|
$persister = $uow->getEntityPersister($mapping['targetEntity']);
|
||||||
|
|
||||||
|
if (!isset($mapping['indexBy'])) {
|
||||||
|
throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $persister->load(array($mapping['mappedBy'] => $coll->getOwner(), $mapping['indexBy'] => $index), null, null, array(), 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the SQL UPDATE that updates a particular row's foreign
|
* Generates the SQL UPDATE that updates a particular row's foreign
|
||||||
* key to null.
|
* key to null.
|
||||||
|
20
lib/Doctrine/ORM/Persisters/PersisterException.php
Normal file
20
lib/Doctrine/ORM/Persisters/PersisterException.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Persisters;
|
||||||
|
|
||||||
|
use Doctrine\ORM\ORMException;
|
||||||
|
|
||||||
|
class PersisterException extends ORMException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return PersisterException
|
||||||
|
*/
|
||||||
|
static public function matchingAssocationFieldRequiresObject($class, $associationName)
|
||||||
|
{
|
||||||
|
return new self(sprintf(
|
||||||
|
"Cannot match on %s::%s with a non-object value. Matching objects by id is " .
|
||||||
|
"not compatible with matching on an in-memory collection, which compares objects by reference.",
|
||||||
|
$class, $associationName
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -178,7 +178,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
|||||||
{
|
{
|
||||||
// Ensure that the filters are applied to the root entity of the inheritance tree
|
// Ensure that the filters are applied to the root entity of the inheritance tree
|
||||||
$targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);
|
$targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);
|
||||||
// we dont care about the $targetTableAlias, in a STI there is only one table.
|
// we don't care about the $targetTableAlias, in a STI there is only one table.
|
||||||
|
|
||||||
return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias);
|
return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
namespace Doctrine\ORM\Persisters;
|
namespace Doctrine\ORM\Persisters;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
|
|
||||||
use Doctrine\Common\Collections\Expr\ExpressionVisitor;
|
use Doctrine\Common\Collections\Expr\ExpressionVisitor;
|
||||||
use Doctrine\Common\Collections\Expr\Comparison;
|
use Doctrine\Common\Collections\Expr\Comparison;
|
||||||
use Doctrine\Common\Collections\Expr\Value;
|
use Doctrine\Common\Collections\Expr\Value;
|
||||||
@ -37,12 +39,18 @@ class SqlExpressionVisitor extends ExpressionVisitor
|
|||||||
*/
|
*/
|
||||||
private $persister;
|
private $persister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Doctrine\ORM\Mapping\ClassMetadata
|
||||||
|
*/
|
||||||
|
private $classMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Doctrine\ORM\Persisters\BasicEntityPersister $persister
|
* @param \Doctrine\ORM\Persisters\BasicEntityPersister $persister
|
||||||
*/
|
*/
|
||||||
public function __construct(BasicEntityPersister $persister)
|
public function __construct(BasicEntityPersister $persister, ClassMetadata $classMetadata)
|
||||||
{
|
{
|
||||||
$this->persister = $persister;
|
$this->persister = $persister;
|
||||||
|
$this->classMetadata = $classMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,6 +65,14 @@ class SqlExpressionVisitor extends ExpressionVisitor
|
|||||||
$field = $comparison->getField();
|
$field = $comparison->getField();
|
||||||
$value = $comparison->getValue()->getValue(); // shortcut for walkValue()
|
$value = $comparison->getValue()->getValue(); // shortcut for walkValue()
|
||||||
|
|
||||||
|
if (isset($this->classMetadata->associationMappings[$field]) &&
|
||||||
|
$value !== null &&
|
||||||
|
! is_object($value) &&
|
||||||
|
! in_array($comparison->getOperator(), array(Comparison::IN, Comparison::NIN))) {
|
||||||
|
|
||||||
|
throw PersisterException::matchingAssocationFieldRequiresObject($this->classMetadata->name, $field);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator());
|
return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +50,15 @@ class SqlValueVisitor extends ExpressionVisitor
|
|||||||
*/
|
*/
|
||||||
public function walkComparison(Comparison $comparison)
|
public function walkComparison(Comparison $comparison)
|
||||||
{
|
{
|
||||||
$value = $comparison->getValue()->getValue();
|
$value = $this->getValueFromComparison($comparison);
|
||||||
$field = $comparison->getField();
|
$field = $comparison->getField();
|
||||||
|
$operator = $comparison->getOperator();
|
||||||
|
|
||||||
|
if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) {
|
||||||
|
return;
|
||||||
|
} else if ($operator === Comparison::NEQ && $value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->values[] = $value;
|
$this->values[] = $value;
|
||||||
$this->types[] = array($field, $value);
|
$this->types[] = array($field, $value);
|
||||||
@ -92,4 +99,20 @@ class SqlValueVisitor extends ExpressionVisitor
|
|||||||
{
|
{
|
||||||
return array($this->values, $this->types);
|
return array($this->values, $this->types);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value from a Comparison. In case of a CONTAINS comparison,
|
||||||
|
* the value is wrapped in %-signs, because it will be used in a LIKE clause.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\Common\Collections\Expr\Comparison $comparison
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function getValueFromComparison(Comparison $comparison)
|
||||||
|
{
|
||||||
|
$value = $comparison->getValue()->getValue();
|
||||||
|
|
||||||
|
return $comparison->getOperator() == Comparison::CONTAINS
|
||||||
|
? "%{$value}%"
|
||||||
|
: $value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace Doctrine\ORM;
|
|||||||
/**
|
/**
|
||||||
* Pessimistic Lock Exception
|
* Pessimistic Lock Exception
|
||||||
*
|
*
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
* @link www.doctrine-project.com
|
* @link www.doctrine-project.com
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user