mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91ea598eb7 | ||
|
|
75e42f8145 | ||
|
|
c6cc39ddee | ||
|
|
211fd2a69e | ||
|
|
8eef747aba | ||
|
|
ceb6924aae | ||
|
|
af4e836f4f | ||
|
|
d650ade2b1 | ||
|
|
521e4880aa | ||
|
|
ee951a19aa | ||
|
|
b1a616dfa5 | ||
|
|
859ca72426 | ||
|
|
a655188e55 | ||
|
|
5be16db9ec | ||
|
|
f6aca7c6ef | ||
|
|
4271706cab | ||
|
|
98d4075de1 | ||
|
|
81cff239f8 | ||
|
|
46dbce9b6c | ||
|
|
64129c66e0 | ||
|
|
479ae3e22a | ||
|
|
0d42c686e1 | ||
|
|
2ba48d58e7 | ||
|
|
c3ab35a8f5 | ||
|
|
6dcbbdae4d | ||
|
|
4a020f2c48 | ||
|
|
5e4811a3cd | ||
|
|
1dd85fe19e | ||
|
|
fd7442a056 | ||
|
|
1d4b188a52 | ||
|
|
6c57ec12ee | ||
|
|
7a3ee2a8da | ||
|
|
5e61b73619 | ||
|
|
23ac0026a0 | ||
|
|
65e85aa471 | ||
|
|
d5b61ec897 | ||
|
|
adc2f24c9a | ||
|
|
8423c0c608 | ||
|
|
44aa098b37 | ||
|
|
839ec8f25a | ||
|
|
0893d2eb00 | ||
|
|
b63babc7b8 | ||
|
|
7350d85a4b | ||
|
|
a6b81d684a | ||
|
|
9e864c3662 | ||
|
|
024cc43189 | ||
|
|
1e79672147 | ||
|
|
3b7448bcf4 | ||
|
|
d5025235f3 | ||
|
|
dbc1a4da4b | ||
|
|
837d3f0f80 | ||
|
|
203fb59115 | ||
|
|
ecdc0fad64 | ||
|
|
e1fd2c158b | ||
|
|
479dae2a99 | ||
|
|
d94c2a11e8 | ||
|
|
d44b9fb05c | ||
|
|
fc16ecc6d7 | ||
|
|
1c9007fb0e | ||
|
|
79dc69b920 | ||
|
|
5e22a08ee1 | ||
|
|
3d43049426 | ||
|
|
95464c95fa | ||
|
|
392d23e401 | ||
|
|
19db4f304d | ||
|
|
aeaeff9002 | ||
|
|
9bc236b4fa | ||
|
|
cd6611ad3e | ||
|
|
41e5fc6b34 | ||
|
|
615a1159a7 | ||
|
|
1059991865 | ||
|
|
18fc94b395 | ||
|
|
b288eac530 | ||
|
|
699cd2cf4b | ||
|
|
c1f10b4673 | ||
|
|
6bff8c338f | ||
|
|
1ca924d021 |
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -4,9 +4,9 @@
|
||||
[submodule "lib/vendor/doctrine-dbal"]
|
||||
path = lib/vendor/doctrine-dbal
|
||||
url = git://github.com/doctrine/dbal.git
|
||||
[submodule "lib/vendor/Symfony/Component/Console"]
|
||||
path = lib/vendor/Symfony/Component/Console
|
||||
url = git://github.com/symfony/Console.git
|
||||
[submodule "lib/vendor/Symfony/Component/Yaml"]
|
||||
path = lib/vendor/Symfony/Component/Yaml
|
||||
url = git://github.com/symfony/Yaml.git
|
||||
[submodule "lib/vendor/Symfony/Component/Console"]
|
||||
path = lib/vendor/Symfony/Component/Console
|
||||
url = git://github.com/symfony/Console.git
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
This document details all the possible changes that you should investigate when updating
|
||||
your project from Doctrine 2.0.x to 2.1
|
||||
|
||||
## Interface for EntityRepository
|
||||
|
||||
The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
|
||||
|
||||
## AnnotationReader changes
|
||||
|
||||
The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way:
|
||||
|
||||
\Doctrine\Common\Annotations\AnnotationRegistry::registerFile('/doctrine-src/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
|
||||
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
// new code necessary starting here
|
||||
$reader->setIgnoreNotImportedAnnotations(true);
|
||||
$reader->setEnableParsePhpImports(false);
|
||||
$reader = new \Doctrine\Common\Annotations\CachedReader(
|
||||
new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache()
|
||||
);
|
||||
|
||||
This is already done inside the ``$config->newDefaultAnnotationDriver``, so everything should automatically work if you are using this method. You can verify if everything still works by executing a console command such as schema-validate that loads all metadata into memory.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
version=2.0.0BETA2
|
||||
dependencies.common=2.0.0BETA4
|
||||
dependencies.dbal=2.0.0BETA4
|
||||
version=2.0.1
|
||||
dependencies.common=2.0.1
|
||||
dependencies.dbal=2.0.1
|
||||
stability=beta
|
||||
build.dir=build
|
||||
dist.dir=dist
|
||||
|
||||
30
build.xml
30
build.xml
@@ -100,30 +100,10 @@
|
||||
</copy>
|
||||
<exec command="sed 's/${version}-DEV/${version}/' ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php > ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php" passthru="true" />
|
||||
<exec command="mv ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php" passthru="true" />
|
||||
<delete dir="${build.dir}/doctrine-orm/Doctrine/Symfony/Component/Yaml/.git" includeemptydirs="true"/>
|
||||
<delete dir="${build.dir}/doctrine-orm/Doctrine/Symfony/Component/Console/.git" includeemptydirs="true"/>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="build" depends="test, build-orm"/>
|
||||
|
||||
<target name="package-phar" depends="build-orm">
|
||||
<pharpackage basedir="${build.dir}/doctrine-orm/" destfile="${dist.dir}/doctrine-orm-${version}.phar" clistub="${build.dir}/doctrine-orm/bin/doctrine.php" signature="sha512">
|
||||
<fileset dir="${build.dir}/doctrine-orm">
|
||||
<include name="**/**" />
|
||||
</fileset>
|
||||
<metadata>
|
||||
<element name="version" value="${version}" />
|
||||
<element name="authors">
|
||||
<element name="Guilherme Blanco"><element name="e-mail" value="guilhermeblanco@gmail.com" /></element>
|
||||
<element name="Benjamin Eberlei"><element name="e-mail" value="kontakt@beberlei.de" /></element>
|
||||
<element name="Jonathan H. Wage"><element name="e-mail" value="jonwage@gmail.com" /></element>
|
||||
<element name="Roman Borschel"><element name="e-mail" value="roman@code-factory.org" /></element>
|
||||
</element>
|
||||
</metadata>
|
||||
</pharpackage>
|
||||
</target>
|
||||
|
||||
<!--
|
||||
Runs the full test suite.
|
||||
-->
|
||||
@@ -186,8 +166,6 @@
|
||||
<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.php" />
|
||||
@@ -223,7 +201,6 @@
|
||||
|
||||
<target name="distribute-download">
|
||||
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" />
|
||||
<copy file="${dist.dir}/doctrine-orm-${version}.phar" todir="${project.download_dir}" />
|
||||
</target>
|
||||
|
||||
<target name="update-dev-version">
|
||||
@@ -235,7 +212,7 @@
|
||||
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
|
||||
</target>
|
||||
|
||||
<target name="release" depends="git-tag,build-packages,package-phar,distribute-download,pirum-release,update-dev-version" />
|
||||
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version" />
|
||||
|
||||
<!--
|
||||
Builds distributable PEAR packages for the Symfony Dependencies
|
||||
@@ -255,7 +232,6 @@
|
||||
<php minimum_version="5.3.0" />
|
||||
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
|
||||
</dependencies>
|
||||
<ignore>bin/</ignore>
|
||||
<ignore>Doctrine/Common/</ignore>
|
||||
<ignore>Doctrine/DBAL/</ignore>
|
||||
<ignore>Doctrine/ORM/</ignore>
|
||||
@@ -278,7 +254,6 @@
|
||||
<php minimum_version="5.3.0" />
|
||||
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
|
||||
</dependencies>
|
||||
<ignore>bin/</ignore>
|
||||
<ignore>Doctrine/Common/</ignore>
|
||||
<ignore>Doctrine/DBAL/</ignore>
|
||||
<ignore>Doctrine/ORM/</ignore>
|
||||
@@ -291,5 +266,4 @@
|
||||
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineSymfonyYaml-${version}.tgz" dir="." passthru="true" />
|
||||
<exec command="sudo pirum build ${project.pirum_dir}" passthru="true" />
|
||||
</target>
|
||||
</project>
|
||||
|
||||
</project>
|
||||
@@ -17,18 +17,11 @@
|
||||
<xs:sequence>
|
||||
<xs:element name="mapped-superclass" type="orm:mapped-superclass" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="entity" type="orm:entity" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:complexType name="emptyType">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="emptyType"/>
|
||||
|
||||
<xs:complexType name="cascade-type">
|
||||
<xs:sequence>
|
||||
@@ -37,9 +30,7 @@
|
||||
<xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="lifecycle-callback-type">
|
||||
@@ -55,32 +46,13 @@
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="lifecycle-callback">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" type="orm:lifecycle-callback-type" use="required" />
|
||||
<xs:attribute name="method" type="xs:NMTOKEN" use="required" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="lifecycle-callbacks">
|
||||
<xs:sequence>
|
||||
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="1" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-query">
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="query" type="xs:string" use="required" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-queries">
|
||||
<xs:sequence>
|
||||
<xs:element name="named-query" type="orm:named-query" minOccurs="1" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
@@ -91,14 +63,12 @@
|
||||
<xs:element name="discriminator-column" type="orm:discriminator-column" minOccurs="0"/>
|
||||
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
|
||||
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
|
||||
<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-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="many-to-one" type="orm:many-to-one" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="many-to-many" type="orm:many-to-many" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="table" type="xs:NMTOKEN" />
|
||||
@@ -106,18 +76,11 @@
|
||||
<xs:attribute name="repository-class" type="xs:string"/>
|
||||
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
|
||||
<xs:attribute name="change-tracking-policy" type="orm:change-tracking-policy" />
|
||||
<xs:attribute name="read-only" type="xs:boolean" default="false" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="mapped-superclass" >
|
||||
<xs:complexContent>
|
||||
<xs:extension base="orm:entity">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:extension>
|
||||
<xs:extension base="orm:entity"/>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
@@ -158,14 +121,10 @@
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="EAGER"/>
|
||||
<xs:enumeration value="LAZY"/>
|
||||
<xs:enumeration value="EXTRALAZY"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="field">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
|
||||
<xs:attribute name="column" type="xs:NMTOKEN" />
|
||||
@@ -176,114 +135,75 @@
|
||||
<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:complexType name="discriminator-column">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="type" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="field-name" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="length" type="xs:NMTOKEN" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="unique-constraint">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="unique-constraints">
|
||||
<xs:sequence>
|
||||
<xs:element name="unique-constraint" type="orm:unique-constraint" minOccurs="1" maxOccurs="unbounded"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="index">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
<xs:attribute name="columns" type="xs:NMTOKENS" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="indexes">
|
||||
<xs:sequence>
|
||||
<xs:element name="index" type="orm:index" minOccurs="1" maxOccurs="unbounded"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="discriminator-mapping">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="value" type="xs:NMTOKEN" use="required"/>
|
||||
<xs:attribute name="class" type="xs:string" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
<xs:attribute name="class" type="xs:NMTOKEN" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="discriminator-map">
|
||||
<xs:sequence>
|
||||
<xs:element name="discriminator-mapping" type="orm:discriminator-mapping" minOccurs="1" maxOccurs="unbounded"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="generator">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="strategy" type="orm:generator-strategy" use="optional" default="AUTO" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="id">
|
||||
<xs:sequence>
|
||||
<xs:element name="generator" type="orm:generator" minOccurs="0" />
|
||||
<xs:element name="sequence-generator" type="orm:sequence-generator" minOccurs="0" maxOccurs="1" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="type" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="type" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="column" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="association-key" type="xs:boolean" default="false" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="sequence-generator">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="sequence-name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="allocation-size" type="xs:integer" use="optional" default="1" />
|
||||
<xs:attribute name="initial-value" type="xs:integer" use="optional" default="1" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="inverse-join-columns">
|
||||
<xs:sequence>
|
||||
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="join-column">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="referenced-column-name" type="xs:NMTOKEN" use="optional" default="id" />
|
||||
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
||||
@@ -291,43 +211,32 @@
|
||||
<xs:attribute name="on-delete" type="orm:fk-action" />
|
||||
<xs:attribute name="on-update" type="orm:fk-action" />
|
||||
<xs:attribute name="column-definition" type="xs:string" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="join-columns">
|
||||
<xs:sequence>
|
||||
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="join-table">
|
||||
<xs:sequence>
|
||||
<xs:element name="join-columns" type="orm:join-columns" />
|
||||
<xs:element name="inverse-join-columns" type="orm:join-columns" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="schema" type="xs:NMTOKEN" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="order-by">
|
||||
<xs:sequence>
|
||||
<xs:element name="order-by-field" type="orm:order-by-field" minOccurs="1" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="order-by-field">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="direction" type="orm:order-by-direction" default="ASC" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="direction" type="orm:order-by-direction" default="ASC" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="order-by-direction">
|
||||
@@ -342,30 +251,24 @@
|
||||
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
|
||||
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
|
||||
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="index-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="one-to-many">
|
||||
<xs:sequence>
|
||||
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
|
||||
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="mapped-by" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="index-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="many-to-one">
|
||||
@@ -374,16 +277,13 @@
|
||||
<xs:choice minOccurs="0" maxOccurs="1">
|
||||
<xs:element name="join-column" type="orm:join-column"/>
|
||||
<xs:element name="join-columns" type="orm:join-columns"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="one-to-one">
|
||||
@@ -392,9 +292,7 @@
|
||||
<xs:choice minOccurs="0" maxOccurs="1">
|
||||
<xs:element name="join-column" type="orm:join-column"/>
|
||||
<xs:element name="join-columns" type="orm:join-columns"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
@@ -402,7 +300,6 @@
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
</xs:schema>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id: Abstract.php 1393 2008-03-06 17:49:16Z guilhermeblanco $
|
||||
*
|
||||
* 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
|
||||
@@ -55,11 +57,6 @@ abstract class AbstractQuery
|
||||
*/
|
||||
const HYDRATE_SINGLE_SCALAR = 4;
|
||||
|
||||
/**
|
||||
* Very simple object hydrator (optimized for performance).
|
||||
*/
|
||||
const HYDRATE_SIMPLEOBJECT = 5;
|
||||
|
||||
/**
|
||||
* @var array The parameter map of this query.
|
||||
*/
|
||||
@@ -162,16 +159,6 @@ abstract class AbstractQuery
|
||||
{
|
||||
return $this->_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all defined parameter types.
|
||||
*
|
||||
* @return array The defined query parameter types.
|
||||
*/
|
||||
public function getParameterTypes()
|
||||
{
|
||||
return $this->_paramTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a query parameter.
|
||||
@@ -184,17 +171,6 @@ abstract class AbstractQuery
|
||||
return isset($this->_params[$key]) ? $this->_params[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a query parameter type.
|
||||
*
|
||||
* @param mixed $key The key (index or name) of the bound parameter.
|
||||
* @return mixed The parameter type of the bound parameter.
|
||||
*/
|
||||
public function getParameterType($key)
|
||||
{
|
||||
return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL query that corresponds to this query object.
|
||||
* The returned SQL syntax depends on the connection driver that is used
|
||||
@@ -216,13 +192,10 @@ abstract class AbstractQuery
|
||||
*/
|
||||
public function setParameter($key, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = Query\ParameterTypeInferer::inferType($value);
|
||||
if ($type !== null) {
|
||||
$this->_paramTypes[$key] = $type;
|
||||
}
|
||||
|
||||
$this->_paramTypes[$key] = $type;
|
||||
$this->_params[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -296,7 +269,7 @@ abstract class AbstractQuery
|
||||
* @param boolean $bool
|
||||
* @param integer $timeToLive
|
||||
* @param string $resultCacheId
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
* @return This query instance.
|
||||
*/
|
||||
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
|
||||
{
|
||||
@@ -358,26 +331,6 @@ abstract class AbstractQuery
|
||||
return $this->_expireResultCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the default fetch mode of an association for this query.
|
||||
*
|
||||
* $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $assocName
|
||||
* @param int $fetchMode
|
||||
* @return AbstractQuery
|
||||
*/
|
||||
public function setFetchMode($class, $assocName, $fetchMode)
|
||||
{
|
||||
if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
|
||||
$fetchMode = Mapping\ClassMetadata::FETCH_LAZY;
|
||||
}
|
||||
|
||||
$this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the processing mode to be used during hydration / result set transformation.
|
||||
*
|
||||
@@ -437,31 +390,6 @@ abstract class AbstractQuery
|
||||
return $this->execute(array(), self::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exactly one result or null.
|
||||
*
|
||||
* @throws NonUniqueResultException
|
||||
* @param int $hydrationMode
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOneOrNullResult($hydrationMode = null)
|
||||
{
|
||||
$result = $this->execute(array(), $hydrationMode);
|
||||
|
||||
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_array($result)) {
|
||||
if (count($result) > 1) {
|
||||
throw new NonUniqueResultException;
|
||||
}
|
||||
return array_shift($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single result of the query.
|
||||
*
|
||||
@@ -548,20 +476,10 @@ abstract class AbstractQuery
|
||||
* @param integer $hydrationMode The hydration mode to use.
|
||||
* @return IterableResult
|
||||
*/
|
||||
public function iterate(array $params = array(), $hydrationMode = null)
|
||||
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
|
||||
{
|
||||
if ($hydrationMode !== null) {
|
||||
$this->setHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
if ($params) {
|
||||
$this->setParameters($params);
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
return $this->_em->newHydrator($this->_hydrationMode)->iterate(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
$this->_doExecute($params, $hydrationMode), $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
}
|
||||
|
||||
@@ -582,6 +500,10 @@ abstract class AbstractQuery
|
||||
$this->setParameters($params);
|
||||
}
|
||||
|
||||
if (isset($this->_params[0])) {
|
||||
throw QueryException::invalidParameterPosition(0);
|
||||
}
|
||||
|
||||
// Check result cache
|
||||
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
|
||||
list($key, $hash) = $this->getResultCacheId();
|
||||
@@ -592,8 +514,8 @@ abstract class AbstractQuery
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
|
||||
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
|
||||
|
||||
|
||||
@@ -20,11 +20,7 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\Common\Cache\Cache,
|
||||
Doctrine\Common\Cache\ArrayCache,
|
||||
Doctrine\Common\Annotations\AnnotationRegistry,
|
||||
Doctrine\Common\Annotations\AnnotationReader,
|
||||
Doctrine\ORM\Mapping\Driver\Driver,
|
||||
Doctrine\ORM\Mapping\Driver\AnnotationDriver;
|
||||
Doctrine\ORM\Mapping\Driver\Driver;
|
||||
|
||||
/**
|
||||
* Configuration container for all configuration options of Doctrine.
|
||||
@@ -124,28 +120,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*/
|
||||
public function newDefaultAnnotationDriver($paths = array())
|
||||
{
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
|
||||
// Register the ORM Annotations in the AnnotationRegistry
|
||||
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
|
||||
|
||||
$reader = new AnnotationReader();
|
||||
$reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache());
|
||||
} else if (version_compare(\Doctrine\Common\Version::VERSION, '2.1.0-DEV', '>=')) {
|
||||
// Register the ORM Annotations in the AnnotationRegistry
|
||||
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
|
||||
|
||||
$reader = new AnnotationReader();
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
$reader->setIgnoreNotImportedAnnotations(true);
|
||||
$reader->setEnableParsePhpImports(false);
|
||||
$reader = new \Doctrine\Common\Annotations\CachedReader(
|
||||
new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache()
|
||||
);
|
||||
} else {
|
||||
$reader = new AnnotationReader();
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
}
|
||||
return new AnnotationDriver($reader, (array)$paths);
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
|
||||
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, (array)$paths);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,16 +163,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
{
|
||||
$this->_attributes['entityNamespaces'] = $entityNamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of registered entity namespace aliases.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getEntityNamespaces()
|
||||
{
|
||||
return $this->_attributes['entityNamespaces'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for the mapping metadata.
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace Doctrine\ORM;
|
||||
|
||||
use Closure, Exception,
|
||||
Doctrine\Common\EventManager,
|
||||
Doctrine\Common\Persistence\ObjectManager,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\DBAL\LockMode,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
@@ -38,7 +37,7 @@ use Closure, Exception,
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class EntityManager implements ObjectManager
|
||||
class EntityManager
|
||||
{
|
||||
/**
|
||||
* The used Configuration.
|
||||
@@ -97,16 +96,12 @@ class EntityManager implements ObjectManager
|
||||
private $proxyFactory;
|
||||
|
||||
/**
|
||||
* The expression builder instance used to generate query expressions.
|
||||
*
|
||||
* @var Doctrine\ORM\Query\Expr
|
||||
* @var ExpressionBuilder The expression builder instance used to generate query expressions.
|
||||
*/
|
||||
private $expressionBuilder;
|
||||
|
||||
/**
|
||||
* Whether the EntityManager is closed or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $closed = false;
|
||||
|
||||
@@ -168,7 +163,7 @@ class EntityManager implements ObjectManager
|
||||
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
|
||||
* </code>
|
||||
*
|
||||
* @return Doctrine\ORM\Query\Expr
|
||||
* @return ExpressionBuilder
|
||||
*/
|
||||
public function getExpressionBuilder()
|
||||
{
|
||||
@@ -203,18 +198,13 @@ class EntityManager implements ObjectManager
|
||||
public function transactional(Closure $func)
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
try {
|
||||
$return = $func($this);
|
||||
|
||||
$func($this);
|
||||
$this->flush();
|
||||
$this->conn->commit();
|
||||
|
||||
return $return ?: true;
|
||||
} catch (Exception $e) {
|
||||
$this->close();
|
||||
$this->conn->rollback();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -688,9 +678,6 @@ class EntityManager implements ObjectManager
|
||||
case Query::HYDRATE_SINGLE_SCALAR:
|
||||
$hydrator = new Internal\Hydration\SingleScalarHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SIMPLEOBJECT:
|
||||
$hydrator = new Internal\Hydration\SimpleObjectHydrator($this);
|
||||
break;
|
||||
default:
|
||||
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
|
||||
$hydrator = new $class($this);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\Common\Persistence\ObjectRepository;
|
||||
|
||||
/**
|
||||
* An EntityRepository serves as a repository for entities with generic as well as
|
||||
@@ -35,7 +34,7 @@ use Doctrine\Common\Persistence\ObjectRepository;
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class EntityRepository implements ObjectRepository
|
||||
class EntityRepository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -78,17 +77,6 @@ class EntityRepository implements ObjectRepository
|
||||
->from($this->_entityName, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Query instance based on a predefined metadata named query.
|
||||
*
|
||||
* @param string $queryName
|
||||
* @return Query
|
||||
*/
|
||||
public function createNamedQuery($queryName)
|
||||
{
|
||||
return $this->_em->createQuery($this->_class->getNamedQuery($queryName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the repository, causing all managed entities to become detached.
|
||||
*/
|
||||
@@ -160,14 +148,11 @@ class EntityRepository implements ObjectRepository
|
||||
* Finds entities by a set of criteria.
|
||||
*
|
||||
* @param array $criteria
|
||||
* @param array|null $orderBy
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return array The objects.
|
||||
* @return array
|
||||
*/
|
||||
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
public function findBy(array $criteria)
|
||||
{
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria, $orderBy, $limit, $offset);
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
/**
|
||||
* Provides event arguments for the onClear event.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Roman Borschel <roman@code-factory.de>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class OnClearEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\EntityManager $em
|
||||
*/
|
||||
public function __construct($em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->em;
|
||||
}
|
||||
}
|
||||
@@ -119,12 +119,4 @@ final class Events
|
||||
* @var string
|
||||
*/
|
||||
const onFlush = 'onFlush';
|
||||
|
||||
/**
|
||||
* The onClear event occurs when the EntityManager#clear() operation is invoked,
|
||||
* after all references to entities have been removed from the unit of work.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const onClear = 'onClear';
|
||||
}
|
||||
@@ -47,18 +47,9 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idFields = $class->getIdentifierFieldNames();
|
||||
foreach ($idFields as $idField) {
|
||||
$value = $class->reflFields[$idField]->getValue($entity);
|
||||
$value = $class->getReflectionProperty($idField)->getValue($entity);
|
||||
if (isset($value)) {
|
||||
if (isset($class->associationMappings[$idField])) {
|
||||
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
|
||||
throw ORMException::entityMissingForeignAssignedId($entity, $value);
|
||||
}
|
||||
|
||||
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
|
||||
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
|
||||
} else {
|
||||
$identifier[$idField] = $value;
|
||||
}
|
||||
$identifier[$idField] = $value;
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
}
|
||||
@@ -67,16 +58,7 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
$idField = $class->identifier[0];
|
||||
$value = $class->reflFields[$idField]->getValue($entity);
|
||||
if (isset($value)) {
|
||||
if (isset($class->associationMappings[$idField])) {
|
||||
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
|
||||
throw ORMException::entityMissingForeignAssignedId($entity, $value);
|
||||
}
|
||||
|
||||
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
|
||||
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
|
||||
} else {
|
||||
$identifier[$idField] = $value;
|
||||
}
|
||||
$identifier[$idField] = $value;
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
}
|
||||
|
||||
@@ -190,12 +190,9 @@ abstract class AbstractHydrator
|
||||
continue;
|
||||
} else {
|
||||
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
|
||||
$fieldName = $this->_rsm->metaMappings[$key];
|
||||
$cache[$key]['isMetaColumn'] = true;
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$cache[$key]['dqlAlias']]);
|
||||
$cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,22 +203,13 @@ abstract class AbstractHydrator
|
||||
|
||||
$dqlAlias = $cache[$key]['dqlAlias'];
|
||||
|
||||
if ($cache[$key]['isIdentifier']) {
|
||||
$id[$dqlAlias] .= '|' . $value;
|
||||
if (isset($cache[$key]['isMetaColumn'])) {
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($cache[$key]['isMetaColumn'])) {
|
||||
if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// in an inheritance hierachy 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.
|
||||
// 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) {
|
||||
continue;
|
||||
if ($cache[$key]['isIdentifier']) {
|
||||
$id[$dqlAlias] .= '|' . $value;
|
||||
}
|
||||
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
|
||||
@@ -287,25 +275,4 @@ abstract class AbstractHydrator
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
protected function registerManaged($class, $entity, $data)
|
||||
{
|
||||
if ($class->isIdentifierComposite) {
|
||||
$id = array();
|
||||
foreach ($class->identifier as $fieldName) {
|
||||
if (isset($class->associationMappings[$fieldName])) {
|
||||
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
|
||||
} else {
|
||||
$id[$fieldName] = $data[$fieldName];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($class->associationMappings[$class->identifier[0]])) {
|
||||
$id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]);
|
||||
} else {
|
||||
$id = array($class->identifier[0] => $data[$class->identifier[0]]);
|
||||
}
|
||||
}
|
||||
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* This local cache is maintained between hydration runs and not cleared.
|
||||
*/
|
||||
private $_ce = array();
|
||||
|
||||
|
||||
/* The following parts are reinitialized on every hydration run. */
|
||||
|
||||
|
||||
private $_identifierMap;
|
||||
private $_resultPointers;
|
||||
private $_idTemplate;
|
||||
@@ -50,7 +50,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
private $_initializedCollections = array();
|
||||
private $_existingCollections = array();
|
||||
//private $_createdEntities;
|
||||
|
||||
|
||||
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
@@ -59,9 +59,6 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->_resultPointers =
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
if (!isset($this->_hints['deferEagerLoad'])) {
|
||||
$this->_hints['deferEagerLoad'] = true;
|
||||
}
|
||||
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
@@ -71,14 +68,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $class;
|
||||
}
|
||||
|
||||
|
||||
// Remember which associations are "fetch joined", so that we know where to inject
|
||||
// collection stubs or proxies and where not.
|
||||
if (isset($this->_rsm->relationMap[$dqlAlias])) {
|
||||
if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
|
||||
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
|
||||
}
|
||||
|
||||
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
|
||||
$sourceClass = $this->_getClassMetadata($sourceClassName);
|
||||
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
|
||||
@@ -115,17 +108,11 @@ class ObjectHydrator extends AbstractHydrator
|
||||
*/
|
||||
protected function _cleanup()
|
||||
{
|
||||
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
|
||||
|
||||
parent::_cleanup();
|
||||
$this->_identifierMap =
|
||||
$this->_initializedCollections =
|
||||
$this->_existingCollections =
|
||||
$this->_resultPointers = array();
|
||||
|
||||
if ($eagerLoad) {
|
||||
$this->_em->getUnitOfWork()->triggerEagerLoads();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +176,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets an entity instance.
|
||||
*
|
||||
@@ -199,43 +186,29 @@ class ObjectHydrator extends AbstractHydrator
|
||||
*/
|
||||
private function _getEntity(array $data, $dqlAlias)
|
||||
{
|
||||
$className = $this->_rsm->aliasMap[$dqlAlias];
|
||||
$className = $this->_rsm->aliasMap[$dqlAlias];
|
||||
if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
|
||||
$discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]];
|
||||
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
|
||||
unset($data[$discrColumn]);
|
||||
}
|
||||
|
||||
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
|
||||
$class = $this->_ce[$className];
|
||||
$this->registerManaged($class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
}
|
||||
|
||||
return $this->_uow->createEntity($className, $data, $this->_hints);
|
||||
}
|
||||
|
||||
|
||||
private function _getEntityFromIdentityMap($className, array $data)
|
||||
{
|
||||
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
|
||||
$class = $this->_ce[$className];
|
||||
/* @var $class ClassMetadata */
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idHash = '';
|
||||
foreach ($class->identifier as $fieldName) {
|
||||
if (isset($class->associationMappings[$fieldName])) {
|
||||
$idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
|
||||
} else {
|
||||
$idHash .= $data[$fieldName] . ' ';
|
||||
}
|
||||
$idHash .= $data[$fieldName] . ' ';
|
||||
}
|
||||
return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName);
|
||||
} else if (isset($class->associationMappings[$class->identifier[0]])) {
|
||||
return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
|
||||
} else {
|
||||
return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a ClassMetadata instance from the local cache.
|
||||
* If the instance is not yet in the local cache, it is loaded into the
|
||||
@@ -293,7 +266,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// Hydrate the data chunks
|
||||
foreach ($rowData as $dqlAlias => $data) {
|
||||
$entityName = $this->_rsm->aliasMap[$dqlAlias];
|
||||
|
||||
|
||||
if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
|
||||
// It's a joined result
|
||||
|
||||
@@ -304,7 +277,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
// Get a reference to the parent object to which the joined element belongs.
|
||||
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
|
||||
$first = reset($this->_resultPointers);
|
||||
$first = reset($this->_resultPointers);
|
||||
$parentObject = $this->_resultPointers[$parentAlias][key($first)];
|
||||
} else if (isset($this->_resultPointers[$parentAlias])) {
|
||||
$parentObject = $this->_resultPointers[$parentAlias];
|
||||
@@ -329,11 +302,11 @@ class ObjectHydrator extends AbstractHydrator
|
||||
} else if ( ! isset($this->_existingCollections[$collKey])) {
|
||||
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
|
||||
}
|
||||
|
||||
|
||||
$indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
|
||||
$index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
|
||||
$indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;
|
||||
|
||||
|
||||
if ( ! $indexExists || ! $indexIsValid) {
|
||||
if (isset($this->_existingCollections[$collKey])) {
|
||||
// Collection exists, only look for the element in the identity map.
|
||||
@@ -422,10 +395,6 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$result[$key] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
|
||||
}
|
||||
|
||||
if (isset($this->_hints['collection'])) {
|
||||
$this->_hints['collection']->hydrateSet($key, $element);
|
||||
}
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array(0 => $element);
|
||||
@@ -433,10 +402,6 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
|
||||
if (isset($this->_hints['collection'])) {
|
||||
$this->_hints['collection']->hydrateAdd($element);
|
||||
}
|
||||
}
|
||||
|
||||
// Update result pointer
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use \PDO;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\Query;
|
||||
|
||||
class SimpleObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadata
|
||||
*/
|
||||
private $class;
|
||||
|
||||
private $declaringClasses = array();
|
||||
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($row, $cache, $result);
|
||||
}
|
||||
|
||||
$this->_em->getUnitOfWork()->triggerEagerLoads();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function _prepare()
|
||||
{
|
||||
if (count($this->_rsm->aliasMap) == 1) {
|
||||
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
|
||||
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
foreach ($this->_rsm->declaringClasses AS $column => $class) {
|
||||
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping not containing exactly one object result.");
|
||||
}
|
||||
if ($this->_rsm->scalarMappings) {
|
||||
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
|
||||
}
|
||||
}
|
||||
|
||||
protected function _hydrateRow(array $sqlResult, array &$cache, array &$result)
|
||||
{
|
||||
$data = array();
|
||||
if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
|
||||
if (!isset($cache[$column])) {
|
||||
if (isset($this->_rsm->fieldMappings[$column])) {
|
||||
$cache[$column]['name'] = $this->_rsm->fieldMappings[$column];
|
||||
$cache[$column]['field'] = true;
|
||||
} else {
|
||||
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache[$column]['field'])) {
|
||||
$value = Type::getType($this->class->fieldMappings[$cache[$column]['name']]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
$data[$cache[$column]['name']] = $value;
|
||||
}
|
||||
$entityName = $this->class->name;
|
||||
} else {
|
||||
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
|
||||
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
|
||||
unset($sqlResult[$discrColumnName]);
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
if (!isset($cache[$column])) {
|
||||
if (isset($this->_rsm->fieldMappings[$column])) {
|
||||
$field = $this->_rsm->fieldMappings[$column];
|
||||
$class = $this->declaringClasses[$column];
|
||||
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
|
||||
$cache[$column]['name'] = $field;
|
||||
$cache[$column]['class'] = $class;
|
||||
}
|
||||
} else if (isset($this->_rsm->relationMap[$column])) {
|
||||
if ($this->_rsm->relationMap[$column] == $entityName || is_subclass_of($entityName, $this->_rsm->relationMap[$column])) {
|
||||
$cache[$column]['name'] = $field;
|
||||
}
|
||||
} else {
|
||||
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache[$column]['class'])) {
|
||||
$value = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
|
||||
// the second and part is to prevent overwrites in case of multiple
|
||||
// inheritance classes using the same property name (See AbstractHydrator)
|
||||
if (isset($cache[$column]) && (!isset($data[$cache[$column]['name']]) || $value !== null)) {
|
||||
$data[$cache[$column]['name']] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
|
||||
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
}
|
||||
|
||||
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
|
||||
}
|
||||
}
|
||||
@@ -63,10 +63,10 @@ class ClassMetadata extends ClassMetadataInfo
|
||||
*/
|
||||
public function __construct($entityName)
|
||||
{
|
||||
parent::__construct($entityName);
|
||||
$this->reflClass = new ReflectionClass($entityName);
|
||||
$this->namespace = $this->reflClass->getNamespaceName();
|
||||
$this->table['name'] = $this->reflClass->getShortName();
|
||||
parent::__construct($this->reflClass->getName()); // do not use $entityName, possible case-problems
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,6 +205,48 @@ class ClassMetadata extends ClassMetadataInfo
|
||||
$this->reflFields[$sourceFieldName] = $refProp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) column name of a mapped field for safe use
|
||||
* in an SQL statement.
|
||||
*
|
||||
* @param string $field
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*/
|
||||
public function getQuotedColumnName($field, $platform)
|
||||
{
|
||||
return isset($this->fieldMappings[$field]['quoted']) ?
|
||||
$platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
|
||||
$this->fieldMappings[$field]['columnName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) primary table name of this class for safe use
|
||||
* in an SQL statement.
|
||||
*
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*/
|
||||
public function getQuotedTableName($platform)
|
||||
{
|
||||
return isset($this->table['quoted']) ?
|
||||
$platform->quoteIdentifier($this->table['name']) :
|
||||
$this->table['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) name of the join table.
|
||||
*
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*/
|
||||
public function getQuotedJoinTableName(array $assoc, $platform)
|
||||
{
|
||||
return isset($assoc['joinTable']['quoted'])
|
||||
? $platform->quoteIdentifier($assoc['joinTable']['name'])
|
||||
: $assoc['joinTable']['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this instance.
|
||||
*
|
||||
@@ -275,10 +317,6 @@ class ClassMetadata extends ClassMetadataInfo
|
||||
$serialized[] = 'isMappedSuperclass';
|
||||
}
|
||||
|
||||
if ($this->containsForeignIdentifier) {
|
||||
$serialized[] = 'containsForeignIdentifier';
|
||||
}
|
||||
|
||||
if ($this->isVersioned) {
|
||||
$serialized[] = 'isVersioned';
|
||||
$serialized[] = 'versionField';
|
||||
@@ -288,14 +326,6 @@ class ClassMetadata extends ClassMetadataInfo
|
||||
$serialized[] = 'lifecycleCallbacks';
|
||||
}
|
||||
|
||||
if ($this->namedQueries) {
|
||||
$serialized[] = 'namedQueries';
|
||||
}
|
||||
|
||||
if ($this->isReadOnly) {
|
||||
$serialized[] = 'isReadOnly';
|
||||
}
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ use ReflectionException,
|
||||
Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\DBAL\Platforms,
|
||||
Doctrine\ORM\Events,
|
||||
Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as ClassMetadataFactoryInterface;
|
||||
Doctrine\ORM\Events;
|
||||
|
||||
/**
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
@@ -37,7 +36,7 @@ use ReflectionException,
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
class ClassMetadataFactory
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
@@ -248,13 +247,11 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
|
||||
// Move down the hierarchy of parent classes, starting from the topmost class
|
||||
$parent = null;
|
||||
$rootEntityFound = false;
|
||||
$visited = array();
|
||||
foreach ($parentClasses as $className) {
|
||||
if (isset($this->loadedMetadata[$className])) {
|
||||
$parent = $this->loadedMetadata[$className];
|
||||
if ( ! $parent->isMappedSuperclass) {
|
||||
$rootEntityFound = true;
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
continue;
|
||||
@@ -263,15 +260,19 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
$class = $this->newClassMetadataInstance($className);
|
||||
|
||||
if ($parent) {
|
||||
$class->setInheritanceType($parent->inheritanceType);
|
||||
$class->setDiscriminatorColumn($parent->discriminatorColumn);
|
||||
if (!$parent->isMappedSuperclass) {
|
||||
$class->setInheritanceType($parent->inheritanceType);
|
||||
$class->setDiscriminatorColumn($parent->discriminatorColumn);
|
||||
}
|
||||
$class->setIdGeneratorType($parent->generatorType);
|
||||
$this->addInheritedFields($class, $parent);
|
||||
$this->addInheritedRelations($class, $parent);
|
||||
$class->setIdentifier($parent->identifier);
|
||||
$class->setVersioned($parent->isVersioned);
|
||||
$class->setVersionField($parent->versionField);
|
||||
$class->setDiscriminatorMap($parent->discriminatorMap);
|
||||
if (!$parent->isMappedSuperclass) {
|
||||
$class->setDiscriminatorMap($parent->discriminatorMap);
|
||||
}
|
||||
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
|
||||
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
|
||||
}
|
||||
@@ -283,10 +284,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
throw MappingException::reflectionFailure($className, $e);
|
||||
}
|
||||
|
||||
// If this class has a parent the id generator strategy is inherited.
|
||||
// However this is only true if the hierachy of parents contains the root entity,
|
||||
// if it consinsts of mapped superclasses these don't necessarily include the id field.
|
||||
if ($parent && $rootEntityFound) {
|
||||
if ($parent && ! $parent->isMappedSuperclass) {
|
||||
if ($parent->isIdGeneratorSequence()) {
|
||||
$class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
|
||||
} else if ($parent->isIdGeneratorTable()) {
|
||||
@@ -313,14 +311,26 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
||||
}
|
||||
|
||||
$this->validateRuntimeMetadata($class, $parent);
|
||||
|
||||
// Verify & complete identifier mapping
|
||||
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
|
||||
throw MappingException::identifierRequired($className);
|
||||
}
|
||||
|
||||
// verify inheritance
|
||||
if (!$parent && !$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
|
||||
if (count($class->discriminatorMap) == 0) {
|
||||
throw MappingException::missingDiscriminatorMap($class->name);
|
||||
}
|
||||
if (!$class->discriminatorColumn) {
|
||||
throw MappingException::missingDiscriminatorColumn($class->name);
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadedMetadata[$className] = $class;
|
||||
|
||||
$parent = $class;
|
||||
|
||||
if ( ! $class->isMappedSuperclass) {
|
||||
$rootEntityFound = true;
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
|
||||
@@ -330,38 +340,6 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
return $loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate runtime metadata is correctly defined.
|
||||
*
|
||||
* @param ClassMetadata $class
|
||||
* @param ClassMetadata $parent
|
||||
*/
|
||||
protected function validateRuntimeMetadata($class, $parent)
|
||||
{
|
||||
// Verify & complete identifier mapping
|
||||
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
|
||||
throw MappingException::identifierRequired($class->name);
|
||||
}
|
||||
|
||||
// verify inheritance
|
||||
if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
|
||||
if (!$parent) {
|
||||
if (count($class->discriminatorMap) == 0) {
|
||||
throw MappingException::missingDiscriminatorMap($class->name);
|
||||
}
|
||||
if (!$class->discriminatorColumn) {
|
||||
throw MappingException::missingDiscriminatorColumn($class->name);
|
||||
}
|
||||
} else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
|
||||
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
|
||||
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
|
||||
}
|
||||
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
|
||||
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
|
||||
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ClassMetadata instance for the given class name.
|
||||
*
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
@@ -40,7 +39,7 @@ use ReflectionClass;
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ClassMetadataInfo implements ClassMetadata
|
||||
class ClassMetadataInfo
|
||||
{
|
||||
/* The inheritance mapping types */
|
||||
/**
|
||||
@@ -122,12 +121,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* association is fetched.
|
||||
*/
|
||||
const FETCH_EAGER = 3;
|
||||
/**
|
||||
* Specifies that an association is to be fetched lazy (on first access) and that
|
||||
* commands such as Collection#count, Collection#slice are issued directly against
|
||||
* the database if the collection is not yet initialized.
|
||||
*/
|
||||
const FETCH_EXTRA_LAZY = 4;
|
||||
/**
|
||||
* Identifies a one-to-one association.
|
||||
*/
|
||||
@@ -204,13 +197,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public $subClasses = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The named queries allowed to be called directly from Repository.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $namedQueries = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
|
||||
* of the mapped entity class.
|
||||
@@ -269,7 +255,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* - <b>scale</b> (integer, optional, schema-only)
|
||||
* The scale of a decimal column. Only valid if the column type is decimal.
|
||||
*
|
||||
[* - <b>'unique'] (string, optional, schema-only)</b>
|
||||
* - <b>unique (string, optional, schema-only)</b>
|
||||
* Whether a unique constraint should be generated for the column.
|
||||
*
|
||||
* @var array
|
||||
@@ -335,6 +321,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* uniqueConstraints => array
|
||||
*
|
||||
* @var array
|
||||
* @todo Rename to just $table
|
||||
*/
|
||||
public $table;
|
||||
|
||||
@@ -383,11 +370,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
|
||||
* through a join table by simply mapping the association as many-to-many with a unique
|
||||
* constraint on the join table.
|
||||
*
|
||||
* - <b>indexBy</b> (string, optional, to-many only)
|
||||
* Specification of a field on target-entity that is used to index the collection by.
|
||||
* This field HAS to be either the primary key or a unique column. Otherwise the collection
|
||||
* does not contain all the entities that are actually related.
|
||||
*
|
||||
* A join table definition has the following structure:
|
||||
* <pre>
|
||||
@@ -410,15 +392,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public $isIdentifierComposite = false;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Flag indicating wheather 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.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $containsForeignIdentifier = false;
|
||||
|
||||
/**
|
||||
* READ-ONLY: The ID generator used for generating IDs for this class.
|
||||
*
|
||||
@@ -483,17 +456,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public $reflClass;
|
||||
|
||||
/**
|
||||
* Is this entity marked as "read-only"?
|
||||
*
|
||||
* That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
|
||||
* optimization for entities that are immutable, either in your domain or through the relation database
|
||||
* (coming from a view, or a history table for example).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $isReadOnly = false;
|
||||
|
||||
/**
|
||||
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
|
||||
* metadata of the class with the given name.
|
||||
@@ -672,32 +634,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->fieldNames[$columnName] : $columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the named query.
|
||||
*
|
||||
* @see ClassMetadataInfo::$namedQueries
|
||||
* @throws MappingException
|
||||
* @param string $queryName The query name
|
||||
* @return string
|
||||
*/
|
||||
public function getNamedQuery($queryName)
|
||||
{
|
||||
if ( ! isset($this->namedQueries[$queryName])) {
|
||||
throw MappingException::queryNotFound($this->name, $queryName);
|
||||
}
|
||||
return $this->namedQueries[$queryName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all named queries of the class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNamedQueries()
|
||||
{
|
||||
return $this->namedQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the given field mapping.
|
||||
*
|
||||
@@ -766,46 +702,14 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
$mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
|
||||
|
||||
// unset optional indexBy attribute if its empty
|
||||
if (!isset($mapping['indexBy']) || !$mapping['indexBy']) {
|
||||
unset($mapping['indexBy']);
|
||||
}
|
||||
|
||||
// If targetEntity is unqualified, assume it is in the same namespace as
|
||||
// the sourceEntity.
|
||||
$mapping['sourceEntity'] = $this->name;
|
||||
|
||||
if (isset($mapping['targetEntity'])) {
|
||||
if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
|
||||
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
|
||||
}
|
||||
|
||||
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
|
||||
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false
|
||||
&& strlen($this->namespace) > 0) {
|
||||
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
|
||||
}
|
||||
|
||||
// Complete id mapping
|
||||
if (isset($mapping['id']) && $mapping['id'] === true) {
|
||||
if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
|
||||
throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
|
||||
}
|
||||
|
||||
if ( ! in_array($mapping['fieldName'], $this->identifier)) {
|
||||
if (count($mapping['joinColumns']) >= 2) {
|
||||
throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
|
||||
$mapping['targetEntity'], $this->name, $mapping['fieldName']
|
||||
);
|
||||
}
|
||||
|
||||
$this->identifier[] = $mapping['fieldName'];
|
||||
$this->containsForeignIdentifier = true;
|
||||
}
|
||||
// Check for composite key
|
||||
if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
|
||||
$this->isIdentifierComposite = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Mandatory attributes for both sides
|
||||
// Mandatory: fieldName, targetEntity
|
||||
if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
|
||||
throw MappingException::missingFieldName($this->name);
|
||||
@@ -825,10 +729,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
} else {
|
||||
$mapping['isOwningSide'] = false;
|
||||
}
|
||||
|
||||
if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
|
||||
throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
|
||||
}
|
||||
|
||||
// Fetch mode. Default fetch mode to LAZY, if not set.
|
||||
if ( ! isset($mapping['fetch'])) {
|
||||
@@ -836,7 +736,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
|
||||
// Cascades
|
||||
$cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
|
||||
$cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array();
|
||||
if (in_array('all', $cascades)) {
|
||||
$cascades = array(
|
||||
'remove',
|
||||
@@ -879,15 +779,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
'referencedColumnName' => 'id'
|
||||
));
|
||||
}
|
||||
|
||||
$uniqueContraintColumns = array();
|
||||
foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
|
||||
if ($mapping['type'] === self::ONE_TO_ONE) {
|
||||
if (count($mapping['joinColumns']) == 1) {
|
||||
$joinColumn['unique'] = true;
|
||||
} else {
|
||||
$uniqueContraintColumns[] = $joinColumn['name'];
|
||||
}
|
||||
$joinColumn['unique'] = true;
|
||||
}
|
||||
if (empty($joinColumn['name'])) {
|
||||
$joinColumn['name'] = $mapping['fieldName'] . '_id';
|
||||
@@ -899,25 +793,12 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
|
||||
? $joinColumn['fieldName'] : $joinColumn['name'];
|
||||
}
|
||||
|
||||
if ($uniqueContraintColumns) {
|
||||
if (!$this->table) {
|
||||
throw new \RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
|
||||
}
|
||||
$this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
|
||||
'columns' => $uniqueContraintColumns
|
||||
);
|
||||
}
|
||||
|
||||
$mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
|
||||
}
|
||||
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
|
||||
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
|
||||
|
||||
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
|
||||
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
|
||||
}
|
||||
//TODO: if orphanRemoval, cascade=remove is implicit!
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
|
||||
(bool) $mapping['orphanRemoval'] : false;
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
@@ -938,8 +819,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
|
||||
}
|
||||
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
|
||||
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
|
||||
//TODO: if orphanRemoval, cascade=remove is implicit!
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
|
||||
(bool) $mapping['orphanRemoval'] : false;
|
||||
|
||||
if (isset($mapping['orderBy'])) {
|
||||
if ( ! is_array($mapping['orderBy'])) {
|
||||
@@ -1070,16 +952,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->isIdentifierComposite = (count($this->identifier) > 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mapped identifier field of this class.
|
||||
*
|
||||
* @return string $identifier
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the class has a (mapped) field with a certain name.
|
||||
*
|
||||
@@ -1118,19 +990,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
if ($this->isIdentifierComposite) {
|
||||
$columnNames = array();
|
||||
foreach ($this->identifier as $idField) {
|
||||
if (isset($this->associationMappings[$idField])) {
|
||||
// no composite pk as fk entity assumption:
|
||||
$columnNames[] = $this->associationMappings[$idField]['joinColumns'][0]['name'];
|
||||
} else {
|
||||
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
|
||||
}
|
||||
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
|
||||
}
|
||||
return $columnNames;
|
||||
} else if(isset($this->fieldMappings[$this->identifier[0]])) {
|
||||
return array($this->fieldMappings[$this->identifier[0]]['columnName']);
|
||||
} else {
|
||||
// no composite pk as fk entity assumption:
|
||||
return array($this->associationMappings[$this->identifier[0]]['joinColumns'][0]['name']);
|
||||
return array($this->fieldMappings[$this->identifier[0]]['columnName']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1273,8 +1137,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function getTemporaryIdTableName()
|
||||
{
|
||||
// replace dots with underscores because PostgreSQL creates temporary tables in a special schema
|
||||
return str_replace('.', '_', $this->table['name'] . '_id_tmp');
|
||||
return $this->table['name'] . '_id_tmp';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1414,7 +1277,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Adds an association mapping without completing/validating it.
|
||||
* This is mainly used to add inherited association mappings to derived classes.
|
||||
*
|
||||
* @param array $mapping
|
||||
* @param AssociationMapping $mapping
|
||||
* @param string $owningClassName The name of the class that defined this mapping.
|
||||
*/
|
||||
public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
|
||||
{
|
||||
@@ -1430,6 +1294,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* This is mainly used to add inherited field mappings to derived classes.
|
||||
*
|
||||
* @param array $mapping
|
||||
* @todo Rename: addInheritedFieldMapping
|
||||
*/
|
||||
public function addInheritedFieldMapping(array $fieldMapping)
|
||||
{
|
||||
@@ -1438,22 +1303,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Adds a named query to this class.
|
||||
*
|
||||
* @throws MappingException
|
||||
* @param array $queryMapping
|
||||
*/
|
||||
public function addNamedQuery(array $queryMapping)
|
||||
{
|
||||
if (isset($this->namedQueries[$queryMapping['name']])) {
|
||||
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
|
||||
}
|
||||
$query = str_replace('__CLASS__', $this->name, $queryMapping['query']);
|
||||
$this->namedQueries[$queryMapping['name']] = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a one-to-one mapping.
|
||||
*
|
||||
@@ -1627,13 +1476,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
public function setDiscriminatorMap(array $map)
|
||||
{
|
||||
foreach ($map as $value => $className) {
|
||||
if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
|
||||
if (strpos($className, '\\') === false && strlen($this->namespace)) {
|
||||
$className = $this->namespace . '\\' . $className;
|
||||
}
|
||||
|
||||
$className = ltrim($className, '\\');
|
||||
$this->discriminatorMap[$value] = $className;
|
||||
|
||||
if ($this->name == $className) {
|
||||
$this->discriminatorValue = $value;
|
||||
} else {
|
||||
@@ -1647,17 +1493,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the class has a named query with the given query name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasNamedQuery($queryName)
|
||||
{
|
||||
return isset($this->namedQueries[$queryName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the class has a mapped association with the given field name.
|
||||
*
|
||||
@@ -1695,74 +1530,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an association that only has a single join column?
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return bool
|
||||
*/
|
||||
public function isAssociationWithSingleJoinColumn($fieldName)
|
||||
{
|
||||
return (
|
||||
isset($this->associationMappings[$fieldName]) &&
|
||||
isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
|
||||
!isset($this->associationMappings[$fieldName]['joinColumns'][1])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the single association join column (if any).
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return string
|
||||
*/
|
||||
public function getSingleAssociationJoinColumnName($fieldName)
|
||||
{
|
||||
if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
|
||||
throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
|
||||
}
|
||||
return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the single association referenced join column name (if any).
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return string
|
||||
*/
|
||||
public function getSingleAssociationReferencedJoinColumnName($fieldName)
|
||||
{
|
||||
if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
|
||||
throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
|
||||
}
|
||||
return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to retrieve a fieldname for either field or association from a given column,
|
||||
*
|
||||
* This method is used in foreign-key as primary-key contexts.
|
||||
*
|
||||
* @param string $columnName
|
||||
* @return string
|
||||
*/
|
||||
public function getFieldForColumn($columnName)
|
||||
{
|
||||
if (isset($this->fieldNames[$columnName])) {
|
||||
return $this->fieldNames[$columnName];
|
||||
} else {
|
||||
foreach ($this->associationMappings AS $assocName => $mapping) {
|
||||
if ($this->isAssociationWithSingleJoinColumn($assocName) &&
|
||||
$this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
|
||||
|
||||
return $assocName;
|
||||
}
|
||||
}
|
||||
|
||||
throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID generator used to generate IDs for instances of this class.
|
||||
*
|
||||
@@ -1834,104 +1601,4 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
{
|
||||
$this->versionField = $versionField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this class as read only, no change tracking is applied to it.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function markReadOnly()
|
||||
{
|
||||
$this->isReadOnly = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A numerically indexed list of field names of this persistent class.
|
||||
*
|
||||
* This array includes identifier fields if present on this class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames()
|
||||
{
|
||||
return array_keys($this->fieldMappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* A numerically indexed list of association names of this persistent class.
|
||||
*
|
||||
* This array includes identifier associations if present on this class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAssociationNames()
|
||||
{
|
||||
return array_keys($this->associationMappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target class name of the given association.
|
||||
*
|
||||
* @param string $assocName
|
||||
* @return string
|
||||
*/
|
||||
public function getAssociationTargetClass($assocName)
|
||||
{
|
||||
if (!isset($this->associationMappings[$assocName])) {
|
||||
throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
|
||||
}
|
||||
return $this->associationMappings[$assocName]['targetEntity'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fully-qualified class name of this persistent class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) column name of a mapped field for safe use
|
||||
* in an SQL statement.
|
||||
*
|
||||
* @param string $field
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*/
|
||||
public function getQuotedColumnName($field, $platform)
|
||||
{
|
||||
return isset($this->fieldMappings[$field]['quoted']) ?
|
||||
$platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
|
||||
$this->fieldMappings[$field]['columnName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) primary table name of this class for safe use
|
||||
* in an SQL statement.
|
||||
*
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*/
|
||||
public function getQuotedTableName($platform)
|
||||
{
|
||||
return isset($this->table['quoted']) ?
|
||||
$platform->quoteIdentifier($this->table['name']) :
|
||||
$this->table['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) name of the join table.
|
||||
*
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*/
|
||||
public function getQuotedJoinTableName(array $assoc, $platform)
|
||||
{
|
||||
return isset($assoc['joinTable']['quoted'])
|
||||
? $platform->quoteIdentifier($assoc['joinTable']['name'])
|
||||
: $assoc['joinTable']['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,11 @@ namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\Common\Cache\ArrayCache,
|
||||
Doctrine\Common\Annotations\AnnotationReader,
|
||||
Doctrine\Common\Annotations\AnnotationRegistry,
|
||||
Doctrine\ORM\Mapping\ClassMetadataInfo,
|
||||
Doctrine\ORM\Mapping\MappingException;
|
||||
|
||||
require __DIR__ . '/DoctrineAnnotations.php';
|
||||
|
||||
/**
|
||||
* The AnnotationDriver reads the mapping metadata from docblock annotations.
|
||||
*
|
||||
@@ -41,7 +42,7 @@ class AnnotationDriver implements Driver
|
||||
*
|
||||
* @var AnnotationReader
|
||||
*/
|
||||
protected $_reader;
|
||||
private $_reader;
|
||||
|
||||
/**
|
||||
* The paths where to look for mapping files.
|
||||
@@ -61,22 +62,22 @@ class AnnotationDriver implements Driver
|
||||
* @param array
|
||||
*/
|
||||
protected $_classNames;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
|
||||
* docblock annotations.
|
||||
*
|
||||
* @param AnnotationReader $reader The AnnotationReader to use, duck-typed.
|
||||
* @param string|array $paths One or multiple paths where mapping classes can be found.
|
||||
*
|
||||
* @param $reader The AnnotationReader to use.
|
||||
* @param string|array $paths One or multiple paths where mapping classes can be found.
|
||||
*/
|
||||
public function __construct($reader, $paths = null)
|
||||
public function __construct(AnnotationReader $reader, $paths = null)
|
||||
{
|
||||
$this->_reader = $reader;
|
||||
if ($paths) {
|
||||
$this->addPaths((array) $paths);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append lookup paths to metadata driver.
|
||||
*
|
||||
@@ -127,21 +128,10 @@ class AnnotationDriver implements Driver
|
||||
|
||||
$classAnnotations = $this->_reader->getClassAnnotations($class);
|
||||
|
||||
// Compatibility with Doctrine Common 3.x
|
||||
if ($classAnnotations && is_int(key($classAnnotations))) {
|
||||
foreach ($classAnnotations as $annot) {
|
||||
$classAnnotations[get_class($annot)] = $annot;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate Entity annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
|
||||
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
|
||||
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
|
||||
|
||||
if ($entityAnnot->readOnly) {
|
||||
$metadata->markReadOnly();
|
||||
}
|
||||
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
@@ -175,18 +165,6 @@ class AnnotationDriver implements Driver
|
||||
$metadata->setPrimaryTable($primaryTable);
|
||||
}
|
||||
|
||||
// Evaluate NamedQueries annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) {
|
||||
$namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries'];
|
||||
|
||||
foreach ($namedQueriesAnnot->value as $namedQuery) {
|
||||
$metadata->addNamedQuery(array(
|
||||
'name' => $namedQuery->name,
|
||||
'query' => $namedQuery->query
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate InheritanceType annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) {
|
||||
$inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType'];
|
||||
@@ -310,10 +288,6 @@ class AnnotationDriver implements Driver
|
||||
throw MappingException::tableIdGeneratorNotImplemented($className);
|
||||
}
|
||||
} else if ($oneToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
|
||||
if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
$mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
|
||||
$mapping['joinColumns'] = $joinColumns;
|
||||
$mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
|
||||
@@ -326,7 +300,6 @@ class AnnotationDriver implements Driver
|
||||
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
|
||||
$mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
|
||||
$mapping['cascade'] = $oneToManyAnnot->cascade;
|
||||
$mapping['indexBy'] = $oneToManyAnnot->indexBy;
|
||||
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch);
|
||||
|
||||
@@ -336,10 +309,6 @@ class AnnotationDriver implements Driver
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
} else if ($manyToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) {
|
||||
if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
$mapping['joinColumns'] = $joinColumns;
|
||||
$mapping['cascade'] = $manyToOneAnnot->cascade;
|
||||
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
|
||||
@@ -385,7 +354,6 @@ class AnnotationDriver implements Driver
|
||||
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
|
||||
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
|
||||
$mapping['cascade'] = $manyToManyAnnot->cascade;
|
||||
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch);
|
||||
|
||||
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
|
||||
@@ -403,13 +371,6 @@ class AnnotationDriver implements Driver
|
||||
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
|
||||
$annotations = $this->_reader->getMethodAnnotations($method);
|
||||
|
||||
// Compatibility with Doctrine Common 3.x
|
||||
if ($annotations && is_int(key($annotations))) {
|
||||
foreach ($annotations as $annot) {
|
||||
$annotations[get_class($annot)] = $annot;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PrePersist'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::prePersist);
|
||||
}
|
||||
@@ -455,20 +416,6 @@ class AnnotationDriver implements Driver
|
||||
{
|
||||
$classAnnotations = $this->_reader->getClassAnnotations(new \ReflectionClass($className));
|
||||
|
||||
// Compatibility with Doctrine Common 3.x
|
||||
if ($classAnnotations && is_int(key($classAnnotations))) {
|
||||
foreach ($classAnnotations as $annot) {
|
||||
if ($annot instanceof \Doctrine\ORM\Mapping\Entity) {
|
||||
return false;
|
||||
}
|
||||
if ($annot instanceof \Doctrine\ORM\Mapping\MappedSuperclass) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! isset($classAnnotations['Doctrine\ORM\Mapping\Entity']) &&
|
||||
! isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']);
|
||||
}
|
||||
@@ -494,20 +441,18 @@ class AnnotationDriver implements Driver
|
||||
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
|
||||
}
|
||||
|
||||
$iterator = new \RegexIterator(
|
||||
new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
),
|
||||
'/^.+\\' . $this->_fileExtension . '$/i',
|
||||
\RecursiveRegexIterator::GET_MATCH
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($path),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$sourceFile = realpath($file[0]);
|
||||
|
||||
if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sourceFile = realpath($file->getPathName());
|
||||
require_once $sourceFile;
|
||||
|
||||
$includedFiles[] = $sourceFile;
|
||||
}
|
||||
}
|
||||
@@ -529,7 +474,7 @@ class AnnotationDriver implements Driver
|
||||
|
||||
/**
|
||||
* Factory method for the Annotation Driver
|
||||
*
|
||||
*
|
||||
* @param array|string $paths
|
||||
* @param AnnotationReader $reader
|
||||
* @return AnnotationDriver
|
||||
|
||||
@@ -55,24 +55,7 @@ class DatabaseDriver implements Driver
|
||||
* @var array
|
||||
*/
|
||||
private $manyToManyTables = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $classNamesForTables = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $fieldNamesForColumns = array();
|
||||
|
||||
/**
|
||||
* The namespace for the generated entities.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
|
||||
* docblock annotations.
|
||||
@@ -84,39 +67,17 @@ class DatabaseDriver implements Driver
|
||||
$this->_sm = $schemaManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tables manually instead of relying on the reverse engeneering 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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
$this->tables = array();
|
||||
foreach ($tables AS $tableName => $table) {
|
||||
/* @var $table Table */
|
||||
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
||||
@@ -134,12 +95,16 @@ class DatabaseDriver implements Driver
|
||||
sort($pkColumns);
|
||||
sort($allForeignKeyColumns);
|
||||
|
||||
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
|
||||
if ($pkColumns == $allForeignKeyColumns) {
|
||||
if (count($table->getForeignKeys()) > 2) {
|
||||
throw new \InvalidArgumentException("ManyToMany table '" . $tableName . "' with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver.");
|
||||
}
|
||||
|
||||
$this->manyToManyTables[$tableName] = $table;
|
||||
} else {
|
||||
// lower-casing is necessary because of Oracle Uppercase Tablenames,
|
||||
// assumption is lower-case + underscore separated.
|
||||
$className = $this->getClassNameForTable($tableName);
|
||||
$className = Inflector::classify(strtolower($tableName));
|
||||
$this->tables[$tableName] = $table;
|
||||
$this->classToTableNames[$className] = $tableName;
|
||||
}
|
||||
@@ -191,7 +156,7 @@ class DatabaseDriver implements Driver
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
|
||||
$fieldMapping['fieldName'] = Inflector::camelize(strtolower($column->getName()));
|
||||
$fieldMapping['columnName'] = $column->getName();
|
||||
$fieldMapping['type'] = strtolower((string) $column->getType());
|
||||
|
||||
@@ -226,10 +191,8 @@ class DatabaseDriver implements Driver
|
||||
|
||||
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;
|
||||
@@ -237,18 +200,12 @@ class DatabaseDriver implements Driver
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower(current($otherFk->getColumns()))));
|
||||
$associationMapping['targetEntity'] = Inflector::classify(strtolower($otherFk->getForeignTableName()));
|
||||
if (current($manyTable->getColumns())->getName() == $localColumn) {
|
||||
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
||||
$associationMapping['inversedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
|
||||
$associationMapping['joinTable'] = array(
|
||||
'name' => strtolower($manyTable->getName()),
|
||||
'joinColumns' => array(),
|
||||
@@ -273,7 +230,7 @@ class DatabaseDriver implements Driver
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
||||
$associationMapping['mappedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
|
||||
}
|
||||
$metadata->mapManyToMany($associationMapping);
|
||||
break;
|
||||
@@ -288,8 +245,8 @@ class DatabaseDriver implements Driver
|
||||
|
||||
$localColumn = current($cols);
|
||||
$associationMapping = array();
|
||||
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
|
||||
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
|
||||
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower($localColumn)));
|
||||
$associationMapping['targetEntity'] = Inflector::classify($foreignTable);
|
||||
|
||||
for ($i = 0; $i < count($cols); $i++) {
|
||||
$associationMapping['joinColumns'][] = array(
|
||||
@@ -322,78 +279,4 @@ class DatabaseDriver implements Driver
|
||||
|
||||
return array_keys($this->classToTableNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class name for a table.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $className
|
||||
* @return void
|
||||
*/
|
||||
public function setClassNameForTable($tableName, $className)
|
||||
{
|
||||
$this->classNamesForTables[$tableName] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field name for a column on a specific table.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $columnName
|
||||
* @param string $fieldName
|
||||
* @return void
|
||||
*/
|
||||
public function setFieldNameForColumn($tableName, $columnName, $fieldName)
|
||||
{
|
||||
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapped class name for a table if it exists. Otherwise return "classified" version.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @return string
|
||||
*/
|
||||
private function getClassNameForTable($tableName)
|
||||
{
|
||||
if (isset($this->classNamesForTables[$tableName])) {
|
||||
return $this->namespace . $this->classNamesForTables[$tableName];
|
||||
}
|
||||
|
||||
return $this->namespace . Inflector::classify(strtolower($tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapped field name for a column, if it exists. Otherwise return camelized version.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $columnName
|
||||
* @param boolean $fk Whether the column is a foreignkey or not.
|
||||
* @return string
|
||||
*/
|
||||
private function getFieldNameForColumn($tableName, $columnName, $fk = false)
|
||||
{
|
||||
if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) {
|
||||
return $this->fieldNamesForColumns[$tableName][$columnName];
|
||||
}
|
||||
|
||||
$columnName = strtolower($columnName);
|
||||
|
||||
// Replace _id if it is a foreignkey column
|
||||
if ($fk) {
|
||||
$columnName = str_replace('_id', '', $columnName);
|
||||
}
|
||||
return Inflector::camelize($columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the namespace for the generated entities.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return void
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* 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
|
||||
@@ -23,41 +25,23 @@ use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/* Annotations */
|
||||
|
||||
/** @Annotation */
|
||||
final class Entity extends Annotation {
|
||||
public $repositoryClass;
|
||||
public $readOnly = false;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class MappedSuperclass extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class InheritanceType extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class DiscriminatorColumn extends Annotation {
|
||||
public $name;
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
public $type;
|
||||
public $length;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class DiscriminatorMap extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class Id extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class GeneratedValue extends Annotation {
|
||||
public $strategy = 'AUTO';
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class Version extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class JoinColumn extends Annotation {
|
||||
public $name;
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
@@ -68,11 +52,7 @@ final class JoinColumn extends Annotation {
|
||||
public $onUpdate;
|
||||
public $columnDefinition;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class JoinColumns extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class Column extends Annotation {
|
||||
public $type = 'string';
|
||||
public $length;
|
||||
@@ -86,8 +66,6 @@ final class Column extends Annotation {
|
||||
public $options = array();
|
||||
public $columnDefinition;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class OneToOne extends Annotation {
|
||||
public $targetEntity;
|
||||
public $mappedBy;
|
||||
@@ -96,111 +74,64 @@ final class OneToOne extends Annotation {
|
||||
public $fetch = 'LAZY';
|
||||
public $orphanRemoval = false;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class OneToMany extends Annotation {
|
||||
public $mappedBy;
|
||||
public $targetEntity;
|
||||
public $cascade;
|
||||
public $fetch = 'LAZY';
|
||||
public $orphanRemoval = false;
|
||||
public $indexBy;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class ManyToOne extends Annotation {
|
||||
public $targetEntity;
|
||||
public $cascade;
|
||||
public $fetch = 'LAZY';
|
||||
public $inversedBy;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class ManyToMany extends Annotation {
|
||||
public $targetEntity;
|
||||
public $mappedBy;
|
||||
public $inversedBy;
|
||||
public $cascade;
|
||||
public $fetch = 'LAZY';
|
||||
public $indexBy;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class ElementCollection extends Annotation {
|
||||
public $tableName;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class Table extends Annotation {
|
||||
public $name;
|
||||
public $schema;
|
||||
public $indexes;
|
||||
public $uniqueConstraints;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class UniqueConstraint extends Annotation {
|
||||
public $name;
|
||||
public $columns;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class Index extends Annotation {
|
||||
public $name;
|
||||
public $columns;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class JoinTable extends Annotation {
|
||||
public $name;
|
||||
public $schema;
|
||||
public $joinColumns = array();
|
||||
public $inverseJoinColumns = array();
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class SequenceGenerator extends Annotation {
|
||||
public $sequenceName;
|
||||
public $allocationSize = 1;
|
||||
public $initialValue = 1;
|
||||
}
|
||||
|
||||
/** @Annotation */
|
||||
final class ChangeTrackingPolicy extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class OrderBy extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class NamedQueries extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class NamedQuery extends Annotation {
|
||||
public $name;
|
||||
public $query;
|
||||
}
|
||||
|
||||
/* Annotations for lifecycle callbacks */
|
||||
/** @Annotation */
|
||||
final class HasLifecycleCallbacks extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class PrePersist extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class PostPersist extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class PreUpdate extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class PostUpdate extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class PreRemove extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class PostRemove extends Annotation {}
|
||||
|
||||
/** @Annotation */
|
||||
final class PostLoad extends Annotation {}
|
||||
|
||||
|
||||
@@ -88,20 +88,15 @@ class DriverChain implements Driver
|
||||
public function getAllClassNames()
|
||||
{
|
||||
$classNames = array();
|
||||
$driverClasses = array();
|
||||
foreach ($this->_drivers AS $namespace => $driver) {
|
||||
$oid = spl_object_hash($driver);
|
||||
if (!isset($driverClasses[$oid])) {
|
||||
$driverClasses[$oid] = $driver->getAllClassNames();
|
||||
}
|
||||
|
||||
foreach ($driverClasses[$oid] AS $className) {
|
||||
$driverClasses = $driver->getAllClassNames();
|
||||
foreach ($driverClasses AS $className) {
|
||||
if (strpos($className, $namespace) === 0) {
|
||||
$classNames[$className] = true;
|
||||
$classNames[] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_keys($classNames);
|
||||
return array_unique($classNames);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,9 +55,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
$metadata->setCustomRepositoryClass(
|
||||
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
|
||||
);
|
||||
if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") {
|
||||
$metadata->markReadOnly();
|
||||
}
|
||||
} else if ($xmlRoot->getName() == 'mapped-superclass') {
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
@@ -72,16 +69,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
|
||||
$metadata->setPrimaryTable($table);
|
||||
|
||||
// Evaluate named queries
|
||||
if (isset($xmlRoot['named-queries'])) {
|
||||
foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
|
||||
$metadata->addNamedQuery(array(
|
||||
'name' => (string)$namedQueryElement['name'],
|
||||
'query' => (string)$namedQueryElement['query']
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($xmlRoot['schema'])) {
|
||||
$metadata->table['schema'] = (string)$xmlRoot['schema'];
|
||||
@@ -207,13 +194,7 @@ class XmlDriver extends AbstractFileDriver
|
||||
}
|
||||
|
||||
// Evaluate <id ...> mappings
|
||||
$associationIds = array();
|
||||
foreach ($xmlRoot->id as $idElement) {
|
||||
if ((bool)$idElement['association-key'] == true) {
|
||||
$associationIds[(string)$idElement['fieldName']] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$mapping = array(
|
||||
'id' => true,
|
||||
'fieldName' => (string)$idElement['name'],
|
||||
@@ -254,10 +235,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
'targetEntity' => (string)$oneToOneElement['target-entity']
|
||||
);
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
if (isset($oneToOneElement['fetch'])) {
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']);
|
||||
}
|
||||
@@ -285,8 +262,8 @@ class XmlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
|
||||
}
|
||||
|
||||
if (isset($oneToOneElement['orphan-removal'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$oneToOneElement['orphan-removal'];
|
||||
if (isset($oneToOneElement->{'orphan-removal'})) {
|
||||
$mapping['orphanRemoval'] = (bool)$oneToOneElement->{'orphan-removal'};
|
||||
}
|
||||
|
||||
$metadata->mapOneToOne($mapping);
|
||||
@@ -310,8 +287,8 @@ class XmlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement['orphan-removal'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$oneToManyElement['orphan-removal'];
|
||||
if (isset($oneToManyElement->{'orphan-removal'})) {
|
||||
$mapping['orphanRemoval'] = (bool)$oneToManyElement->{'orphan-removal'};
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement->{'order-by'})) {
|
||||
@@ -322,10 +299,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
$mapping['orderBy'] = $orderBy;
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement->{'index-by'})) {
|
||||
$mapping['indexBy'] = (string)$oneToManyElement->{'index-by'};
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
}
|
||||
}
|
||||
@@ -338,10 +311,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
'targetEntity' => (string)$manyToOneElement['target-entity']
|
||||
);
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
if (isset($manyToOneElement['fetch'])) {
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']);
|
||||
}
|
||||
@@ -356,6 +325,9 @@ class XmlDriver extends AbstractFileDriver
|
||||
$joinColumns[] = $this->_getJoinColumnMapping($manyToOneElement->{'join-column'});
|
||||
} else if (isset($manyToOneElement->{'join-columns'})) {
|
||||
foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
if (!isset($joinColumnElement['name'])) {
|
||||
$joinColumnElement['name'] = $name;
|
||||
}
|
||||
$joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement);
|
||||
}
|
||||
}
|
||||
@@ -429,10 +401,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
$mapping['orderBy'] = $orderBy;
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement->{'index-by'})) {
|
||||
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'};
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
$metadata->setCustomRepositoryClass(
|
||||
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
|
||||
);
|
||||
if (isset($element['readOnly']) && $element['readOnly'] == true) {
|
||||
$metadata->markReadOnly();
|
||||
}
|
||||
} else if ($element['type'] == 'mappedSuperclass') {
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
@@ -65,21 +62,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
}
|
||||
$metadata->setPrimaryTable($table);
|
||||
|
||||
// Evaluate named queries
|
||||
if (isset($element['namedQueries'])) {
|
||||
foreach ($element['namedQueries'] as $name => $queryMapping) {
|
||||
if (is_string($queryMapping)) {
|
||||
$queryMapping = array('query' => $queryMapping);
|
||||
}
|
||||
|
||||
if ( ! isset($queryMapping['name'])) {
|
||||
$queryMapping['name'] = $name;
|
||||
}
|
||||
|
||||
$metadata->addNamedQuery($queryMapping);
|
||||
}
|
||||
}
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($element['schema'])) {
|
||||
$metadata->table['schema'] = $element['schema'];
|
||||
@@ -153,15 +135,9 @@ class YamlDriver extends AbstractFileDriver
|
||||
}
|
||||
}
|
||||
|
||||
$associationIds = array();
|
||||
if (isset($element['id'])) {
|
||||
// Evaluate identifier settings
|
||||
foreach ($element['id'] as $name => $idElement) {
|
||||
if (isset($idElement['associationKey']) && $idElement['associationKey'] == true) {
|
||||
$associationIds[$name] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($idElement['type'])) {
|
||||
throw MappingException::propertyTypeIsRequired($className, $name);
|
||||
}
|
||||
@@ -258,10 +234,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
'targetEntity' => $oneToOneElement['targetEntity']
|
||||
);
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
if (isset($oneToOneElement['fetch'])) {
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']);
|
||||
}
|
||||
@@ -294,10 +266,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $oneToOneElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($oneToOneElement['orphanRemoval'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$oneToOneElement['orphanRemoval'];
|
||||
}
|
||||
|
||||
$metadata->mapOneToOne($mapping);
|
||||
}
|
||||
}
|
||||
@@ -319,18 +287,10 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $oneToManyElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement['orphanRemoval'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$oneToManyElement['orphanRemoval'];
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement['orderBy'])) {
|
||||
$mapping['orderBy'] = $oneToManyElement['orderBy'];
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement['indexBy'])) {
|
||||
$mapping['indexBy'] = $oneToManyElement['indexBy'];
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
}
|
||||
}
|
||||
@@ -343,10 +303,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
'targetEntity' => $manyToOneElement['targetEntity']
|
||||
);
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
if (isset($manyToOneElement['fetch'])) {
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']);
|
||||
}
|
||||
@@ -375,10 +331,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $manyToOneElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($manyToOneElement['orphanRemoval'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToOneElement['orphanRemoval'];
|
||||
}
|
||||
|
||||
$metadata->mapManyToOne($mapping);
|
||||
}
|
||||
}
|
||||
@@ -434,18 +386,10 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $manyToManyElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['orphanRemoval'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['orderBy'])) {
|
||||
$mapping['orderBy'] = $manyToManyElement['orderBy'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['indexBy'])) {
|
||||
$mapping['indexBy'] = $manyToManyElement['indexBy'];
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
}
|
||||
}
|
||||
@@ -506,6 +450,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
*/
|
||||
protected function _loadMappingFile($file)
|
||||
{
|
||||
return \Symfony\Component\Yaml\Yaml::parse($file);
|
||||
return \Symfony\Component\Yaml\Yaml::load($file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,11 +73,6 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
return new self("No mapping found for field '$fieldName' on class '$className'.");
|
||||
}
|
||||
|
||||
public static function queryNotFound($className, $queryName)
|
||||
{
|
||||
return new self("No query found named '$queryName' on class '$className'.");
|
||||
}
|
||||
|
||||
public static function oneToManyRequiresMappedBy($fieldName)
|
||||
{
|
||||
return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute.");
|
||||
@@ -165,10 +160,6 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once');
|
||||
}
|
||||
|
||||
public static function duplicateQueryMapping($entity, $queryName) {
|
||||
return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once');
|
||||
}
|
||||
|
||||
public static function singleIdNotAllowedOnCompositePrimaryKey($entity) {
|
||||
return new self('Single id is not allowed on composite primary key in entity '.$entity);
|
||||
}
|
||||
@@ -240,57 +231,4 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
{
|
||||
return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $targetEntity
|
||||
* @param string $targetField
|
||||
* @return self
|
||||
*/
|
||||
public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId($className, $targetEntity, $targetField)
|
||||
{
|
||||
return new self("It is not possible to map entity '".$className."' with a composite primary key ".
|
||||
"as part of the primary key of another entity '".$targetEntity."#".$targetField."'.");
|
||||
}
|
||||
|
||||
public static function noSingleAssociationJoinColumnFound($className, $field)
|
||||
{
|
||||
return new self("'$className#$field' is not an association with a single join column.");
|
||||
}
|
||||
|
||||
public static function noFieldNameFoundForColumn($className, $column)
|
||||
{
|
||||
return new self("Cannot find a field on '$className' that is mapped to column '$column'. Either the ".
|
||||
"field does not exist or an association exists but it has multiple join columns.");
|
||||
}
|
||||
|
||||
public static function illegalOrphanRemovalOnIdentifierAssociation($className, $field)
|
||||
{
|
||||
return new self("The orphan removal option is not allowed on an association that is ".
|
||||
"part of the identifier in '$className#$field'.");
|
||||
}
|
||||
|
||||
public static function illegalInverseIdentifierAssocation($className, $field)
|
||||
{
|
||||
return new self("An inverse association is not allowed to be identifier in '$className#$field'.");
|
||||
}
|
||||
|
||||
public static function illegalToManyIdentifierAssoaction($className, $field)
|
||||
{
|
||||
return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'.");
|
||||
}
|
||||
|
||||
public static function noInheritanceOnMappedSuperClass($className)
|
||||
{
|
||||
return new self("Its not supported to define inheritance information on a mapped superclass '" . $className . "'.");
|
||||
}
|
||||
|
||||
public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName)
|
||||
{
|
||||
return new self(
|
||||
"Entity '" . $className . "' has to be part of the descriminator map of '" . $rootClassName . "' " .
|
||||
"to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " .
|
||||
"to avoid this exception from occuring."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -34,25 +34,10 @@ class ORMException extends Exception
|
||||
return new self("It's a requirement to specify a Metadata Driver and pass it ".
|
||||
"to Doctrine\ORM\Configuration::setMetadataDriverImpl().");
|
||||
}
|
||||
|
||||
public static function entityMissingForeignAssignedId($entity, $relatedEntity)
|
||||
{
|
||||
return new self(
|
||||
"Entity of type " . get_class($entity) . " has identity through a foreign entity " . get_class($relatedEntity) . ", " .
|
||||
"however this entity has no ientity itself. You have to call EntityManager#persist() on the related entity " .
|
||||
"and make sure it an identifier was generated before trying to persist '" . get_class($entity) . "'. In case " .
|
||||
"of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call " .
|
||||
"EntityManager#flush() between both persist operations."
|
||||
);
|
||||
}
|
||||
|
||||
public static function entityMissingAssignedId($entity)
|
||||
{
|
||||
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID. " .
|
||||
"The identifier generation strategy for this entity requires the ID field to be populated before ".
|
||||
"EntityManager#persist() is called. If you want automatically generated identifiers instead " .
|
||||
"you need to adjust the metadata mapping accordingly."
|
||||
);
|
||||
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID.");
|
||||
}
|
||||
|
||||
public static function unrecognizedField($field)
|
||||
|
||||
@@ -33,7 +33,6 @@ class OptimisticLockException extends ORMException
|
||||
|
||||
public function __construct($msg, $entity)
|
||||
{
|
||||
parent::__construct($msg);
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ final class PersistentCollection implements Collection
|
||||
* The association mapping the collection belongs to.
|
||||
* This is currently either a OneToManyMapping or a ManyToManyMapping.
|
||||
*
|
||||
* @var array
|
||||
* @var Doctrine\ORM\Mapping\AssociationMapping
|
||||
*/
|
||||
private $association;
|
||||
|
||||
@@ -404,12 +404,22 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function contains($element)
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->coll->contains($element) ||
|
||||
$this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->contains($this, $element);
|
||||
}
|
||||
/* DRAFT
|
||||
if ($this->initialized) {
|
||||
return $this->coll->contains($element);
|
||||
} else {
|
||||
if ($element is MANAGED) {
|
||||
if ($this->coll->contains($element)) {
|
||||
return true;
|
||||
}
|
||||
$exists = check db for existence;
|
||||
if ($exists) {
|
||||
$this->coll->add($element);
|
||||
}
|
||||
return $exists;
|
||||
}
|
||||
return false;
|
||||
}*/
|
||||
|
||||
$this->initialize();
|
||||
return $this->coll->contains($element);
|
||||
@@ -465,12 +475,6 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->count($this) + $this->coll->count();
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
return $this->coll->count();
|
||||
}
|
||||
@@ -572,7 +576,6 @@ final class PersistentCollection implements Collection
|
||||
}
|
||||
}
|
||||
$this->coll->clear();
|
||||
$this->initialized = true; // direct call, {@link initialize()} is too expensive
|
||||
if ($this->association['isOwningSide']) {
|
||||
$this->changed();
|
||||
$this->em->getUnitOfWork()->scheduleCollectionDeletion($this);
|
||||
@@ -672,12 +675,6 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->slice($this, $offset, $length);
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
return $this->coll->slice($offset, $length);
|
||||
}
|
||||
|
||||
@@ -125,31 +125,6 @@ abstract class AbstractCollectionPersister
|
||||
}
|
||||
}
|
||||
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
throw new \BadMethodCallException("Counting the size of this persistent collection is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function contains(PersistentCollection $coll, $element)
|
||||
{
|
||||
throw new \BadMethodCallException("Checking for existance of an element is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function containsKey(PersistentCollection $coll, $key)
|
||||
{
|
||||
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function get(PersistentCollection $coll, $index)
|
||||
{
|
||||
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL statement used for deleting a row from the collection.
|
||||
*
|
||||
|
||||
@@ -28,11 +28,24 @@ use Doctrine\ORM\Mapping\ClassMetadata,
|
||||
* types in the hierarchy.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
{
|
||||
/**
|
||||
* Map from column names to class metadata instances that declare the field the column is mapped to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $declaringClassMap = array();
|
||||
|
||||
/**
|
||||
* Map from column names to class names that declare the field the association with join column is mapped to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $declaringJoinColumnMap = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -56,12 +69,49 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
|
||||
protected function _processSQLResult(array $sqlResult)
|
||||
{
|
||||
$data = array();
|
||||
$discrColumnName = $this->_platform->getSQLResultCasing($this->_class->discriminatorColumn['name']);
|
||||
$entityName = $this->_class->discriminatorMap[$sqlResult[$discrColumnName]];
|
||||
unset($sqlResult[$discrColumnName]);
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
$realColumnName = $this->_resultColumnNames[$column];
|
||||
if (isset($this->declaringClassMap[$column])) {
|
||||
$class = $this->declaringClassMap[$column];
|
||||
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
|
||||
$field = $class->fieldNames[$realColumnName];
|
||||
if (isset($data[$field])) {
|
||||
$data[$realColumnName] = $value;
|
||||
} else {
|
||||
$data[$field] = Type::getType($class->fieldMappings[$field]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
}
|
||||
} else if (isset($this->declaringJoinColumnMap[$column])) {
|
||||
if ($this->declaringJoinColumnMap[$column] == $entityName || is_subclass_of($entityName, $this->declaringJoinColumnMap[$column])) {
|
||||
$data[$realColumnName] = $value;
|
||||
}
|
||||
} else {
|
||||
$data[$realColumnName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return array($entityName, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class)
|
||||
{
|
||||
$columnName = $class->columnNames[$field];
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
|
||||
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
|
||||
$this->_resultColumnNames[$columnAlias] = $columnName;
|
||||
$this->declaringClassMap[$columnAlias] = $class;
|
||||
}
|
||||
|
||||
return "$sql AS $columnAlias";
|
||||
}
|
||||
@@ -70,7 +120,10 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
{
|
||||
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++;
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
|
||||
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
|
||||
$this->_resultColumnNames[$resultColumnName] = $joinColumnName;
|
||||
$this->declaringJoinColumnMap[$resultColumnName] = $className;
|
||||
}
|
||||
|
||||
return $tableAlias . ".$joinColumnName AS $columnAlias";
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace Doctrine\ORM\Persisters;
|
||||
use PDO,
|
||||
Doctrine\DBAL\LockMode,
|
||||
Doctrine\DBAL\Types\Type,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\OptimisticLockException,
|
||||
Doctrine\ORM\EntityManager,
|
||||
@@ -70,7 +69,6 @@ use PDO,
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
*/
|
||||
class BasicEntityPersister
|
||||
@@ -109,15 +107,15 @@ class BasicEntityPersister
|
||||
* @var array
|
||||
*/
|
||||
protected $_queuedInserts = array();
|
||||
|
||||
|
||||
/**
|
||||
* ResultSetMapping that is used for all queries. Is generated lazily once per request.
|
||||
* Case-sensitive mappings of column names as they appear in an SQL result set
|
||||
* to column names as they are defined in the mapping. This is necessary because different
|
||||
* RDBMS vendors return column names in result sets in different casings.
|
||||
*
|
||||
* TODO: Evaluate Caching in combination with the other cached SQL snippets.
|
||||
*
|
||||
* @var Query\ResultSetMapping
|
||||
* @var array
|
||||
*/
|
||||
protected $_rsm;
|
||||
protected $_resultColumnNames = array();
|
||||
|
||||
/**
|
||||
* The map of column names to DBAL mapping types of all prepared columns used
|
||||
@@ -144,14 +142,6 @@ class BasicEntityPersister
|
||||
* @var string
|
||||
*/
|
||||
protected $_selectColumnListSql;
|
||||
|
||||
/**
|
||||
* The JOIN SQL fragement 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.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_selectJoinSql;
|
||||
|
||||
/**
|
||||
* Counter for creating unique SQL table and column aliases.
|
||||
@@ -344,16 +334,9 @@ class BasicEntityPersister
|
||||
$where = array();
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
|
||||
foreach ($this->_class->identifier as $idField) {
|
||||
if (isset($this->_class->associationMappings[$idField])) {
|
||||
$targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
|
||||
$where[] = $this->_class->associationMappings[$idField]['joinColumns'][0]['name'];
|
||||
$params[] = $id[$idField];
|
||||
$types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
|
||||
} else {
|
||||
$where[] = $this->_class->getQuotedColumnName($idField, $this->_platform);
|
||||
$params[] = $id[$idField];
|
||||
$types[] = $this->_class->fieldMappings[$idField]['type'];
|
||||
}
|
||||
$where[] = $this->_class->getQuotedColumnName($idField, $this->_platform);
|
||||
$params[] = $id[$idField];
|
||||
$types[] = $this->_class->fieldMappings[$idField]['type'];
|
||||
}
|
||||
|
||||
if ($versioned) {
|
||||
@@ -375,7 +358,7 @@ class BasicEntityPersister
|
||||
|
||||
$result = $this->_conn->executeUpdate($sql, $params, $types);
|
||||
|
||||
if ($versioned && ! $result) {
|
||||
if ($this->_class->isVersioned && ! $result) {
|
||||
throw OptimisticLockException::lockFailed($entity);
|
||||
}
|
||||
}
|
||||
@@ -504,8 +487,6 @@ class BasicEntityPersister
|
||||
foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) {
|
||||
if ($newVal === null) {
|
||||
$result[$owningTable][$sourceColumn] = null;
|
||||
} else if ($targetClass->containsForeignIdentifier) {
|
||||
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->getFieldForColumn($targetColumn)];
|
||||
} else {
|
||||
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
|
||||
}
|
||||
@@ -567,19 +548,10 @@ class BasicEntityPersister
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
|
||||
if ($entity !== null) {
|
||||
$hints[Query::HINT_REFRESH] = true;
|
||||
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
|
||||
}
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
|
||||
if ($this->_selectJoinSql) {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
} else {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
|
||||
}
|
||||
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
|
||||
return $entities ? $entities[0] : null;
|
||||
return $this->_createEntity($result, $entity, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -588,17 +560,14 @@ class BasicEntityPersister
|
||||
*
|
||||
* @param array $assoc The association to load.
|
||||
* @param object $sourceEntity The entity that owns the association (not necessarily the "owning side").
|
||||
* @param object $targetEntity The existing ghost entity (proxy) to load, if any.
|
||||
* @param array $identifier The identifier of the entity to load. Must be provided if
|
||||
* the association to load represents the owning side, otherwise
|
||||
* the identifier is derived from the $sourceEntity.
|
||||
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
|
||||
*/
|
||||
public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array())
|
||||
public function loadOneToOneEntity(array $assoc, $sourceEntity, $targetEntity, array $identifier = array())
|
||||
{
|
||||
if ($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) {
|
||||
return $foundEntity;
|
||||
}
|
||||
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if ($assoc['isOwningSide']) {
|
||||
@@ -621,7 +590,7 @@ class BasicEntityPersister
|
||||
}
|
||||
*/
|
||||
|
||||
$targetEntity = $this->load($identifier, null, $assoc, $hints);
|
||||
$targetEntity = $this->load($identifier, $targetEntity, $assoc, $hints);
|
||||
|
||||
// Complete bidirectional association, if necessary
|
||||
if ($targetEntity !== null && $isInverseSingleValued) {
|
||||
@@ -633,11 +602,7 @@ class BasicEntityPersister
|
||||
// TRICKY: since the association is specular source and target are flipped
|
||||
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
|
||||
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
// unset the old value and set the new sql aliased value here. By definition
|
||||
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
|
||||
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
|
||||
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
unset($identifier[$targetKeyColumn]);
|
||||
$identifier[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
} else {
|
||||
throw MappingException::joinColumnMustPointToMappedField(
|
||||
$sourceClass->name, $sourceKeyColumn
|
||||
@@ -645,7 +610,7 @@ class BasicEntityPersister
|
||||
}
|
||||
}
|
||||
|
||||
$targetEntity = $this->load($identifier, null, $assoc);
|
||||
$targetEntity = $this->load($identifier, $targetEntity, $assoc);
|
||||
|
||||
if ($targetEntity !== null) {
|
||||
$targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity);
|
||||
@@ -667,9 +632,79 @@ class BasicEntityPersister
|
||||
$sql = $this->_getSelectEntitiesSQL($id);
|
||||
list($params, $types) = $this->expandParameters($id);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
|
||||
$metaColumns = array();
|
||||
$newData = array();
|
||||
|
||||
// Refresh simple state
|
||||
foreach ($result as $column => $value) {
|
||||
$column = $this->_resultColumnNames[$column];
|
||||
if (isset($this->_class->fieldNames[$column])) {
|
||||
$fieldName = $this->_class->fieldNames[$column];
|
||||
$newValue = $this->_conn->convertToPHPValue($value, $this->_class->fieldMappings[$fieldName]['type']);
|
||||
$this->_class->reflFields[$fieldName]->setValue($entity, $newValue);
|
||||
$newData[$fieldName] = $newValue;
|
||||
} else {
|
||||
$metaColumns[$column] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh associations
|
||||
foreach ($this->_class->associationMappings as $field => $assoc) {
|
||||
$value = $this->_class->reflFields[$field]->getValue($entity);
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
if ($value instanceof Proxy && ! $value->__isInitialized__) {
|
||||
continue; // skip uninitialized proxies
|
||||
}
|
||||
|
||||
if ($assoc['isOwningSide']) {
|
||||
$joinColumnValues = array();
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
|
||||
if ($metaColumns[$srcColumn] !== null) {
|
||||
$joinColumnValues[$targetColumn] = $metaColumns[$srcColumn];
|
||||
}
|
||||
}
|
||||
if ( ! $joinColumnValues && $value !== null) {
|
||||
$this->_class->reflFields[$field]->setValue($entity, null);
|
||||
$newData[$field] = null;
|
||||
} else if ($value !== null) {
|
||||
// Check identity map first, if the entity is not there,
|
||||
// place a proxy in there instead.
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
if ($found = $this->_em->getUnitOfWork()->tryGetById($joinColumnValues, $targetClass->rootEntityName)) {
|
||||
$this->_class->reflFields[$field]->setValue($entity, $found);
|
||||
// Complete inverse side, if necessary.
|
||||
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
|
||||
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
|
||||
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($found, $entity);
|
||||
}
|
||||
$newData[$field] = $found;
|
||||
} else {
|
||||
// FIXME: What is happening with subClassees here?
|
||||
$proxy = $this->_em->getProxyFactory()->getProxy($assoc['targetEntity'], $joinColumnValues);
|
||||
$this->_class->reflFields[$field]->setValue($entity, $proxy);
|
||||
$newData[$field] = $proxy;
|
||||
$this->_em->getUnitOfWork()->registerManaged($proxy, $joinColumnValues, array());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Inverse side of 1-1/1-x can never be lazy.
|
||||
//$newData[$field] = $assoc->load($entity, null, $this->_em);
|
||||
$newData[$field] = $this->_em->getUnitOfWork()->getEntityPersister($assoc['targetEntity'])
|
||||
->loadOneToOneEntity($assoc, $entity, null);
|
||||
}
|
||||
} else if ($value instanceof PersistentCollection && $value->isInitialized()) {
|
||||
$value->setInitialized(false);
|
||||
// no matter if dirty or non-dirty entities are already loaded, smoke them out!
|
||||
// the beauty of it being, they are still in the identity map
|
||||
$value->unwrap()->clear();
|
||||
$newData[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_em->getUnitOfWork()->setOriginalEntityData($entity, $newData);
|
||||
|
||||
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
|
||||
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
||||
@@ -684,83 +719,22 @@ class BasicEntityPersister
|
||||
* Loads a list of entities by a list of field criteria.
|
||||
*
|
||||
* @param array $criteria
|
||||
* @param array $orderBy
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
* @return array
|
||||
*/
|
||||
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
|
||||
public function loadAll(array $criteria = array())
|
||||
{
|
||||
$entities = array();
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy);
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
|
||||
if ($this->_selectJoinSql) {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
} else {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
|
||||
}
|
||||
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get (sliced or full) elements of the given collection.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
|
||||
return $this->loadArrayFromStatement($assoc, $stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an array of entities from a given dbal statement.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param Doctrine\DBAL\Statement $stmt
|
||||
* @return array
|
||||
*/
|
||||
private function loadArrayFromStatement($assoc, $stmt)
|
||||
{
|
||||
$hints = array('deferEagerLoads' => true);
|
||||
|
||||
if (isset($assoc['indexBy'])) {
|
||||
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
|
||||
$rsm->addIndexBy('r', $assoc['indexBy']);
|
||||
} else {
|
||||
$rsm = $this->_rsm;
|
||||
foreach ($result as $row) {
|
||||
$entities[] = $this->_createEntity($row);
|
||||
}
|
||||
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
return $hydrator->hydrateAll($stmt, $rsm, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate a collection from a given dbal statement.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param Doctrine\DBAL\Statement $stmt
|
||||
* @param PersistentCollection $coll
|
||||
*/
|
||||
private function loadCollectionFromStatement($assoc, $stmt, $coll)
|
||||
{
|
||||
$hints = array('deferEagerLoads' => true, 'collection' => $coll);
|
||||
|
||||
if (isset($assoc['indexBy'])) {
|
||||
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
|
||||
$rsm->addIndexBy('r', $assoc['indexBy']);
|
||||
} else {
|
||||
$rsm = $this->_rsm;
|
||||
}
|
||||
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
$hydrator->hydrateAll($stmt, $rsm, $hints);
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -769,34 +743,16 @@ class BasicEntityPersister
|
||||
* @param ManyToManyMapping $assoc The association mapping of the association being loaded.
|
||||
* @param object $sourceEntity The entity that owns the collection.
|
||||
* @param PersistentCollection $coll The collection to fill.
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
* @return array
|
||||
*/
|
||||
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
{
|
||||
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
|
||||
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
|
||||
}
|
||||
|
||||
private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$criteria = array();
|
||||
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
|
||||
$joinTableConditions = array();
|
||||
if ($assoc['isOwningSide']) {
|
||||
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform);
|
||||
foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
|
||||
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
|
||||
if (isset($sourceClass->associationMappings[$field])) {
|
||||
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
|
||||
}
|
||||
|
||||
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
|
||||
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
$criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
} else {
|
||||
throw MappingException::joinColumnMustPointToMappedField(
|
||||
$sourceClass->name, $sourceKeyColumn
|
||||
@@ -805,19 +761,10 @@ class BasicEntityPersister
|
||||
}
|
||||
} else {
|
||||
$owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']];
|
||||
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform);
|
||||
// TRICKY: since the association is inverted source and target are flipped
|
||||
foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
|
||||
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
|
||||
if (isset($sourceClass->associationMappings[$field])) {
|
||||
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
|
||||
}
|
||||
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
|
||||
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
$criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
} else {
|
||||
throw MappingException::joinColumnMustPointToMappedField(
|
||||
$sourceClass->name, $sourceKeyColumn
|
||||
@@ -826,9 +773,79 @@ class BasicEntityPersister
|
||||
}
|
||||
}
|
||||
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
return $this->_conn->executeQuery($sql, $params, $types);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$coll->hydrateAdd($this->_createEntity($result));
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or fills a single entity object from an SQL result.
|
||||
*
|
||||
* @param $result The SQL result.
|
||||
* @param object $entity The entity object to fill, if any.
|
||||
* @param array $hints Hints for entity creation.
|
||||
* @return object The filled and managed entity object or NULL, if the SQL result is empty.
|
||||
*/
|
||||
private function _createEntity($result, $entity = null, array $hints = array())
|
||||
{
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($entityName, $data) = $this->_processSQLResult($result);
|
||||
|
||||
if ($entity !== null) {
|
||||
$hints[Query::HINT_REFRESH] = true;
|
||||
$id = array();
|
||||
if ($this->_class->isIdentifierComposite) {
|
||||
foreach ($this->_class->identifier as $fieldName) {
|
||||
$id[$fieldName] = $data[$fieldName];
|
||||
}
|
||||
} else {
|
||||
$id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
|
||||
}
|
||||
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
|
||||
}
|
||||
|
||||
return $this->_em->getUnitOfWork()->createEntity($entityName, $data, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an SQL result set row that contains data for an entity of the type
|
||||
* this persister is responsible for.
|
||||
*
|
||||
* Subclasses are supposed to override this method if they need to change the
|
||||
* hydration procedure for entities loaded through basic find operations or
|
||||
* lazy-loading (not DQL).
|
||||
*
|
||||
* @param array $sqlResult The SQL result set row to process.
|
||||
* @return array A tuple where the first value is the actual type of the entity and
|
||||
* the second value the prepared data of the entity (a map from field
|
||||
* names to values).
|
||||
*/
|
||||
protected function _processSQLResult(array $sqlResult)
|
||||
{
|
||||
$data = array();
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
$column = $this->_resultColumnNames[$column];
|
||||
if (isset($this->_class->fieldNames[$column])) {
|
||||
$field = $this->_class->fieldNames[$column];
|
||||
if (isset($data[$field])) {
|
||||
$data[$column] = $value;
|
||||
} else {
|
||||
$data[$field] = Type::getType($this->_class->fieldMappings[$field]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
} else {
|
||||
$data[$column] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return array($this->_class->name, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -838,21 +855,19 @@ class BasicEntityPersister
|
||||
* @param AssociationMapping $assoc
|
||||
* @param string $orderBy
|
||||
* @param int $lockMode
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
* @param array $orderBy
|
||||
* @return string
|
||||
* @todo Refactor: _getSelectSQL(...)
|
||||
*/
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0)
|
||||
{
|
||||
$joinSql = $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ?
|
||||
$this->_getSelectManyToManyJoinSQL($assoc) : '';
|
||||
|
||||
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
|
||||
|
||||
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
|
||||
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : '';
|
||||
$orderBySql = $assoc !== null && isset($assoc['orderBy']) ?
|
||||
$this->_getCollectionOrderBySQL($assoc['orderBy'], $this->_getSQLTableAlias($this->_class->name))
|
||||
: '';
|
||||
|
||||
$lockSql = '';
|
||||
if ($lockMode == LockMode::PESSIMISTIC_READ) {
|
||||
@@ -861,12 +876,12 @@ class BasicEntityPersister
|
||||
$lockSql = ' ' . $this->_platform->getWriteLockSql();
|
||||
}
|
||||
|
||||
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL()
|
||||
return 'SELECT ' . $this->_getSelectColumnListSQL()
|
||||
. $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->_getSQLTableAlias($this->_class->name), $lockMode)
|
||||
. $this->_selectJoinSql . $joinSql
|
||||
. $joinSql
|
||||
. ($conditionSql ? ' WHERE ' . $conditionSql : '')
|
||||
. $orderBySql, $limit, $offset)
|
||||
. $orderBySql
|
||||
. $lockSql;
|
||||
}
|
||||
|
||||
@@ -878,12 +893,12 @@ class BasicEntityPersister
|
||||
* @return string
|
||||
* @todo Rename: _getOrderBySQL
|
||||
*/
|
||||
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
|
||||
protected final function _getCollectionOrderBySQL(array $orderBy, $baseTableAlias)
|
||||
{
|
||||
$orderBySql = '';
|
||||
foreach ($orderBy as $fieldName => $orientation) {
|
||||
if ( ! isset($this->_class->fieldMappings[$fieldName])) {
|
||||
throw ORMException::unrecognizedField($fieldName);
|
||||
ORMException::unrecognizedField($fieldName);
|
||||
}
|
||||
|
||||
$tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ?
|
||||
@@ -917,8 +932,6 @@ class BasicEntityPersister
|
||||
}
|
||||
|
||||
$columnList = '';
|
||||
$this->_rsm = new Query\ResultSetMapping();
|
||||
$this->_rsm->addEntityResult($this->_class->name, 'r'); // r for root
|
||||
|
||||
// Add regular columns to select list
|
||||
foreach ($this->_class->fieldNames as $field) {
|
||||
@@ -926,87 +939,10 @@ class BasicEntityPersister
|
||||
$columnList .= $this->_getSelectColumnSQL($field, $this->_class);
|
||||
}
|
||||
|
||||
$this->_selectJoinSql = '';
|
||||
$eagerAliasCounter = 0;
|
||||
foreach ($this->_class->associationMappings as $assocField => $assoc) {
|
||||
$assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class);
|
||||
if ($assocColumnSQL) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
$columnList .= $assocColumnSQL;
|
||||
}
|
||||
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) {
|
||||
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
continue; // now this is why you shouldn't use inheritance
|
||||
}
|
||||
|
||||
$assocAlias = 'e' . ($eagerAliasCounter++);
|
||||
$this->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
|
||||
|
||||
foreach ($eagerEntity->fieldNames AS $field) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
$columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias);
|
||||
}
|
||||
|
||||
foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) {
|
||||
$assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias);
|
||||
if ($assoc2ColumnSQL) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
$columnList .= $assoc2ColumnSQL;
|
||||
}
|
||||
}
|
||||
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
|
||||
$first = true;
|
||||
if ($assoc['isOwningSide']) {
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
|
||||
foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
|
||||
if (!$first) {
|
||||
$this->_selectJoinSql .= ' AND ';
|
||||
}
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.'.$sourceCol.' = ' .
|
||||
$this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.'.$targetCol.' ';
|
||||
$first = false;
|
||||
}
|
||||
} else {
|
||||
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
|
||||
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
|
||||
foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
|
||||
if (!$first) {
|
||||
$this->_selectJoinSql .= ' AND ';
|
||||
}
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.'.$sourceCol.' = ' .
|
||||
$this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_selectColumnListSql = $columnList;
|
||||
$this->_selectColumnListSql = $columnList . $this->_getSelectJoinColumnsSQL($this->_class);
|
||||
|
||||
return $this->_selectColumnListSql;
|
||||
}
|
||||
|
||||
protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
|
||||
{
|
||||
$columnList = '';
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
|
||||
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
|
||||
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . ".$srcColumn AS $columnAlias";
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
|
||||
}
|
||||
}
|
||||
return $columnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL join fragment used when selecting entities from a
|
||||
@@ -1030,15 +966,8 @@ class BasicEntityPersister
|
||||
$joinSql = '';
|
||||
foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
|
||||
if ($joinSql != '') $joinSql .= ' AND ';
|
||||
|
||||
if ($this->_class->containsForeignIdentifier && !isset($this->_class->fieldNames[$sourceColumn])) {
|
||||
$quotedColumn = $sourceColumn; // join columns cannot be quoted
|
||||
} else {
|
||||
$quotedColumn = $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$joinSql .= $this->_getSQLTableAlias($this->_class->name) .
|
||||
'.' . $quotedColumn . ' = '
|
||||
'.' . $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform) . ' = '
|
||||
. $joinTableName . '.' . $joinTableColumn;
|
||||
}
|
||||
|
||||
@@ -1110,18 +1039,46 @@ class BasicEntityPersister
|
||||
* @param string $field The field name.
|
||||
* @param ClassMetadata $class The class that declares this field. The table this class is
|
||||
* mapped to must own the column for the given field.
|
||||
* @param string $alias
|
||||
*/
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class)
|
||||
{
|
||||
$columnName = $class->columnNames[$field];
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field);
|
||||
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
|
||||
$this->_resultColumnNames[$columnAlias] = $columnName;
|
||||
}
|
||||
|
||||
return "$sql AS $columnAlias";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL snippet for all join columns of the given class that are to be
|
||||
* placed in an SQL SELECT statement.
|
||||
*
|
||||
* @param $class
|
||||
* @return string
|
||||
* @todo Not reused... inline?
|
||||
*/
|
||||
private function _getSelectJoinColumnsSQL(ClassMetadata $class)
|
||||
{
|
||||
$sql = '';
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
|
||||
$sql .= ', ' . $this->_getSQLTableAlias($this->_class->name) . ".$srcColumn AS $columnAlias";
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
|
||||
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL table alias for the given class name.
|
||||
*
|
||||
@@ -1129,18 +1086,14 @@ class BasicEntityPersister
|
||||
* @return string The SQL table alias.
|
||||
* @todo Reconsider. Binding table aliases to class names is not such a good idea.
|
||||
*/
|
||||
protected function _getSQLTableAlias($className, $assocName = '')
|
||||
protected function _getSQLTableAlias($className)
|
||||
{
|
||||
if ($assocName) {
|
||||
$className .= '#'.$assocName;
|
||||
}
|
||||
|
||||
if (isset($this->_sqlTableAliases[$className])) {
|
||||
return $this->_sqlTableAliases[$className];
|
||||
}
|
||||
$tableAlias = 't' . $this->_sqlAliasCounter++;
|
||||
|
||||
$this->_sqlTableAliases[$className] = $tableAlias;
|
||||
|
||||
return $tableAlias;
|
||||
}
|
||||
|
||||
@@ -1213,89 +1166,48 @@ class BasicEntityPersister
|
||||
} else {
|
||||
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
|
||||
}
|
||||
|
||||
|
||||
$conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
|
||||
} else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) {
|
||||
// very careless developers could potentially open up this normally hidden api for userland attacks,
|
||||
// therefore checking for spaces and function calls which are not allowed.
|
||||
|
||||
// found a join column condition, not really a "field"
|
||||
$conditionSql .= $field;
|
||||
} else if ($assoc !== null) {
|
||||
if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) {
|
||||
$owningAssoc = $assoc['isOwningSide'] ? $assoc : $this->_em->getClassMetadata($assoc['targetEntity'])
|
||||
->associationMappings[$assoc['mappedBy']];
|
||||
$conditionSql .= $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform) . '.' . $field;
|
||||
} else {
|
||||
$conditionSql .= $field;
|
||||
}
|
||||
} else {
|
||||
throw ORMException::unrecognizedField($field);
|
||||
}
|
||||
$conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ?');
|
||||
$conditionSql .= ' = ?';
|
||||
}
|
||||
return $conditionSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with (sliced or full list) of elements in the specified collection.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
|
||||
return $this->loadArrayFromStatement($assoc, $stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a collection of entities in a one-to-many association.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param PersistentCollection $coll The collection to load/fill.
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
* @param OneToManyMapping $assoc
|
||||
* @param array $criteria The criteria by which to select the entities.
|
||||
* @param PersistentCollection The collection to load/fill.
|
||||
*/
|
||||
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
{
|
||||
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
|
||||
$this->loadCollectionFromStatement($assoc, $stmt, $coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build criteria and execute SQL statement to fetch the one to many entities from.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
* @return Doctrine\DBAL\Statement
|
||||
*/
|
||||
private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$criteria = array();
|
||||
$owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']];
|
||||
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
|
||||
|
||||
$tableAlias = isset($owningAssoc['inherited']) ?
|
||||
$this->_getSQLTableAlias($owningAssoc['inherited'])
|
||||
: $this->_getSQLTableAlias($this->_class->name);
|
||||
|
||||
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
|
||||
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
|
||||
if (isset($sourceClass->associationMappings[$field])) {
|
||||
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
|
||||
}
|
||||
$criteria[$tableAlias . "." . $targetKeyColumn] = $value;
|
||||
} else {
|
||||
$criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
}
|
||||
$criteria[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
}
|
||||
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
|
||||
return $this->_conn->executeQuery($sql, $params, $types);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$coll->hydrateAdd($this->_createEntity($result));
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1309,18 +1221,10 @@ class BasicEntityPersister
|
||||
$params = $types = array();
|
||||
|
||||
foreach ($criteria AS $field => $value) {
|
||||
if ($value === null) {
|
||||
continue; // skip null values.
|
||||
}
|
||||
|
||||
$type = null;
|
||||
if (isset($this->_class->fieldMappings[$field])) {
|
||||
$type = Type::getType($this->_class->fieldMappings[$field]['type'])->getBindingType();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$type += Connection::ARRAY_PARAM_OFFSET;
|
||||
}
|
||||
|
||||
$params[] = $value;
|
||||
$types[] = $type;
|
||||
}
|
||||
@@ -1333,17 +1237,19 @@ class BasicEntityPersister
|
||||
* @param object $entity
|
||||
* @return boolean TRUE if the entity exists in the database, FALSE otherwise.
|
||||
*/
|
||||
public function exists($entity, array $extraConditions = array())
|
||||
public function exists($entity)
|
||||
{
|
||||
$criteria = $this->_class->getIdentifierValues($entity);
|
||||
if ($extraConditions) {
|
||||
$criteria = array_merge($criteria, $extraConditions);
|
||||
}
|
||||
|
||||
$sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform)
|
||||
. ' ' . $this->_getSQLTableAlias($this->_class->name)
|
||||
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
|
||||
|
||||
return (bool) $this->_conn->fetchColumn($sql, array_values($criteria));
|
||||
}
|
||||
|
||||
//TODO
|
||||
/*protected function _getOneToOneEagerFetchSQL()
|
||||
{
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -20,16 +20,13 @@
|
||||
namespace Doctrine\ORM\Persisters;
|
||||
|
||||
use Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\DBAL\LockMode,
|
||||
Doctrine\ORM\Query\ResultSetMapping;
|
||||
Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* The joined subclass persister maps a single entity instance to several tables in the
|
||||
* database as it is defined by the <tt>Class Table Inheritance</tt> strategy.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
* @see http://martinfowler.com/eaaCatalog/classTableInheritance.html
|
||||
*/
|
||||
@@ -238,17 +235,13 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0)
|
||||
{
|
||||
$idColumns = $this->_class->getIdentifierColumnNames();
|
||||
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
|
||||
|
||||
// Create the column list fragment only once
|
||||
if ($this->_selectColumnListSql === null) {
|
||||
|
||||
$this->_rsm = new ResultSetMapping();
|
||||
$this->_rsm->addEntityResult($this->_class->name, 'r');
|
||||
|
||||
// Add regular columns
|
||||
$columnList = '';
|
||||
foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
|
||||
@@ -284,8 +277,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
|
||||
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
|
||||
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
|
||||
}
|
||||
|
||||
// INNER JOIN parent tables
|
||||
@@ -343,25 +335,19 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
|
||||
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
|
||||
|
||||
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
|
||||
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $baseTableAlias) : '';
|
||||
$orderBySql = '';
|
||||
if ($assoc != null && isset($assoc['orderBy'])) {
|
||||
$orderBySql = $this->_getCollectionOrderBySQL($assoc['orderBy'], $baseTableAlias);
|
||||
}
|
||||
|
||||
if ($this->_selectColumnListSql === null) {
|
||||
$this->_selectColumnListSql = $columnList;
|
||||
}
|
||||
|
||||
$lockSql = '';
|
||||
if ($lockMode == LockMode::PESSIMISTIC_READ) {
|
||||
$lockSql = ' ' . $this->_platform->getReadLockSql();
|
||||
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
|
||||
$lockSql = ' ' . $this->_platform->getWriteLockSql();
|
||||
}
|
||||
|
||||
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_selectColumnListSql
|
||||
return 'SELECT ' . $this->_selectColumnListSql
|
||||
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias
|
||||
. $joinSql
|
||||
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset)
|
||||
. $lockSql;
|
||||
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
namespace Doctrine\ORM\Persisters;
|
||||
|
||||
use Doctrine\ORM\PersistentCollection,
|
||||
Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
|
||||
/**
|
||||
* Persister for many-to-many collections.
|
||||
@@ -118,21 +117,13 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
||||
if ($isComposite) {
|
||||
if ($class1->containsForeignIdentifier) {
|
||||
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
$params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
} else {
|
||||
$params[] = array_pop($identifier1);
|
||||
}
|
||||
} else {
|
||||
if ($isComposite) {
|
||||
if ($class2->containsForeignIdentifier) {
|
||||
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
$params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
} else {
|
||||
$params[] = array_pop($identifier2);
|
||||
}
|
||||
@@ -182,119 +173,4 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
$params = array();
|
||||
$mapping = $coll->getMapping();
|
||||
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
if ($mapping['isOwningSide']) {
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$joinColumns = $mapping['relationToSourceKeyColumns'];
|
||||
} else {
|
||||
$mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']];
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$joinColumns = $mapping['relationToTargetKeyColumns'];
|
||||
}
|
||||
|
||||
$whereClause = '';
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($joinColumns[$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($class->containsForeignIdentifier) {
|
||||
$params[] = $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = 'SELECT count(*) FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
|
||||
|
||||
return $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return array
|
||||
*/
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
return $this->_em->getUnitOfWork()
|
||||
->getEntityPersister($mapping['targetEntity'])
|
||||
->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param object $element
|
||||
*/
|
||||
public function contains(PersistentCollection $coll, $element)
|
||||
{
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
|
||||
// shortcut for new entities
|
||||
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$mapping = $coll->getMapping();
|
||||
|
||||
if (!$mapping['isOwningSide']) {
|
||||
$sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$sourceId = $uow->getEntityIdentifier($element);
|
||||
$targetId = $uow->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
$mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
|
||||
} else {
|
||||
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$sourceId = $uow->getEntityIdentifier($coll->getOwner());
|
||||
$targetId = $uow->getEntityIdentifier($element);
|
||||
}
|
||||
$joinTable = $mapping['joinTable'];
|
||||
|
||||
$whereClause = '';
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
} else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
|
||||
|
||||
return (bool)$this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
namespace Doctrine\ORM\Persisters;
|
||||
|
||||
use Doctrine\ORM\PersistentCollection,
|
||||
Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
|
||||
/**
|
||||
* Persister for one-to-many collections.
|
||||
@@ -117,69 +116,4 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
*/
|
||||
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
|
||||
{}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
|
||||
$params = array();
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
$where = '';
|
||||
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
|
||||
if ($where != '') {
|
||||
$where .= ' AND ';
|
||||
}
|
||||
$where .= $joinColumn['name'] . " = ?";
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$params[] = $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])];
|
||||
} else {
|
||||
$params[] = $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SELECT count(*) FROM " . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
|
||||
return $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return \Doctrine\Common\Collections\ArrayCollection
|
||||
*/
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
return $this->_em->getUnitOfWork()
|
||||
->getEntityPersister($mapping['targetEntity'])
|
||||
->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param object $element
|
||||
*/
|
||||
public function contains(PersistentCollection $coll, $element)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
|
||||
// shortcut for new entities
|
||||
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only works with single id identifier entities. Will throw an exception in Entity Persisters
|
||||
// if that is not the case for the 'mappedBy' field.
|
||||
$id = current( $uow->getEntityIdentifier($coll->getOwner()) );
|
||||
|
||||
return $uow->getEntityPersister($mapping['targetEntity'])
|
||||
->exists($element, array($mapping['mappedBy'] => $id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
* SINGLE_TABLE strategy.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
* @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html
|
||||
*/
|
||||
@@ -49,8 +48,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
|
||||
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
|
||||
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
|
||||
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
|
||||
|
||||
// Append subclass columns
|
||||
foreach ($this->_class->subClasses as $subClassName) {
|
||||
@@ -88,9 +86,9 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
protected function _getSQLTableAlias($className, $assocName = '')
|
||||
protected function _getSQLTableAlias($className)
|
||||
{
|
||||
return parent::_getSQLTableAlias($this->_class->rootEntityName, $assocName);
|
||||
return parent::_getSQLTableAlias($this->_class->rootEntityName);
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
|
||||
@@ -172,7 +172,7 @@ class ProxyFactory
|
||||
}
|
||||
|
||||
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
|
||||
$methods .= "\n" . ' public function ';
|
||||
$methods .= PHP_EOL . ' public function ';
|
||||
if ($method->returnsReference()) {
|
||||
$methods .= '&';
|
||||
}
|
||||
@@ -208,10 +208,10 @@ class ProxyFactory
|
||||
}
|
||||
|
||||
$methods .= $parameterString . ')';
|
||||
$methods .= "\n" . ' {' . "\n";
|
||||
$methods .= ' $this->__load();' . "\n";
|
||||
$methods .= PHP_EOL . ' {' . PHP_EOL;
|
||||
$methods .= ' $this->_load();' . PHP_EOL;
|
||||
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
|
||||
$methods .= "\n" . ' }' . "\n";
|
||||
$methods .= PHP_EOL . ' }' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,26 +269,17 @@ class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
|
||||
$this->_entityPersister = $entityPersister;
|
||||
$this->_identifier = $identifier;
|
||||
}
|
||||
/** @private */
|
||||
public function __load()
|
||||
private function _load()
|
||||
{
|
||||
if (!$this->__isInitialized__ && $this->_entityPersister) {
|
||||
$this->__isInitialized__ = true;
|
||||
|
||||
if (method_exists($this, "__wakeup")) {
|
||||
// call this after __isInitialized__to avoid infinite recursion
|
||||
// but before loading to emulate what ClassMetadata::newInstance()
|
||||
// provides.
|
||||
$this->__wakeup();
|
||||
}
|
||||
|
||||
if ($this->_entityPersister->load($this->_identifier, $this) === null) {
|
||||
throw new \Doctrine\ORM\EntityNotFoundException();
|
||||
}
|
||||
unset($this->_entityPersister, $this->_identifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
<methods>
|
||||
|
||||
public function __sleep()
|
||||
|
||||
@@ -53,15 +53,6 @@ final class Query extends AbstractQuery
|
||||
* @var string
|
||||
*/
|
||||
const HINT_REFRESH = 'doctrine.refresh';
|
||||
|
||||
|
||||
/**
|
||||
* Internal hint: is set to the proxy entity that is currently triggered for loading
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
|
||||
|
||||
/**
|
||||
* The forcePartialLoad query hint forces a particular query to return
|
||||
* partial objects.
|
||||
@@ -238,83 +229,44 @@ final class Query extends AbstractQuery
|
||||
throw QueryException::invalidParameterNumber();
|
||||
}
|
||||
|
||||
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
|
||||
|
||||
if ($this->_resultSetMapping === null) {
|
||||
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
|
||||
}
|
||||
|
||||
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes query parameter mappings
|
||||
*
|
||||
* @param array $paramMappings
|
||||
* @return array
|
||||
*/
|
||||
private function processParameterMappings($paramMappings)
|
||||
{
|
||||
$sqlParams = $types = array();
|
||||
|
||||
|
||||
foreach ($this->_params as $key => $value) {
|
||||
if ( ! isset($paramMappings[$key])) {
|
||||
throw QueryException::unknownParameter($key);
|
||||
}
|
||||
|
||||
if (isset($this->_paramTypes[$key])) {
|
||||
foreach ($paramMappings[$key] as $position) {
|
||||
$types[$position] = $this->_paramTypes[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$sqlPositions = $paramMappings[$key];
|
||||
$value = array_values($this->processParameterValue($value));
|
||||
$countValue = count($value);
|
||||
|
||||
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
|
||||
$sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
|
||||
|
||||
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
|
||||
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
|
||||
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
} else {
|
||||
$class = $this->_em->getClassMetadata(get_class($value));
|
||||
$idValues = $class->getIdentifierValues($value);
|
||||
}
|
||||
$sqlPositions = $paramMappings[$key];
|
||||
$sqlParams += array_combine((array)$sqlPositions, $idValues);
|
||||
} else {
|
||||
foreach ($paramMappings[$key] as $position) {
|
||||
$sqlParams[$position] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($sqlParams) {
|
||||
ksort($sqlParams);
|
||||
$sqlParams = array_values($sqlParams);
|
||||
}
|
||||
|
||||
return array($sqlParams, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an individual parameter value
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
private function processParameterValue($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
for ($i = 0, $l = count($value); $i < $l; $i++) {
|
||||
$paramValue = $this->processParameterValue($value[$i]);
|
||||
|
||||
// TODO: What about Entities that have composite primary key?
|
||||
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
|
||||
}
|
||||
|
||||
return array($value);
|
||||
if ($this->_resultSetMapping === null) {
|
||||
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
|
||||
}
|
||||
|
||||
if ( ! (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)))) {
|
||||
return array($value);
|
||||
}
|
||||
|
||||
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
|
||||
return array_values($this->_em->getUnitOfWork()->getEntityIdentifier($value));
|
||||
}
|
||||
|
||||
$class = $this->_em->getClassMetadata(get_class($value));
|
||||
|
||||
return array_values($class->getIdentifierValues($value));
|
||||
|
||||
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST;
|
||||
|
||||
/**
|
||||
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
|
||||
*
|
||||
* @since 2.1
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class CoalesceExpression extends Node
|
||||
{
|
||||
public $scalarExpressions = array();
|
||||
|
||||
|
||||
public function __construct(array $scalarExpressions)
|
||||
{
|
||||
$this->scalarExpressions = $scalarExpressions;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
{
|
||||
return $sqlWalker->walkCoalesceExpression($this);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* 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
|
||||
@@ -25,6 +27,7 @@ namespace Doctrine\ORM\Query\AST;
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision: 3938 $
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
|
||||
/**
|
||||
* "DATE_ADD(date1, interval, unit)"
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class DateAddFunction extends FunctionNode
|
||||
{
|
||||
public $firstDateExpression = null;
|
||||
public $intervalExpression = null;
|
||||
public $unit = null;
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
$unit = strtolower($this->unit);
|
||||
if ($unit == "day") {
|
||||
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression(
|
||||
$this->firstDateExpression->dispatch($sqlWalker),
|
||||
$this->intervalExpression->dispatch($sqlWalker)
|
||||
);
|
||||
} else if ($unit == "month") {
|
||||
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression(
|
||||
$this->firstDateExpression->dispatch($sqlWalker),
|
||||
$this->intervalExpression->dispatch($sqlWalker)
|
||||
);
|
||||
} else {
|
||||
throw QueryException::semanticalError('DATE_ADD() only supports units of type day and month.');
|
||||
}
|
||||
}
|
||||
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$this->intervalExpression = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$this->unit = $parser->StringPrimary();
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
|
||||
/**
|
||||
* "DATE_DIFF(date1, date2)"
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class DateDiffFunction extends FunctionNode
|
||||
{
|
||||
public $date1;
|
||||
public $date2;
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression(
|
||||
$this->date1->dispatch($sqlWalker),
|
||||
$this->date2->dispatch($sqlWalker)
|
||||
);
|
||||
}
|
||||
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->date1 = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$this->date2 = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
|
||||
/**
|
||||
* "DATE_ADD(date1, interval, unit)"
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class DateSubFunction extends DateAddFunction
|
||||
{
|
||||
public $firstDateExpression = null;
|
||||
public $intervalExpression = null;
|
||||
public $unit = null;
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
$unit = strtolower($this->unit);
|
||||
if ($unit == "day") {
|
||||
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression(
|
||||
$this->firstDateExpression->dispatch($sqlWalker),
|
||||
$this->intervalExpression->dispatch($sqlWalker)
|
||||
);
|
||||
} else if ($unit == "month") {
|
||||
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression(
|
||||
$this->firstDateExpression->dispatch($sqlWalker),
|
||||
$this->intervalExpression->dispatch($sqlWalker)
|
||||
);
|
||||
} else {
|
||||
throw QueryException::semanticalError('DATE_SUB() only supports units of type day and month.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,8 @@ class SizeFunction extends FunctionNode
|
||||
|
||||
if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) {
|
||||
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
|
||||
$targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$targetTableAlias = $sqlWalker->getSqlTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
|
||||
$sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE ';
|
||||
|
||||
@@ -76,8 +76,8 @@ class SizeFunction extends FunctionNode
|
||||
$joinTable = $owningAssoc['joinTable'];
|
||||
|
||||
// SQL table aliases
|
||||
$joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']);
|
||||
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$joinTableAlias = $sqlWalker->getSqlTableAlias($joinTable['name']);
|
||||
$sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
|
||||
// join to target table
|
||||
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE ';
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST;
|
||||
|
||||
/**
|
||||
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
|
||||
*
|
||||
* @since 2.1
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class NullIfExpression extends Node
|
||||
{
|
||||
public $firstExpression;
|
||||
|
||||
public $secondExpression;
|
||||
|
||||
public function __construct($firstExpression, $secondExpression)
|
||||
{
|
||||
$this->firstExpression = $firstExpression;
|
||||
$this->secondExpression = $secondExpression;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
{
|
||||
return $sqlWalker->walkNullIfExpression($this);
|
||||
}
|
||||
}
|
||||
@@ -63,11 +63,9 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
$idColumnList = implode(', ', $idColumnNames);
|
||||
|
||||
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
|
||||
$sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $primaryDqlAlias);
|
||||
|
||||
$this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
|
||||
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
|
||||
|
||||
$sqlWalker->setSqlTableAlias($primaryClass->table['name'] . $primaryDqlAlias, 't0');
|
||||
$rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias);
|
||||
$fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array())));
|
||||
$this->_insertSql .= $sqlWalker->walkFromClause($fromClause);
|
||||
@@ -98,7 +96,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
}
|
||||
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
|
||||
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
|
||||
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
|
||||
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
$em = $sqlWalker->getEntityManager();
|
||||
$conn = $em->getConnection();
|
||||
$platform = $conn->getDatabasePlatform();
|
||||
|
||||
|
||||
$updateClause = $AST->updateClause;
|
||||
|
||||
$primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName);
|
||||
@@ -64,14 +64,11 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
$idColumnList = implode(', ', $idColumnNames);
|
||||
|
||||
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
|
||||
$sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $updateClause->aliasIdentificationVariable);
|
||||
|
||||
$this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
|
||||
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
|
||||
|
||||
$sqlWalker->setSqlTableAlias($primaryClass->table['name'] . $updateClause->aliasIdentificationVariable, 't0');
|
||||
$rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable);
|
||||
$fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array())));
|
||||
|
||||
$this->_insertSql .= $sqlWalker->walkFromClause($fromClause);
|
||||
|
||||
// 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect)
|
||||
@@ -88,9 +85,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
|
||||
foreach ($updateItems as $updateItem) {
|
||||
$field = $updateItem->pathExpression->field;
|
||||
|
||||
if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited']) ||
|
||||
isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) {
|
||||
isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) {
|
||||
$newValue = $updateItem->newValue;
|
||||
|
||||
if ( ! $affected) {
|
||||
@@ -106,9 +102,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
//FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage.
|
||||
if ($newValue instanceof AST\InputParameter) {
|
||||
$paramKey = $newValue->name;
|
||||
$this->_sqlParameters[$i]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey);
|
||||
$this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey);
|
||||
|
||||
$this->_sqlParameters[$i][] = $sqlWalker->getQuery()->getParameter($paramKey);
|
||||
++$this->_numParametersInUpdateClause;
|
||||
}
|
||||
}
|
||||
@@ -126,18 +120,15 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
|
||||
// 4. Store DDL for temporary identifier table.
|
||||
$columnDefinitions = array();
|
||||
|
||||
foreach ($idColumnNames as $idColumnName) {
|
||||
$columnDefinitions[$idColumnName] = array(
|
||||
'notnull' => true,
|
||||
'type' => Type::getType($rootClass->getTypeOfColumn($idColumnName))
|
||||
);
|
||||
}
|
||||
|
||||
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
|
||||
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
|
||||
|
||||
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
|
||||
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,23 +146,11 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
$conn->executeUpdate($this->_createTempTableSql);
|
||||
|
||||
// Insert identifiers. Parameters from the update clause are cut off.
|
||||
$numUpdated = $conn->executeUpdate(
|
||||
$this->_insertSql,
|
||||
array_slice($params, $this->_numParametersInUpdateClause),
|
||||
array_slice($types, $this->_numParametersInUpdateClause)
|
||||
);
|
||||
$numUpdated = $conn->executeUpdate($this->_insertSql, array_slice($params, $this->_numParametersInUpdateClause), $types);
|
||||
|
||||
// Execute UPDATE statements
|
||||
for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) {
|
||||
$parameters = array();
|
||||
$types = array();
|
||||
|
||||
if (isset($this->_sqlParameters[$i])) {
|
||||
$parameters = isset($this->_sqlParameters[$i]['parameters']) ? $this->_sqlParameters[$i]['parameters'] : array();
|
||||
$types = isset($this->_sqlParameters[$i]['types']) ? $this->_sqlParameters[$i]['types'] : array();
|
||||
}
|
||||
|
||||
$conn->executeUpdate($this->_sqlStatements[$i], $parameters, $types);
|
||||
$conn->executeUpdate($this->_sqlStatements[$i], isset($this->_sqlParameters[$i]) ? $this->_sqlParameters[$i] : array());
|
||||
}
|
||||
|
||||
// Drop temporary table
|
||||
|
||||
@@ -443,28 +443,6 @@ class Expr
|
||||
return new Expr\Func($x . ' NOT IN', (array) $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IS NULL expression with the given arguments.
|
||||
*
|
||||
* @param string $x Field in string format to be restricted by IS NULL
|
||||
* @return string
|
||||
*/
|
||||
public function isNull($x)
|
||||
{
|
||||
return $x . ' IS NULL';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IS NOT NULL expression with the given arguments.
|
||||
*
|
||||
* @param string $x Field in string format to be restricted by IS NOT NULL
|
||||
* @return string
|
||||
*/
|
||||
public function isNotNull($x)
|
||||
{
|
||||
return $x . ' IS NOT NULL';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LIKE() comparison expression with the given arguments.
|
||||
*
|
||||
|
||||
@@ -32,9 +32,9 @@ namespace Doctrine\ORM\Query\Expr;
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class Andx extends Composite
|
||||
class Andx extends Base
|
||||
{
|
||||
protected $_separator = ' AND ';
|
||||
protected $_separator = ') AND (';
|
||||
protected $_allowedClasses = array(
|
||||
'Doctrine\ORM\Query\Expr\Comparison',
|
||||
'Doctrine\ORM\Query\Expr\Func',
|
||||
|
||||
@@ -39,7 +39,7 @@ abstract class Base
|
||||
protected $_postSeparator = ')';
|
||||
protected $_allowedClasses = array();
|
||||
|
||||
protected $_parts = array();
|
||||
private $_parts = array();
|
||||
|
||||
public function __construct($args = array())
|
||||
{
|
||||
@@ -55,7 +55,7 @@ abstract class Base
|
||||
|
||||
public function add($arg)
|
||||
{
|
||||
if ( $arg !== null || ($arg instanceof self && $arg->count() > 0)) {
|
||||
if ( ! empty($arg) || ($arg instanceof self && $arg->count() > 0)) {
|
||||
// If we decide to keep Expr\Base instances, we can use this check
|
||||
if ( ! is_string($arg)) {
|
||||
$class = get_class($arg);
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\Expr;
|
||||
|
||||
/**
|
||||
* Expression class for building DQL and parts
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class Composite extends Base
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->count() === 1) {
|
||||
return (string) $this->_parts[0];
|
||||
}
|
||||
|
||||
$components = array();
|
||||
|
||||
foreach ($this->_parts as $part) {
|
||||
$components[] = $this->processQueryPart($part);
|
||||
}
|
||||
|
||||
return implode($this->_separator, $components);
|
||||
}
|
||||
|
||||
|
||||
private function processQueryPart($part)
|
||||
{
|
||||
$queryPart = (string) $part;
|
||||
|
||||
if (is_object($part) && $part instanceof self && $part->count() > 1) {
|
||||
return $this->_preSeparator . $queryPart . $this->_postSeparator;
|
||||
}
|
||||
|
||||
// Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND")
|
||||
if (mb_stripos($queryPart, ' OR ') !== false || mb_stripos($queryPart, ' AND ') !== false) {
|
||||
return $this->_preSeparator . $queryPart . $this->_postSeparator;
|
||||
}
|
||||
|
||||
return $queryPart;
|
||||
}
|
||||
}
|
||||
@@ -45,23 +45,20 @@ class Join
|
||||
private $_alias;
|
||||
private $_conditionType;
|
||||
private $_condition;
|
||||
private $_indexBy;
|
||||
|
||||
public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = null)
|
||||
public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null)
|
||||
{
|
||||
$this->_joinType = $joinType;
|
||||
$this->_join = $join;
|
||||
$this->_alias = $alias;
|
||||
$this->_joinType = $joinType;
|
||||
$this->_join = $join;
|
||||
$this->_alias = $alias;
|
||||
$this->_conditionType = $conditionType;
|
||||
$this->_condition = $condition;
|
||||
$this->_indexBy = $indexBy;
|
||||
$this->_condition = $condition;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return strtoupper($this->_joinType) . ' JOIN ' . $this->_join
|
||||
. ($this->_alias ? ' ' . $this->_alias : '')
|
||||
. ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '')
|
||||
. ($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : '');
|
||||
. ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '');
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,9 @@ namespace Doctrine\ORM\Query\Expr;
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class Orx extends Composite
|
||||
class Orx extends Base
|
||||
{
|
||||
protected $_separator = ' OR ';
|
||||
protected $_separator = ') OR (';
|
||||
protected $_allowedClasses = array(
|
||||
'Doctrine\ORM\Query\Expr\Andx',
|
||||
'Doctrine\ORM\Query\Expr\Comparison',
|
||||
|
||||
@@ -126,7 +126,7 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
'[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}',
|
||||
'(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?',
|
||||
"'(?:[^']|'')*'",
|
||||
'\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}'
|
||||
'\?[1-9][0-9]*|:[a-z][a-z0-9_]+'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
|
||||
use Doctrine\DBAL\Connection,
|
||||
Doctrine\DBAL\Types\Type;
|
||||
|
||||
/**
|
||||
* Provides an enclosed support for parameter infering.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class ParameterTypeInferer
|
||||
{
|
||||
/**
|
||||
* Infer type of a given value, returning a compatible constant:
|
||||
* - Type (Doctrine\DBAL\Types\Type::*)
|
||||
* - Connection (Doctrine\DBAL\Connection::PARAM_*)
|
||||
*
|
||||
* @param mixed $value Parameter value
|
||||
*
|
||||
* @return mixed Parameter type constant
|
||||
*/
|
||||
public static function inferType($value)
|
||||
{
|
||||
switch (true) {
|
||||
case is_integer($value):
|
||||
return Type::INTEGER;
|
||||
|
||||
case ($value instanceof \DateTime):
|
||||
return Type::DATETIME;
|
||||
|
||||
case is_array($value):
|
||||
$key = key($value);
|
||||
|
||||
if (is_integer($value[$key])) {
|
||||
return Connection::PARAM_INT_ARRAY;
|
||||
}
|
||||
|
||||
return Connection::PARAM_STR_ARRAY;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
return \PDO::PARAM_STR;
|
||||
}
|
||||
}
|
||||
@@ -45,22 +45,19 @@ class Parser
|
||||
|
||||
/** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */
|
||||
private static $_NUMERIC_FUNCTIONS = array(
|
||||
'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
|
||||
'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
|
||||
'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
|
||||
'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
|
||||
'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
|
||||
'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction',
|
||||
'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction',
|
||||
'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
|
||||
'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
|
||||
'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
|
||||
'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
|
||||
'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
|
||||
'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction'
|
||||
);
|
||||
|
||||
/** READ-ONLY: Maps BUILT-IN datetime function names to AST class names. */
|
||||
private static $_DATETIME_FUNCTIONS = array(
|
||||
'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction',
|
||||
'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction',
|
||||
'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction',
|
||||
'date_add' => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction',
|
||||
'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction',
|
||||
'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction'
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -234,7 +231,7 @@ class Parser
|
||||
* If they match, updates the lookahead token; otherwise raises a syntax
|
||||
* error.
|
||||
*
|
||||
* @param int token type
|
||||
* @param int|string token type or value
|
||||
* @return void
|
||||
* @throws QueryException If the tokens dont match.
|
||||
*/
|
||||
@@ -926,10 +923,6 @@ class Parser
|
||||
$token = $this->_lexer->lookahead;
|
||||
$identVariable = $this->IdentificationVariable();
|
||||
|
||||
if (!isset($this->_queryComponents[$identVariable])) {
|
||||
$this->semanticalError('Identification Variable ' . $identVariable .' used in join path expression but was not defined before.');
|
||||
}
|
||||
|
||||
$this->match(Lexer::T_DOT);
|
||||
$this->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
@@ -1331,10 +1324,6 @@ class Parser
|
||||
$token = $this->_lexer->lookahead;
|
||||
$identVariable = $this->IdentificationVariable();
|
||||
|
||||
if (!isset($this->_queryComponents[$identVariable])) {
|
||||
$this->semanticalError('Cannot group by undefined identification variable.');
|
||||
}
|
||||
|
||||
return $identVariable;
|
||||
}
|
||||
|
||||
@@ -1644,11 +1633,7 @@ class Parser
|
||||
return $this->StateFieldPathExpression();
|
||||
} else if ($lookahead == Lexer::T_INTEGER || $lookahead == Lexer::T_FLOAT) {
|
||||
return $this->SimpleArithmeticExpression();
|
||||
} else if ($lookahead == Lexer::T_CASE || $lookahead == Lexer::T_COALESCE || $lookahead == Lexer::T_NULLIF) {
|
||||
// Since NULLIF and COALESCE can be identified as a function,
|
||||
// we need to check if before check for FunctionDeclaration
|
||||
return $this->CaseExpression();
|
||||
} else if ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
|
||||
} else if ($this->_isFunction()) {
|
||||
// We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator)
|
||||
$this->_lexer->peek(); // "("
|
||||
$peek = $this->_peekBeyondClosingParenthesis();
|
||||
@@ -1656,12 +1641,8 @@ class Parser
|
||||
if ($this->_isMathOperator($peek)) {
|
||||
return $this->SimpleArithmeticExpression();
|
||||
}
|
||||
|
||||
if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
|
||||
return $this->AggregateExpression();
|
||||
} else {
|
||||
return $this->FunctionDeclaration();
|
||||
}
|
||||
|
||||
return $this->FunctionDeclaration();
|
||||
} else if ($lookahead == Lexer::T_STRING) {
|
||||
return $this->StringPrimary();
|
||||
} else if ($lookahead == Lexer::T_INPUT_PARAMETER) {
|
||||
@@ -1669,6 +1650,8 @@ class Parser
|
||||
} else if ($lookahead == Lexer::T_TRUE || $lookahead == Lexer::T_FALSE) {
|
||||
$this->match($lookahead);
|
||||
return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
|
||||
} else if ($lookahead == Lexer::T_CASE || $lookahead == Lexer::T_COALESCE || $lookahead == Lexer::T_NULLIF) {
|
||||
return $this->CaseExpression();
|
||||
} else {
|
||||
$this->syntaxError();
|
||||
}
|
||||
@@ -1676,66 +1659,11 @@ class Parser
|
||||
|
||||
public function CaseExpression()
|
||||
{
|
||||
$lookahead = $this->_lexer->lookahead['type'];
|
||||
|
||||
// if "CASE" "WHEN" => GeneralCaseExpression
|
||||
// else if "CASE" => SimpleCaseExpression
|
||||
// [DONE] else if "COALESCE" => CoalesceExpression
|
||||
// [DONE] else if "NULLIF" => NullifExpression
|
||||
switch ($lookahead) {
|
||||
case Lexer::T_NULLIF:
|
||||
return $this->NullIfExpression();
|
||||
|
||||
case Lexer::T_COALESCE:
|
||||
return $this->CoalesceExpression();
|
||||
|
||||
default:
|
||||
$this->semanticalError('CaseExpression not yet supported.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
|
||||
*
|
||||
* @return Doctrine\ORM\Query\AST\CoalesceExpression
|
||||
*/
|
||||
public function CoalesceExpression()
|
||||
{
|
||||
$this->match(Lexer::T_COALESCE);
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
// Process ScalarExpressions (1..N)
|
||||
$scalarExpressions = array();
|
||||
$scalarExpressions[] = $this->ScalarExpression();
|
||||
|
||||
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
|
||||
$this->match(Lexer::T_COMMA);
|
||||
$scalarExpressions[] = $this->ScalarExpression();
|
||||
}
|
||||
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
|
||||
return new AST\CoalesceExpression($scalarExpressions);
|
||||
}
|
||||
|
||||
/**
|
||||
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
|
||||
*
|
||||
* @return Doctrine\ORM\Query\AST\ExistsExpression
|
||||
*/
|
||||
public function NullIfExpression()
|
||||
{
|
||||
$this->match(Lexer::T_NULLIF);
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$firstExpression = $this->ScalarExpression();
|
||||
$this->match(Lexer::T_COMMA);
|
||||
$secondExpression = $this->ScalarExpression();
|
||||
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
|
||||
return new AST\NullIfExpression($firstExpression, $secondExpression);
|
||||
// else if "COALESCE" => CoalesceExpression
|
||||
// else if "NULLIF" => NullifExpression
|
||||
$this->semanticalError('CaseExpression not yet supported.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1774,16 +1702,12 @@ class Parser
|
||||
}
|
||||
} else if ($this->_isFunction()) {
|
||||
$this->_lexer->peek(); // "("
|
||||
|
||||
$lookaheadType = $this->_lexer->lookahead['type'];
|
||||
$beyond = $this->_peekBeyondClosingParenthesis();
|
||||
|
||||
$beyond = $this->_peekBeyondClosingParenthesis();
|
||||
|
||||
if ($this->_isMathOperator($beyond)) {
|
||||
$expression = $this->ScalarExpression();
|
||||
} else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
|
||||
$expression = $this->AggregateExpression();
|
||||
} else if (in_array ($lookaheadType, array(Lexer::T_CASE, Lexer::T_COALESCE, Lexer::T_NULLIF))) {
|
||||
$expression = $this->CaseExpression();
|
||||
} else {
|
||||
// Shortcut: ScalarExpression => Function
|
||||
$expression = $this->FunctionDeclaration();
|
||||
@@ -1793,8 +1717,7 @@ class Parser
|
||||
$expression = $this->PartialObjectExpression();
|
||||
$identVariable = $expression->identificationVariable;
|
||||
} else if ($this->_lexer->lookahead['type'] == Lexer::T_INTEGER ||
|
||||
$this->_lexer->lookahead['type'] == Lexer::T_FLOAT ||
|
||||
$this->_lexer->lookahead['type'] == Lexer::T_STRING) {
|
||||
$this->_lexer->lookahead['type'] == Lexer::T_FLOAT) {
|
||||
// Shortcut: ScalarExpression => SimpleArithmeticExpression
|
||||
$expression = $this->SimpleArithmeticExpression();
|
||||
} else {
|
||||
@@ -1863,8 +1786,15 @@ class Parser
|
||||
}
|
||||
|
||||
$this->_lexer->peek();
|
||||
$beyond = $this->_peekBeyondClosingParenthesis();
|
||||
|
||||
$expression = $this->ScalarExpression();
|
||||
if ($this->_isMathOperator($beyond)) {
|
||||
$expression = $this->ScalarExpression();
|
||||
} else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
|
||||
$expression = $this->AggregateExpression();
|
||||
} else {
|
||||
$expression = $this->FunctionDeclaration();
|
||||
}
|
||||
|
||||
$expr = new AST\SimpleSelectExpression($expression);
|
||||
|
||||
@@ -2290,7 +2220,7 @@ class Parser
|
||||
/**
|
||||
* ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
|
||||
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
|
||||
* | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
|
||||
* | FunctionsReturningDatetime | IdentificationVariable
|
||||
*/
|
||||
public function ArithmeticPrimary()
|
||||
{
|
||||
@@ -2314,11 +2244,7 @@ class Parser
|
||||
if ($peek['value'] == '.') {
|
||||
return $this->SingleValuedPathExpression();
|
||||
}
|
||||
|
||||
if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
|
||||
return $this->ResultVariable();
|
||||
}
|
||||
|
||||
|
||||
return $this->StateFieldPathExpression();
|
||||
|
||||
case Lexer::T_INPUT_PARAMETER:
|
||||
@@ -2373,8 +2299,7 @@ class Parser
|
||||
if ($peek['value'] == '.') {
|
||||
return $this->StateFieldPathExpression();
|
||||
} else if ($peek['value'] == '(') {
|
||||
// do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
|
||||
return $this->FunctionDeclaration();
|
||||
return $this->FunctionsReturningStrings();
|
||||
} else {
|
||||
$this->syntaxError("'.' or '('");
|
||||
}
|
||||
|
||||
@@ -75,7 +75,8 @@ class QueryException extends \Doctrine\ORM\ORMException
|
||||
public static function invalidPathExpression($pathExpr)
|
||||
{
|
||||
return new self(
|
||||
"Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'."
|
||||
"Invalid PathExpression '" . $pathExpr->identificationVariable .
|
||||
"." . implode('.', $pathExpr->parts) . "'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,10 +136,4 @@ class QueryException extends \Doctrine\ORM\ORMException
|
||||
"in the query."
|
||||
);
|
||||
}
|
||||
|
||||
public static function instanceOfUnrelatedClass($className, $rootClass)
|
||||
{
|
||||
return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " .
|
||||
"inheritance hierachy exists between these two classes.");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* 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
|
||||
@@ -112,13 +114,6 @@ class ResultSetMapping
|
||||
* @var array
|
||||
*/
|
||||
public $declaringClasses = array();
|
||||
|
||||
/**
|
||||
* This is necessary to hydrate derivate foreign keys correctly.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $isIdentifierColumn = array();
|
||||
|
||||
/**
|
||||
* Adds an entity result to this ResultSetMapping.
|
||||
@@ -388,17 +383,14 @@ class ResultSetMapping
|
||||
/**
|
||||
* Adds a meta column (foreign key or discriminator column) to the result set.
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $columnName
|
||||
* @param string $fieldName
|
||||
* @param bool
|
||||
* @param $alias
|
||||
* @param $columnName
|
||||
* @param $fieldName
|
||||
*/
|
||||
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false)
|
||||
public function addMetaResult($alias, $columnName, $fieldName)
|
||||
{
|
||||
$this->metaMappings[$columnName] = $fieldName;
|
||||
$this->columnOwnerMap[$columnName] = $alias;
|
||||
if ($isIdentifierColumn) {
|
||||
$this->isIdentifierColumn[$alias][$columnName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
|
||||
/**
|
||||
* A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields
|
||||
*
|
||||
* @author Michael Ridgway <mcridgway@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class ResultSetMappingBuilder extends ResultSetMapping
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @param EntityManager
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a root entity and all of its fields to the result set.
|
||||
*
|
||||
* @param string $class The class name of the root entity.
|
||||
* @param string $alias The unique alias to use for the root entity.
|
||||
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
|
||||
*/
|
||||
public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array())
|
||||
{
|
||||
$this->addEntityResult($class, $alias);
|
||||
$this->addAllClassFields($class, $alias, $renamedColumns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a joined entity and all of its fields to the result set.
|
||||
*
|
||||
* @param string $class The class name of the joined entity.
|
||||
* @param string $alias The unique alias to use for the joined entity.
|
||||
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
|
||||
* @param object $relation The association field that connects the parent entity result with the joined entity result.
|
||||
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
|
||||
*/
|
||||
public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array())
|
||||
{
|
||||
$this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);
|
||||
$this->addAllClassFields($class, $alias, $renamedColumns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all fields of the given class to the result set mapping (columns and meta fields)
|
||||
*/
|
||||
protected function addAllClassFields($class, $alias, $renamedColumns = array())
|
||||
{
|
||||
$classMetadata = $this->em->getClassMetadata($class);
|
||||
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
|
||||
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
|
||||
}
|
||||
$platform = $this->em->getConnection()->getDatabasePlatform();
|
||||
foreach ($classMetadata->getColumnNames() AS $columnName) {
|
||||
$propertyName = $classMetadata->getFieldName($columnName);
|
||||
if (isset($renamedColumns[$columnName])) {
|
||||
$columnName = $renamedColumns[$columnName];
|
||||
}
|
||||
if (isset($this->fieldMappings[$columnName])) {
|
||||
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
|
||||
}
|
||||
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
|
||||
}
|
||||
foreach ($classMetadata->associationMappings AS $associationMapping) {
|
||||
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
foreach ($associationMapping['joinColumns'] AS $joinColumn) {
|
||||
$columnName = $joinColumn['name'];
|
||||
$renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName;
|
||||
if (isset($this->metaMappings[$renamedColumnName])) {
|
||||
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
|
||||
}
|
||||
$this->addMetaResult($alias, $platform->getSQLResultCasing($renamedColumnName), $platform->getSQLResultCasing($columnName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class SqlWalker implements TreeWalker
|
||||
private $_aliasCounter = 0;
|
||||
private $_tableAliasCounter = 0;
|
||||
private $_scalarResultCounter = 1;
|
||||
private $_sqlParamIndex = 0;
|
||||
private $_sqlParamIndex = 1;
|
||||
|
||||
/**
|
||||
* @var ParserResult
|
||||
@@ -192,9 +192,9 @@ class SqlWalker implements TreeWalker
|
||||
* @param string $dqlAlias The DQL alias.
|
||||
* @return string Generated table alias.
|
||||
*/
|
||||
public function getSQLTableAlias($tableName, $dqlAlias = '')
|
||||
public function getSqlTableAlias($tableName, $dqlAlias = '')
|
||||
{
|
||||
$tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
|
||||
$tableName .= $dqlAlias;
|
||||
|
||||
if ( ! isset($this->_tableAliasMap[$tableName])) {
|
||||
$this->_tableAliasMap[$tableName] = strtolower(substr($tableName, 0, 1)) . $this->_tableAliasCounter++ . '_';
|
||||
@@ -212,9 +212,9 @@ class SqlWalker implements TreeWalker
|
||||
* @param string $dqlAlias
|
||||
* @return string
|
||||
*/
|
||||
public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
|
||||
public function setSqlTableAlias($tableName, $alias, $dqlAlias = '')
|
||||
{
|
||||
$tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
|
||||
$tableName .= $dqlAlias;
|
||||
|
||||
$this->_tableAliasMap[$tableName] = $alias;
|
||||
|
||||
@@ -227,7 +227,7 @@ class SqlWalker implements TreeWalker
|
||||
* @param string $columnName
|
||||
* @return string
|
||||
*/
|
||||
public function getSQLColumnAlias($columnName)
|
||||
public function getSqlColumnAlias($columnName)
|
||||
{
|
||||
return $columnName . $this->_aliasCounter++;
|
||||
}
|
||||
@@ -303,7 +303,7 @@ class SqlWalker implements TreeWalker
|
||||
if ($sql != '') {
|
||||
$sql .= ', ';
|
||||
}
|
||||
$sql .= $this->getSQLTableAlias($tableName, $dqlAlias) . '.' .
|
||||
$sql .= $this->getSqlTableAlias($tableName, $dqlAlias) . '.' .
|
||||
$qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . " $orientation";
|
||||
}
|
||||
}
|
||||
@@ -342,7 +342,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
$sql .= ($sql != '' ? ' AND ' : '')
|
||||
. (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.' : '')
|
||||
. (($this->_useSqlTableAliases) ? $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.' : '')
|
||||
. $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
|
||||
}
|
||||
}
|
||||
@@ -502,7 +502,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
if ($this->_useSqlTableAliases) {
|
||||
$sql .= $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.';
|
||||
$sql .= $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.';
|
||||
}
|
||||
|
||||
$sql .= reset($assoc['targetToSourceKeyColumns']);
|
||||
@@ -526,8 +526,9 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkSelectClause($selectClause)
|
||||
{
|
||||
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
|
||||
$sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
|
||||
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '') . implode(
|
||||
', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)
|
||||
);
|
||||
|
||||
$addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
|
||||
$this->_query->getHydrationMode() == Query::HYDRATE_OBJECT
|
||||
@@ -550,15 +551,14 @@ class SqlWalker implements TreeWalker
|
||||
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
|
||||
// Add discriminator columns to SQL
|
||||
$rootClass = $this->_em->getClassMetadata($class->rootEntityName);
|
||||
$tblAlias = $this->getSQLTableAlias($rootClass->table['name'], $dqlAlias);
|
||||
$tblAlias = $this->getSqlTableAlias($rootClass->table['name'], $dqlAlias);
|
||||
$discrColumn = $rootClass->discriminatorColumn;
|
||||
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
|
||||
|
||||
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
|
||||
$columnAlias = $this->getSqlColumnAlias($discrColumn['name']);
|
||||
$sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
|
||||
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
|
||||
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $discrColumn['fieldName']);
|
||||
|
||||
// Add foreign key columns to SQL, if necessary
|
||||
if ($addMetaColumns) {
|
||||
@@ -567,18 +567,16 @@ class SqlWalker implements TreeWalker
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
if (isset($assoc['inherited'])) {
|
||||
$owningClass = $this->_em->getClassMetadata($assoc['inherited']);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($owningClass->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($owningClass->table['name'], $dqlAlias);
|
||||
} else {
|
||||
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
}
|
||||
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
$columnAlias = $this->getSQLColumnAlias($srcColumn);
|
||||
|
||||
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
|
||||
|
||||
$columnAlias = $this->getSqlColumnAlias($srcColumn);
|
||||
$sql .= ", $sqlTableAlias." . $srcColumn . ' AS ' . $columnAlias;
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
|
||||
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -586,24 +584,20 @@ class SqlWalker implements TreeWalker
|
||||
} else {
|
||||
// Add foreign key columns to SQL, if necessary
|
||||
if ($addMetaColumns) {
|
||||
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
$columnAlias = $this->getSQLColumnAlias($srcColumn);
|
||||
|
||||
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
|
||||
|
||||
$columnAlias = $this->getSqlColumnAlias($srcColumn);
|
||||
$sql .= ', ' . $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
|
||||
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql .= implode(', ', $sqlSelectExpressions);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
@@ -628,7 +622,7 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
. $this->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
|
||||
if ($class->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
|
||||
@@ -744,8 +738,8 @@ class SqlWalker implements TreeWalker
|
||||
$targetClass = $this->_em->getClassMetadata($relation['targetEntity']);
|
||||
$sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']);
|
||||
$targetTableName = $targetClass->getQuotedTableName($this->_platform);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name'], $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable);
|
||||
$targetTableAlias = $this->getSqlTableAlias($targetClass->table['name'], $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable);
|
||||
|
||||
// Ensure we got the owning side, since it has all mapping info
|
||||
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
|
||||
@@ -756,21 +750,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
}
|
||||
|
||||
if ($joinVarDecl->indexBy) {
|
||||
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
|
||||
$this->_rsm->addIndexBy(
|
||||
$joinVarDecl->indexBy->simpleStateFieldPathExpression->identificationVariable,
|
||||
$joinVarDecl->indexBy->simpleStateFieldPathExpression->field
|
||||
);
|
||||
} else if (isset($relation['indexBy'])) {
|
||||
$this->_rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
|
||||
}
|
||||
|
||||
// This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
|
||||
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
|
||||
// The owning side is necessary at this point because only it contains the JoinColumn information.
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
|
||||
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
@@ -778,25 +758,21 @@ class SqlWalker implements TreeWalker
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($relation['isOwningSide']) {
|
||||
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
$sql .= $sourceTableAlias . '.' . $sourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
|
||||
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
|
||||
$sql .= $sourceTableAlias . '.' . $sourceColumn
|
||||
. ' = '
|
||||
. $targetTableAlias . '.' . $quotedTargetColumn;
|
||||
} else {
|
||||
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
|
||||
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
|
||||
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn
|
||||
. ' = '
|
||||
. $targetTableAlias . '.' . $sourceColumn;
|
||||
}
|
||||
}
|
||||
} else if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) {
|
||||
// Join relation table
|
||||
$joinTable = $assoc['joinTable'];
|
||||
$joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
|
||||
$joinTableAlias = $this->getSqlTableAlias($joinTable['name'], $joinedDqlAlias);
|
||||
$sql .= $sourceClass->getQuotedJoinTableName($assoc, $this->_platform) . ' ' . $joinTableAlias . ' ON ';
|
||||
|
||||
$first = true;
|
||||
@@ -804,25 +780,17 @@ class SqlWalker implements TreeWalker
|
||||
foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$sourceColumn])) {
|
||||
$quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
$sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform)
|
||||
. ' = '
|
||||
. $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
} else {
|
||||
foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
$sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform)
|
||||
. ' = '
|
||||
. $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,25 +804,17 @@ class SqlWalker implements TreeWalker
|
||||
foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
$sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform)
|
||||
. ' = '
|
||||
. $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
} else {
|
||||
foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$sourceColumn])) {
|
||||
$quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$sourceColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
$sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$sourceColumn], $this->_platform)
|
||||
. ' = '
|
||||
. $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -879,60 +839,6 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a CoalesceExpression AST node and generates the corresponding SQL.
|
||||
*
|
||||
* @param CoalesceExpression $coalesceExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkCoalesceExpression($coalesceExpression)
|
||||
{
|
||||
$sql = 'COALESCE(';
|
||||
|
||||
$scalarExpressions = array();
|
||||
|
||||
foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
|
||||
$scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
|
||||
}
|
||||
|
||||
$sql .= implode(', ', $scalarExpressions) . ')';
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public function walkCaseExpression($expression)
|
||||
{
|
||||
switch (true) {
|
||||
case ($expression instanceof AST\CoalesceExpression):
|
||||
return $this->walkCoalesceExpression($expression);
|
||||
|
||||
case ($expression instanceof AST\NullIfExpression):
|
||||
return $this->walkNullIfExpression($expression);
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a NullIfExpression AST node and generates the corresponding SQL.
|
||||
*
|
||||
* @param NullIfExpression $nullIfExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkNullIfExpression($nullIfExpression)
|
||||
{
|
||||
$firstExpression = is_string($nullIfExpression->firstExpression)
|
||||
? $this->_conn->quote($nullIfExpression->firstExpression)
|
||||
: $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
|
||||
|
||||
$secondExpression = is_string($nullIfExpression->secondExpression)
|
||||
? $this->_conn->quote($nullIfExpression->secondExpression)
|
||||
: $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
|
||||
|
||||
return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a SelectExpression AST node and generates the corresponding SQL.
|
||||
@@ -964,10 +870,10 @@ class SqlWalker implements TreeWalker
|
||||
$tableName = $class->getTableName();
|
||||
}
|
||||
|
||||
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($tableName, $dqlAlias);
|
||||
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
|
||||
|
||||
$columnAlias = $this->getSQLColumnAlias($columnName);
|
||||
$columnAlias = $this->getSqlColumnAlias($columnName);
|
||||
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
@@ -1016,12 +922,12 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
} else if (
|
||||
}
|
||||
else if (
|
||||
$expr instanceof AST\SimpleArithmeticExpression ||
|
||||
$expr instanceof AST\ArithmeticTerm ||
|
||||
$expr instanceof AST\ArithmeticFactor ||
|
||||
$expr instanceof AST\ArithmeticPrimary ||
|
||||
$expr instanceof AST\Literal
|
||||
$expr instanceof AST\ArithmeticPrimary
|
||||
) {
|
||||
if ( ! $selectExpression->fieldIdentificationVariable) {
|
||||
$resultAlias = $this->_scalarResultCounter++;
|
||||
@@ -1030,32 +936,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
$columnAlias = 'sclr' . $this->_aliasCounter++;
|
||||
|
||||
if ($expr instanceof AST\Literal) {
|
||||
$sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias;
|
||||
} else {
|
||||
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
|
||||
}
|
||||
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
} else if (
|
||||
$expr instanceof AST\NullIfExpression ||
|
||||
$expr instanceof AST\CoalesceExpression ||
|
||||
$expr instanceof AST\CaseExpression
|
||||
) {
|
||||
if ( ! $selectExpression->fieldIdentificationVariable) {
|
||||
$resultAlias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
$resultAlias = $selectExpression->fieldIdentificationVariable;
|
||||
}
|
||||
|
||||
$columnAlias = 'sclr' . $this->_aliasCounter++;
|
||||
|
||||
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
|
||||
|
||||
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
@@ -1092,8 +973,8 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
|
||||
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
|
||||
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($tableName, $dqlAlias);
|
||||
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
|
||||
$sql .= $sqlTableAlias . '.' . $class->getQuotedColumnName($fieldName, $this->_platform)
|
||||
. ' AS ' . $columnAlias;
|
||||
|
||||
@@ -1108,7 +989,7 @@ class SqlWalker implements TreeWalker
|
||||
if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
|
||||
foreach ($class->subClasses as $subClassName) {
|
||||
$subClass = $this->_em->getClassMetadata($subClassName);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($subClass->table['name'], $dqlAlias);
|
||||
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
|
||||
if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
|
||||
continue;
|
||||
@@ -1116,7 +997,7 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
|
||||
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
|
||||
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
|
||||
$sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform)
|
||||
. ' AS ' . $columnAlias;
|
||||
|
||||
@@ -1130,7 +1011,7 @@ class SqlWalker implements TreeWalker
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
$columnAlias = $this->getSQLColumnAlias($srcColumn);
|
||||
$columnAlias = $this->getSqlColumnAlias($srcColumn);
|
||||
$sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
|
||||
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
|
||||
}
|
||||
@@ -1197,7 +1078,7 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
. $this->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
|
||||
if ($class->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
|
||||
@@ -1284,7 +1165,7 @@ class SqlWalker implements TreeWalker
|
||||
} else {
|
||||
// IdentificationVariable
|
||||
$class = $this->_queryComponents[$expr]['metadata'];
|
||||
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
|
||||
$tableAlias = $this->getSqlTableAlias($class->getTableName(), $expr);
|
||||
$first = true;
|
||||
|
||||
foreach ($class->identifier as $identifier) {
|
||||
@@ -1316,25 +1197,9 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkGroupByClause($groupByClause)
|
||||
{
|
||||
$sql = '';
|
||||
foreach ($groupByClause->groupByItems AS $groupByItem) {
|
||||
if (is_string($groupByItem)) {
|
||||
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
|
||||
if ($sql != '') {
|
||||
$sql .= ', ';
|
||||
}
|
||||
$groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
|
||||
$groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD;
|
||||
$sql .= $this->walkGroupByItem($groupByItem);
|
||||
}
|
||||
} else {
|
||||
if ($sql != '') {
|
||||
$sql .= ', ';
|
||||
}
|
||||
$sql .= $this->walkGroupByItem($groupByItem);
|
||||
}
|
||||
}
|
||||
return ' GROUP BY ' . $sql;
|
||||
return ' GROUP BY ' . implode(
|
||||
', ', array_map(array($this, 'walkGroupByItem'), $groupByClause->groupByItems)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1360,7 +1225,7 @@ class SqlWalker implements TreeWalker
|
||||
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform);
|
||||
|
||||
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
|
||||
$this->setSqlTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
|
||||
|
||||
$this->_rootAliases[] = $deleteClause->aliasIdentificationVariable;
|
||||
|
||||
@@ -1379,7 +1244,7 @@ class SqlWalker implements TreeWalker
|
||||
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform);
|
||||
|
||||
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
|
||||
$this->setSqlTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
|
||||
|
||||
$this->_rootAliases[] = $updateClause->aliasIdentificationVariable;
|
||||
|
||||
@@ -1543,8 +1408,8 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$targetTableAlias = $this->getSqlTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
|
||||
$sql .= $targetClass->getQuotedTableName($this->_platform)
|
||||
. ' ' . $targetTableAlias . ' WHERE ';
|
||||
@@ -1578,9 +1443,9 @@ class SqlWalker implements TreeWalker
|
||||
$joinTable = $owningAssoc['joinTable'];
|
||||
|
||||
// SQL table aliases
|
||||
$joinTableAlias = $this->getSQLTableAlias($joinTable['name']);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$joinTableAlias = $this->getSqlTableAlias($joinTable['name']);
|
||||
$targetTableAlias = $this->getSqlTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
|
||||
|
||||
// join to target table
|
||||
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform)
|
||||
@@ -1737,10 +1602,6 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= $this->_conn->quote($class->discriminatorValue);
|
||||
} else {
|
||||
$discrMap = array_flip($class->discriminatorMap);
|
||||
if (!isset($discrMap[$entityClassName])) {
|
||||
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
|
||||
}
|
||||
|
||||
$sql .= $this->_conn->quote($discrMap[$entityClassName]);
|
||||
}
|
||||
|
||||
@@ -1917,12 +1778,6 @@ class SqlWalker implements TreeWalker
|
||||
public function walkArithmeticTerm($term)
|
||||
{
|
||||
if (is_string($term)) {
|
||||
if (isset($this->_queryComponents[$term])) {
|
||||
$columnName = $this->_queryComponents[$term]['token']['value'];
|
||||
|
||||
return $this->_scalarResultAliasMap[$columnName];
|
||||
}
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,92 +217,25 @@ class QueryBuilder
|
||||
->setFirstResult($this->_firstResult)
|
||||
->setMaxResults($this->_maxResults);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the FIRST root alias of the query. This is the first entity alias involved
|
||||
* Gets the root alias of the query. This is the first entity alias involved
|
||||
* in the construction of the query.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
*
|
||||
* echo $qb->getRootAlias(); // u
|
||||
* echo $qb->getRootAlias(); // u
|
||||
* </code>
|
||||
*
|
||||
* @deprecated Please use $qb->getRootAliases() instead.
|
||||
* @return string $rootAlias
|
||||
* @todo Rename/Refactor: getRootAliases(), there can be multiple roots!
|
||||
*/
|
||||
public function getRootAlias()
|
||||
{
|
||||
$aliases = $this->getRootAliases();
|
||||
return $aliases[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root aliases of the query. This is the entity aliases involved
|
||||
* in the construction of the query.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
*
|
||||
* $qb->getRootAliases(); // array('u')
|
||||
* </code>
|
||||
*
|
||||
* @return array $rootAliases
|
||||
*/
|
||||
public function getRootAliases()
|
||||
{
|
||||
$aliases = array();
|
||||
|
||||
foreach ($this->_dqlParts['from'] as &$fromClause) {
|
||||
if (is_string($fromClause)) {
|
||||
$spacePos = strrpos($fromClause, ' ');
|
||||
$from = substr($fromClause, 0, $spacePos);
|
||||
$alias = substr($fromClause, $spacePos + 1);
|
||||
|
||||
$fromClause = new Query\Expr\From($from, $alias);
|
||||
}
|
||||
|
||||
$aliases[] = $fromClause->getAlias();
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root entities of the query. This is the entity aliases involved
|
||||
* in the construction of the query.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
*
|
||||
* $qb->getRootEntities(); // array('User')
|
||||
* </code>
|
||||
*
|
||||
* @return array $rootEntities
|
||||
*/
|
||||
public function getRootEntities()
|
||||
{
|
||||
$entities = array();
|
||||
|
||||
foreach ($this->_dqlParts['from'] as &$fromClause) {
|
||||
if (is_string($fromClause)) {
|
||||
$spacePos = strrpos($fromClause, ' ');
|
||||
$from = substr($fromClause, 0, $spacePos);
|
||||
$alias = substr($fromClause, $spacePos + 1);
|
||||
|
||||
$fromClause = new Query\Expr\From($from, $alias);
|
||||
}
|
||||
|
||||
$entities[] = $fromClause->getFrom();
|
||||
}
|
||||
|
||||
return $entities;
|
||||
return $this->_dqlParts['from'][0]->getAlias();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,13 +256,10 @@ class QueryBuilder
|
||||
*/
|
||||
public function setParameter($key, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = Query\ParameterTypeInferer::inferType($value);
|
||||
if ($type !== null) {
|
||||
$this->_paramTypes[$key] = $type;
|
||||
}
|
||||
|
||||
$this->_paramTypes[$key] = $type;
|
||||
$this->_params[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -352,13 +282,8 @@ class QueryBuilder
|
||||
*/
|
||||
public function setParameters(array $params, array $types = array())
|
||||
{
|
||||
foreach ($params as $key => $value) {
|
||||
if (isset($types[$key])) {
|
||||
$this->setParameter($key, $value, $types[$key]);
|
||||
} else {
|
||||
$this->setParameter($key, $value);
|
||||
}
|
||||
}
|
||||
$this->_paramTypes = $types;
|
||||
$this->_params = $params;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -443,29 +368,9 @@ class QueryBuilder
|
||||
public function add($dqlPartName, $dqlPart, $append = false)
|
||||
{
|
||||
$isMultiple = is_array($this->_dqlParts[$dqlPartName]);
|
||||
|
||||
// This is introduced for backwards compatibility reasons.
|
||||
// TODO: Remove for 3.0
|
||||
if ($dqlPartName == 'join') {
|
||||
$newDqlPart = array();
|
||||
foreach ($dqlPart AS $k => $v) {
|
||||
if (is_numeric($k)) {
|
||||
$newDqlPart[$this->getRootAlias()] = $v;
|
||||
} else {
|
||||
$newDqlPart[$k] = $v;
|
||||
}
|
||||
}
|
||||
$dqlPart = $newDqlPart;
|
||||
}
|
||||
|
||||
if ($append && $isMultiple) {
|
||||
if (is_array($dqlPart)) {
|
||||
$key = key($dqlPart);
|
||||
|
||||
$this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
|
||||
} else {
|
||||
$this->_dqlParts[$dqlPartName][] = $dqlPart;
|
||||
}
|
||||
$this->_dqlParts[$dqlPartName][] = $dqlPart;
|
||||
} else {
|
||||
$this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
|
||||
}
|
||||
@@ -618,12 +523,11 @@ class QueryBuilder
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @param string $indexBy The index for the join
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
|
||||
public function join($join, $alias, $conditionType = null, $condition = null)
|
||||
{
|
||||
return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy);
|
||||
return $this->innerJoin($join, $alias, $conditionType, $condition);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,18 +547,12 @@ class QueryBuilder
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @param string $indexBy The index for the join
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
|
||||
public function innerJoin($join, $alias, $conditionType = null, $condition = null)
|
||||
{
|
||||
$rootAlias = substr($join, 0, strpos($join, '.'));
|
||||
if (!in_array($rootAlias, $this->getRootAliases())) {
|
||||
$rootAlias = $this->getRootAlias();
|
||||
}
|
||||
|
||||
return $this->add('join', array(
|
||||
$rootAlias => new Expr\Join(Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
|
||||
return $this->add('join', new Expr\Join(
|
||||
Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition
|
||||
), true);
|
||||
}
|
||||
|
||||
@@ -676,18 +574,12 @@ class QueryBuilder
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @param string $indexBy The index for the join
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
|
||||
public function leftJoin($join, $alias, $conditionType = null, $condition = null)
|
||||
{
|
||||
$rootAlias = substr($join, 0, strpos($join, '.'));
|
||||
if (!in_array($rootAlias, $this->getRootAliases())) {
|
||||
$rootAlias = $this->getRootAlias();
|
||||
}
|
||||
|
||||
return $this->add('join', array(
|
||||
$rootAlias => new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
|
||||
return $this->add('join', new Expr\Join(
|
||||
Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition
|
||||
), true);
|
||||
}
|
||||
|
||||
@@ -737,7 +629,7 @@ class QueryBuilder
|
||||
*/
|
||||
public function where($predicates)
|
||||
{
|
||||
if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) {
|
||||
if ( ! (func_num_args() == 1 && ($predicates instanceof Expr\Andx || $predicates instanceof Expr\Orx))) {
|
||||
$predicates = new Expr\Andx(func_get_args());
|
||||
}
|
||||
|
||||
@@ -973,36 +865,14 @@ class QueryBuilder
|
||||
|
||||
private function _getDQLForSelect()
|
||||
{
|
||||
$dql = 'SELECT' . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
|
||||
|
||||
$fromParts = $this->getDQLPart('from');
|
||||
$joinParts = $this->getDQLPart('join');
|
||||
$fromClauses = array();
|
||||
|
||||
// Loop through all FROM clauses
|
||||
if ( ! empty($fromParts)) {
|
||||
$dql .= ' FROM ';
|
||||
|
||||
foreach ($fromParts as $from) {
|
||||
$fromClause = (string) $from;
|
||||
|
||||
if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) {
|
||||
foreach ($joinParts[$from->getAlias()] as $join) {
|
||||
$fromClause .= ' ' . ((string) $join);
|
||||
}
|
||||
}
|
||||
|
||||
$fromClauses[] = $fromClause;
|
||||
}
|
||||
}
|
||||
|
||||
$dql .= implode(', ', $fromClauses)
|
||||
return 'SELECT'
|
||||
. $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '))
|
||||
. $this->_getReducedDQLQueryPart('from', array('pre' => ' FROM ', 'separator' => ', '))
|
||||
. $this->_getReducedDQLQueryPart('join', array('pre' => ' ', 'separator' => ' '))
|
||||
. $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
|
||||
. $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', '))
|
||||
. $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING '))
|
||||
. $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
|
||||
|
||||
return $dql;
|
||||
}
|
||||
|
||||
private function _getReducedDQLQueryPart($queryPartName, $options = array())
|
||||
|
||||
@@ -78,10 +78,6 @@ class ConvertMappingCommand extends Console\Command\Command
|
||||
'num-spaces', null, InputOption::VALUE_OPTIONAL,
|
||||
'Defines the number of indentation spaces', 4
|
||||
),
|
||||
new InputOption(
|
||||
'namespace', null, InputOption::VALUE_OPTIONAL,
|
||||
'Defines a namespace for the generated entity classes, if converted from database.'
|
||||
),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
Convert mapping information between supported formats.
|
||||
@@ -111,17 +107,11 @@ EOT
|
||||
$em = $this->getHelper('em')->getEntityManager();
|
||||
|
||||
if ($input->getOption('from-database') === true) {
|
||||
$databaseDriver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
|
||||
$em->getConnection()->getSchemaManager()
|
||||
);
|
||||
|
||||
$em->getConfiguration()->setMetadataDriverImpl(
|
||||
$databaseDriver
|
||||
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
|
||||
$em->getConnection()->getSchemaManager()
|
||||
)
|
||||
);
|
||||
|
||||
if (($namespace = $input->getOption('namespace')) !== null) {
|
||||
$databaseDriver->setNamespace($namespace);
|
||||
}
|
||||
}
|
||||
|
||||
$cmf = new DisconnectedClassMetadataFactory();
|
||||
@@ -147,7 +137,8 @@ EOT
|
||||
|
||||
$toType = strtolower($input->getArgument('to-type'));
|
||||
|
||||
$exporter = $this->getExporter($toType, $destPath);
|
||||
$cme = new ClassMetadataExporter();
|
||||
$exporter = $cme->getExporter($toType, $destPath);
|
||||
$exporter->setOverwriteExistingFiles( ($input->getOption('force') !== false) );
|
||||
|
||||
if ($toType == 'annotation') {
|
||||
@@ -176,11 +167,4 @@ EOT
|
||||
$output->write('No Metadata Classes to process.' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getExporter($toType, $destPath)
|
||||
{
|
||||
$cme = new ClassMetadataExporter();
|
||||
|
||||
return $cme->getExporter($toType, $destPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
/**
|
||||
* Show information about mapped entities
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.1
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class InfoCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('orm:info')
|
||||
->setDescription('Show basic information about all mapped entities')
|
||||
->setHelp(<<<EOT
|
||||
The <info>doctrine:mapping:info</info> shows basic information about which
|
||||
entities exist and possibly if their mapping information contains errors or
|
||||
not.
|
||||
EOT
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/* @var $entityManager Doctrine\ORM\EntityManager */
|
||||
$entityManager = $this->getHelper('em')->getEntityManager();
|
||||
|
||||
$entityClassNames = $entityManager->getConfiguration()
|
||||
->getMetadataDriverImpl()
|
||||
->getAllClassNames();
|
||||
|
||||
if (!$entityClassNames) {
|
||||
throw new \Exception(
|
||||
'You do not have any mapped Doctrine ORM entities according to the current configuration. '.
|
||||
'If you have entities or mapping files you should check your mapping configuration for errors.'
|
||||
);
|
||||
}
|
||||
|
||||
$output->writeln(sprintf("Found <info>%d</info> mapped entities:", count($entityClassNames)));
|
||||
|
||||
foreach ($entityClassNames as $entityClassName) {
|
||||
try {
|
||||
$cm = $entityManager->getClassMetadata($entityClassName);
|
||||
$output->writeln(sprintf("<info>[OK]</info> %s", $entityClassName));
|
||||
} catch (MappingException $e) {
|
||||
$output->writeln("<error>[FAIL]</error> ".$entityClassName);
|
||||
$output->writeln(sprintf("<comment>%s</comment>", $e->getMessage()));
|
||||
$output->writeln('');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ EOT
|
||||
|
||||
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
|
||||
{
|
||||
$output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL);
|
||||
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
|
||||
|
||||
if ($input->getOption('dump-sql') === true) {
|
||||
$sqls = $schemaTool->getCreateSchemaSql($metadatas);
|
||||
|
||||
@@ -92,7 +92,7 @@ EOT
|
||||
}
|
||||
$output->write('Database schema dropped successfully!' . PHP_EOL);
|
||||
} else {
|
||||
$output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL);
|
||||
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
|
||||
|
||||
if ($isFullDatabaseDrop) {
|
||||
$sqls = $schemaTool->getDropDatabaseSQL();
|
||||
|
||||
@@ -28,8 +28,7 @@ use Symfony\Component\Console\Input\InputArgument,
|
||||
Doctrine\ORM\Tools\SchemaTool;
|
||||
|
||||
/**
|
||||
* Command to generate the SQL needed to update the database schema to match
|
||||
* the current mapping information.
|
||||
* Command to update the database schema for a set of classes based on their mappings.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
@@ -39,58 +38,37 @@ use Symfony\Component\Console\Input\InputArgument,
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Ryan Weaver <ryan@thatsquality.com>
|
||||
*/
|
||||
class UpdateCommand extends AbstractCommand
|
||||
{
|
||||
protected $name = 'orm:schema-tool:update';
|
||||
|
||||
/**
|
||||
* @see Console\Command\Command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName($this->name)
|
||||
->setName('orm:schema-tool:update')
|
||||
->setDescription(
|
||||
'Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.'
|
||||
'Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.'
|
||||
)
|
||||
->setDefinition(array(
|
||||
new InputOption(
|
||||
'complete', null, InputOption::VALUE_NONE,
|
||||
'If defined, all assets of the database which are not relevant to the current metadata will be dropped.'
|
||||
),
|
||||
|
||||
new InputOption(
|
||||
'dump-sql', null, InputOption::VALUE_NONE,
|
||||
'Dumps the generated SQL statements to the screen (does not execute them).'
|
||||
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
|
||||
),
|
||||
new InputOption(
|
||||
'force', null, InputOption::VALUE_NONE,
|
||||
'Causes the generated SQL statements to be physically executed against your database.'
|
||||
"Don't ask for the incremental update of the database, but force the operation to run."
|
||||
),
|
||||
));
|
||||
|
||||
$fullName = $this->getName();
|
||||
$this->setHelp(<<<EOT
|
||||
The <info>$fullName</info> command generates the SQL needed to
|
||||
synchronize the database schema with the current mapping metadata of the
|
||||
default entity manager.
|
||||
|
||||
For example, if you add metadata for a new column to an entity, this command
|
||||
would generate and output the SQL needed to add the new column to the database:
|
||||
|
||||
<info>$fullName --dump-sql</info>
|
||||
|
||||
Alternatively, you can execute the generated queries:
|
||||
|
||||
<info>$fullName --force</info>
|
||||
|
||||
Finally, be aware that if the <info>--complete</info> option is passed, this
|
||||
task will drop all database assets (e.g. tables, etc) that are *not* described
|
||||
by the current metadata. In other words, without this option, this task leaves
|
||||
untouched any "extra" tables that exist in the database, but which aren't
|
||||
described by any metadata.
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.
|
||||
Beware that if --complete is not defined, it will do a save update, which does not delete any tables, sequences or affected foreign keys.
|
||||
If defined, all assets of the database which are not relevant to the current metadata are dropped by this command.
|
||||
EOT
|
||||
);
|
||||
}
|
||||
@@ -100,36 +78,26 @@ EOT
|
||||
// Defining if update is complete or not (--complete not defined means $saveMode = true)
|
||||
$saveMode = ($input->getOption('complete') !== true);
|
||||
|
||||
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
|
||||
if (0 == count($sqls)) {
|
||||
$output->writeln('Nothing to update - your database is already in sync with the current entity metadata.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$dumpSql = (true === $input->getOption('dump-sql'));
|
||||
$force = (true === $input->getOption('force'));
|
||||
if ($dumpSql && $force) {
|
||||
throw new \InvalidArgumentException('You can pass either the --dump-sql or the --force option (but not both simultaneously).');
|
||||
}
|
||||
|
||||
if ($dumpSql) {
|
||||
$output->writeln(implode(';' . PHP_EOL, $sqls));
|
||||
} else if ($force) {
|
||||
$output->writeln('Updating database schema...');
|
||||
if ($input->getOption('dump-sql') === true) {
|
||||
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
|
||||
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);
|
||||
} else if ($input->getOption('force') === true) {
|
||||
$output->write('Updating database schema...' . PHP_EOL);
|
||||
$schemaTool->updateSchema($metadatas, $saveMode);
|
||||
$output->writeln(sprintf('Database schema updated successfully! "<info>%s</info>" queries were executed', count($sqls)));
|
||||
$output->write('Database schema updated successfully!' . PHP_EOL);
|
||||
} else {
|
||||
$output->writeln('<comment>ATTENTION</comment>: This operation should not be executed in a production environment.');
|
||||
$output->writeln(' Use the incremental update to detect changes during development and use');
|
||||
$output->writeln(' the SQL DDL provided to manually update your database in production.');
|
||||
$output->writeln('');
|
||||
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL);
|
||||
$output->write('Use the incremental update to detect changes during development and use' . PHP_EOL);
|
||||
$output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL);
|
||||
|
||||
$output->writeln(sprintf('The Schema-Tool would execute <info>"%s"</info> queries to update the database.', count($sqls)));
|
||||
$output->writeln('Please run the operation by passing one of the following options:');
|
||||
|
||||
$output->writeln(sprintf(' <info>%s --force</info> to execute the command', $this->getName()));
|
||||
$output->writeln(sprintf(' <info>%s --dump-sql</info> to dump the SQL statements to the screen', $this->getName()));
|
||||
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
|
||||
|
||||
if (count($sqls)) {
|
||||
$output->write('Schema-Tool would execute ' . count($sqls) . ' queries to update the database.' . PHP_EOL);
|
||||
$output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL);
|
||||
} else {
|
||||
$output->write('Nothing to update. The database is in sync with the current entity metadata.' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ class ConsoleRunner
|
||||
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\InfoCommand()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -70,10 +70,10 @@ class ConvertDoctrine1Schema
|
||||
if (is_dir($path)) {
|
||||
$files = glob($path . '/*.yml');
|
||||
foreach ($files as $file) {
|
||||
$schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($file));
|
||||
$schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::load($file));
|
||||
}
|
||||
} else {
|
||||
$schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($path));
|
||||
$schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::load($path));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,17 +52,6 @@ class DisconnectedClassMetadataFactory extends ClassMetadataFactory
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate runtime metadata is correctly defined.
|
||||
*
|
||||
* @param ClassMetadata $class
|
||||
* @param ClassMetadata $parent
|
||||
*/
|
||||
protected function validateRuntimeMetadata($class, $parent)
|
||||
{
|
||||
// validate nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -91,8 +91,6 @@ class EntityGenerator
|
||||
|
||||
<namespace>
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
<entityAnnotation>
|
||||
<entityClassName>
|
||||
{
|
||||
@@ -103,7 +101,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
'/**
|
||||
* <description>
|
||||
*
|
||||
* @return <variableType>
|
||||
* @return <variableType>$<variableName>
|
||||
*/
|
||||
public function <methodName>()
|
||||
{
|
||||
@@ -148,18 +146,11 @@ public function <methodName>()
|
||||
}
|
||||
';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
|
||||
$this->_annotationsPrefix = 'ORM\\';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and write entity classes for the given array of ClassMetadataInfo instances
|
||||
*
|
||||
* @param array $metadatas
|
||||
* @param string $outputDirectory
|
||||
* @param string $outputDirectory
|
||||
* @return void
|
||||
*/
|
||||
public function generate(array $metadatas, $outputDirectory)
|
||||
@@ -173,7 +164,7 @@ public function <methodName>()
|
||||
* Generated and write entity class to disk for the given ClassMetadataInfo instance
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param string $outputDirectory
|
||||
* @param string $outputDirectory
|
||||
* @return void
|
||||
*/
|
||||
public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
|
||||
@@ -188,11 +179,11 @@ public function <methodName>()
|
||||
$this->_isNew = !file_exists($path) || (file_exists($path) && $this->_regenerateEntityIfExists);
|
||||
|
||||
if ( ! $this->_isNew) {
|
||||
$this->_parseTokensInEntityFile(file_get_contents($path));
|
||||
$this->_parseTokensInEntityFile($path);
|
||||
}
|
||||
|
||||
if ($this->_backupExisting && file_exists($path)) {
|
||||
$backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
|
||||
$backupPath = dirname($path) . DIRECTORY_SEPARATOR . "~" . basename($path);
|
||||
if (!copy($path, $backupPath)) {
|
||||
throw new \RuntimeException("Attempt to backup overwritten entitiy file but copy operation failed.");
|
||||
}
|
||||
@@ -210,7 +201,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @return string $code
|
||||
*/
|
||||
public function generateEntityClass(ClassMetadataInfo $metadata)
|
||||
@@ -236,8 +227,8 @@ public function <methodName>()
|
||||
/**
|
||||
* Generate the updated code for the given ClassMetadataInfo and entity at path
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param string $path
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param string $path
|
||||
* @return string $code;
|
||||
*/
|
||||
public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
|
||||
@@ -254,7 +245,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Set the number of spaces the exported class should have
|
||||
*
|
||||
* @param integer $numSpaces
|
||||
* @param integer $numSpaces
|
||||
* @return void
|
||||
*/
|
||||
public function setNumSpaces($numSpaces)
|
||||
@@ -266,7 +257,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Set the extension to use when writing php files to disk
|
||||
*
|
||||
* @param string $extension
|
||||
* @param string $extension
|
||||
* @return void
|
||||
*/
|
||||
public function setExtension($extension)
|
||||
@@ -287,7 +278,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Set whether or not to generate annotations for the entity
|
||||
*
|
||||
* @param bool $bool
|
||||
* @param bool $bool
|
||||
* @return void
|
||||
*/
|
||||
public function setGenerateAnnotations($bool)
|
||||
@@ -302,16 +293,13 @@ public function <methodName>()
|
||||
*/
|
||||
public function setAnnotationPrefix($prefix)
|
||||
{
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
|
||||
return;
|
||||
}
|
||||
$this->_annotationsPrefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to try and update the entity if it already exists
|
||||
*
|
||||
* @param bool $bool
|
||||
* @param bool $bool
|
||||
* @return void
|
||||
*/
|
||||
public function setUpdateEntityIfExists($bool)
|
||||
@@ -412,46 +400,28 @@ public function <methodName>()
|
||||
|
||||
/**
|
||||
* @todo this won't work if there is a namespace in brackets and a class outside of it.
|
||||
* @param string $src
|
||||
* @param string $path
|
||||
*/
|
||||
private function _parseTokensInEntityFile($src)
|
||||
private function _parseTokensInEntityFile($path)
|
||||
{
|
||||
$tokens = token_get_all($src);
|
||||
$tokens = token_get_all(file_get_contents($path));
|
||||
$lastSeenNamespace = "";
|
||||
$lastSeenClass = false;
|
||||
|
||||
$inNamespace = false;
|
||||
$inClass = false;
|
||||
|
||||
for ($i = 0; $i < count($tokens); $i++) {
|
||||
$token = $tokens[$i];
|
||||
if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($inNamespace) {
|
||||
if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
|
||||
$lastSeenNamespace .= $token[1];
|
||||
} else if (is_string($token) && in_array($token, array(';', '{'))) {
|
||||
$inNamespace = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($inClass) {
|
||||
$inClass = false;
|
||||
$lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
|
||||
if ($token[0] == T_NAMESPACE) {
|
||||
$lastSeenNamespace = $tokens[$i+2][1] . "\\";
|
||||
} else if ($token[0] == T_NS_SEPARATOR) {
|
||||
$lastSeenNamespace .= $tokens[$i+1][1] . "\\";
|
||||
} else if ($token[0] == T_CLASS) {
|
||||
$lastSeenClass = $lastSeenNamespace . $tokens[$i+2][1];
|
||||
$this->_staticReflection[$lastSeenClass]['properties'] = array();
|
||||
$this->_staticReflection[$lastSeenClass]['methods'] = array();
|
||||
}
|
||||
|
||||
if ($token[0] == T_NAMESPACE) {
|
||||
$lastSeenNamespace = "";
|
||||
$inNamespace = true;
|
||||
} else if ($token[0] == T_CLASS) {
|
||||
$inClass = true;
|
||||
} else if ($token[0] == T_FUNCTION) {
|
||||
if ($tokens[$i+2][0] == T_STRING) {
|
||||
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
|
||||
} else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
|
||||
} else if ($tokens[$i+2][0] == T_AMPERSAND && $tokens[$i+3][0] == T_STRING) {
|
||||
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
|
||||
}
|
||||
} else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
|
||||
@@ -462,14 +432,6 @@ public function <methodName>()
|
||||
|
||||
private function _hasProperty($property, ClassMetadataInfo $metadata)
|
||||
{
|
||||
if ($this->_extendsClass()) {
|
||||
// don't generate property if its already on the base class.
|
||||
$reflClass = new \ReflectionClass($this->_getClassToExtend());
|
||||
if ($reflClass->hasProperty($property)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
isset($this->_staticReflection[$metadata->name]) &&
|
||||
in_array($property, $this->_staticReflection[$metadata->name]['properties'])
|
||||
@@ -478,14 +440,6 @@ public function <methodName>()
|
||||
|
||||
private function _hasMethod($method, ClassMetadataInfo $metadata)
|
||||
{
|
||||
if ($this->_extendsClass()) {
|
||||
// don't generate method if its already on the base class.
|
||||
$reflClass = new \ReflectionClass($this->_getClassToExtend());
|
||||
if ($reflClass->hasMethod($method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
isset($this->_staticReflection[$metadata->name]) &&
|
||||
in_array($method, $this->_staticReflection[$metadata->name]['methods'])
|
||||
@@ -548,7 +502,7 @@ public function <methodName>()
|
||||
}
|
||||
|
||||
if ($metadata->isMappedSuperclass) {
|
||||
$lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSuperClass';
|
||||
$lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSupperClass';
|
||||
} else {
|
||||
$lines[] = ' * @' . $this->_annotationsPrefix . 'Entity';
|
||||
}
|
||||
@@ -702,18 +656,11 @@ public function <methodName>()
|
||||
|
||||
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null)
|
||||
{
|
||||
if ($type == "add") {
|
||||
$addMethod = explode("\\", $typeHint);
|
||||
$addMethod = end($addMethod);
|
||||
$methodName = $type . $addMethod;
|
||||
} else {
|
||||
$methodName = $type . Inflector::classify($fieldName);
|
||||
}
|
||||
$methodName = $type . Inflector::classify($fieldName);
|
||||
|
||||
if ($this->_hasMethod($methodName, $metadata)) {
|
||||
return;
|
||||
}
|
||||
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
|
||||
|
||||
$var = sprintf('_%sMethodTemplate', $type);
|
||||
$template = self::$$var;
|
||||
@@ -746,7 +693,6 @@ public function <methodName>()
|
||||
if ($this->_hasMethod($methodName, $metadata)) {
|
||||
return;
|
||||
}
|
||||
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
|
||||
|
||||
$replacements = array(
|
||||
'<name>' => $this->_annotationsPrefix . $name,
|
||||
@@ -844,7 +790,7 @@ public function <methodName>()
|
||||
if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
|
||||
if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
|
||||
|
||||
$typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
|
||||
$typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
|
||||
}
|
||||
|
||||
if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
|
||||
@@ -898,7 +844,7 @@ public function <methodName>()
|
||||
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'OrderBy({';
|
||||
|
||||
foreach ($associationMapping['orderBy'] as $name => $direction) {
|
||||
$lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",';
|
||||
$lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",';
|
||||
}
|
||||
|
||||
$lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
|
||||
|
||||
@@ -120,30 +120,22 @@ class PhpExporter extends AbstractExporter
|
||||
|
||||
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
|
||||
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
|
||||
$method = 'mapOneToMany';
|
||||
$potentialAssociationMappingIndexes = array(
|
||||
'mappedBy',
|
||||
'orphanRemoval',
|
||||
'orderBy',
|
||||
$method = 'mapOneToMany';
|
||||
$oneToManyMappingArray = array(
|
||||
'mappedBy' => $associationMapping['mappedBy'],
|
||||
'orphanRemoval' => $associationMapping['orphanRemoval'],
|
||||
'orderBy' => $associationMapping['orderBy']
|
||||
);
|
||||
foreach ($potentialAssociationMappingIndexes as $index) {
|
||||
if (isset($associationMapping[$index])) {
|
||||
$oneToManyMappingArray[$index] = $associationMapping[$index];
|
||||
}
|
||||
}
|
||||
|
||||
$associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray);
|
||||
} else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) {
|
||||
$method = 'mapManyToMany';
|
||||
$potentialAssociationMappingIndexes = array(
|
||||
'mappedBy',
|
||||
'joinTable',
|
||||
'orderBy',
|
||||
$method = 'mapManyToMany';
|
||||
$manyToManyMappingArray = array(
|
||||
'mappedBy' => $associationMapping['mappedBy'],
|
||||
'joinTable' => $associationMapping['joinTable'],
|
||||
'orderBy' => $associationMapping['orderBy']
|
||||
);
|
||||
foreach ($potentialAssociationMappingIndexes as $index) {
|
||||
if (isset($associationMapping[$index])) {
|
||||
$manyToManyMappingArray[$index] = $associationMapping[$index];
|
||||
}
|
||||
}
|
||||
|
||||
$associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class YamlExporter extends AbstractExporter
|
||||
*
|
||||
* TODO: Should this code be pulled out in to a toArray() method in ClassMetadata
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @return mixed $exported
|
||||
*/
|
||||
public function exportClassMetadata(ClassMetadataInfo $metadata)
|
||||
@@ -84,9 +84,9 @@ class YamlExporter extends AbstractExporter
|
||||
if (isset($metadata->table['uniqueConstraints'])) {
|
||||
$array['uniqueConstraints'] = $metadata->table['uniqueConstraints'];
|
||||
}
|
||||
|
||||
|
||||
$fieldMappings = $metadata->fieldMappings;
|
||||
|
||||
|
||||
$ids = array();
|
||||
foreach ($fieldMappings as $name => $fieldMapping) {
|
||||
$fieldMapping['column'] = $fieldMapping['columnName'];
|
||||
@@ -94,7 +94,7 @@ class YamlExporter extends AbstractExporter
|
||||
$fieldMapping['columnName'],
|
||||
$fieldMapping['fieldName']
|
||||
);
|
||||
|
||||
|
||||
if ($fieldMapping['column'] == $name) {
|
||||
unset($fieldMapping['column']);
|
||||
}
|
||||
@@ -111,7 +111,7 @@ class YamlExporter extends AbstractExporter
|
||||
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
|
||||
$ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $this->_getIdGeneratorTypeString($metadata->generatorType);
|
||||
}
|
||||
|
||||
|
||||
if ($ids) {
|
||||
$array['fields'] = $ids;
|
||||
}
|
||||
@@ -145,7 +145,7 @@ class YamlExporter extends AbstractExporter
|
||||
'targetEntity' => $associationMapping['targetEntity'],
|
||||
'cascade' => $cascade,
|
||||
);
|
||||
|
||||
|
||||
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
$joinColumns = $associationMapping['joinColumns'];
|
||||
$newJoinColumns = array();
|
||||
@@ -164,7 +164,7 @@ class YamlExporter extends AbstractExporter
|
||||
'joinColumns' => $newJoinColumns,
|
||||
'orphanRemoval' => $associationMapping['orphanRemoval'],
|
||||
);
|
||||
|
||||
|
||||
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
|
||||
$array['oneToOne'][$name] = $associationMappingArray;
|
||||
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
|
||||
@@ -172,7 +172,7 @@ class YamlExporter extends AbstractExporter
|
||||
'mappedBy' => $associationMapping['mappedBy'],
|
||||
'inversedBy' => $associationMapping['inversedBy'],
|
||||
'orphanRemoval' => $associationMapping['orphanRemoval'],
|
||||
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
|
||||
'orderBy' => $associationMapping['orderBy']
|
||||
);
|
||||
|
||||
$associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray);
|
||||
@@ -181,10 +181,10 @@ class YamlExporter extends AbstractExporter
|
||||
$manyToManyMappingArray = array(
|
||||
'mappedBy' => $associationMapping['mappedBy'],
|
||||
'inversedBy' => $associationMapping['inversedBy'],
|
||||
'joinTable' => isset($associationMapping['joinTable']) ? $associationMapping['joinTable'] : null,
|
||||
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
|
||||
'joinTable' => $associationMapping['joinTable'],
|
||||
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
|
||||
);
|
||||
|
||||
|
||||
$associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray);
|
||||
$array['manyToMany'][$name] = $associationMappingArray;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ class SchemaTool
|
||||
|
||||
// Add a FK constraint on the ID column
|
||||
$table->addUnnamedForeignKeyConstraint(
|
||||
$this->_em->getClassMetadata($class->rootEntityName)->getQuotedTableName($this->_platform),
|
||||
$this->_em->getClassMetadata($class->rootEntityName)->getTableName(),
|
||||
array($columnName), array($columnName), array('onDelete' => 'CASCADE')
|
||||
);
|
||||
}
|
||||
@@ -199,22 +199,6 @@ class SchemaTool
|
||||
$this->_gatherRelationsSql($class, $table, $schema);
|
||||
}
|
||||
|
||||
$pkColumns = array();
|
||||
foreach ($class->identifier AS $identifierField) {
|
||||
if (isset($class->fieldMappings[$identifierField])) {
|
||||
$pkColumns[] = $class->getQuotedColumnName($identifierField, $this->_platform);
|
||||
} else if (isset($class->associationMappings[$identifierField])) {
|
||||
/* @var $assoc \Doctrine\ORM\Mapping\OneToOne */
|
||||
$assoc = $class->associationMappings[$identifierField];
|
||||
foreach ($assoc['joinColumns'] AS $joinColumn) {
|
||||
$pkColumns[] = $joinColumn['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$table->hasIndex('primary')) {
|
||||
$table->setPrimaryKey($pkColumns);
|
||||
}
|
||||
|
||||
if (isset($class->table['indexes'])) {
|
||||
foreach ($class->table['indexes'] AS $indexName => $indexData) {
|
||||
$table->addIndex($indexData['columns'], $indexName);
|
||||
@@ -301,11 +285,10 @@ class SchemaTool
|
||||
$pkColumns[] = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform);
|
||||
}
|
||||
}
|
||||
|
||||
// For now, this is a hack required for single table inheritence, since this method is called
|
||||
// twice by single table inheritence relations
|
||||
if(!$table->hasIndex('primary')) {
|
||||
//$table->setPrimaryKey($pkColumns);
|
||||
$table->setPrimaryKey($pkColumns);
|
||||
}
|
||||
|
||||
return $columns;
|
||||
@@ -426,47 +409,13 @@ class SchemaTool
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class metadata that is responsible for the definition of the referenced column name.
|
||||
*
|
||||
* Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its
|
||||
* not a simple field, go through all identifier field names that are associations recursivly and
|
||||
* find that referenced column name.
|
||||
*
|
||||
* TODO: Is there any way to make this code more pleasing?
|
||||
*
|
||||
* @param ClassMetadata $class
|
||||
* @param string $referencedColumnName
|
||||
* @return array(ClassMetadata, referencedFieldName)
|
||||
*/
|
||||
private function getDefiningClass($class, $referencedColumnName)
|
||||
{
|
||||
$referencedFieldName = $class->getFieldName($referencedColumnName);
|
||||
|
||||
if ($class->hasField($referencedFieldName)) {
|
||||
return array($class, $referencedFieldName);
|
||||
} else if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) {
|
||||
// it seems to be an entity as foreign key
|
||||
foreach ($class->getIdentifierFieldNames() AS $fieldName) {
|
||||
if ($class->hasAssociation($fieldName) && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) {
|
||||
return $this->getDefiningClass(
|
||||
$this->_em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']),
|
||||
$class->getSingleAssociationReferencedJoinColumnName($fieldName)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather columns and fk constraints that are required for one part of relationship.
|
||||
*
|
||||
* @param array $joinColumns
|
||||
* @param \Doctrine\DBAL\Schema\Table $theJoinTable
|
||||
* @param ClassMetadata $class
|
||||
* @param array $mapping
|
||||
* @param \Doctrine\ORM\Mapping\AssociationMapping $mapping
|
||||
* @param array $primaryKeyColumns
|
||||
* @param array $uniqueConstraints
|
||||
*/
|
||||
@@ -475,13 +424,12 @@ class SchemaTool
|
||||
$localColumns = array();
|
||||
$foreignColumns = array();
|
||||
$fkOptions = array();
|
||||
$foreignTableName = $class->getQuotedTableName($this->_platform);
|
||||
|
||||
foreach ($joinColumns as $joinColumn) {
|
||||
$columnName = $joinColumn['name'];
|
||||
list($definingClass, $referencedFieldName) = $this->getDefiningClass($class, $joinColumn['referencedColumnName']);
|
||||
$referencedFieldName = $class->getFieldName($joinColumn['referencedColumnName']);
|
||||
|
||||
if (!$definingClass) {
|
||||
if ( ! $class->hasField($referencedFieldName)) {
|
||||
throw new \Doctrine\ORM\ORMException(
|
||||
"Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ".
|
||||
$mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist."
|
||||
@@ -497,7 +445,7 @@ class SchemaTool
|
||||
// It might exist already if the foreign key is mapped into a regular
|
||||
// property as well.
|
||||
|
||||
$fieldMapping = $definingClass->getFieldMapping($referencedFieldName);
|
||||
$fieldMapping = $class->getFieldMapping($referencedFieldName);
|
||||
|
||||
$columnDef = null;
|
||||
if (isset($joinColumn['columnDefinition'])) {
|
||||
@@ -516,7 +464,9 @@ class SchemaTool
|
||||
$columnOptions['precision'] = $fieldMapping['precision'];
|
||||
}
|
||||
|
||||
$theJoinTable->addColumn($columnName, $fieldMapping['type'], $columnOptions);
|
||||
$theJoinTable->addColumn(
|
||||
$columnName, $class->getTypeOfColumn($joinColumn['referencedColumnName']), $columnOptions
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($joinColumn['unique']) && $joinColumn['unique'] == true) {
|
||||
@@ -533,7 +483,7 @@ class SchemaTool
|
||||
}
|
||||
|
||||
$theJoinTable->addUnnamedForeignKeyConstraint(
|
||||
$foreignTableName, $localColumns, $foreignColumns, $fkOptions
|
||||
$class->getTableName(), $localColumns, $foreignColumns, $fkOptions
|
||||
);
|
||||
}
|
||||
|
||||
@@ -592,62 +542,65 @@ class SchemaTool
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SQL to drop the tables defined by the passed classes.
|
||||
*
|
||||
*
|
||||
* @param array $classes
|
||||
* @return array
|
||||
*/
|
||||
public function getDropSchemaSQL(array $classes)
|
||||
{
|
||||
$visitor = new \Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector($this->_platform);
|
||||
$schema = $this->getSchemaFromMetadata($classes);
|
||||
|
||||
$sm = $this->_em->getConnection()->getSchemaManager();
|
||||
$fullSchema = $sm->createSchema();
|
||||
foreach ($fullSchema->getTables() AS $table) {
|
||||
if (!$schema->hasTable($table->getName())) {
|
||||
foreach ($table->getForeignKeys() AS $foreignKey) {
|
||||
/* @var $foreignKey \Doctrine\DBAL\Schema\ForeignKeyConstraint */
|
||||
if ($schema->hasTable($foreignKey->getForeignTableName())) {
|
||||
$visitor->acceptForeignKey($table, $foreignKey);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$visitor->acceptTable($table);
|
||||
foreach ($table->getForeignKeys() AS $foreignKey) {
|
||||
$visitor->acceptForeignKey($table, $foreignKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->_platform->supportsSequences()) {
|
||||
foreach ($schema->getSequences() AS $sequence) {
|
||||
$visitor->acceptSequence($sequence);
|
||||
}
|
||||
foreach ($schema->getTables() AS $table) {
|
||||
/* @var $sequence Table */
|
||||
if ($table->hasPrimaryKey()) {
|
||||
$columns = $table->getPrimaryKey()->getColumns();
|
||||
if (count($columns) == 1) {
|
||||
$checkSequence = $table->getName() . "_" . $columns[0] . "_seq";
|
||||
if ($fullSchema->hasSequence($checkSequence)) {
|
||||
$visitor->acceptSequence($fullSchema->getSequence($checkSequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = array();
|
||||
$orderedTables = array();
|
||||
|
||||
foreach ($classes AS $class) {
|
||||
if ($class->isIdGeneratorSequence() && !$class->isMappedSuperclass && $class->name == $class->rootEntityName && $this->_platform->supportsSequences()) {
|
||||
$sql[] = $this->_platform->getDropSequenceSQL($class->sequenceGeneratorDefinition['sequenceName']);
|
||||
}
|
||||
}
|
||||
|
||||
return $visitor->getQueries();
|
||||
$commitOrder = $this->_getCommitOrder($classes);
|
||||
$associationTables = $this->_getAssociationTables($commitOrder);
|
||||
|
||||
// Drop association tables first
|
||||
foreach ($associationTables as $associationTable) {
|
||||
if (!in_array($associationTable, $orderedTables)) {
|
||||
$orderedTables[] = $associationTable;
|
||||
}
|
||||
}
|
||||
|
||||
// Drop tables in reverse commit order
|
||||
for ($i = count($commitOrder) - 1; $i >= 0; --$i) {
|
||||
$class = $commitOrder[$i];
|
||||
|
||||
if (($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName)
|
||||
|| $class->isMappedSuperclass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($class->getTableName(), $orderedTables)) {
|
||||
$orderedTables[] = $class->getTableName();
|
||||
}
|
||||
}
|
||||
|
||||
$dropTablesSql = array();
|
||||
foreach ($orderedTables AS $tableName) {
|
||||
/* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */
|
||||
$foreignKeys = $sm->listTableForeignKeys($tableName);
|
||||
foreach ($foreignKeys AS $foreignKey) {
|
||||
$sql[] = $this->_platform->getDropForeignKeySQL($foreignKey, $tableName);
|
||||
}
|
||||
$dropTablesSql[] = $this->_platform->getDropTableSQL($tableName);
|
||||
}
|
||||
|
||||
return array_merge($sql, $dropTablesSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the database schema of the given classes by comparing the ClassMetadata
|
||||
* instances to the current database schema that is inspected. If $saveMode is set
|
||||
* to true the command is executed in the Database, else SQL is returned.
|
||||
* ins$tableNametances to the current database schema that is inspected.
|
||||
*
|
||||
* @param array $classes
|
||||
* @param boolean $saveMode
|
||||
* @return void
|
||||
*/
|
||||
public function updateSchema(array $classes, $saveMode=false)
|
||||
@@ -663,11 +616,8 @@ class SchemaTool
|
||||
/**
|
||||
* Gets the sequence of SQL statements that need to be performed in order
|
||||
* to bring the given class mappings in-synch with the relational schema.
|
||||
* If $saveMode is set to true the command is executed in the Database,
|
||||
* else SQL is returned.
|
||||
*
|
||||
* @param array $classes The classes to consider.
|
||||
* @param boolean $saveMode True for writing to DB, false for SQL string
|
||||
* @return array The sequence of SQL statements.
|
||||
*/
|
||||
public function getUpdateSchemaSql(array $classes, $saveMode=false)
|
||||
@@ -686,4 +636,44 @@ class SchemaTool
|
||||
return $schemaDiff->toSql($this->_platform);
|
||||
}
|
||||
}
|
||||
|
||||
private function _getCommitOrder(array $classes)
|
||||
{
|
||||
$calc = new CommitOrderCalculator;
|
||||
|
||||
// Calculate dependencies
|
||||
foreach ($classes as $class) {
|
||||
$calc->addClass($class);
|
||||
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide']) {
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if ( ! $calc->hasClass($targetClass->name)) {
|
||||
$calc->addClass($targetClass);
|
||||
}
|
||||
|
||||
// add dependency ($targetClass before $class)
|
||||
$calc->addDependency($targetClass, $class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $calc->getCommitOrder();
|
||||
}
|
||||
|
||||
private function _getAssociationTables(array $classes)
|
||||
{
|
||||
$associationTables = array();
|
||||
|
||||
foreach ($classes as $class) {
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] == ClassMetadata::MANY_TO_MANY) {
|
||||
$associationTables[] = $assoc['joinTable']['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $associationTables;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
<?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 LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Tools;
|
||||
|
||||
use Doctrine\Common\ClassLoader;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Doctrine\ORM\Configuration;
|
||||
use Doctrine\ORM\Mapping\Driver\XmlDriver;
|
||||
use Doctrine\ORM\Mapping\Driver\YamlDriver;
|
||||
|
||||
/**
|
||||
* Convenience class for setting up Doctrine from different installations and configurations.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class Setup
|
||||
{
|
||||
/**
|
||||
* Use this method to register all autoloaders for a setup where Doctrine is checked out from
|
||||
* its github repository at {@link http://github.com/doctrine/doctrine2}
|
||||
*
|
||||
* @param string $gitCheckoutRootPath
|
||||
* @return void
|
||||
*/
|
||||
static public function registerAutoloadGit($gitCheckoutRootPath)
|
||||
{
|
||||
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
|
||||
require_once $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php";
|
||||
}
|
||||
|
||||
$loader = new ClassLoader("Doctrine\Common", $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib");
|
||||
$loader->register();
|
||||
|
||||
$loader = new ClassLoader("Doctrine\DBAL", $gitCheckoutRootPath . "/lib/vendor/doctrine-dbal/lib");
|
||||
$loader->register();
|
||||
|
||||
$loader = new ClassLoader("Doctrine\ORM", $gitCheckoutRootPath . "/lib");
|
||||
$loader->register();
|
||||
|
||||
$loader = new ClassLoader("Symfony\Component", $gitCheckoutRootPath . "/lib/vendor");
|
||||
$loader->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to register all autoloaders for a setup where Doctrine is installed
|
||||
* though {@link http://pear.doctrine-project.org}.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static public function registerAutoloadPEAR()
|
||||
{
|
||||
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
|
||||
require_once "Doctrine/Common/ClassLoader.php";
|
||||
}
|
||||
|
||||
$loader = new ClassLoader("Doctrine");
|
||||
$loader->register();
|
||||
|
||||
$parts = explode(PATH_SEPARATOR, get_include_path());
|
||||
|
||||
foreach ($parts AS $includePath) {
|
||||
if ($includePath != "." && file_exists($includePath . "/Doctrine")) {
|
||||
$loader = new ClassLoader("Symfony\Component", $includePath . "/Doctrine");
|
||||
$loader->register();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to register all autoloads for a downloaded Doctrine library.
|
||||
* Pick the directory the library was uncompressed into.
|
||||
*
|
||||
* @param string $directory
|
||||
*/
|
||||
static public function registerAutoloadDirectory($directory)
|
||||
{
|
||||
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
|
||||
require_once $directory . "/Doctrine/Common/ClassLoader.php";
|
||||
}
|
||||
|
||||
$loader = new ClassLoader("Doctrine", $directory);
|
||||
$loader->register();
|
||||
|
||||
$loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine");
|
||||
$loader->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration with an annotation metadata driver.
|
||||
*
|
||||
* @param array $paths
|
||||
* @param boolean $isDevMode
|
||||
* @param string $proxyDir
|
||||
* @param Cache $cache
|
||||
* @return Configuration
|
||||
*/
|
||||
static public function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
|
||||
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths));
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration with an annotation metadata driver.
|
||||
*
|
||||
* @param array $paths
|
||||
* @param boolean $isDevMode
|
||||
* @param string $proxyDir
|
||||
* @param Cache $cache
|
||||
* @return Configuration
|
||||
*/
|
||||
static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
|
||||
$config->setMetadataDriverImpl(new XmlDriver($paths));
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration with an annotation metadata driver.
|
||||
*
|
||||
* @param array $paths
|
||||
* @param boolean $isDevMode
|
||||
* @param string $proxyDir
|
||||
* @param Cache $cache
|
||||
* @return Configuration
|
||||
*/
|
||||
static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
|
||||
$config->setMetadataDriverImpl(new YamlDriver($paths));
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration without a metadata driver.
|
||||
*
|
||||
* @param bool $isDevMode
|
||||
* @param string $proxyDir
|
||||
* @param Cache $cache
|
||||
* @return Configuration
|
||||
*/
|
||||
static public function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$proxyDir = $proxyDir ?: sys_get_temp_dir();
|
||||
if ($isDevMode === false && $cache === null) {
|
||||
if (extension_loaded('apc')) {
|
||||
$cache = new \Doctrine\Common\Cache\ApcCache;
|
||||
} else if (extension_loaded('xcache')) {
|
||||
$cache = new \Doctrine\Common\Cache\XcacheCache;
|
||||
} else if (extension_loaded('memcache')) {
|
||||
$memcache = new \Memcache();
|
||||
$memcache->connect('127.0.0.1');
|
||||
$cache = new \Doctrine\Common\Cache\MemcacheCache();
|
||||
$cache->setMemcache($memcache);
|
||||
} else {
|
||||
$cache = new ArrayCache;
|
||||
}
|
||||
} else if ($cache === null) {
|
||||
$cache = new ArrayCache;
|
||||
}
|
||||
$cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions
|
||||
|
||||
$config = new Configuration();
|
||||
$config->setMetadataCacheImpl($cache);
|
||||
$config->setQueryCacheImpl($cache);
|
||||
$config->setResultCacheImpl($cache);
|
||||
$config->setProxyDir( $proxyDir );
|
||||
$config->setProxyNamespace('DoctrineProxies');
|
||||
$config->setAutoGenerateProxyClasses($isDevMode);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -218,13 +218,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
//private $_readOnlyObjects = array();
|
||||
|
||||
/**
|
||||
* Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $eagerLoadingEntities = array();
|
||||
|
||||
/**
|
||||
* Initializes a new UnitOfWork instance, bound to the given EntityManager.
|
||||
*
|
||||
@@ -406,11 +399,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$actualData = array();
|
||||
foreach ($class->reflFields as $name => $refProp) {
|
||||
$value = $refProp->getValue($entity);
|
||||
if (isset($class->associationMappings[$name])
|
||||
&& ($class->associationMappings[$name]['type'] & ClassMetadata::TO_MANY)
|
||||
&& $value !== null
|
||||
if ($class->isCollectionValuedAssociation($name) && $value !== null
|
||||
&& ! ($value instanceof PersistentCollection)) {
|
||||
|
||||
// If $value is not a Collection then use an ArrayCollection.
|
||||
if ( ! $value instanceof Collection) {
|
||||
$value = new ArrayCollection($value);
|
||||
@@ -429,7 +419,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$coll->setDirty( ! $coll->isEmpty());
|
||||
$class->reflFields[$name]->setValue($entity, $coll);
|
||||
$actualData[$name] = $coll;
|
||||
} else if ( (! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField) ) {
|
||||
} else if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) {
|
||||
$actualData[$name] = $value;
|
||||
}
|
||||
}
|
||||
@@ -455,7 +445,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
// and we have a copy of the original data
|
||||
$originalData = $this->originalEntityData[$oid];
|
||||
$isChangeTrackingNotify = $class->isChangeTrackingNotify();
|
||||
$changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) ? $this->entityChangeSets[$oid] : array();
|
||||
$changeSet = $isChangeTrackingNotify ? $this->entityChangeSets[$oid] : array();
|
||||
|
||||
foreach ($actualData as $propName => $actualValue) {
|
||||
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
|
||||
@@ -477,7 +467,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
} else if ($isChangeTrackingNotify) {
|
||||
continue;
|
||||
} else if ($orgValue !== $actualValue) {
|
||||
} else if (is_object($orgValue) && $orgValue !== $actualValue) {
|
||||
$changeSet[$propName] = array($orgValue, $actualValue);
|
||||
} else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) {
|
||||
$changeSet[$propName] = array($orgValue, $actualValue);
|
||||
}
|
||||
}
|
||||
@@ -515,9 +507,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$class = $this->em->getClassMetadata($className);
|
||||
|
||||
// Skip class if instances are read-only
|
||||
if ($class->isReadOnly) {
|
||||
continue;
|
||||
}
|
||||
//if ($class->isReadOnly) {
|
||||
// continue;
|
||||
//}
|
||||
|
||||
// If change tracking is explicit or happens through notification, then only compute
|
||||
// changes on entities of that type that are explicitly marked for synchronization.
|
||||
@@ -573,12 +565,10 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$oid = spl_object_hash($entry);
|
||||
if ($state == self::STATE_NEW) {
|
||||
if ( ! $assoc['isCascadePersist']) {
|
||||
throw new InvalidArgumentException("A new entity was found through the relationship '"
|
||||
. $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' that was not"
|
||||
. " configured to cascade persist operations for entity: " . self::objToStr($entry) . "."
|
||||
throw new InvalidArgumentException("A new entity was found through a relationship that was not"
|
||||
. " configured to cascade persist operations: " . self::objToStr($entry) . "."
|
||||
. " Explicitly persist the new entity or configure cascading persist operations"
|
||||
. " on the relationship. If you cannot find out which entity causes the problem"
|
||||
. " implement '" . $assoc['targetEntity'] . "#__toString()' to get a clue.");
|
||||
. " on the relationship.");
|
||||
}
|
||||
$this->persistNew($targetClass, $entry);
|
||||
$this->computeChangeSet($targetClass, $entry);
|
||||
@@ -748,7 +738,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate);
|
||||
|
||||
foreach ($this->entityUpdates as $oid => $entity) {
|
||||
if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) {
|
||||
if (get_class($entity) == $className || $entity instanceof Proxy && $entity instanceof $className) {
|
||||
|
||||
if ($hasPreUpdateLifecycleCallbacks) {
|
||||
$class->invokeLifecycleCallbacks(Events::preUpdate, $entity);
|
||||
@@ -761,9 +751,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->entityChangeSets[$oid]) {
|
||||
$persister->update($entity);
|
||||
}
|
||||
$persister->update($entity);
|
||||
unset($this->entityUpdates[$oid]);
|
||||
|
||||
if ($hasPostUpdateLifecycleCallbacks) {
|
||||
@@ -790,7 +778,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$hasListeners = $this->evm->hasListeners(Events::postRemove);
|
||||
|
||||
foreach ($this->entityDeletions as $oid => $entity) {
|
||||
if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) {
|
||||
if (get_class($entity) == $className || $entity instanceof Proxy && $entity instanceof $className) {
|
||||
$persister->delete($entity);
|
||||
unset(
|
||||
$this->entityDeletions[$oid],
|
||||
@@ -834,8 +822,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
// See if there are any new classes in the changeset, that are not in the
|
||||
// commit order graph yet (dont have a node).
|
||||
|
||||
// TODO: Can we know the know the possible $newNodes based on something more efficient? IdentityMap?
|
||||
$newNodes = array();
|
||||
foreach ($entityChangeSet as $oid => $entity) {
|
||||
$className = get_class($entity);
|
||||
@@ -847,7 +833,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
|
||||
// Calculate dependencies for new nodes
|
||||
while ($class = array_pop($newNodes)) {
|
||||
foreach ($newNodes as $class) {
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
@@ -862,7 +848,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$targetSubClass = $this->em->getClassMetadata($subClassName);
|
||||
if ( ! $calc->hasClass($subClassName)) {
|
||||
$calc->addClass($targetSubClass);
|
||||
$newNodes[] = $targetSubClass;
|
||||
}
|
||||
$calc->addDependency($targetSubClass, $class);
|
||||
}
|
||||
@@ -987,7 +972,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->isInIdentityMap($entity)) {
|
||||
$this->removeFromIdentityMap($entity);
|
||||
}
|
||||
unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
|
||||
unset($this->entityInsertions[$oid]);
|
||||
return; // entity has not been persisted yet, so nothing more to do.
|
||||
}
|
||||
|
||||
@@ -1002,7 +987,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
if ( ! isset($this->entityDeletions[$oid])) {
|
||||
$this->entityDeletions[$oid] = $entity;
|
||||
$this->entityStates[$oid] = self::STATE_REMOVED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1105,22 +1089,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!$class->idGenerator->isPostInsertGenerator()) {
|
||||
// if we have a pre insert generator we can't be sure that having an id
|
||||
// really means that the entity exists. We have to verify this through
|
||||
// the last resort: a db lookup
|
||||
|
||||
// Last try before db lookup: check the identity map.
|
||||
if ($this->tryGetById($id, $class->rootEntityName)) {
|
||||
return self::STATE_DETACHED;
|
||||
} else {
|
||||
// db lookup
|
||||
if ($this->getEntityPersister(get_class($entity))->exists($entity)) {
|
||||
return self::STATE_DETACHED;
|
||||
} else {
|
||||
return self::STATE_NEW;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return self::STATE_DETACHED;
|
||||
}
|
||||
@@ -1314,10 +1282,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
|
||||
$visited[$oid] = $entity; // mark visited
|
||||
|
||||
// Cascade first, because scheduleForDelete() removes the entity from the identity map, which
|
||||
// can cause problems when a lazy proxy has to be initialized for the cascade operation.
|
||||
$this->cascadeRemove($entity, $visited);
|
||||
|
||||
$class = $this->em->getClassMetadata(get_class($entity));
|
||||
$entityState = $this->getEntityState($entity);
|
||||
@@ -1341,6 +1305,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
throw new UnexpectedValueException("Unexpected entity state: $entityState.");
|
||||
}
|
||||
|
||||
$this->cascadeRemove($entity, $visited);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1387,10 +1352,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
|
||||
$managedCopy = $entity;
|
||||
} else {
|
||||
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
|
||||
$entity->__load();
|
||||
}
|
||||
|
||||
// Try to look the entity up in the identity map.
|
||||
$id = $class->getIdentifierValues($entity);
|
||||
|
||||
@@ -1429,7 +1390,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
|
||||
// Throw exception if versions dont match.
|
||||
if ($managedCopyVersion != $entityVersion) {
|
||||
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion);
|
||||
throw OptimisticLockException::lockFailedVersionMissmatch($entityVersion, $managedCopyVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,8 +1440,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
if ($assoc2['isCascadeMerge']) {
|
||||
$managedCol->initialize();
|
||||
// clear and set dirty a managed collection if its not also the same collection to merge from.
|
||||
if (!$managedCol->isEmpty() && $managedCol != $mergeCol) {
|
||||
if (!$managedCol->isEmpty()) {
|
||||
$managedCol->unwrap()->clear();
|
||||
$managedCol->setDirty(true);
|
||||
if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) {
|
||||
@@ -1679,10 +1639,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||
if ($relatedEntities instanceof Collection) {
|
||||
if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($relatedEntities instanceof PersistentCollection) {
|
||||
// Unwrap so that foreach() does not initialize
|
||||
$relatedEntities = $relatedEntities->unwrap();
|
||||
@@ -1710,7 +1666,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ( ! $assoc['isCascadePersist']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||
if (($relatedEntities instanceof Collection || is_array($relatedEntities))) {
|
||||
if ($relatedEntities instanceof PersistentCollection) {
|
||||
@@ -1739,11 +1694,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ( ! $assoc['isCascadeRemove']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($entity instanceof Proxy && !$entity->__isInitialized__) {
|
||||
$entity->__load();
|
||||
}
|
||||
|
||||
//TODO: If $entity instanceof Proxy => Initialize ?
|
||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||
if ($relatedEntities instanceof Collection || is_array($relatedEntities)) {
|
||||
// If its a PersistentCollection initialization is intended! No unwrap!
|
||||
@@ -1765,7 +1716,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*/
|
||||
public function lock($entity, $lockMode, $lockVersion = null)
|
||||
{
|
||||
if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
|
||||
if ($this->getEntityState($entity) != self::STATE_MANAGED) {
|
||||
throw new InvalidArgumentException("Entity is not MANAGED.");
|
||||
}
|
||||
|
||||
@@ -1832,10 +1783,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->commitOrderCalculator !== null) {
|
||||
$this->commitOrderCalculator->clear();
|
||||
}
|
||||
|
||||
if ($this->evm->hasListeners(Events::onClear)) {
|
||||
$this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1891,38 +1838,27 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($class->isIdentifierComposite) {
|
||||
$id = array();
|
||||
foreach ($class->identifier as $fieldName) {
|
||||
if (isset($class->associationMappings[$fieldName])) {
|
||||
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
|
||||
} else {
|
||||
$id[$fieldName] = $data[$fieldName];
|
||||
}
|
||||
$id[$fieldName] = $data[$fieldName];
|
||||
}
|
||||
$idHash = implode(' ', $id);
|
||||
} else {
|
||||
if (isset($class->associationMappings[$class->identifier[0]])) {
|
||||
$idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']];
|
||||
} else {
|
||||
$idHash = $data[$class->identifier[0]];
|
||||
}
|
||||
$idHash = $data[$class->identifier[0]];
|
||||
$id = array($class->identifier[0] => $idHash);
|
||||
}
|
||||
|
||||
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
|
||||
|
||||
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
|
||||
$entity = $this->identityMap[$class->rootEntityName][$idHash];
|
||||
$oid = spl_object_hash($entity);
|
||||
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
|
||||
$entity->__isInitialized__ = true;
|
||||
$overrideLocalValues = true;
|
||||
$this->originalEntityData[$oid] = $data;
|
||||
if ($entity instanceof NotifyPropertyChanged) {
|
||||
$entity->addPropertyChangedListener($this);
|
||||
}
|
||||
} else {
|
||||
$overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
|
||||
}
|
||||
|
||||
if ($overrideLocalValues) {
|
||||
$this->originalEntityData[$oid] = $data;
|
||||
}
|
||||
} else {
|
||||
$entity = $class->newInstance();
|
||||
$oid = spl_object_hash($entity);
|
||||
@@ -1942,9 +1878,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$class->reflFields[$field]->setValue($entity, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Loading the entity right here, if its in the eager loading map get rid of it there.
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
|
||||
|
||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
|
||||
@@ -1959,15 +1892,10 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
if ($assoc['isOwningSide']) {
|
||||
$associatedId = array();
|
||||
// TODO: Is this even computed right in all cases of composite keys?
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
|
||||
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
|
||||
if ($joinColumnValue !== null) {
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
|
||||
} else {
|
||||
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
|
||||
}
|
||||
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
|
||||
}
|
||||
}
|
||||
if ( ! $associatedId) {
|
||||
@@ -1975,10 +1903,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$class->reflFields[$field]->setValue($entity, null);
|
||||
$this->originalEntityData[$oid][$field] = null;
|
||||
} else {
|
||||
if (!isset($hints['fetchMode'][$class->name][$field])) {
|
||||
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
||||
}
|
||||
|
||||
// Foreign key is set
|
||||
// Check identity map first
|
||||
// FIXME: Can break easily with composite keys if join column values are in
|
||||
@@ -1986,38 +1910,16 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$relatedIdHash = implode(' ', $associatedId);
|
||||
if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
|
||||
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
|
||||
|
||||
// if this is an uninitialized proxy, we are deferring eager loads,
|
||||
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
||||
// then we cann append this entity for eager loading!
|
||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
||||
isset($hints['deferEagerLoad']) &&
|
||||
!$targetClass->isIdentifierComposite &&
|
||||
$newValue instanceof Proxy &&
|
||||
$newValue->__isInitialized__ === false) {
|
||||
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
}
|
||||
} else {
|
||||
if ($targetClass->subClasses) {
|
||||
// If it might be a subtype, it can not be lazy. There isn't even
|
||||
// a way to solve this with deferred eager loading, which means putting
|
||||
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
|
||||
// If it might be a subtype, it can not be lazy
|
||||
$newValue = $this->getEntityPersister($assoc['targetEntity'])
|
||||
->loadOneToOneEntity($assoc, $entity, $associatedId);
|
||||
->loadOneToOneEntity($assoc, $entity, null, $associatedId);
|
||||
} else {
|
||||
// Deferred eager load only works for single identifier classes
|
||||
|
||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER) {
|
||||
if (isset($hints['deferEagerLoad']) && !$targetClass->isIdentifierComposite) {
|
||||
// TODO: Is there a faster approach?
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
} else {
|
||||
// TODO: This is very imperformant, ignore it?
|
||||
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
||||
}
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
||||
// TODO: Maybe it could be optimized to do an eager fetch with a JOIN inside
|
||||
// the persister instead of this rather unperformant approach.
|
||||
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
||||
} else {
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
}
|
||||
@@ -2031,30 +1933,25 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
$this->originalEntityData[$oid][$field] = $newValue;
|
||||
$class->reflFields[$field]->setValue($entity, $newValue);
|
||||
|
||||
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
|
||||
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
|
||||
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Inverse side of x-to-one can never be lazy
|
||||
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])
|
||||
->loadOneToOneEntity($assoc, $entity));
|
||||
->loadOneToOneEntity($assoc, $entity, null));
|
||||
}
|
||||
} else {
|
||||
// Inject collection
|
||||
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
|
||||
$pColl->setOwner($entity, $assoc);
|
||||
|
||||
|
||||
$reflField = $class->reflFields[$field];
|
||||
$reflField->setValue($entity, $pColl);
|
||||
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
||||
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) {
|
||||
$pColl->setInitialized(false);
|
||||
} else {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
} else {
|
||||
$pColl->setInitialized(false);
|
||||
}
|
||||
$this->originalEntityData[$oid][$field] = $pColl;
|
||||
}
|
||||
@@ -2073,25 +1970,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function triggerEagerLoads()
|
||||
{
|
||||
if (!$this->eagerLoadingEntities) {
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid infinite recursion
|
||||
$eagerLoadingEntities = $this->eagerLoadingEntities;
|
||||
$this->eagerLoadingEntities = array();
|
||||
|
||||
foreach ($eagerLoadingEntities AS $entityName => $ids) {
|
||||
$class = $this->em->getClassMetadata($entityName);
|
||||
$this->getEntityPersister($entityName)->loadAll(array_combine($class->identifier, array(array_values($ids))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes (loads) an uninitialized persistent collection of an entity.
|
||||
*
|
||||
@@ -2171,7 +2049,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* @return array The identifier values.
|
||||
*/
|
||||
public function getEntityIdentifier($entity)
|
||||
{
|
||||
{
|
||||
return $this->entityIdentifiers[spl_object_hash($entity)];
|
||||
}
|
||||
|
||||
@@ -2234,7 +2112,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* Gets the EntityPersister for an Entity.
|
||||
*
|
||||
* @param string $entityName The name of the Entity.
|
||||
* @return Doctrine\ORM\Persisters\AbstractEntityPersister
|
||||
* @return Doctrine\ORM\Persister\AbstractEntityPersister
|
||||
*/
|
||||
public function getEntityPersister($entityName)
|
||||
{
|
||||
@@ -2299,7 +2177,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*/
|
||||
public function clearEntityChangeSet($oid)
|
||||
{
|
||||
$this->entityChangeSets[$oid] = array();
|
||||
unset($this->entityChangeSets[$oid]);
|
||||
}
|
||||
|
||||
/* PropertyChangedListener implementation */
|
||||
@@ -2379,28 +2257,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
return $this->collectionUpdates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to initialize a lazy loading proxy or persistent collection.
|
||||
*
|
||||
* @param object
|
||||
* @return void
|
||||
*/
|
||||
public function initializeObject($obj)
|
||||
{
|
||||
if ($obj instanceof Proxy) {
|
||||
$obj->__load();
|
||||
} else if ($obj instanceof PersistentCollection) {
|
||||
$obj->initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to show an object as string.
|
||||
*
|
||||
* @param object $obj
|
||||
* @return string
|
||||
*/
|
||||
|
||||
private static function objToStr($obj)
|
||||
{
|
||||
return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj);
|
||||
|
||||
@@ -36,7 +36,7 @@ class Version
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.1.2';
|
||||
const VERSION = '2.0.2';
|
||||
|
||||
/**
|
||||
* Compares a Doctrine version with the current one.
|
||||
|
||||
2
lib/vendor/Symfony/Component/Console
vendored
2
lib/vendor/Symfony/Component/Console
vendored
Submodule lib/vendor/Symfony/Component/Console updated: 3762cec59a...76280e55c7
2
lib/vendor/Symfony/Component/Yaml
vendored
2
lib/vendor/Symfony/Component/Yaml
vendored
Submodule lib/vendor/Symfony/Component/Yaml updated: c3e1d03eff...0603a77aba
2
lib/vendor/doctrine-common
vendored
2
lib/vendor/doctrine-common
vendored
Submodule lib/vendor/doctrine-common updated: b385ca7708...9d414673bb
2
lib/vendor/doctrine-dbal
vendored
2
lib/vendor/doctrine-dbal
vendored
Submodule lib/vendor/doctrine-dbal updated: e0b69790ab...ae084d9c69
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Use this configuration file as a template to run the tests against any dbms.
|
||||
Procedure:
|
||||
1) Save a copy of this file with a name of your chosing. It doesn't matter
|
||||
where you place it as long as you know where it is.
|
||||
i.e. "mysqlconf.xml" (It needs the ending .xml).
|
||||
2) Edit the file and fill in your settings (database name, type, username, etc.)
|
||||
Just change the "value"s, not the names of the var elements.
|
||||
3) To run the tests against the database type the following from within the
|
||||
tests/ folder: phpunit -c <filename> ...
|
||||
Example: phpunit -c mysqlconf.xml AllTests
|
||||
-->
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="./tests/Doctrine/Tests/TestInit.php"
|
||||
>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine ORM Test Suite">
|
||||
<directory>./tests/Doctrine/Tests/ORM</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
|
||||
<php>
|
||||
<!-- "Real" test database -->
|
||||
<!-- uncomment, otherwise sqlite memory runs
|
||||
<var name="db_type" value="pdo_mysql"/>
|
||||
<var name="db_host" value="localhost" />
|
||||
<var name="db_username" value="root" />
|
||||
<var name="db_password" value="" />
|
||||
<var name="db_name" value="doctrine_tests" />
|
||||
<var name="db_port" value="3306"/>-->
|
||||
<!--<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit">-->
|
||||
|
||||
<!-- Database for temporary connections (i.e. to drop/create the main database) -->
|
||||
<var name="tmpdb_type" value="pdo_mysql"/>
|
||||
<var name="tmpdb_host" value="localhost" />
|
||||
<var name="tmpdb_username" value="root" />
|
||||
<var name="tmpdb_password" value="" />
|
||||
<var name="tmpdb_name" value="doctrine_tests_tmp" />
|
||||
<var name="tmpdb_port" value="3306"/>
|
||||
</php>
|
||||
|
||||
</phpunit>
|
||||
21
run-all.sh
21
run-all.sh
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases.
|
||||
# Just create the phpunit.xmls as described in the array below and configure the specific files <php /> section
|
||||
# to connect to that database. Just omit a file if you dont have that database and the tests will be skipped.
|
||||
|
||||
configs[1]="mysql.phpunit.xml"
|
||||
configs[2]='postgres.phpunit.xml'
|
||||
configs[3]='sqlite.phpunit.xml'
|
||||
configs[4]='oracle.phpunit.xml'
|
||||
configs[5]='db2.phpunit.xml'
|
||||
configs[6]='pdo-ibm.phpunit.xml'
|
||||
configs[7]='sqlsrv.phpunit.xml'
|
||||
|
||||
for i in "${configs[@]}"; do
|
||||
if [ -f "$i" ];
|
||||
then
|
||||
echo "RUNNING TESTS WITH CONFIG $i"
|
||||
phpunit -c "$i" "$@"
|
||||
fi;
|
||||
done
|
||||
33
tests/Doctrine/Tests/AllTests.php
Normal file
33
tests/Doctrine/Tests/AllTests.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests;
|
||||
|
||||
use Doctrine\Tests\Common;
|
||||
use Doctrine\Tests\ORM;
|
||||
use Doctrine\Tests\DBAL;
|
||||
|
||||
if (!defined('PHPUnit_MAIN_METHOD')) {
|
||||
define('PHPUnit_MAIN_METHOD', 'AllTests::main');
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/TestInit.php';
|
||||
|
||||
class AllTests
|
||||
{
|
||||
public static function main()
|
||||
{
|
||||
\PHPUnit_TextUI_TestRunner::run(self::suite());
|
||||
}
|
||||
|
||||
public static function suite()
|
||||
{
|
||||
$suite = new DoctrineTestSuite('Doctrine Tests');
|
||||
$suite->addTest(ORM\AllTests::suite());
|
||||
|
||||
return $suite;
|
||||
}
|
||||
}
|
||||
|
||||
if (PHPUnit_MAIN_METHOD == 'AllTests::main') {
|
||||
AllTests::main();
|
||||
}
|
||||
19
tests/Doctrine/Tests/DbalFunctionalTestSuite.php
Normal file
19
tests/Doctrine/Tests/DbalFunctionalTestSuite.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests;
|
||||
|
||||
class DbalFunctionalTestSuite extends DbalTestSuite
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
if ( ! isset($this->sharedFixture['conn'])) {
|
||||
$this->sharedFixture['conn'] = TestUtil::getConnection();
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->sharedFixture['conn']->close();
|
||||
$this->sharedFixture = null;
|
||||
}
|
||||
}
|
||||
10
tests/Doctrine/Tests/DbalTestSuite.php
Normal file
10
tests/Doctrine/Tests/DbalTestSuite.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests;
|
||||
|
||||
/**
|
||||
* The outermost test suite for all dbal related testcases & suites.
|
||||
*/
|
||||
class DbalTestSuite extends DoctrineTestSuite
|
||||
{
|
||||
}
|
||||
11
tests/Doctrine/Tests/DoctrineTestSuite.php
Normal file
11
tests/Doctrine/Tests/DoctrineTestSuite.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests;
|
||||
|
||||
/**
|
||||
* Doctrine's basic test suite implementation. Provides functionality needed by all
|
||||
* test suites.
|
||||
*/
|
||||
class DoctrineTestSuite extends \PHPUnit_Framework_TestSuite
|
||||
{
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setProxyDir(__DIR__ . '/../Proxies');
|
||||
$config->setProxyNamespace('Doctrine\Tests\Proxies');
|
||||
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver());
|
||||
$config->setMetadataDriverImpl(\Doctrine\ORM\Mapping\Driver\AnnotationDriver::create());
|
||||
}
|
||||
if (is_null($eventManager)) {
|
||||
$eventManager = new \Doctrine\Common\EventManager();
|
||||
|
||||
@@ -59,7 +59,7 @@ class EntityPersisterMock extends \Doctrine\ORM\Persisters\BasicEntityPersister
|
||||
$this->_updates[] = $entity;
|
||||
}
|
||||
|
||||
public function exists($entity, array $extraConditions = array())
|
||||
public function exists($entity)
|
||||
{
|
||||
$this->existsCalled = true;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="cms_users")
|
||||
* @NamedQueries({
|
||||
* @NamedQuery(name="all", query="SELECT u FROM __CLASS__ u")
|
||||
* })
|
||||
*/
|
||||
class CmsUser
|
||||
{
|
||||
@@ -31,7 +28,7 @@ class CmsUser
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "merge"}, orphanRemoval=true)
|
||||
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
|
||||
*/
|
||||
public $phonenumbers;
|
||||
/**
|
||||
|
||||
@@ -17,11 +17,6 @@ class CompanyEmployee extends CompanyPerson
|
||||
* @Column(type="string", length=255)
|
||||
*/
|
||||
private $department;
|
||||
|
||||
/**
|
||||
* @Column(type="datetime", nullable=true)
|
||||
*/
|
||||
private $startDate;
|
||||
|
||||
public function getSalary() {
|
||||
return $this->salary;
|
||||
@@ -38,12 +33,4 @@ class CompanyEmployee extends CompanyPerson
|
||||
public function setDepartment($dep) {
|
||||
$this->department = $dep;
|
||||
}
|
||||
|
||||
public function getStartDate() {
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function setStartDate($date) {
|
||||
$this->startDate = $date;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC117;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC117ApproveChanges
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer") @GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC117ArticleDetails")
|
||||
* @JoinColumn(name="details_id", referencedColumnName="article_id")
|
||||
*/
|
||||
private $articleDetails;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC117Reference")
|
||||
* @JoinColumns({
|
||||
* @JoinColumn(name="source_id", referencedColumnName="source_id"),
|
||||
* @JoinColumn(name="target_id", referencedColumnName="target_id")
|
||||
* })
|
||||
*/
|
||||
private $reference;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC117Translation")
|
||||
* @JoinColumns({
|
||||
* @JoinColumn(name="trans_article_id", referencedColumnName="article_id"),
|
||||
* @JoinColumn(name="trans_language", referencedColumnName="language")
|
||||
* })
|
||||
*/
|
||||
private $translation;
|
||||
|
||||
public function __construct($details, $reference, $translation)
|
||||
{
|
||||
$this->articleDetails = $details;
|
||||
$this->reference = $reference;
|
||||
$this->translation = $translation;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getArticleDetails()
|
||||
{
|
||||
return $this->articleDetails;
|
||||
}
|
||||
|
||||
public function getReference()
|
||||
{
|
||||
return $this->reference;
|
||||
}
|
||||
|
||||
public function getTranslation()
|
||||
{
|
||||
return $this->translation;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC117;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC117Article
|
||||
{
|
||||
/** @Id @Column(type="integer", name="article_id") @GeneratedValue */
|
||||
private $id;
|
||||
/** @Column */
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="DDC117Reference", mappedBy="source", cascade={"remove"})
|
||||
*/
|
||||
private $references;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="DDC117ArticleDetails", mappedBy="article", cascade={"persist", "remove"})
|
||||
*/
|
||||
private $details;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="DDC117Translation", mappedBy="article", cascade={"persist", "remove"})
|
||||
*/
|
||||
private $translations;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="DDC117Link", mappedBy="source")
|
||||
*/
|
||||
private $links;
|
||||
|
||||
public function __construct($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->references = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
public function setDetails($details)
|
||||
{
|
||||
$this->details = $details;
|
||||
}
|
||||
|
||||
public function id()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function addReference($reference)
|
||||
{
|
||||
$this->references[] = $reference;
|
||||
}
|
||||
|
||||
public function references()
|
||||
{
|
||||
return $this->references;
|
||||
}
|
||||
|
||||
public function addTranslation($language, $title)
|
||||
{
|
||||
$this->translations[] = new DDC117Translation($this, $language, $title);
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
return $this->details->getText();
|
||||
}
|
||||
|
||||
public function getDetails()
|
||||
{
|
||||
return $this->details;
|
||||
}
|
||||
|
||||
public function resetText()
|
||||
{
|
||||
$this->details = null;
|
||||
}
|
||||
|
||||
public function getTranslations()
|
||||
{
|
||||
return $this->translations;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC117;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC117ArticleDetails
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @OneToOne(targetEntity="DDC117Article", inversedBy="details")
|
||||
* @JoinColumn(name="article_id", referencedColumnName="article_id")
|
||||
*/
|
||||
private $article;
|
||||
|
||||
/**
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $text;
|
||||
|
||||
public function __construct($article, $text)
|
||||
{
|
||||
$this->article = $article;
|
||||
$article->setDetails($this);
|
||||
|
||||
$this->update($text);
|
||||
}
|
||||
|
||||
public function update($text)
|
||||
{
|
||||
$this->text = $text;
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC117;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC117Editor
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer") @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string")
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="DDC117Translation", inversedBy="reviewedByEditors")
|
||||
* @JoinTable(
|
||||
* inverseJoinColumns={
|
||||
* @JoinColumn(name="article_id", referencedColumnName="article_id"),
|
||||
* @JoinColumn(name="language", referencedColumnName="language")
|
||||
* },
|
||||
* joinColumns={
|
||||
* @JoinColumn(name="editor_id", referencedColumnName="id")
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
public $reviewingTranslations;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC117Translation", inversedBy="lastTranslatedBy")
|
||||
* @JoinColumns({
|
||||
* @JoinColumn(name="lt_article_id", referencedColumnName="article_id"),
|
||||
* @JoinColumn(name="lt_language", referencedColumnName="language")
|
||||
* })
|
||||
*/
|
||||
public $lastTranslation;
|
||||
|
||||
public function __construct($name = "")
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->reviewingTranslations = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
public function addLastTranslation(DDC117Translation $t)
|
||||
{
|
||||
$this->lastTranslation = $t;
|
||||
$t->lastTranslatedBy[] = $this;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC117;
|
||||
|
||||
/**
|
||||
* Foreign Key Entity without additional fields!
|
||||
*
|
||||
* @Entity
|
||||
*/
|
||||
class DDC117Link
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="DDC117Article", inversedBy="links")
|
||||
* @JoinColumn(name="source_id", referencedColumnName="article_id")
|
||||
*/
|
||||
public $source;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="DDC117Article")
|
||||
* @JoinColumn(name="target_id", referencedColumnName="article_id")
|
||||
*/
|
||||
public $target;
|
||||
|
||||
public function __construct($source, $target, $description)
|
||||
{
|
||||
$this->source = $source;
|
||||
$this->target = $target;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC117;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC117Reference
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="DDC117Article", inversedBy="references")
|
||||
* @JoinColumn(name="source_id", referencedColumnName="article_id")
|
||||
*/
|
||||
private $source;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="DDC117Article", inversedBy="references")
|
||||
* @JoinColumn(name="target_id", referencedColumnName="article_id")
|
||||
*/
|
||||
private $target;
|
||||
|
||||
/**
|
||||
* @column(type="string")
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @column(type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
public function __construct($source, $target, $description)
|
||||
{
|
||||
$source->addReference($this);
|
||||
$target->addReference($this);
|
||||
|
||||
$this->source = $source;
|
||||
$this->target = $target;
|
||||
$this->description = $description;
|
||||
$this->created = new \DateTime("now");
|
||||
}
|
||||
|
||||
public function source()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
public function target()
|
||||
{
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
public function setDescription($desc)
|
||||
{
|
||||
$this->description = $desc;
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC117;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC117Translation
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="DDC117Article")
|
||||
* @JoinColumn(name="article_id", referencedColumnName="article_id")
|
||||
*/
|
||||
private $article;
|
||||
|
||||
/**
|
||||
* @Id @column(type="string")
|
||||
*/
|
||||
private $language;
|
||||
|
||||
/**
|
||||
* @column(type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="DDC117Editor", mappedBy="reviewingTranslations")
|
||||
*/
|
||||
public $reviewedByEditors;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="DDC117Editor", mappedBy="lastTranslation")
|
||||
*/
|
||||
public $lastTranslatedBy;
|
||||
|
||||
public function __construct($article, $language, $title)
|
||||
{
|
||||
$this->article = $article;
|
||||
$this->language = $language;
|
||||
$this->title = $title;
|
||||
$this->reviewedByEditors = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->lastTranslatedBy = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
public function getArticleId()
|
||||
{
|
||||
return $this->article->id();
|
||||
}
|
||||
|
||||
public function getLanguage()
|
||||
{
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
public function getLastTranslatedBy()
|
||||
{
|
||||
return $this->lastTranslatedBy;
|
||||
}
|
||||
|
||||
public function getReviewedByEditors()
|
||||
{
|
||||
return $this->reviewedByEditors;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ class ECommerceCustomer
|
||||
* only one customer at the time, while a customer can choose only one
|
||||
* mentor. Not properly appropriate but it works.
|
||||
*
|
||||
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"}, fetch="EAGER")
|
||||
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"})
|
||||
* @JoinColumn(name="mentor_id", referencedColumnName="id")
|
||||
*/
|
||||
private $mentor;
|
||||
|
||||
@@ -56,7 +56,6 @@ class ECommerceProduct
|
||||
private $related;
|
||||
|
||||
public $isCloned = false;
|
||||
public $wakeUp = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -167,12 +166,4 @@ class ECommerceProduct
|
||||
{
|
||||
$this->isCloned = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing docblock contents here
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->wakeUp = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Legacy;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="legacy_articles")
|
||||
*/
|
||||
class LegacyArticle
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(name="iArticleId", type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
public $_id;
|
||||
/**
|
||||
* @Column(name="sTopic", type="string", length=255)
|
||||
*/
|
||||
public $_topic;
|
||||
/**
|
||||
* @Column(name="sText", type="text")
|
||||
*/
|
||||
public $_text;
|
||||
/**
|
||||
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_articles")
|
||||
* @JoinColumn(name="iUserId", referencedColumnName="iUserId")
|
||||
*/
|
||||
public $_user;
|
||||
public function setAuthor(LegacyUser $author) {
|
||||
$this->_user = $author;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Legacy;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="legacy_cars")
|
||||
*/
|
||||
class LegacyCar
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(name="iCarId", type="integer", nullable=false)
|
||||
*/
|
||||
public $_id;
|
||||
/**
|
||||
* @ManyToMany(targetEntity="LegacyUser", mappedBy="_cars")
|
||||
*/
|
||||
public $_users;
|
||||
|
||||
/**
|
||||
* @Column(name="sDescription", type="string", length=255, unique=true)
|
||||
*/
|
||||
public $_description;
|
||||
|
||||
function getDescription()
|
||||
{
|
||||
return $this->_description;
|
||||
}
|
||||
|
||||
public function addUser(LegacyUser $user) {
|
||||
$this->_users[] = $user;
|
||||
}
|
||||
|
||||
public function getUsers() {
|
||||
return $this->_users;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user