Starting using the new files I've already worked on
Esse commit está contido em:
Arquivo executável
+8
@@ -0,0 +1,8 @@
|
||||
Requires:
|
||||
PHP 5.3
|
||||
PHP-cli
|
||||
|
||||
In your console, run:
|
||||
sudo php mind install
|
||||
|
||||
You're good to go
|
||||
Arquivo executável
+24
@@ -0,0 +1,24 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010 TheWebMind.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
Arquivo executável
+7
@@ -0,0 +1,7 @@
|
||||
See INSTALL.txt file to see how to install it.
|
||||
After installation, for help, type:
|
||||
$ mind -h
|
||||
|
||||
http://thewebmind.org
|
||||
http://docs.thewebmind.org
|
||||
contact@thewebmind.org
|
||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 98 KiB |
@@ -0,0 +1,55 @@
|
||||
@user=users.
|
||||
@project=projects.
|
||||
@version=versions.
|
||||
@object=objects.
|
||||
@component=components.
|
||||
@property=properties.
|
||||
|
||||
project has many users.
|
||||
|
||||
project has name:varchar(255, required).
|
||||
project has info:text().
|
||||
project has creator:int() // a user key.
|
||||
project has dt_creation:date(-1, "Exec:now()").
|
||||
|
||||
user has many projects.
|
||||
|
||||
user has name:varchar(255).
|
||||
user has login:varchar(40, required).
|
||||
user has pwd:varchar(40, required).
|
||||
user has status:char(1);
|
||||
user has type:char(1, {A=Admin|N=Normal}).
|
||||
|
||||
project has many versions.
|
||||
|
||||
version has version:varchar(9, required).
|
||||
version has tag:varchar(60).
|
||||
version has obs:text(2048).
|
||||
version has originalcode:text().
|
||||
version has machine_lang:varchar(16) // the chosen language, like PHP, Java Python.
|
||||
version has framework:varchar(60) // the chosen framework to generate the code.
|
||||
version has database:varchar(16) // the chosen DBMS to deal with .
|
||||
version has a user.
|
||||
|
||||
version has many objects.
|
||||
|
||||
objects have type:char(1, {T=Table|C=Class|I=Interface}).
|
||||
objects have name:varchar(256).
|
||||
every object has version:int(-1).
|
||||
object has locked:int(-1, "Exec:0") // use 0 or 1.
|
||||
object has info:varchar(2048).
|
||||
|
||||
each object may have many components.
|
||||
|
||||
components have type:char(1, {P=Propertie|M=Method|A=Annotation}).
|
||||
each component have properties.
|
||||
|
||||
All the properties have name:varchar(255, required).
|
||||
And the properties have value:text().
|
||||
Also, the properties have comment:varchar(255).
|
||||
|
||||
properties may have many properties.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*######################################################
|
||||
# Generated by Mind 00:09 10/27/2010 #
|
||||
# Generate PostgreSQL DataBase Commands #
|
||||
######################################################*/
|
||||
|
||||
|
||||
/* DDL: table project */
|
||||
CREATE TABLE project
|
||||
(
|
||||
pk_project integer unique not null,
|
||||
name varchar(255) not null ,
|
||||
info text ,
|
||||
creator int4 ,
|
||||
dt_creation timestamp,
|
||||
PRIMARY KEY(pk_project)
|
||||
);
|
||||
|
||||
|
||||
/* DDL: table user */
|
||||
CREATE TABLE user
|
||||
(
|
||||
pk_user integer unique not null,
|
||||
name varchar(255) ,
|
||||
login varchar(40) not null ,
|
||||
pwd varchar(40) not null ,
|
||||
status char(1) ,
|
||||
type char(1) ,
|
||||
PRIMARY KEY(pk_user)
|
||||
);
|
||||
|
||||
|
||||
/* DDL: table project_user */
|
||||
CREATE TABLE project_user
|
||||
(
|
||||
pk_project_user integer unique not null,
|
||||
fk_project integer ,
|
||||
fk_user integer ,
|
||||
PRIMARY KEY(pk_project_user),
|
||||
FOREIGN KEY(fk_user) REFERENCES user(pk_user),
|
||||
FOREIGN KEY(fk_project) REFERENCES project(pk_project)
|
||||
);
|
||||
|
||||
|
||||
/* DDL: table version */
|
||||
CREATE TABLE version
|
||||
(
|
||||
pk_version integer unique not null,
|
||||
version varchar(9) not null ,
|
||||
tag varchar(60) ,
|
||||
obs text ,
|
||||
originalcode text ,
|
||||
machine_lang varchar(16) ,
|
||||
framework varchar(60) ,
|
||||
database varchar(16) ,
|
||||
fk_project integer ,
|
||||
fk_user integer ,
|
||||
PRIMARY KEY(pk_version),
|
||||
FOREIGN KEY (fk_user) REFERENCES user(pk_user),
|
||||
FOREIGN KEY (fk_project) REFERENCES project(pk_project)
|
||||
);
|
||||
|
||||
|
||||
/* DDL: table object */
|
||||
CREATE TABLE object
|
||||
(
|
||||
pk_object integer unique not null,
|
||||
type char(1) ,
|
||||
name varchar(256) ,
|
||||
version int4 ,
|
||||
locked int4 default 0,
|
||||
info varchar(2048) ,
|
||||
fk_version integer ,
|
||||
PRIMARY KEY(pk_object),
|
||||
FOREIGN KEY (fk_version) REFERENCES version(pk_version)
|
||||
);
|
||||
|
||||
|
||||
/* DDL: table component */
|
||||
CREATE TABLE component
|
||||
(
|
||||
pk_component integer unique not null,
|
||||
type char(1) ,
|
||||
fk_object integer ,
|
||||
PRIMARY KEY(pk_component),
|
||||
FOREIGN KEY (fk_object) REFERENCES object(pk_object)
|
||||
);
|
||||
|
||||
|
||||
/* DDL: table property */
|
||||
CREATE TABLE property
|
||||
(
|
||||
pk_property integer unique not null,
|
||||
name varchar(255) not null ,
|
||||
value text ,
|
||||
comment varchar(255) ,
|
||||
fk_component integer ,
|
||||
PRIMARY KEY(pk_property),
|
||||
FOREIGN KEY (fk_component) REFERENCES component(pk_component)
|
||||
);
|
||||
|
||||
|
||||
/* DDL: table property_pointer */
|
||||
CREATE TABLE property_pointer
|
||||
(
|
||||
pk_property_pointer integer unique not null,
|
||||
main_property integer not null ,
|
||||
fk_property integer ,
|
||||
PRIMARY KEY(pk_property_pointer),
|
||||
FOREIGN KEY (fk_property) REFERENCES property(pk_property)
|
||||
);
|
||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 11 KiB |
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 12 KiB |
@@ -0,0 +1,5 @@
|
||||
Diagrama de sequência
|
||||
http://www.websequencediagrams.com/
|
||||
Diagrama ER
|
||||
http://thewebmind.org/
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
$_REQ= Array();
|
||||
$_REQ['request_method']= $_SERVER['REQUEST_METHOD'];
|
||||
$_REQ['env']= 'http';
|
||||
switch($_REQ['request_method'])
|
||||
{
|
||||
case 'GET' : $_REQ['data']= $_GET;
|
||||
break;
|
||||
case 'POST': $_REQ['data']= $_POST;
|
||||
break;
|
||||
case 'PUT' : parse_str(file_get_contents('php://input'), $put_vars);
|
||||
$_REQ['data'] = $put_vars;
|
||||
break;
|
||||
default:
|
||||
$_REQ['data']= null;
|
||||
}
|
||||
include('mind3rd/API/utils.php');
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* this file will be only accessed trhough the console
|
||||
* if you pass the parameter install to it, it will
|
||||
* create the SQLite database, start it, and try to
|
||||
* create shortcuts, allowing the use of the comand
|
||||
* mind, in your console
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var _MIND
|
||||
* @global mixed $_GLOBALS['_MIND'] This variable contains many information about the proect, the system and also have some methods an attributes to deal with such data
|
||||
*/
|
||||
$_MIND= Array();
|
||||
$_MIND['env']= 'shell';
|
||||
|
||||
// checks if it has received any argument
|
||||
if(sizeOf($_SERVER['argv'])>0)
|
||||
{
|
||||
// installation should create the required SQLite database and a shortcut command
|
||||
if($_SERVER['argv'][1] == 'install')
|
||||
{
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
|
||||
{
|
||||
echo ".";
|
||||
echo shell_exec("sudo touch /bin/mind; echo .");
|
||||
echo shell_exec("sudo echo '#!/usr/bin/env php' > /bin/mind; echo .");
|
||||
echo shell_exec("sudo echo '<?php' >> /bin/mind; echo .");
|
||||
|
||||
echo shell_exec("sudo echo \"".'\$_REQ= Array(); \$_REQ[\'env\']= \'shell\';'.
|
||||
" define('_MINDSRC_', '". getcwd()."'); ".
|
||||
" require('".getcwd()."/mind3rd/API/utils.php');\" >> /bin/mind; echo .");
|
||||
echo shell_exec("sudo chmod 777 /bin/mind; echo .");
|
||||
}else{
|
||||
// TODO: prepare it to other operating systems
|
||||
}
|
||||
if($db = new SQLiteDatabase('mind3rd/SQLite/mind'))
|
||||
{
|
||||
$DDL= file_get_contents('mind3rd/SQLite/ddl.sql');
|
||||
if(!$db->queryExec($DDL))
|
||||
{
|
||||
die($err);
|
||||
}
|
||||
$db->queryExec("INSERT into user(name, login, pwd, status, type) VALUES ('Administrator', 'admin', '".sha1('admin')."', 'A', 'A');");
|
||||
}else{
|
||||
die($err);
|
||||
}
|
||||
echo "Done\n";
|
||||
exit;
|
||||
}
|
||||
// if not installing, it should be redirected to mind3rd/API/shell.php
|
||||
include('location:./mind3rd/API/shell.php');
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<http_no_such_file> [ERROR] No such file!
|
||||
</http_no_such_file>
|
||||
<shell_no_such_file> [ERROR] No such function!
|
||||
Type help for help
|
||||
</shell_no_such_file>
|
||||
<autenticate_requiredFields> [ERROR] You must send a user and a password to autenticate
|
||||
If you are using the console, you can send only the user. A password will be required soon
|
||||
</autenticate_requiredFields>
|
||||
<autenticate_requiredPwd> I'll need a password, please:
|
||||
</autenticate_requiredPwd>
|
||||
<auth_fail> Wrong user or password
|
||||
</auth_fail>
|
||||
<not_allowed> Your session is not valid. Please, execute
|
||||
> autenticate $user
|
||||
or send a user and a password to registrate
|
||||
</not_allowed>
|
||||
</root>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
class Mind
|
||||
{
|
||||
public $about= null;
|
||||
public $defaults= null;
|
||||
public $conf= null;
|
||||
public $l10n= null;
|
||||
|
||||
public function write($k, $echo=true)
|
||||
{
|
||||
//$msg= $this->l10n->xpath("/root/speach[@voice='".$k."']");
|
||||
//echo "/root/".$k;
|
||||
$msg= (string)$this->l10n->$k;
|
||||
if($echo)
|
||||
echo $msg;
|
||||
return $msg;
|
||||
}
|
||||
|
||||
public function Mind(){
|
||||
$path= _MINDSRC_;
|
||||
$this->about= parse_ini_file($path.'/mind3rd/env/about.ini');
|
||||
$this->defaults= parse_ini_file($path.'/mind3rd/env/defaults.ini');
|
||||
$this->conf= parse_ini_file($path.'/mind3rd/env/mind.ini');
|
||||
$this->l10n= SimpleXML_load_file($path.'/mind3rd/API/L10N/'.$this->defaults['defaul_human_language'].'.xml');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
class Mind
|
||||
{
|
||||
public $about= null;
|
||||
public $defaults= null;
|
||||
public $conf= null;
|
||||
public $l10n= null;
|
||||
|
||||
public function write($k, $echo=true)
|
||||
{
|
||||
//$msg= $this->l10n->xpath("/root/speach[@voice='".$k."']");
|
||||
//echo "/root/".$k;
|
||||
$msg= (string)$this->l10n->$k;
|
||||
if(!$echo)
|
||||
echo $msg;
|
||||
return $msg;
|
||||
}
|
||||
|
||||
public function Mind(){
|
||||
$this->about= parse_ini_file('mind3rd/env/about.ini');
|
||||
$this->defaults= parse_ini_file('mind3rd/env/defaults.ini');
|
||||
$this->conf= parse_ini_file('mind3rd/env/mind.ini');
|
||||
$this->l10n= SimpleXML_load_file('mind3rd/API/L10N/'.$this->defaults['defaul_human_language'].'.xml');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 41
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib
|
||||
END
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
Doctrine
|
||||
dir
|
||||
|
||||
vendor
|
||||
dir
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 50
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine
|
||||
END
|
||||
@@ -0,0 +1,31 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
ORM
|
||||
dir
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 54
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM
|
||||
END
|
||||
AbstractQuery.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 72
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/AbstractQuery.php
|
||||
END
|
||||
NoResultException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 76
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/NoResultException.php
|
||||
END
|
||||
NonUniqueResultException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/NonUniqueResultException.php
|
||||
END
|
||||
Events.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 65
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Events.php
|
||||
END
|
||||
Query.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 64
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Query.php
|
||||
END
|
||||
TransactionRequiredException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 87
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/TransactionRequiredException.php
|
||||
END
|
||||
EntityManager.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 72
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/EntityManager.php
|
||||
END
|
||||
Configuration.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 72
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Configuration.php
|
||||
END
|
||||
UnitOfWork.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 69
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/UnitOfWork.php
|
||||
END
|
||||
EntityNotFoundException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 82
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/EntityNotFoundException.php
|
||||
END
|
||||
QueryBuilder.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 71
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/QueryBuilder.php
|
||||
END
|
||||
PersistentCollection.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 79
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/PersistentCollection.php
|
||||
END
|
||||
OptimisticLockException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 82
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/OptimisticLockException.php
|
||||
END
|
||||
ORMException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 71
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/ORMException.php
|
||||
END
|
||||
NativeQuery.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 70
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/NativeQuery.php
|
||||
END
|
||||
EntityRepository.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 75
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/EntityRepository.php
|
||||
END
|
||||
PessimisticLockException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/PessimisticLockException.php
|
||||
END
|
||||
Version.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 66
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Version.php
|
||||
END
|
||||
README.markdown
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 70
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/README.markdown
|
||||
END
|
||||
@@ -0,0 +1,698 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine/ORM
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
AbstractQuery.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
1fd2c25dd82df8e7a527496c84dc20dd
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
17176
|
||||
|
||||
NoResultException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
ae1222460f654b077ebe2039f5c81f83
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1325
|
||||
|
||||
Proxy
|
||||
dir
|
||||
|
||||
Event
|
||||
dir
|
||||
|
||||
NonUniqueResultException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
d29625e50e4441489e330226edc85380
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1184
|
||||
|
||||
Events.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
3440a63347dcdc8909ee829f11a7bcf8
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4115
|
||||
|
||||
Mapping
|
||||
dir
|
||||
|
||||
Query.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
5fafd8c3faa47d92185d7a534ce34a66
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
16058
|
||||
|
||||
TransactionRequiredException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.173324Z
|
||||
a8bd25855fcc0c41dfbc4a3fabc66539
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1596
|
||||
|
||||
Id
|
||||
dir
|
||||
|
||||
EntityManager.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
3eaf04d788f1c90462721b695853d25f
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
22517
|
||||
|
||||
Configuration.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
42c1608574fa3ce633440c10d9af5dd1
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
14974
|
||||
|
||||
UnitOfWork.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.173324Z
|
||||
7264f6dfdc0250fddd6f486562c83e82
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
87147
|
||||
|
||||
EntityNotFoundException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
8ba06972a4f209c006a34031e082ae2f
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1271
|
||||
|
||||
Tools
|
||||
dir
|
||||
|
||||
NativeQuery.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
4465109f6a32d350e3e079dffcd7fb04
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2123
|
||||
|
||||
EntityRepository.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
b80e3588d7328bdd374ad0a2d5ced872
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
7149
|
||||
|
||||
ORMException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
57e8ab7a70fd700fe9da092d6a3a50e3
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3957
|
||||
|
||||
OptimisticLockException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
621e0117b72550e8d934f19d065e039f
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2183
|
||||
|
||||
PersistentCollection.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
d3fdfdfe1cc5da080709b7f19bdc5d8f
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
18206
|
||||
|
||||
QueryBuilder.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
16d80984987db0f4adfded9171a0af6b
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
28527
|
||||
|
||||
Persisters
|
||||
dir
|
||||
|
||||
Query
|
||||
dir
|
||||
|
||||
PessimisticLockException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.123322Z
|
||||
7b1a35b610010960c76235ce5f428e2d
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1495
|
||||
|
||||
Version.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.173324Z
|
||||
f7a488723c409a4d1f650223c72f45b5
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2034
|
||||
|
||||
Internal
|
||||
dir
|
||||
|
||||
README.markdown
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:46.173324Z
|
||||
d41d8cd98f00b204e9800998ecf8427e
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
0
|
||||
|
||||
+590
@@ -0,0 +1,590 @@
|
||||
<?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
|
||||
* 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;
|
||||
|
||||
use Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\Query\QueryException;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
*/
|
||||
abstract class AbstractQuery
|
||||
{
|
||||
/* Hydration mode constants */
|
||||
/**
|
||||
* Hydrates an object graph. This is the default behavior.
|
||||
*/
|
||||
const HYDRATE_OBJECT = 1;
|
||||
/**
|
||||
* Hydrates an array graph.
|
||||
*/
|
||||
const HYDRATE_ARRAY = 2;
|
||||
/**
|
||||
* Hydrates a flat, rectangular result set with scalar values.
|
||||
*/
|
||||
const HYDRATE_SCALAR = 3;
|
||||
/**
|
||||
* Hydrates a single scalar value.
|
||||
*/
|
||||
const HYDRATE_SINGLE_SCALAR = 4;
|
||||
|
||||
/**
|
||||
* @var array The parameter map of this query.
|
||||
*/
|
||||
protected $_params = array();
|
||||
|
||||
/**
|
||||
* @var array The parameter type map of this query.
|
||||
*/
|
||||
protected $_paramTypes = array();
|
||||
|
||||
/**
|
||||
* @var ResultSetMapping The user-specified ResultSetMapping to use.
|
||||
*/
|
||||
protected $_resultSetMapping;
|
||||
|
||||
/**
|
||||
* @var Doctrine\ORM\EntityManager The entity manager used by this query object.
|
||||
*/
|
||||
protected $_em;
|
||||
|
||||
/**
|
||||
* @var array The map of query hints.
|
||||
*/
|
||||
protected $_hints = array();
|
||||
|
||||
/**
|
||||
* @var integer The hydration mode.
|
||||
*/
|
||||
protected $_hydrationMode = self::HYDRATE_OBJECT;
|
||||
|
||||
/**
|
||||
* The locally set cache driver used for caching result sets of this query.
|
||||
*
|
||||
* @var CacheDriver
|
||||
*/
|
||||
protected $_resultCacheDriver;
|
||||
|
||||
/**
|
||||
* Boolean flag for whether or not to cache the results of this query.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $_useResultCache;
|
||||
|
||||
/**
|
||||
* @var string The id to store the result cache entry under.
|
||||
*/
|
||||
protected $_resultCacheId;
|
||||
|
||||
/**
|
||||
* @var boolean Boolean value that indicates whether or not expire the result cache.
|
||||
*/
|
||||
protected $_expireResultCache = false;
|
||||
|
||||
/**
|
||||
* @var int Result Cache lifetime.
|
||||
*/
|
||||
protected $_resultCacheTTL;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $entityManager
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the associated EntityManager of this Query instance.
|
||||
*
|
||||
* @return Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the resources used by the query object.
|
||||
*
|
||||
* Resets Parameters, Parameter Types and Query Hints.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
$this->_params = array();
|
||||
$this->_paramTypes = array();
|
||||
$this->_hints = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all defined parameters.
|
||||
*
|
||||
* @return array The defined query parameters.
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a query parameter.
|
||||
*
|
||||
* @param mixed $key The key (index or name) of the bound parameter.
|
||||
* @return mixed The value of the bound parameter.
|
||||
*/
|
||||
public function getParameter($key)
|
||||
{
|
||||
return isset($this->_params[$key]) ? $this->_params[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL query that corresponds to this query object.
|
||||
* The returned SQL syntax depends on the connection driver that is used
|
||||
* by this query object at the time of this method call.
|
||||
*
|
||||
* @return string SQL query
|
||||
*/
|
||||
abstract public function getSQL();
|
||||
|
||||
/**
|
||||
* Sets a query parameter.
|
||||
*
|
||||
* @param string|integer $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param string $type The parameter type. If specified, the given value will be run through
|
||||
* the type conversion of this type. This is usually not needed for
|
||||
* strings and numeric types.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setParameter($key, $value, $type = null)
|
||||
{
|
||||
if ($type !== null) {
|
||||
$this->_paramTypes[$key] = $type;
|
||||
}
|
||||
$this->_params[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a collection of query parameters.
|
||||
*
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ResultSetMapping that should be used for hydration.
|
||||
*
|
||||
* @param ResultSetMapping $rsm
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setResultSetMapping(Query\ResultSetMapping $rsm)
|
||||
{
|
||||
$this->_resultSetMapping = $rsm;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a cache driver to be used for caching result sets.
|
||||
*
|
||||
* @param Doctrine\Common\Cache\Cache $driver Cache driver
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setResultCacheDriver($resultCacheDriver = null)
|
||||
{
|
||||
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
|
||||
throw ORMException::invalidResultCacheDriver();
|
||||
}
|
||||
$this->_resultCacheDriver = $resultCacheDriver;
|
||||
if ($resultCacheDriver) {
|
||||
$this->_useResultCache = true;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache driver used for caching result sets.
|
||||
*
|
||||
* @return Doctrine\Common\Cache\Cache Cache driver
|
||||
*/
|
||||
public function getResultCacheDriver()
|
||||
{
|
||||
if ($this->_resultCacheDriver) {
|
||||
return $this->_resultCacheDriver;
|
||||
} else {
|
||||
return $this->_em->getConfiguration()->getResultCacheImpl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to cache the results of this query and if so, for
|
||||
* how long and which ID to use for the cache entry.
|
||||
*
|
||||
* @param boolean $bool
|
||||
* @param integer $timeToLive
|
||||
* @param string $resultCacheId
|
||||
* @return This query instance.
|
||||
*/
|
||||
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
|
||||
{
|
||||
$this->_useResultCache = $bool;
|
||||
if ($timeToLive) {
|
||||
$this->setResultCacheLifetime($timeToLive);
|
||||
}
|
||||
if ($resultCacheId) {
|
||||
$this->_resultCacheId = $resultCacheId;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how long the result cache will be active before expire.
|
||||
*
|
||||
* @param integer $timeToLive How long the cache entry is valid.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setResultCacheLifetime($timeToLive)
|
||||
{
|
||||
if ($timeToLive !== null) {
|
||||
$timeToLive = (int) $timeToLive;
|
||||
}
|
||||
|
||||
$this->_resultCacheTTL = $timeToLive;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the lifetime of resultset cache.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getResultCacheLifetime()
|
||||
{
|
||||
return $this->_resultCacheTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the result cache is active or not.
|
||||
*
|
||||
* @param boolean $expire Whether or not to force resultset cache expiration.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function expireResultCache($expire = true)
|
||||
{
|
||||
$this->_expireResultCache = $expire;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves if the resultset cache is active or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getExpireResultCache()
|
||||
{
|
||||
return $this->_expireResultCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the processing mode to be used during hydration / result set transformation.
|
||||
*
|
||||
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
|
||||
* One of the Query::HYDRATE_* constants.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setHydrationMode($hydrationMode)
|
||||
{
|
||||
$this->_hydrationMode = $hydrationMode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hydration mode currently used by the query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getHydrationMode()
|
||||
{
|
||||
return $this->_hydrationMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of results for the query.
|
||||
*
|
||||
* Alias for execute(array(), $hydrationMode = HYDRATE_OBJECT).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResult($hydrationMode = self::HYDRATE_OBJECT)
|
||||
{
|
||||
return $this->execute(array(), $hydrationMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of results for the query.
|
||||
*
|
||||
* Alias for execute(array(), HYDRATE_ARRAY).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArrayResult()
|
||||
{
|
||||
return $this->execute(array(), self::HYDRATE_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scalar results for the query.
|
||||
*
|
||||
* Alias for execute(array(), HYDRATE_SCALAR).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getScalarResult()
|
||||
{
|
||||
return $this->execute(array(), self::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single result of the query.
|
||||
*
|
||||
* Enforces the presence as well as the uniqueness of the result.
|
||||
*
|
||||
* If the result is not unique, a NonUniqueResultException is thrown.
|
||||
* If there is no result, a NoResultException is thrown.
|
||||
*
|
||||
* @param integer $hydrationMode
|
||||
* @return mixed
|
||||
* @throws NonUniqueResultException If the query result is not unique.
|
||||
* @throws NoResultException If the query returned no result.
|
||||
*/
|
||||
public function getSingleResult($hydrationMode = null)
|
||||
{
|
||||
$result = $this->execute(array(), $hydrationMode);
|
||||
|
||||
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
|
||||
throw new NoResultException;
|
||||
}
|
||||
|
||||
if (is_array($result)) {
|
||||
if (count($result) > 1) {
|
||||
throw new NonUniqueResultException;
|
||||
}
|
||||
return array_shift($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single scalar result of the query.
|
||||
*
|
||||
* Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
|
||||
*
|
||||
* @return mixed
|
||||
* @throws QueryException If the query result is not unique.
|
||||
*/
|
||||
public function getSingleScalarResult()
|
||||
{
|
||||
return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query hint. If the hint name is not recognized, it is silently ignored.
|
||||
*
|
||||
* @param string $name The name of the hint.
|
||||
* @param mixed $value The value of the hint.
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setHint($name, $value)
|
||||
{
|
||||
$this->_hints[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
|
||||
*
|
||||
* @param string $name The name of the hint.
|
||||
* @return mixed The value of the hint or FALSE, if the hint name is not recognized.
|
||||
*/
|
||||
public function getHint($name)
|
||||
{
|
||||
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns an IterableResult that can be used to incrementally
|
||||
* iterate over the result.
|
||||
*
|
||||
* @param array $params The query parameters.
|
||||
* @param integer $hydrationMode The hydration mode to use.
|
||||
* @return IterableResult
|
||||
*/
|
||||
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
|
||||
{
|
||||
return $this->_em->newHydrator($this->_hydrationMode)->iterate(
|
||||
$this->_doExecute($params, $hydrationMode), $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query.
|
||||
*
|
||||
* @param string $params Any additional query parameters.
|
||||
* @param integer $hydrationMode Processing mode to be used during the hydration process.
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute($params = array(), $hydrationMode = null)
|
||||
{
|
||||
// If there are still pending insertions in the UnitOfWork we need to flush
|
||||
// in order to guarantee a correct result.
|
||||
//TODO: Think this over. Its tricky. Not doing this can lead to strange results
|
||||
// potentially, but doing it could result in endless loops when querying during
|
||||
// a flush, i.e. inside an event listener.
|
||||
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
if ($hydrationMode !== null) {
|
||||
$this->setHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
if ($params) {
|
||||
$this->setParameters($params);
|
||||
}
|
||||
|
||||
if (isset($this->_params[0])) {
|
||||
throw QueryException::invalidParameterPosition(0);
|
||||
}
|
||||
|
||||
// Check result cache
|
||||
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
|
||||
$id = $this->_getResultCacheId();
|
||||
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($id);
|
||||
|
||||
if ($cached === false) {
|
||||
// Cache miss.
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
|
||||
$cacheDriver->save($id, $result, $this->_resultCacheTTL);
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
// Cache hit.
|
||||
return $cached;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
if (is_numeric($stmt)) {
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the result cache id to use to store the result set cache entry.
|
||||
* If this is not explicitely set by the developer then a hash is automatically
|
||||
* generated for you.
|
||||
*
|
||||
* @param string $id
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setResultCacheId($id)
|
||||
{
|
||||
$this->_resultCacheId = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result cache id to use to store the result set cache entry.
|
||||
* Will return the configured id if it exists otherwise a hash will be
|
||||
* automatically generated for you.
|
||||
*
|
||||
* @return string $id
|
||||
*/
|
||||
protected function _getResultCacheId()
|
||||
{
|
||||
if ($this->_resultCacheId) {
|
||||
return $this->_resultCacheId;
|
||||
} else {
|
||||
$sql = $this->getSql();
|
||||
ksort($this->_hints);
|
||||
return md5(implode(";", (array)$sql) . var_export($this->_params, true) .
|
||||
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns a the resulting Statement object.
|
||||
*
|
||||
* @return Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
|
||||
*/
|
||||
abstract protected function _doExecute();
|
||||
|
||||
/**
|
||||
* Cleanup Query resource when clone is called.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->_params = array();
|
||||
$this->_paramTypes = array();
|
||||
$this->_hints = array();
|
||||
}
|
||||
}
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\Common\Cache\Cache,
|
||||
Doctrine\ORM\Mapping\Driver\Driver;
|
||||
|
||||
/**
|
||||
* Configuration container for all configuration options of Doctrine.
|
||||
* It combines all configuration options from DBAL & ORM.
|
||||
*
|
||||
* @since 2.0
|
||||
* @internal When adding a new configuration option just write a getter/setter pair.
|
||||
* @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 Configuration extends \Doctrine\DBAL\Configuration
|
||||
{
|
||||
/**
|
||||
* Sets the directory where Doctrine generates any necessary proxy class files.
|
||||
*
|
||||
* @param string $dir
|
||||
*/
|
||||
public function setProxyDir($dir)
|
||||
{
|
||||
$this->_attributes['proxyDir'] = $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the directory where Doctrine generates any necessary proxy class files.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProxyDir()
|
||||
{
|
||||
return isset($this->_attributes['proxyDir']) ?
|
||||
$this->_attributes['proxyDir'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean flag that indicates whether proxy classes should always be regenerated
|
||||
* during each script execution.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getAutoGenerateProxyClasses()
|
||||
{
|
||||
return isset($this->_attributes['autoGenerateProxyClasses']) ?
|
||||
$this->_attributes['autoGenerateProxyClasses'] : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean flag that indicates whether proxy classes should always be regenerated
|
||||
* during each script execution.
|
||||
*
|
||||
* @param boolean $bool
|
||||
*/
|
||||
public function setAutoGenerateProxyClasses($bool)
|
||||
{
|
||||
$this->_attributes['autoGenerateProxyClasses'] = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace where proxy classes reside.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProxyNamespace()
|
||||
{
|
||||
return isset($this->_attributes['proxyNamespace']) ?
|
||||
$this->_attributes['proxyNamespace'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace where proxy classes reside.
|
||||
*
|
||||
* @param string $ns
|
||||
*/
|
||||
public function setProxyNamespace($ns)
|
||||
{
|
||||
$this->_attributes['proxyNamespace'] = $ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @param Driver $driverImpl
|
||||
* @todo Force parameter to be a Closure to ensure lazy evaluation
|
||||
* (as soon as a metadata cache is in effect, the driver never needs to initialize).
|
||||
*/
|
||||
public function setMetadataDriverImpl(Driver $driverImpl)
|
||||
{
|
||||
$this->_attributes['metadataDriverImpl'] = $driverImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new default annotation driver with a correctly configured annotation reader.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return Mapping\Driver\AnnotationDriver
|
||||
*/
|
||||
public function newDefaultAnnotationDriver($paths = array())
|
||||
{
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
|
||||
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, (array)$paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a namespace under a certain alias.
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $namespace
|
||||
*/
|
||||
public function addEntityNamespace($alias, $namespace)
|
||||
{
|
||||
$this->_attributes['entityNamespaces'][$alias] = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a registered namespace alias to the full namespace.
|
||||
*
|
||||
* @param string $entityNamespaceAlias
|
||||
* @return string
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function getEntityNamespace($entityNamespaceAlias)
|
||||
{
|
||||
if ( ! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) {
|
||||
throw ORMException::unknownEntityNamespace($entityNamespaceAlias);
|
||||
}
|
||||
|
||||
return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entity alias map
|
||||
*
|
||||
* @param array $entityAliasMap
|
||||
* @return void
|
||||
*/
|
||||
public function setEntityNamespaces(array $entityNamespaces)
|
||||
{
|
||||
$this->_attributes['entityNamespaces'] = $entityNamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for the mapping metadata.
|
||||
*
|
||||
* @throws ORMException
|
||||
* @return Mapping\Driver\Driver
|
||||
*/
|
||||
public function getMetadataDriverImpl()
|
||||
{
|
||||
return isset($this->_attributes['metadataDriverImpl']) ?
|
||||
$this->_attributes['metadataDriverImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for query result caching.
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getResultCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['resultCacheImpl']) ?
|
||||
$this->_attributes['resultCacheImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for query result caching.
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setResultCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['resultCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for the query cache (SQL cache).
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getQueryCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['queryCacheImpl']) ?
|
||||
$this->_attributes['queryCacheImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for the query cache (SQL cache).
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setQueryCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['queryCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getMetadataCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['metadataCacheImpl']) ?
|
||||
$this->_attributes['metadataCacheImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setMetadataCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['metadataCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a named DQL query to the configuration.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @param string $dql The DQL query string.
|
||||
*/
|
||||
public function addNamedQuery($name, $dql)
|
||||
{
|
||||
$this->_attributes['namedQueries'][$name] = $dql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a previously registered named DQL query.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @return string The DQL query.
|
||||
*/
|
||||
public function getNamedQuery($name)
|
||||
{
|
||||
if ( ! isset($this->_attributes['namedQueries'][$name])) {
|
||||
throw ORMException::namedQueryNotFound($name);
|
||||
}
|
||||
return $this->_attributes['namedQueries'][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a named native query to the configuration.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @param string $sql The native SQL query string.
|
||||
* @param ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query.
|
||||
*/
|
||||
public function addNamedNativeQuery($name, $sql, Query\ResultSetMapping $rsm)
|
||||
{
|
||||
$this->_attributes['namedNativeQueries'][$name] = array($sql, $rsm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the components of a previously registered named native query.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @return array A tuple with the first element being the SQL string and the second
|
||||
* element being the ResultSetMapping.
|
||||
*/
|
||||
public function getNamedNativeQuery($name)
|
||||
{
|
||||
if ( ! isset($this->_attributes['namedNativeQueries'][$name])) {
|
||||
throw ORMException::namedNativeQueryNotFound($name);
|
||||
}
|
||||
return $this->_attributes['namedNativeQueries'][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that this Configuration instance contains settings that are
|
||||
* suitable for a production environment.
|
||||
*
|
||||
* @throws ORMException If a configuration setting has a value that is not
|
||||
* suitable for a production environment.
|
||||
*/
|
||||
public function ensureProductionSettings()
|
||||
{
|
||||
if ( !$this->getQueryCacheImpl()) {
|
||||
throw ORMException::queryCacheNotConfigured();
|
||||
}
|
||||
if ( !$this->getMetadataCacheImpl()) {
|
||||
throw ORMException::metadataCacheNotConfigured();
|
||||
}
|
||||
if ($this->getAutoGenerateProxyClasses()) {
|
||||
throw ORMException::proxyClassesAlwaysRegenerating();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom DQL function that produces a string value.
|
||||
* Such a function can then be used in any DQL statement in any place where string
|
||||
* functions are allowed.
|
||||
*
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $className
|
||||
*/
|
||||
public function addCustomStringFunction($name, $className)
|
||||
{
|
||||
$this->_attributes['customStringFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom string DQL function.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomStringFunction($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
return isset($this->_attributes['customStringFunctions'][$name]) ?
|
||||
$this->_attributes['customStringFunctions'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of custom DQL string functions.
|
||||
*
|
||||
* Keys must be function names and values the FQCN of the implementing class.
|
||||
* The function names will be case-insensitive in DQL.
|
||||
*
|
||||
* Any previously added string functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL string functions.
|
||||
*/
|
||||
public function setCustomStringFunctions(array $functions)
|
||||
{
|
||||
$this->_attributes['customStringFunctions'] = array_change_key_case($functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom DQL function that produces a numeric value.
|
||||
* Such a function can then be used in any DQL statement in any place where numeric
|
||||
* functions are allowed.
|
||||
*
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $className
|
||||
*/
|
||||
public function addCustomNumericFunction($name, $className)
|
||||
{
|
||||
$this->_attributes['customNumericFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom numeric DQL function.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomNumericFunction($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
return isset($this->_attributes['customNumericFunctions'][$name]) ?
|
||||
$this->_attributes['customNumericFunctions'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of custom DQL numeric functions.
|
||||
*
|
||||
* Keys must be function names and values the FQCN of the implementing class.
|
||||
* The function names will be case-insensitive in DQL.
|
||||
*
|
||||
* Any previously added numeric functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL numeric functions.
|
||||
*/
|
||||
public function setCustomNumericFunctions(array $functions)
|
||||
{
|
||||
$this->_attributes['customNumericFunctions'] = array_change_key_case($functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom DQL function that produces a date/time value.
|
||||
* Such a function can then be used in any DQL statement in any place where date/time
|
||||
* functions are allowed.
|
||||
*
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $className
|
||||
*/
|
||||
public function addCustomDatetimeFunction($name, $className)
|
||||
{
|
||||
$this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom date/time DQL function.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomDatetimeFunction($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
return isset($this->_attributes['customDatetimeFunctions'][$name]) ?
|
||||
$this->_attributes['customDatetimeFunctions'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of custom DQL date/time functions.
|
||||
*
|
||||
* Keys must be function names and values the FQCN of the implementing class.
|
||||
* The function names will be case-insensitive in DQL.
|
||||
*
|
||||
* Any previously added date/time functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL date/time functions.
|
||||
*/
|
||||
public function setCustomDatetimeFunctions(array $functions)
|
||||
{
|
||||
$this->_attributes['customDatetimeFunctions'] = array_change_key_case($functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hydrator class for the given hydration mode name.
|
||||
*
|
||||
* @param string $modeName The hydration mode name.
|
||||
* @return string $hydrator The hydrator class name.
|
||||
*/
|
||||
public function getCustomHydrationMode($modeName)
|
||||
{
|
||||
return isset($this->_attributes['customHydrationModes'][$modeName]) ?
|
||||
$this->_attributes['customHydrationModes'][$modeName] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom hydration mode.
|
||||
*
|
||||
* @param string $modeName The hydration mode name.
|
||||
* @param string $hydrator The hydrator class name.
|
||||
*/
|
||||
public function addCustomHydrationMode($modeName, $hydrator)
|
||||
{
|
||||
$this->_attributes['customHydrationModes'][$modeName] = $hydrator;
|
||||
}
|
||||
}
|
||||
+715
@@ -0,0 +1,715 @@
|
||||
<?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;
|
||||
|
||||
use Closure, Exception,
|
||||
Doctrine\Common\EventManager,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\DBAL\LockMode,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Mapping\ClassMetadataFactory,
|
||||
Doctrine\ORM\Query\ResultSetMapping,
|
||||
Doctrine\ORM\Proxy\ProxyFactory;
|
||||
|
||||
/**
|
||||
* The EntityManager is the central access point to ORM functionality.
|
||||
*
|
||||
* @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 EntityManager
|
||||
{
|
||||
/**
|
||||
* The used Configuration.
|
||||
*
|
||||
* @var Doctrine\ORM\Configuration
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* The database connection used by the EntityManager.
|
||||
*
|
||||
* @var Doctrine\DBAL\Connection
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
/**
|
||||
* The metadata factory, used to retrieve the ORM metadata of entity classes.
|
||||
*
|
||||
* @var Doctrine\ORM\Mapping\ClassMetadataFactory
|
||||
*/
|
||||
private $metadataFactory;
|
||||
|
||||
/**
|
||||
* The EntityRepository instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $repositories = array();
|
||||
|
||||
/**
|
||||
* The UnitOfWork used to coordinate object-level transactions.
|
||||
*
|
||||
* @var Doctrine\ORM\UnitOfWork
|
||||
*/
|
||||
private $unitOfWork;
|
||||
|
||||
/**
|
||||
* The event manager that is the central point of the event system.
|
||||
*
|
||||
* @var Doctrine\Common\EventManager
|
||||
*/
|
||||
private $eventManager;
|
||||
|
||||
/**
|
||||
* The maintained (cached) hydrators. One instance per type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $hydrators = array();
|
||||
|
||||
/**
|
||||
* The proxy factory used to create dynamic proxies.
|
||||
*
|
||||
* @var Doctrine\ORM\Proxy\ProxyFactory
|
||||
*/
|
||||
private $proxyFactory;
|
||||
|
||||
/**
|
||||
* @var ExpressionBuilder The expression builder instance used to generate query expressions.
|
||||
*/
|
||||
private $expressionBuilder;
|
||||
|
||||
/**
|
||||
* Whether the EntityManager is closed or not.
|
||||
*/
|
||||
private $closed = false;
|
||||
|
||||
/**
|
||||
* Creates a new EntityManager that operates on the given database connection
|
||||
* and uses the given Configuration and EventManager implementations.
|
||||
*
|
||||
* @param Doctrine\DBAL\Connection $conn
|
||||
* @param Doctrine\ORM\Configuration $config
|
||||
* @param Doctrine\Common\EventManager $eventManager
|
||||
*/
|
||||
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->config = $config;
|
||||
$this->eventManager = $eventManager;
|
||||
$this->metadataFactory = new ClassMetadataFactory($this);
|
||||
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
|
||||
$this->unitOfWork = new UnitOfWork($this);
|
||||
$this->proxyFactory = new ProxyFactory($this,
|
||||
$config->getProxyDir(),
|
||||
$config->getProxyNamespace(),
|
||||
$config->getAutoGenerateProxyClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database connection object used by the EntityManager.
|
||||
*
|
||||
* @return Doctrine\DBAL\Connection
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the metadata factory used to gather the metadata of classes.
|
||||
*
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadataFactory
|
||||
*/
|
||||
public function getMetadataFactory()
|
||||
{
|
||||
return $this->metadataFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder();
|
||||
* $expr = $em->getExpressionBuilder();
|
||||
* $qb->select('u')->from('User', 'u')
|
||||
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
|
||||
* </code>
|
||||
*
|
||||
* @return ExpressionBuilder
|
||||
*/
|
||||
public function getExpressionBuilder()
|
||||
{
|
||||
if ($this->expressionBuilder === null) {
|
||||
$this->expressionBuilder = new Query\Expr;
|
||||
}
|
||||
return $this->expressionBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a transaction on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.beginTransaction().
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a function in a transaction.
|
||||
*
|
||||
* The function gets passed this EntityManager instance as an (optional) parameter.
|
||||
*
|
||||
* {@link flush} is invoked prior to transaction commit.
|
||||
*
|
||||
* If an exception occurs during execution of the function or flushing or transaction commit,
|
||||
* the transaction is rolled back, the EntityManager closed and the exception re-thrown.
|
||||
*
|
||||
* @param Closure $func The function to execute transactionally.
|
||||
*/
|
||||
public function transactional(Closure $func)
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
try {
|
||||
$func($this);
|
||||
$this->flush();
|
||||
$this->conn->commit();
|
||||
} catch (Exception $e) {
|
||||
$this->close();
|
||||
$this->conn->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a transaction on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.commit().
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->conn->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a rollback on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.rollback().
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
$this->conn->rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ORM metadata descriptor for a class.
|
||||
*
|
||||
* The class name must be the fully-qualified class name without a leading backslash
|
||||
* (as it is returned by get_class($obj)) or an aliased class name.
|
||||
*
|
||||
* Examples:
|
||||
* MyProject\Domain\User
|
||||
* sales:PriceRequest
|
||||
*
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
* @internal Performance-sensitive method.
|
||||
*/
|
||||
public function getClassMetadata($className)
|
||||
{
|
||||
return $this->metadataFactory->getMetadataFor($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Query object.
|
||||
*
|
||||
* @param string The DQL string.
|
||||
* @return Doctrine\ORM\Query
|
||||
*/
|
||||
public function createQuery($dql = "")
|
||||
{
|
||||
$query = new Query($this);
|
||||
if ( ! empty($dql)) {
|
||||
$query->setDql($dql);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Query from a named query.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Doctrine\ORM\Query
|
||||
*/
|
||||
public function createNamedQuery($name)
|
||||
{
|
||||
return $this->createQuery($this->config->getNamedQuery($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a native SQL query.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param ResultSetMapping $rsm The ResultSetMapping to use.
|
||||
* @return NativeQuery
|
||||
*/
|
||||
public function createNativeQuery($sql, ResultSetMapping $rsm)
|
||||
{
|
||||
$query = new NativeQuery($this);
|
||||
$query->setSql($sql);
|
||||
$query->setResultSetMapping($rsm);
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a NativeQuery from a named native query.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Doctrine\ORM\NativeQuery
|
||||
*/
|
||||
public function createNamedNativeQuery($name)
|
||||
{
|
||||
list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
|
||||
return $this->createNativeQuery($sql, $rsm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a QueryBuilder instance
|
||||
*
|
||||
* @return QueryBuilder $qb
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes all changes to objects that have been queued up to now to the database.
|
||||
* This effectively synchronizes the in-memory state of managed objects with the
|
||||
* database.
|
||||
*
|
||||
* @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that
|
||||
* makes use of optimistic locking fails.
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an Entity by its identifier.
|
||||
*
|
||||
* This is just a convenient shortcut for getRepository($entityName)->find($id).
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param mixed $identifier
|
||||
* @param int $lockMode
|
||||
* @param int $lockVersion
|
||||
* @return object
|
||||
*/
|
||||
public function find($entityName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
|
||||
{
|
||||
return $this->getRepository($entityName)->find($identifier, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the entity identified by the given type and identifier
|
||||
* without actually loading it, if the entity is not yet loaded.
|
||||
*
|
||||
* @param string $entityName The name of the entity type.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
* @return object The entity reference.
|
||||
*/
|
||||
public function getReference($entityName, $identifier)
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
|
||||
// Check identity map first, if its already in there just return it.
|
||||
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
|
||||
return $entity;
|
||||
}
|
||||
if ($class->subClasses) {
|
||||
$entity = $this->find($entityName, $identifier);
|
||||
} else {
|
||||
if ( ! is_array($identifier)) {
|
||||
$identifier = array($class->identifier[0] => $identifier);
|
||||
}
|
||||
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
|
||||
$this->unitOfWork->registerManaged($entity, $identifier, array());
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a partial reference to the entity identified by the given type and identifier
|
||||
* without actually loading it, if the entity is not yet loaded.
|
||||
*
|
||||
* The returned reference may be a partial object if the entity is not yet loaded/managed.
|
||||
* If it is a partial object it will not initialize the rest of the entity state on access.
|
||||
* Thus you can only ever safely access the identifier of an entity obtained through
|
||||
* this method.
|
||||
*
|
||||
* The use-cases for partial references involve maintaining bidirectional associations
|
||||
* without loading one side of the association or to update an entity without loading it.
|
||||
* Note, however, that in the latter case the original (persistent) entity data will
|
||||
* never be visible to the application (especially not event listeners) as it will
|
||||
* never be loaded in the first place.
|
||||
*
|
||||
* @param string $entityName The name of the entity type.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
* @return object The (partial) entity reference.
|
||||
*/
|
||||
public function getPartialReference($entityName, $identifier)
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
|
||||
// Check identity map first, if its already in there just return it.
|
||||
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
|
||||
return $entity;
|
||||
}
|
||||
if ( ! is_array($identifier)) {
|
||||
$identifier = array($class->identifier[0] => $identifier);
|
||||
}
|
||||
|
||||
$entity = $class->newInstance();
|
||||
$class->setIdentifierValues($entity, $identifier);
|
||||
$this->unitOfWork->registerManaged($entity, $identifier, array());
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached.
|
||||
*
|
||||
* @param string $entityName
|
||||
*/
|
||||
public function clear($entityName = null)
|
||||
{
|
||||
if ($entityName === null) {
|
||||
$this->unitOfWork->clear();
|
||||
} else {
|
||||
//TODO
|
||||
throw new ORMException("EntityManager#clear(\$entityName) not yet implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached. The EntityManager may no longer
|
||||
* be used after it is closed.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->clear();
|
||||
$this->closed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the EntityManager to make an instance managed and persistent.
|
||||
*
|
||||
* The entity will be entered into the database at or before transaction
|
||||
* commit or as a result of the flush operation.
|
||||
*
|
||||
* NOTE: The persist operation always considers entities that are not yet known to
|
||||
* this EntityManager as NEW. Do not pass detached entities to the persist operation.
|
||||
*
|
||||
* @param object $object The instance to make managed and persistent.
|
||||
*/
|
||||
public function persist($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->persist($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entity instance.
|
||||
*
|
||||
* A removed entity will be removed from the database at or before transaction commit
|
||||
* or as a result of the flush operation.
|
||||
*
|
||||
* @param object $entity The entity instance to remove.
|
||||
*/
|
||||
public function remove($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->remove($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the persistent state of an entity from the database,
|
||||
* overriding any local changes that have not yet been persisted.
|
||||
*
|
||||
* @param object $entity The entity to refresh.
|
||||
*/
|
||||
public function refresh($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->refresh($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches an entity from the EntityManager, causing a managed entity to
|
||||
* become detached. Unflushed changes made to the entity if any
|
||||
* (including removal of the entity), will not be synchronized to the database.
|
||||
* Entities which previously referenced the detached entity will continue to
|
||||
* reference it.
|
||||
*
|
||||
* @param object $entity The entity to detach.
|
||||
*/
|
||||
public function detach($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->unitOfWork->detach($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the state of a detached entity into the persistence context
|
||||
* of this EntityManager and returns the managed copy of the entity.
|
||||
* The entity passed to merge will not become associated/managed with this EntityManager.
|
||||
*
|
||||
* @param object $entity The detached entity to merge into the persistence context.
|
||||
* @return object The managed copy of the entity.
|
||||
*/
|
||||
public function merge($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
return $this->unitOfWork->merge($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given entity. Can create a shallow or a deep copy.
|
||||
*
|
||||
* @param object $entity The entity to copy.
|
||||
* @return object The new entity.
|
||||
* @todo Implementation need. This is necessary since $e2 = clone $e1; throws an E_FATAL when access anything on $e:
|
||||
* Fatal error: Maximum function nesting level of '100' reached, aborting!
|
||||
*/
|
||||
public function copy($entity, $deep = false)
|
||||
{
|
||||
throw new \BadMethodCallException("Not implemented.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lock on the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param int $lockMode
|
||||
* @param int $lockVersion
|
||||
* @throws OptimisticLockException
|
||||
* @throws PessimisticLockException
|
||||
*/
|
||||
public function lock($entity, $lockMode, $lockVersion = null)
|
||||
{
|
||||
$this->unitOfWork->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repository for an entity class.
|
||||
*
|
||||
* @param string $entityName The name of the entity.
|
||||
* @return EntityRepository The repository class.
|
||||
*/
|
||||
public function getRepository($entityName)
|
||||
{
|
||||
$entityName = ltrim($entityName, '\\');
|
||||
if (isset($this->repositories[$entityName])) {
|
||||
return $this->repositories[$entityName];
|
||||
}
|
||||
|
||||
$metadata = $this->getClassMetadata($entityName);
|
||||
$customRepositoryClassName = $metadata->customRepositoryClassName;
|
||||
|
||||
if ($customRepositoryClassName !== null) {
|
||||
$repository = new $customRepositoryClassName($this, $metadata);
|
||||
} else {
|
||||
$repository = new EntityRepository($this, $metadata);
|
||||
}
|
||||
|
||||
$this->repositories[$entityName] = $repository;
|
||||
|
||||
return $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an entity instance is managed in this EntityManager.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
|
||||
*/
|
||||
public function contains($entity)
|
||||
{
|
||||
return $this->unitOfWork->isScheduledForInsert($entity) ||
|
||||
$this->unitOfWork->isInIdentityMap($entity) &&
|
||||
! $this->unitOfWork->isScheduledForDelete($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the EventManager used by the EntityManager.
|
||||
*
|
||||
* @return Doctrine\Common\EventManager
|
||||
*/
|
||||
public function getEventManager()
|
||||
{
|
||||
return $this->eventManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Configuration used by the EntityManager.
|
||||
*
|
||||
* @return Doctrine\ORM\Configuration
|
||||
*/
|
||||
public function getConfiguration()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the EntityManager is closed or currently not active.
|
||||
*
|
||||
* @throws ORMException If the EntityManager is closed.
|
||||
*/
|
||||
private function errorIfClosed()
|
||||
{
|
||||
if ($this->closed) {
|
||||
throw ORMException::entityManagerClosed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
|
||||
*
|
||||
* @return Doctrine\ORM\UnitOfWork
|
||||
*/
|
||||
public function getUnitOfWork()
|
||||
{
|
||||
return $this->unitOfWork;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a hydrator for the given hydration mode.
|
||||
*
|
||||
* This method caches the hydrator instances which is used for all queries that don't
|
||||
* selectively iterate over the result.
|
||||
*
|
||||
* @param int $hydrationMode
|
||||
* @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||
*/
|
||||
public function getHydrator($hydrationMode)
|
||||
{
|
||||
if ( ! isset($this->hydrators[$hydrationMode])) {
|
||||
$this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
|
||||
}
|
||||
|
||||
return $this->hydrators[$hydrationMode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance for the given hydration mode.
|
||||
*
|
||||
* @param int $hydrationMode
|
||||
* @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||
*/
|
||||
public function newHydrator($hydrationMode)
|
||||
{
|
||||
switch ($hydrationMode) {
|
||||
case Query::HYDRATE_OBJECT:
|
||||
$hydrator = new Internal\Hydration\ObjectHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_ARRAY:
|
||||
$hydrator = new Internal\Hydration\ArrayHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SCALAR:
|
||||
$hydrator = new Internal\Hydration\ScalarHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SINGLE_SCALAR:
|
||||
$hydrator = new Internal\Hydration\SingleScalarHydrator($this);
|
||||
break;
|
||||
default:
|
||||
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
|
||||
$hydrator = new $class($this);
|
||||
break;
|
||||
}
|
||||
throw ORMException::invalidHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
return $hydrator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the proxy factory used by the EntityManager to create entity proxies.
|
||||
*
|
||||
* @return ProxyFactory
|
||||
*/
|
||||
public function getProxyFactory()
|
||||
{
|
||||
return $this->proxyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create EntityManager instances.
|
||||
*
|
||||
* @param mixed $conn An array with the connection parameters or an existing
|
||||
* Connection instance.
|
||||
* @param Configuration $config The Configuration instance to use.
|
||||
* @param EventManager $eventManager The EventManager instance to use.
|
||||
* @return EntityManager The created EntityManager.
|
||||
*/
|
||||
public static function create($conn, Configuration $config, EventManager $eventManager = null)
|
||||
{
|
||||
if (!$config->getMetadataDriverImpl()) {
|
||||
throw ORMException::missingMappingDriverImpl();
|
||||
}
|
||||
|
||||
if (is_array($conn)) {
|
||||
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager()));
|
||||
} else if ($conn instanceof Connection) {
|
||||
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
|
||||
throw ORMException::mismatchedEventManager();
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Invalid argument: " . $conn);
|
||||
}
|
||||
|
||||
return new EntityManager($conn, $config, $conn->getEventManager());
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Exception thrown when a Proxy fails to retrieve an Entity result.
|
||||
*
|
||||
* @author robo
|
||||
* @since 2.0
|
||||
*/
|
||||
class EntityNotFoundException extends ORMException
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Entity was not found.');
|
||||
}
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
/**
|
||||
* An EntityRepository serves as a repository for entities with generic as well as
|
||||
* business specific methods for retrieving entities.
|
||||
*
|
||||
* This class is designed for inheritance and users can subclass this class to
|
||||
* write their own repositories with business-specific methods to locate entities.
|
||||
*
|
||||
* @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 EntityRepository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_entityName;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $_em;
|
||||
|
||||
/**
|
||||
* @var Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
protected $_class;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>EntityRepository</tt>.
|
||||
*
|
||||
* @param EntityManager $em The EntityManager to use.
|
||||
* @param ClassMetadata $classMetadata The class descriptor.
|
||||
*/
|
||||
public function __construct($em, Mapping\ClassMetadata $class)
|
||||
{
|
||||
$this->_entityName = $class->name;
|
||||
$this->_em = $em;
|
||||
$this->_class = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new QueryBuilder instance that is prepopulated for this entity name
|
||||
*
|
||||
* @param string $alias
|
||||
* @return QueryBuilder $qb
|
||||
*/
|
||||
public function createQueryBuilder($alias)
|
||||
{
|
||||
return $this->_em->createQueryBuilder()
|
||||
->select($alias)
|
||||
->from($this->_entityName, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the repository, causing all managed entities to become detached.
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->_em->clear($this->_class->rootEntityName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an entity by its primary key / identifier.
|
||||
*
|
||||
* @param $id The identifier.
|
||||
* @param int $lockMode
|
||||
* @param int $lockVersion
|
||||
* @return object The entity.
|
||||
*/
|
||||
public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
|
||||
{
|
||||
// Check identity map first
|
||||
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
|
||||
if ($lockMode != LockMode::NONE) {
|
||||
$this->_em->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
return $entity; // Hit!
|
||||
}
|
||||
|
||||
if ( ! is_array($id) || count($id) <= 1) {
|
||||
// @todo FIXME: Not correct. Relies on specific order.
|
||||
$value = is_array($id) ? array_values($id) : array($id);
|
||||
$id = array_combine($this->_class->identifier, $value);
|
||||
}
|
||||
|
||||
if ($lockMode == LockMode::NONE) {
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
|
||||
} else if ($lockMode == LockMode::OPTIMISTIC) {
|
||||
if (!$this->_class->isVersioned) {
|
||||
throw OptimisticLockException::notVersioned($this->_entityName);
|
||||
}
|
||||
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
|
||||
|
||||
$this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
|
||||
|
||||
return $entity;
|
||||
} else {
|
||||
if (!$this->_em->getConnection()->isTransactionActive()) {
|
||||
throw TransactionRequiredException::transactionRequired();
|
||||
}
|
||||
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all entities in the repository.
|
||||
*
|
||||
* @return array The entities.
|
||||
*/
|
||||
public function findAll()
|
||||
{
|
||||
return $this->findBy(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities by a set of criteria.
|
||||
*
|
||||
* @param array $criteria
|
||||
* @return array
|
||||
*/
|
||||
public function findBy(array $criteria)
|
||||
{
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single entity by a set of criteria.
|
||||
*
|
||||
* @param array $criteria
|
||||
* @return object
|
||||
*/
|
||||
public function findOneBy(array $criteria)
|
||||
{
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds support for magic finders.
|
||||
*
|
||||
* @return array|object The found entity/entities.
|
||||
* @throws BadMethodCallException If the method called is an invalid find* method
|
||||
* or no find* method at all and therefore an invalid
|
||||
* method call.
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
if (substr($method, 0, 6) == 'findBy') {
|
||||
$by = substr($method, 6, strlen($method));
|
||||
$method = 'findBy';
|
||||
} else if (substr($method, 0, 9) == 'findOneBy') {
|
||||
$by = substr($method, 9, strlen($method));
|
||||
$method = 'findOneBy';
|
||||
} else {
|
||||
throw new \BadMethodCallException(
|
||||
"Undefined method '$method'. The method name must start with ".
|
||||
"either findBy or findOneBy!"
|
||||
);
|
||||
}
|
||||
|
||||
if ( !isset($arguments[0])) {
|
||||
// we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
|
||||
throw ORMException::findByRequiresParameter($method.$by);
|
||||
}
|
||||
|
||||
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
|
||||
|
||||
if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
|
||||
return $this->$method(array($fieldName => $arguments[0]));
|
||||
} else {
|
||||
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getEntityName()
|
||||
{
|
||||
return $this->_entityName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Mapping\ClassMetadata
|
||||
*/
|
||||
protected function getClassMetadata()
|
||||
{
|
||||
return $this->_class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Container for all ORM events.
|
||||
*
|
||||
* This class cannot be instantiated.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
final class Events
|
||||
{
|
||||
private function __construct() {}
|
||||
/**
|
||||
* The preRemove event occurs for a given entity before the respective
|
||||
* EntityManager remove operation for that entity is executed.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const preRemove = 'preRemove';
|
||||
/**
|
||||
* The postRemove event occurs for an entity after the entity has
|
||||
* been deleted. It will be invoked after the database delete operations.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postRemove = 'postRemove';
|
||||
/**
|
||||
* The prePersist event occurs for a given entity before the respective
|
||||
* EntityManager persist operation for that entity is executed.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const prePersist = 'prePersist';
|
||||
/**
|
||||
* The postPersist event occurs for an entity after the entity has
|
||||
* been made persistent. It will be invoked after the database insert operations.
|
||||
* Generated primary key values are available in the postPersist event.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postPersist = 'postPersist';
|
||||
/**
|
||||
* The preUpdate event occurs before the database update operations to
|
||||
* entity data.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const preUpdate = 'preUpdate';
|
||||
/**
|
||||
* The postUpdate event occurs after the database update operations to
|
||||
* entity data.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postUpdate = 'postUpdate';
|
||||
/**
|
||||
* The postLoad event occurs for an entity after the entity has been loaded
|
||||
* into the current EntityManager from the database or after the refresh operation
|
||||
* has been applied to it.
|
||||
*
|
||||
* Note that the postLoad event occurs for an entity before any associations have been
|
||||
* initialized. Therefore it is not safe to access associations in a postLoad callback
|
||||
* or event handler.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postLoad = 'postLoad';
|
||||
/**
|
||||
* The loadClassMetadata event occurs after the mapping metadata for a class
|
||||
* has been loaded from a mapping source (annotations/xml/yaml).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const loadClassMetadata = 'loadClassMetadata';
|
||||
|
||||
/**
|
||||
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
* after any changes to managed entities have been determined but before any
|
||||
* actual database operations are executed. The event is only raised if there is
|
||||
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
|
||||
* the onFlush event is not raised.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const onFlush = 'onFlush';
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Represents a native SQL query.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
final class NativeQuery extends AbstractQuery
|
||||
{
|
||||
private $_sql;
|
||||
|
||||
/**
|
||||
* Sets the SQL of the query.
|
||||
*
|
||||
* @param string $sql
|
||||
* @return NativeQuery This query instance.
|
||||
*/
|
||||
public function setSQL($sql)
|
||||
{
|
||||
$this->_sql = $sql;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL query.
|
||||
*
|
||||
* @return mixed The built SQL query or an array of all SQL queries.
|
||||
* @override
|
||||
*/
|
||||
public function getSQL()
|
||||
{
|
||||
return $this->_sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _doExecute()
|
||||
{
|
||||
$stmt = $this->_em->getConnection()->prepare($this->_sql);
|
||||
$params = $this->_params;
|
||||
foreach ($params as $key => $value) {
|
||||
if (isset($this->_paramTypes[$key])) {
|
||||
$stmt->bindValue($key, $value, $this->_paramTypes[$key]);
|
||||
} else {
|
||||
$stmt->bindValue($key, $value);
|
||||
}
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Exception thrown when an ORM query unexpectedly does not return any results.
|
||||
*
|
||||
* @author robo
|
||||
* @since 2.0
|
||||
*/
|
||||
class NoResultException extends ORMException
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('No result was found for query although at least one row was expected.');
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Exception thrown when an ORM query unexpectedly returns more than one result.
|
||||
*
|
||||
* @author robo
|
||||
* @since 2.0
|
||||
*/
|
||||
class NonUniqueResultException extends ORMException {}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<?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;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Base exception class for all ORM exceptions.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ORMException extends Exception
|
||||
{
|
||||
public static function missingMappingDriverImpl()
|
||||
{
|
||||
return new self("It's a requirement to specify a Metadata Driver and pass it ".
|
||||
"to Doctrine\ORM\Configuration::setMetadataDriverImpl().");
|
||||
}
|
||||
|
||||
public static function entityMissingAssignedId($entity)
|
||||
{
|
||||
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID.");
|
||||
}
|
||||
|
||||
public static function unrecognizedField($field)
|
||||
{
|
||||
return new self("Unrecognized field: $field");
|
||||
}
|
||||
|
||||
public static function invalidFlushMode($mode)
|
||||
{
|
||||
return new self("'$mode' is an invalid flush mode.");
|
||||
}
|
||||
|
||||
public static function entityManagerClosed()
|
||||
{
|
||||
return new self("The EntityManager is closed.");
|
||||
}
|
||||
|
||||
public static function invalidHydrationMode($mode)
|
||||
{
|
||||
return new self("'$mode' is an invalid hydration mode.");
|
||||
}
|
||||
|
||||
public static function mismatchedEventManager()
|
||||
{
|
||||
return new self("Cannot use different EventManager instances for EntityManager and Connection.");
|
||||
}
|
||||
|
||||
public static function findByRequiresParameter($methodName)
|
||||
{
|
||||
return new self("You need to pass a parameter to '".$methodName."'");
|
||||
}
|
||||
|
||||
public static function invalidFindByCall($entityName, $fieldName, $method)
|
||||
{
|
||||
return new self(
|
||||
"Entity '".$entityName."' has no field '".$fieldName."'. ".
|
||||
"You can therefore not call '".$method."' on the entities' repository"
|
||||
);
|
||||
}
|
||||
|
||||
public static function invalidFindByInverseAssociation($entityName, $associationFieldName)
|
||||
{
|
||||
return new self(
|
||||
"You cannot search for the association field '".$entityName."#".$associationFieldName."', ".
|
||||
"because it is the inverse side of an association. Find methods only work on owning side associations."
|
||||
);
|
||||
}
|
||||
|
||||
public static function invalidResultCacheDriver() {
|
||||
return new self("Invalid result cache driver; it must implement \Doctrine\Common\Cache\Cache.");
|
||||
}
|
||||
|
||||
public static function notSupported() {
|
||||
return new self("This behaviour is (currently) not supported by Doctrine 2");
|
||||
}
|
||||
|
||||
public static function queryCacheNotConfigured()
|
||||
{
|
||||
return new self('Query Cache is not configured.');
|
||||
}
|
||||
|
||||
public static function metadataCacheNotConfigured()
|
||||
{
|
||||
return new self('Class Metadata Cache is not configured.');
|
||||
}
|
||||
|
||||
public static function proxyClassesAlwaysRegenerating()
|
||||
{
|
||||
return new self('Proxy Classes are always regenerating.');
|
||||
}
|
||||
|
||||
public static function unknownEntityNamespace($entityNamespaceAlias)
|
||||
{
|
||||
return new self(
|
||||
"Unknown Entity namespace alias '$entityNamespaceAlias'."
|
||||
);
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* An OptimisticLockException is thrown when a version check on an object
|
||||
* that uses optimistic locking through a version field fails.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
*/
|
||||
class OptimisticLockException extends ORMException
|
||||
{
|
||||
private $entity;
|
||||
|
||||
public function __construct($msg, $entity)
|
||||
{
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity that caused the exception.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getEntity()
|
||||
{
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
public static function lockFailed($entity)
|
||||
{
|
||||
return new self("The optimistic lock on an entity failed.", $entity);
|
||||
}
|
||||
|
||||
public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion)
|
||||
{
|
||||
return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity);
|
||||
}
|
||||
|
||||
public static function notVersioned($entityName)
|
||||
{
|
||||
return new self("Cannot obtain optimistic lock on unversioned entity " . $entityName, null);
|
||||
}
|
||||
}
|
||||
+681
@@ -0,0 +1,681 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\Common\Collections\Collection,
|
||||
Closure;
|
||||
|
||||
/**
|
||||
* A PersistentCollection represents a collection of elements that have persistent state.
|
||||
*
|
||||
* Collections of entities represent only the associations (links) to those entities.
|
||||
* That means, if the collection is part of a many-many mapping and you remove
|
||||
* entities from the collection, only the links in the relation table are removed (on flush).
|
||||
* Similarly, if you remove entities from a collection that is part of a one-many
|
||||
* mapping this will only result in the nulling out of the foreign keys on flush.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||
* @todo Design for inheritance to allow custom implementations?
|
||||
*/
|
||||
final class PersistentCollection implements Collection
|
||||
{
|
||||
/**
|
||||
* A snapshot of the collection at the moment it was fetched from the database.
|
||||
* This is used to create a diff of the collection at commit time.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $snapshot = array();
|
||||
|
||||
/**
|
||||
* The entity that owns this collection.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $owner;
|
||||
|
||||
/**
|
||||
* The association mapping the collection belongs to.
|
||||
* This is currently either a OneToManyMapping or a ManyToManyMapping.
|
||||
*
|
||||
* @var Doctrine\ORM\Mapping\AssociationMapping
|
||||
*/
|
||||
private $association;
|
||||
|
||||
/**
|
||||
* The EntityManager that manages the persistence of the collection.
|
||||
*
|
||||
* @var Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* The name of the field on the target entities that points to the owner
|
||||
* of the collection. This is only set if the association is bi-directional.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $backRefFieldName;
|
||||
|
||||
/**
|
||||
* The class descriptor of the collection's entity type.
|
||||
*/
|
||||
private $typeClass;
|
||||
|
||||
/**
|
||||
* Whether the collection is dirty and needs to be synchronized with the database
|
||||
* when the UnitOfWork that manages its persistent state commits.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $isDirty = false;
|
||||
|
||||
/**
|
||||
* Whether the collection has already been initialized.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $initialized = true;
|
||||
|
||||
/**
|
||||
* The wrapped Collection instance.
|
||||
*
|
||||
* @var Collection
|
||||
*/
|
||||
private $coll;
|
||||
|
||||
/**
|
||||
* Creates a new persistent collection.
|
||||
*
|
||||
* @param EntityManager $em The EntityManager the collection will be associated with.
|
||||
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
|
||||
* @param array The collection elements.
|
||||
*/
|
||||
public function __construct(EntityManager $em, $class, $coll)
|
||||
{
|
||||
$this->coll = $coll;
|
||||
$this->em = $em;
|
||||
$this->typeClass = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Sets the collection's owning entity together with the AssociationMapping that
|
||||
* describes the association between the owner and the elements of the collection.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param AssociationMapping $assoc
|
||||
*/
|
||||
public function setOwner($entity, array $assoc)
|
||||
{
|
||||
$this->owner = $entity;
|
||||
$this->association = $assoc;
|
||||
$this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy'];
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Gets the collection owner.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getOwner()
|
||||
{
|
||||
return $this->owner;
|
||||
}
|
||||
|
||||
public function getTypeClass()
|
||||
{
|
||||
return $this->typeClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Adds an element to a collection during hydration. This will automatically
|
||||
* complete bidirectional associations in the case of a one-to-many association.
|
||||
*
|
||||
* @param mixed $element The element to add.
|
||||
*/
|
||||
public function hydrateAdd($element)
|
||||
{
|
||||
$this->coll->add($element);
|
||||
// If _backRefFieldName is set and its a one-to-many association,
|
||||
// we need to set the back reference.
|
||||
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||
// Set back reference to owner
|
||||
$this->typeClass->reflFields[$this->backRefFieldName]
|
||||
->setValue($element, $this->owner);
|
||||
$this->em->getUnitOfWork()->setOriginalEntityProperty(
|
||||
spl_object_hash($element),
|
||||
$this->backRefFieldName,
|
||||
$this->owner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Sets a keyed element in the collection during hydration.
|
||||
*
|
||||
* @param mixed $key The key to set.
|
||||
* $param mixed $value The element to set.
|
||||
*/
|
||||
public function hydrateSet($key, $element)
|
||||
{
|
||||
$this->coll->set($key, $element);
|
||||
// If _backRefFieldName is set, then the association is bidirectional
|
||||
// and we need to set the back reference.
|
||||
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||
// Set back reference to owner
|
||||
$this->typeClass->reflFields[$this->backRefFieldName]
|
||||
->setValue($element, $this->owner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the collection by loading its contents from the database
|
||||
* if the collection is not yet initialized.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
if ( ! $this->initialized && $this->association) {
|
||||
if ($this->isDirty) {
|
||||
// Has NEW objects added through add(). Remember them.
|
||||
$newObjects = $this->coll->toArray();
|
||||
}
|
||||
$this->coll->clear();
|
||||
$this->em->getUnitOfWork()->loadCollection($this);
|
||||
$this->takeSnapshot();
|
||||
// Reattach NEW objects added through add(), if any.
|
||||
if (isset($newObjects)) {
|
||||
foreach ($newObjects as $obj) {
|
||||
$this->coll->add($obj);
|
||||
}
|
||||
$this->isDirty = true;
|
||||
}
|
||||
$this->initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Tells this collection to take a snapshot of its current state.
|
||||
*/
|
||||
public function takeSnapshot()
|
||||
{
|
||||
$this->snapshot = $this->coll->toArray();
|
||||
$this->isDirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Returns the last snapshot of the elements in the collection.
|
||||
*
|
||||
* @return array The last snapshot of the elements.
|
||||
*/
|
||||
public function getSnapshot()
|
||||
{
|
||||
return $this->snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* getDeleteDiff
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDeleteDiff()
|
||||
{
|
||||
return array_udiff_assoc($this->snapshot, $this->coll->toArray(),
|
||||
function($a, $b) {return $a === $b ? 0 : 1;});
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* getInsertDiff
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInsertDiff()
|
||||
{
|
||||
return array_udiff_assoc($this->coll->toArray(), $this->snapshot,
|
||||
function($a, $b) {return $a === $b ? 0 : 1;});
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Gets the association mapping of the collection.
|
||||
*
|
||||
* @return Doctrine\ORM\Mapping\AssociationMapping
|
||||
*/
|
||||
public function getMapping()
|
||||
{
|
||||
return $this->association;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this collection as changed/dirty.
|
||||
*/
|
||||
private function changed()
|
||||
{
|
||||
if ( ! $this->isDirty) {
|
||||
$this->isDirty = true;
|
||||
if ($this->association !== null && $this->association['isOwningSide'] && $this->association['type'] == ClassMetadata::MANY_TO_MANY &&
|
||||
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
|
||||
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean flag indicating whether this collection is dirty which means
|
||||
* its state needs to be synchronized with the database.
|
||||
*
|
||||
* @return boolean TRUE if the collection is dirty, FALSE otherwise.
|
||||
*/
|
||||
public function isDirty()
|
||||
{
|
||||
return $this->isDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean flag, indicating whether this collection is dirty.
|
||||
*
|
||||
* @param boolean $dirty Whether the collection should be marked dirty or not.
|
||||
*/
|
||||
public function setDirty($dirty)
|
||||
{
|
||||
$this->isDirty = $dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initialized flag of the collection, forcing it into that state.
|
||||
*
|
||||
* @param boolean $bool
|
||||
*/
|
||||
public function setInitialized($bool)
|
||||
{
|
||||
$this->initialized = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this collection has been initialized.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isInitialized()
|
||||
{
|
||||
return $this->initialized;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function first()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->first();
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function last()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
// TODO: If the keys are persistent as well (not yet implemented)
|
||||
// and the collection is not initialized and orphanRemoval is
|
||||
// not used we can issue a straight SQL delete/update on the
|
||||
// association (table). Without initializing the collection.
|
||||
$this->initialize();
|
||||
$removed = $this->coll->remove($key);
|
||||
if ($removed) {
|
||||
$this->changed();
|
||||
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
||||
$this->association['orphanRemoval']) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
|
||||
}
|
||||
}
|
||||
|
||||
return $removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeElement($element)
|
||||
{
|
||||
// TODO: Assuming the identity of entities in a collection is always based
|
||||
// on their primary key (there is no equals/hashCode in PHP),
|
||||
// if the collection is not initialized, we could issue a straight
|
||||
// SQL DELETE/UPDATE on the association (table) without initializing
|
||||
// the collection.
|
||||
/*if ( ! $this->initialized) {
|
||||
$this->em->getUnitOfWork()->getCollectionPersister($this->association)
|
||||
->deleteRows($this, $element);
|
||||
}*/
|
||||
|
||||
$this->initialize();
|
||||
$removed = $this->coll->removeElement($element);
|
||||
if ($removed) {
|
||||
$this->changed();
|
||||
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
||||
$this->association['orphanRemoval']) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
||||
}
|
||||
}
|
||||
return $removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function containsKey($key)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->containsKey($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function contains($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->exists($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function indexOf($element)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->indexOf($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->getKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->initialize();
|
||||
$this->coll->set($key, $value);
|
||||
$this->changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($value)
|
||||
{
|
||||
$this->coll->add($value);
|
||||
$this->changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function map(Closure $func)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->map($func);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filter(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->filter($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function forAll(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->forAll($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function partition(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->partition($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
if ($this->initialized && $this->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
|
||||
foreach ($this->coll as $element) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
||||
}
|
||||
}
|
||||
$this->coll->clear();
|
||||
if ($this->association['isOwningSide']) {
|
||||
$this->changed();
|
||||
$this->em->getUnitOfWork()->scheduleCollectionDeletion($this);
|
||||
$this->takeSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by PHP when this collection is serialized. Ensures that only the
|
||||
* elements are properly serialized.
|
||||
*
|
||||
* @internal Tried to implement Serializable first but that did not work well
|
||||
* with circular references. This solution seems simpler and works well.
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return array('coll', 'initialized');
|
||||
}
|
||||
|
||||
/* ArrayAccess implementation */
|
||||
|
||||
/**
|
||||
* @see containsKey()
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->containsKey($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see get()
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see add()
|
||||
* @see set()
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if ( ! isset($offset)) {
|
||||
return $this->add($value);
|
||||
}
|
||||
return $this->set($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see remove()
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
return $this->remove($offset);
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->coll->key();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element of the collection at the current iterator position.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->coll->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the internal iterator position to the next element.
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return $this->coll->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the wrapped Collection instance.
|
||||
*/
|
||||
public function unwrap()
|
||||
{
|
||||
return $this->coll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a slice of $length elements starting at position $offset from the Collection.
|
||||
*
|
||||
* If $length is null it returns all elements from $offset to the end of the Collection.
|
||||
* Keys have to be preserved by this method. Calling this method will only return the
|
||||
* selected slice and NOT change the elements contained in the collection slice is called on.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return array
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->coll->slice($offset, $length);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Pessimistic Lock Exception
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class PessimisticLockException extends ORMException
|
||||
{
|
||||
public static function lockFailed()
|
||||
{
|
||||
return new self("The pessimistic lock failed.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,562 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\DBAL\LockMode,
|
||||
Doctrine\ORM\Query\Parser,
|
||||
Doctrine\ORM\Query\QueryException;
|
||||
|
||||
/**
|
||||
* A Query object represents a DQL query.
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
final class Query extends AbstractQuery
|
||||
{
|
||||
/* Query STATES */
|
||||
/**
|
||||
* A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
|
||||
*/
|
||||
const STATE_CLEAN = 1;
|
||||
/**
|
||||
* A query object is in state DIRTY when it has DQL parts that have not yet been
|
||||
* parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
|
||||
* is called.
|
||||
*/
|
||||
const STATE_DIRTY = 2;
|
||||
|
||||
/* Query HINTS */
|
||||
/**
|
||||
* The refresh hint turns any query into a refresh query with the result that
|
||||
* any local changes in entities are overridden with the fetched values.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_REFRESH = 'doctrine.refresh';
|
||||
/**
|
||||
* The forcePartialLoad query hint forces a particular query to return
|
||||
* partial objects.
|
||||
*
|
||||
* @var string
|
||||
* @todo Rename: HINT_OPTIMIZE
|
||||
*/
|
||||
const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
|
||||
/**
|
||||
* The includeMetaColumns query hint causes meta columns like foreign keys and
|
||||
* discriminator columns to be selected and returned as part of the query result.
|
||||
*
|
||||
* This hint does only apply to non-object queries.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
|
||||
|
||||
/**
|
||||
* An array of class names that implement Doctrine\ORM\Query\TreeWalker and
|
||||
* are iterated and executed after the DQL has been parsed into an AST.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
|
||||
|
||||
/**
|
||||
* A string with a class name that implements Doctrine\ORM\Query\TreeWalker
|
||||
* and is used for generating the target SQL from any DQL AST tree.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
|
||||
|
||||
//const HINT_READ_ONLY = 'doctrine.readOnly';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const HINT_LOCK_MODE = 'doctrine.lockMode';
|
||||
|
||||
/**
|
||||
* @var integer $_state The current state of this query.
|
||||
*/
|
||||
private $_state = self::STATE_CLEAN;
|
||||
|
||||
/**
|
||||
* @var string $_dql Cached DQL query.
|
||||
*/
|
||||
private $_dql = null;
|
||||
|
||||
/**
|
||||
* @var Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information.
|
||||
*/
|
||||
private $_parserResult;
|
||||
|
||||
/**
|
||||
* @var integer The first result to return (the "offset").
|
||||
*/
|
||||
private $_firstResult = null;
|
||||
|
||||
/**
|
||||
* @var integer The maximum number of results to return (the "limit").
|
||||
*/
|
||||
private $_maxResults = null;
|
||||
|
||||
/**
|
||||
* @var CacheDriver The cache driver used for caching queries.
|
||||
*/
|
||||
private $_queryCache;
|
||||
|
||||
/**
|
||||
* @var boolean Boolean value that indicates whether or not expire the query cache.
|
||||
*/
|
||||
private $_expireQueryCache = false;
|
||||
|
||||
/**
|
||||
* @var int Query Cache lifetime.
|
||||
*/
|
||||
private $_queryCacheTTL;
|
||||
|
||||
/**
|
||||
* @var boolean Whether to use a query cache, if available. Defaults to TRUE.
|
||||
*/
|
||||
private $_useQueryCache = true;
|
||||
|
||||
// End of Caching Stuff
|
||||
|
||||
/**
|
||||
* Initializes a new Query instance.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $entityManager
|
||||
*/
|
||||
/*public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
parent::__construct($entityManager);
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Gets the SQL query/queries that correspond to this DQL query.
|
||||
*
|
||||
* @return mixed The built sql query or an array of all sql queries.
|
||||
* @override
|
||||
*/
|
||||
public function getSQL()
|
||||
{
|
||||
return $this->_parse()->getSQLExecutor()->getSQLStatements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding AST for this DQL query.
|
||||
*
|
||||
* @return Doctrine\ORM\Query\AST\SelectStatement |
|
||||
* Doctrine\ORM\Query\AST\UpdateStatement |
|
||||
* Doctrine\ORM\Query\AST\DeleteStatement
|
||||
*/
|
||||
public function getAST()
|
||||
{
|
||||
$parser = new Parser($this);
|
||||
return $parser->getAST();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the DQL query, if necessary, and stores the parser result.
|
||||
*
|
||||
* Note: Populates $this->_parserResult as a side-effect.
|
||||
*
|
||||
* @return Doctrine\ORM\Query\ParserResult
|
||||
*/
|
||||
private function _parse()
|
||||
{
|
||||
if ($this->_state === self::STATE_CLEAN) {
|
||||
return $this->_parserResult;
|
||||
}
|
||||
|
||||
// Check query cache.
|
||||
if ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver())) {
|
||||
$hash = $this->_getQueryCacheId();
|
||||
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
|
||||
if ($cached === false) {
|
||||
// Cache miss.
|
||||
$parser = new Parser($this);
|
||||
$this->_parserResult = $parser->parse();
|
||||
$queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
|
||||
} else {
|
||||
// Cache hit.
|
||||
$this->_parserResult = $cached;
|
||||
}
|
||||
} else {
|
||||
$parser = new Parser($this);
|
||||
$this->_parserResult = $parser->parse();
|
||||
}
|
||||
$this->_state = self::STATE_CLEAN;
|
||||
|
||||
return $this->_parserResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _doExecute()
|
||||
{
|
||||
$executor = $this->_parse()->getSqlExecutor();
|
||||
|
||||
// Prepare parameters
|
||||
$paramMappings = $this->_parserResult->getParameterMappings();
|
||||
|
||||
if (count($paramMappings) != count($this->_params)) {
|
||||
throw QueryException::invalidParameterNumber();
|
||||
}
|
||||
|
||||
$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];
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if ($this->_resultSetMapping === null) {
|
||||
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
|
||||
}
|
||||
|
||||
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a cache driver to be used for caching queries.
|
||||
*
|
||||
* @param Doctrine_Cache_Interface|null $driver Cache driver
|
||||
* @return Query This query instance.
|
||||
*/
|
||||
public function setQueryCacheDriver($queryCache)
|
||||
{
|
||||
$this->_queryCache = $queryCache;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines whether the query should make use of a query cache, if available.
|
||||
*
|
||||
* @param boolean $bool
|
||||
* @return @return Query This query instance.
|
||||
*/
|
||||
public function useQueryCache($bool)
|
||||
{
|
||||
$this->_useQueryCache = $bool;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache driver used for query caching.
|
||||
*
|
||||
* @return CacheDriver The cache driver used for query caching or NULL, if this
|
||||
* Query does not use query caching.
|
||||
*/
|
||||
public function getQueryCacheDriver()
|
||||
{
|
||||
if ($this->_queryCache) {
|
||||
return $this->_queryCache;
|
||||
} else {
|
||||
return $this->_em->getConfiguration()->getQueryCacheImpl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how long the query cache will be active before expire.
|
||||
*
|
||||
* @param integer $timeToLive How long the cache entry is valid
|
||||
* @return Query This query instance.
|
||||
*/
|
||||
public function setQueryCacheLifetime($timeToLive)
|
||||
{
|
||||
if ($timeToLive !== null) {
|
||||
$timeToLive = (int) $timeToLive;
|
||||
}
|
||||
$this->_queryCacheTTL = $timeToLive;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the lifetime of resultset cache.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getQueryCacheLifetime()
|
||||
{
|
||||
return $this->_queryCacheTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the query cache is active or not.
|
||||
*
|
||||
* @param boolean $expire Whether or not to force query cache expiration.
|
||||
* @return Query This query instance.
|
||||
*/
|
||||
public function expireQueryCache($expire = true)
|
||||
{
|
||||
$this->_expireQueryCache = $expire;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves if the query cache is active or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getExpireQueryCache()
|
||||
{
|
||||
return $this->_expireQueryCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
parent::free();
|
||||
$this->_dql = null;
|
||||
$this->_state = self::STATE_CLEAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DQL query string.
|
||||
*
|
||||
* @param string $dqlQuery DQL Query
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setDQL($dqlQuery)
|
||||
{
|
||||
if ($dqlQuery !== null) {
|
||||
$this->_dql = $dqlQuery;
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DQL query that is represented by this query object.
|
||||
*
|
||||
* @return string DQL query
|
||||
*/
|
||||
public function getDQL()
|
||||
{
|
||||
return $this->_dql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of this query object
|
||||
* By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
|
||||
* part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
|
||||
*
|
||||
* @see AbstractQuery::STATE_CLEAN
|
||||
* @see AbstractQuery::STATE_DIRTY
|
||||
*
|
||||
* @return integer Return the query state
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check if an arbitrary piece of DQL exists
|
||||
*
|
||||
* @param string $dql Arbitrary piece of DQL to check for
|
||||
* @return boolean
|
||||
*/
|
||||
public function contains($dql)
|
||||
{
|
||||
return stripos($this->getDQL(), $dql) === false ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of the first result to retrieve (the "offset").
|
||||
*
|
||||
* @param integer $firstResult The first result to return.
|
||||
* @return Query This query object.
|
||||
*/
|
||||
public function setFirstResult($firstResult)
|
||||
{
|
||||
$this->_firstResult = $firstResult;
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the first result the query object was set to retrieve (the "offset").
|
||||
* Returns NULL if {@link setFirstResult} was not applied to this query.
|
||||
*
|
||||
* @return integer The position of the first result.
|
||||
*/
|
||||
public function getFirstResult()
|
||||
{
|
||||
return $this->_firstResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of results to retrieve (the "limit").
|
||||
*
|
||||
* @param integer $maxResults
|
||||
* @return Query This query object.
|
||||
*/
|
||||
public function setMaxResults($maxResults)
|
||||
{
|
||||
$this->_maxResults = $maxResults;
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of results the query object was set to retrieve (the "limit").
|
||||
* Returns NULL if {@link setMaxResults} was not applied to this query.
|
||||
*
|
||||
* @return integer Maximum number of results.
|
||||
*/
|
||||
public function getMaxResults()
|
||||
{
|
||||
return $this->_maxResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns an IterableResult that can be used to incrementally
|
||||
* iterated over the result.
|
||||
*
|
||||
* @param array $params The query parameters.
|
||||
* @param integer $hydrationMode The hydration mode to use.
|
||||
* @return IterableResult
|
||||
*/
|
||||
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
|
||||
{
|
||||
$this->setHint(self::HINT_INTERNAL_ITERATION, true);
|
||||
return parent::iterate($params, $hydrationMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setHint($name, $value)
|
||||
{
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
return parent::setHint($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setHydrationMode($hydrationMode)
|
||||
{
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
return parent::setHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lock mode for this Query.
|
||||
*
|
||||
* @see Doctrine\DBAL\LockMode
|
||||
* @param int $lockMode
|
||||
* @return Query
|
||||
*/
|
||||
public function setLockMode($lockMode)
|
||||
{
|
||||
if ($lockMode == LockMode::PESSIMISTIC_READ || $lockMode == LockMode::PESSIMISTIC_WRITE) {
|
||||
if (!$this->_em->getConnection()->isTransactionActive()) {
|
||||
throw TransactionRequiredException::transactionRequired();
|
||||
}
|
||||
}
|
||||
|
||||
$this->setHint(self::HINT_LOCK_MODE, $lockMode);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current lock mode for this query.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLockMode()
|
||||
{
|
||||
$lockMode = $this->getHint(self::HINT_LOCK_MODE);
|
||||
if (!$lockMode) {
|
||||
return LockMode::NONE;
|
||||
}
|
||||
return $lockMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
|
||||
*
|
||||
* The query cache
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _getQueryCacheId()
|
||||
{
|
||||
ksort($this->_hints);
|
||||
|
||||
return md5(
|
||||
$this->getDql() . var_export($this->_hints, true) .
|
||||
'&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
|
||||
'&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup Query resource when clone is called.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
parent::__clone();
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
}
|
||||
}
|
||||
+935
@@ -0,0 +1,935 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
|
||||
/**
|
||||
* This class is responsible for building DQL query strings via an object oriented
|
||||
* PHP interface.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class QueryBuilder
|
||||
{
|
||||
/* The query types. */
|
||||
const SELECT = 0;
|
||||
const DELETE = 1;
|
||||
const UPDATE = 2;
|
||||
|
||||
/** The builder states. */
|
||||
const STATE_DIRTY = 0;
|
||||
const STATE_CLEAN = 1;
|
||||
|
||||
/**
|
||||
* @var EntityManager The EntityManager used by this QueryBuilder.
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
/**
|
||||
* @var array The array of DQL parts collected.
|
||||
*/
|
||||
private $_dqlParts = array(
|
||||
'select' => array(),
|
||||
'from' => array(),
|
||||
'join' => array(),
|
||||
'set' => array(),
|
||||
'where' => null,
|
||||
'groupBy' => array(),
|
||||
'having' => null,
|
||||
'orderBy' => array()
|
||||
);
|
||||
|
||||
/**
|
||||
* @var integer The type of query this is. Can be select, update or delete.
|
||||
*/
|
||||
private $_type = self::SELECT;
|
||||
|
||||
/**
|
||||
* @var integer The state of the query object. Can be dirty or clean.
|
||||
*/
|
||||
private $_state = self::STATE_CLEAN;
|
||||
|
||||
/**
|
||||
* @var string The complete DQL string for this query.
|
||||
*/
|
||||
private $_dql;
|
||||
|
||||
/**
|
||||
* @var array The query parameters.
|
||||
*/
|
||||
private $_params = array();
|
||||
|
||||
/**
|
||||
* @var array The parameter type map of this query.
|
||||
*/
|
||||
private $_paramTypes = array();
|
||||
|
||||
/**
|
||||
* @var integer The index of the first result to retrieve.
|
||||
*/
|
||||
private $_firstResult = null;
|
||||
|
||||
/**
|
||||
* @var integer The maximum number of results to retrieve.
|
||||
*/
|
||||
private $_maxResults = null;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>.
|
||||
*
|
||||
* @param EntityManager $em The EntityManager to use.
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
|
||||
* This producer method is intended for convenient inline usage. Example:
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where($qb->expr()->eq('u.id', 1));
|
||||
* </code>
|
||||
*
|
||||
* For more complex expression construction, consider storing the expression
|
||||
* builder object in a local variable.
|
||||
*
|
||||
* @return Expr
|
||||
*/
|
||||
public function expr()
|
||||
{
|
||||
return $this->_em->getExpressionBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the currently built query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated EntityManager for this query builder.
|
||||
*
|
||||
* @return EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of this query builder instance.
|
||||
*
|
||||
* @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the complete DQL string formed by the current specifications of this QueryBuilder.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* echo $qb->getDql(); // SELECT u FROM User u
|
||||
* </code>
|
||||
*
|
||||
* @return string The DQL query string.
|
||||
*/
|
||||
public function getDQL()
|
||||
{
|
||||
if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) {
|
||||
return $this->_dql;
|
||||
}
|
||||
|
||||
$dql = '';
|
||||
|
||||
switch ($this->_type) {
|
||||
case self::DELETE:
|
||||
$dql = $this->_getDQLForDelete();
|
||||
break;
|
||||
|
||||
case self::UPDATE:
|
||||
$dql = $this->_getDQLForUpdate();
|
||||
break;
|
||||
|
||||
case self::SELECT:
|
||||
default:
|
||||
$dql = $this->_getDQLForSelect();
|
||||
break;
|
||||
}
|
||||
|
||||
$this->_state = self::STATE_CLEAN;
|
||||
$this->_dql = $dql;
|
||||
|
||||
return $dql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Query instance from the current specifications of the builder.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
* $q = $qb->getQuery();
|
||||
* $results = $q->execute();
|
||||
* </code>
|
||||
*
|
||||
* @return Query
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->_em->createQuery($this->getDQL())
|
||||
->setParameters($this->_params, $this->_paramTypes)
|
||||
->setFirstResult($this->_firstResult)
|
||||
->setMaxResults($this->_maxResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
*
|
||||
* echo $qb->getRootAlias(); // u
|
||||
* </code>
|
||||
*
|
||||
* @return string $rootAlias
|
||||
* @todo Rename/Refactor: getRootAliases(), there can be multiple roots!
|
||||
*/
|
||||
public function getRootAlias()
|
||||
{
|
||||
return $this->_dqlParts['from'][0]->getAlias();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query parameter for the query being constructed.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = :user_id')
|
||||
* ->setParameter(':user_id', 1);
|
||||
* </code>
|
||||
*
|
||||
* @param string|integer $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function setParameter($key, $value, $type = null)
|
||||
{
|
||||
if ($type !== null) {
|
||||
$this->_paramTypes[$key] = $type;
|
||||
}
|
||||
$this->_params[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a collection of query parameters for the query being constructed.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = :user_id1 OR u.id = :user_id2')
|
||||
* ->setParameters(array(
|
||||
* ':user_id1' => 1,
|
||||
* ':user_id2' => 2
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* @param array $params The query parameters to set.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function setParameters(array $params, array $types = array())
|
||||
{
|
||||
$this->_paramTypes = $types;
|
||||
$this->_params = $params;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all defined query parameters for the query being constructed.
|
||||
*
|
||||
* @return array The currently defined query parameters.
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a (previously set) query parameter of the query being constructed.
|
||||
*
|
||||
* @param mixed $key The key (index or name) of the bound parameter.
|
||||
* @return mixed The value of the bound parameter.
|
||||
*/
|
||||
public function getParameter($key)
|
||||
{
|
||||
return isset($this->_params[$key]) ? $this->_params[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of the first result to retrieve (the "offset").
|
||||
*
|
||||
* @param integer $firstResult The first result to return.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function setFirstResult($firstResult)
|
||||
{
|
||||
$this->_firstResult = $firstResult;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the first result the query object was set to retrieve (the "offset").
|
||||
* Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
|
||||
*
|
||||
* @return integer The position of the first result.
|
||||
*/
|
||||
public function getFirstResult()
|
||||
{
|
||||
return $this->_firstResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of results to retrieve (the "limit").
|
||||
*
|
||||
* @param integer $maxResults The maximum number of results to retrieve.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function setMaxResults($maxResults)
|
||||
{
|
||||
$this->_maxResults = $maxResults;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of results the query object was set to retrieve (the "limit").
|
||||
* Returns NULL if {@link setMaxResults} was not applied to this query builder.
|
||||
*
|
||||
* @return integer Maximum number of results.
|
||||
*/
|
||||
public function getMaxResults()
|
||||
{
|
||||
return $this->_maxResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Either appends to or replaces a single, generic query part.
|
||||
*
|
||||
* The available parts are: 'select', 'from', 'join', 'set', 'where',
|
||||
* 'groupBy', 'having' and 'orderBy'.
|
||||
*
|
||||
* @param string $dqlPartName
|
||||
* @param string $dqlPart
|
||||
* @param string $append
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function add($dqlPartName, $dqlPart, $append = false)
|
||||
{
|
||||
$isMultiple = is_array($this->_dqlParts[$dqlPartName]);
|
||||
|
||||
if ($append && $isMultiple) {
|
||||
$this->_dqlParts[$dqlPartName][] = $dqlPart;
|
||||
} else {
|
||||
$this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
|
||||
}
|
||||
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an item that is to be returned in the query result.
|
||||
* Replaces any previously specified selections, if any.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u', 'p')
|
||||
* ->from('User', 'u')
|
||||
* ->leftJoin('u.Phonenumbers', 'p');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $select The selection expressions.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function select($select = null)
|
||||
{
|
||||
$this->_type = self::SELECT;
|
||||
|
||||
if (empty($select)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$selects = is_array($select) ? $select : func_get_args();
|
||||
|
||||
return $this->add('select', new Expr\Select($selects), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item that is to be returned in the query result.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->addSelect('p')
|
||||
* ->from('User', 'u')
|
||||
* ->leftJoin('u.Phonenumbers', 'p');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $select The selection expression.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function addSelect($select = null)
|
||||
{
|
||||
$this->_type = self::SELECT;
|
||||
|
||||
if (empty($select)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$selects = is_array($select) ? $select : func_get_args();
|
||||
|
||||
return $this->add('select', new Expr\Select($selects), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the query being built into a bulk delete query that ranges over
|
||||
* a certain entity type.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->delete('User', 'u')
|
||||
* ->where('u.id = :user_id');
|
||||
* ->setParameter(':user_id', 1);
|
||||
* </code>
|
||||
*
|
||||
* @param string $delete The class/type whose instances are subject to the deletion.
|
||||
* @param string $alias The class/type alias used in the constructed query.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function delete($delete = null, $alias = null)
|
||||
{
|
||||
$this->_type = self::DELETE;
|
||||
|
||||
if ( ! $delete) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->add('from', new Expr\From($delete, $alias));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the query being built into a bulk update query that ranges over
|
||||
* a certain entity type.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->update('User', 'u')
|
||||
* ->set('u.password', md5('password'))
|
||||
* ->where('u.id = ?');
|
||||
* </code>
|
||||
*
|
||||
* @param string $update The class/type whose instances are subject to the update.
|
||||
* @param string $alias The class/type alias used in the constructed query.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function update($update = null, $alias = null)
|
||||
{
|
||||
$this->_type = self::UPDATE;
|
||||
|
||||
if ( ! $update) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->add('from', new Expr\From($update, $alias));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and add a query root corresponding to the entity identified by the given alias,
|
||||
* forming a cartesian product with any existing query roots.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* </code>
|
||||
*
|
||||
* @param string $from The class name.
|
||||
* @param string $alias The alias of the class.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function from($from, $alias)
|
||||
{
|
||||
return $this->add('from', new Expr\From($from, $alias), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a join over an entity association to the query.
|
||||
*
|
||||
* The entities in the joined association will be fetched as part of the query
|
||||
* result if the alias used for the joined association is placed in the select
|
||||
* expressions.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
|
||||
* </code>
|
||||
*
|
||||
* @param string $join The relationship to join
|
||||
* @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
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function join($join, $alias, $conditionType = null, $condition = null)
|
||||
{
|
||||
return $this->innerJoin($join, $alias, $conditionType, $condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a join over an entity association to the query.
|
||||
*
|
||||
* The entities in the joined association will be fetched as part of the query
|
||||
* result if the alias used for the joined association is placed in the select
|
||||
* expressions.
|
||||
*
|
||||
* [php]
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
|
||||
*
|
||||
* @param string $join The relationship to join
|
||||
* @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
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function innerJoin($join, $alias, $conditionType = null, $condition = null)
|
||||
{
|
||||
return $this->add('join', new Expr\Join(
|
||||
Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition
|
||||
), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a left join over an entity association to the query.
|
||||
*
|
||||
* The entities in the joined association will be fetched as part of the query
|
||||
* result if the alias used for the joined association is placed in the select
|
||||
* expressions.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
|
||||
* </code>
|
||||
*
|
||||
* @param string $join The relationship to join
|
||||
* @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
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function leftJoin($join, $alias, $conditionType = null, $condition = null)
|
||||
{
|
||||
return $this->add('join', new Expr\Join(
|
||||
Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition
|
||||
), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value for a field in a bulk update query.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->update('User', 'u')
|
||||
* ->set('u.password', md5('password'))
|
||||
* ->where('u.id = ?');
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The key/field to set.
|
||||
* @param string $value The value, expression, placeholder, etc.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies one or more restrictions to the query result.
|
||||
* Replaces any previously specified restrictions, if any.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = ?');
|
||||
*
|
||||
* // You can optionally programatically build and/or expressions
|
||||
* $qb = $em->createQueryBuilder();
|
||||
*
|
||||
* $or = $qb->expr()->orx();
|
||||
* $or->add($qb->expr()->eq('u.id', 1));
|
||||
* $or->add($qb->expr()->eq('u.id', 2));
|
||||
*
|
||||
* $qb->update('User', 'u')
|
||||
* ->set('u.password', md5('password'))
|
||||
* ->where($or);
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $predicates The restriction predicates.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function where($predicates)
|
||||
{
|
||||
if ( ! (func_num_args() == 1 && ($predicates instanceof Expr\Andx || $predicates instanceof Expr\Orx))) {
|
||||
$predicates = new Expr\Andx(func_get_args());
|
||||
}
|
||||
|
||||
return $this->add('where', $predicates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more restrictions to the query results, forming a logical
|
||||
* conjunction with any previously specified restrictions.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.username LIKE ?')
|
||||
* ->andWhere('u.is_active = 1');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $where The query restrictions.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
* @see where()
|
||||
*/
|
||||
public function andWhere($where)
|
||||
{
|
||||
$where = $this->getDQLPart('where');
|
||||
$args = func_get_args();
|
||||
|
||||
if ($where instanceof Expr\Andx) {
|
||||
$where->addMultiple($args);
|
||||
} else {
|
||||
array_unshift($args, $where);
|
||||
$where = new Expr\Andx($args);
|
||||
}
|
||||
|
||||
return $this->add('where', $where, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more restrictions to the query results, forming a logical
|
||||
* disjunction with any previously specified restrictions.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = 1')
|
||||
* ->orWhere('u.id = 2');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $where The WHERE statement
|
||||
* @return QueryBuilder $qb
|
||||
* @see where()
|
||||
*/
|
||||
public function orWhere($where)
|
||||
{
|
||||
$where = $this->getDqlPart('where');
|
||||
$args = func_get_args();
|
||||
|
||||
if ($where instanceof Expr\Orx) {
|
||||
$where->addMultiple($args);
|
||||
} else {
|
||||
array_unshift($args, $where);
|
||||
$where = new Expr\Orx($args);
|
||||
}
|
||||
|
||||
return $this->add('where', $where, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a grouping over the results of the query.
|
||||
* Replaces any previously specified groupings, if any.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->groupBy('u.id');
|
||||
* </code>
|
||||
*
|
||||
* @param string $groupBy The grouping expression.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function groupBy($groupBy)
|
||||
{
|
||||
return $this->add('groupBy', new Expr\GroupBy(func_get_args()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a grouping expression to the query.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->groupBy('u.lastLogin');
|
||||
* ->addGroupBy('u.createdAt')
|
||||
* </code>
|
||||
*
|
||||
* @param string $groupBy The grouping expression.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function addGroupBy($groupBy)
|
||||
{
|
||||
return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a restriction over the groups of the query.
|
||||
* Replaces any previous having restrictions, if any.
|
||||
*
|
||||
* @param mixed $having The restriction over the groups.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function having($having)
|
||||
{
|
||||
if ( ! (func_num_args() == 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) {
|
||||
$having = new Expr\Andx(func_get_args());
|
||||
}
|
||||
|
||||
return $this->add('having', $having);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a restriction over the groups of the query, forming a logical
|
||||
* conjunction with any existing having restrictions.
|
||||
*
|
||||
* @param mixed $having The restriction to append.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function andHaving($having)
|
||||
{
|
||||
$having = $this->getDqlPart('having');
|
||||
$args = func_get_args();
|
||||
|
||||
if ($having instanceof Expr\Andx) {
|
||||
$having->addMultiple($args);
|
||||
} else {
|
||||
array_unshift($args, $having);
|
||||
$having = new Expr\Andx($args);
|
||||
}
|
||||
|
||||
return $this->add('having', $having);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a restriction over the groups of the query, forming a logical
|
||||
* disjunction with any existing having restrictions.
|
||||
*
|
||||
* @param mixed $having The restriction to add.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function orHaving($having)
|
||||
{
|
||||
$having = $this->getDqlPart('having');
|
||||
$args = func_get_args();
|
||||
|
||||
if ($having instanceof Expr\Orx) {
|
||||
$having->addMultiple($args);
|
||||
} else {
|
||||
array_unshift($args, $having);
|
||||
$having = new Expr\Orx($args);
|
||||
}
|
||||
|
||||
return $this->add('having', $having);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an ordering for the query results.
|
||||
* Replaces any previously specified orderings, if any.
|
||||
*
|
||||
* @param string $sort The ordering expression.
|
||||
* @param string $order The ordering direction.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function orderBy($sort, $order = null)
|
||||
{
|
||||
return $this->add('orderBy', $sort instanceof Expr\OrderBy ? $sort
|
||||
: new Expr\OrderBy($sort, $order));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an ordering to the query results.
|
||||
*
|
||||
* @param string $sort The ordering expression.
|
||||
* @param string $order The ordering direction.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function addOrderBy($sort, $order = null)
|
||||
{
|
||||
return $this->add('orderBy', new Expr\OrderBy($sort, $order), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a query part by its name.
|
||||
*
|
||||
* @param string $queryPartName
|
||||
* @return mixed $queryPart
|
||||
* @todo Rename: getQueryPart (or remove?)
|
||||
*/
|
||||
public function getDQLPart($queryPartName)
|
||||
{
|
||||
return $this->_dqlParts[$queryPartName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all query parts.
|
||||
*
|
||||
* @return array $dqlParts
|
||||
* @todo Rename: getQueryParts (or remove?)
|
||||
*/
|
||||
public function getDQLParts()
|
||||
{
|
||||
return $this->_dqlParts;
|
||||
}
|
||||
|
||||
private function _getDQLForDelete()
|
||||
{
|
||||
return 'DELETE'
|
||||
. $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', '))
|
||||
. $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
|
||||
. $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
|
||||
}
|
||||
|
||||
private function _getDQLForUpdate()
|
||||
{
|
||||
return 'UPDATE'
|
||||
. $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', '))
|
||||
. $this->_getReducedDQLQueryPart('set', array('pre' => ' SET ', 'separator' => ', '))
|
||||
. $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
|
||||
. $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
|
||||
}
|
||||
|
||||
private function _getDQLForSelect()
|
||||
{
|
||||
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' => ', '));
|
||||
}
|
||||
|
||||
private function _getReducedDQLQueryPart($queryPartName, $options = array())
|
||||
{
|
||||
$queryPart = $this->getDQLPart($queryPartName);
|
||||
|
||||
if (empty($queryPart)) {
|
||||
return (isset($options['empty']) ? $options['empty'] : '');
|
||||
}
|
||||
|
||||
return (isset($options['pre']) ? $options['pre'] : '')
|
||||
. (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart)
|
||||
. (isset($options['post']) ? $options['post'] : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset DQL parts
|
||||
*
|
||||
* @param array $parts
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function resetDQLParts($parts = null)
|
||||
{
|
||||
if (is_null($parts)) {
|
||||
$parts = array_keys($this->_dqlParts);
|
||||
}
|
||||
foreach ($parts as $part) {
|
||||
$this->resetDQLPart($part);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset single DQL part
|
||||
*
|
||||
* @param string $part
|
||||
* @return QueryBuilder;
|
||||
*/
|
||||
public function resetDQLPart($part)
|
||||
{
|
||||
if (is_array($this->_dqlParts[$part])) {
|
||||
$this->_dqlParts[$part] = array();
|
||||
} else {
|
||||
$this->_dqlParts[$part] = null;
|
||||
}
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representation of this QueryBuilder which corresponds to
|
||||
* the final DQL query being constructed.
|
||||
*
|
||||
* @return string The string representation of this QueryBuilder.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getDQL();
|
||||
}
|
||||
}
|
||||
externo
+40
@@ -0,0 +1,40 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Is thrown when a transaction is required for the current operation, but there is none open.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class TransactionRequiredException extends ORMException
|
||||
{
|
||||
static public function transactionRequired()
|
||||
{
|
||||
return new self('An open transaction is required for this operation.');
|
||||
}
|
||||
}
|
||||
+2253
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,55 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Class to store and retrieve the version of Doctrine
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @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 Version
|
||||
{
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.0.0RC1-DEV';
|
||||
|
||||
/**
|
||||
* Compares a Doctrine version with the current one.
|
||||
*
|
||||
* @param string $version Doctrine version to compare.
|
||||
* @return int Returns -1 if older, 0 if it is the same, 1 if version
|
||||
* passed as argument is newer.
|
||||
*/
|
||||
public static function compare($version)
|
||||
{
|
||||
$currentVersion = str_replace(' ', '', strtolower(self::VERSION));
|
||||
$version = str_replace(' ', '', $version);
|
||||
|
||||
return version_compare($version, $currentVersion);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
<?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
|
||||
* 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;
|
||||
|
||||
use Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\Query\QueryException;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
*/
|
||||
abstract class AbstractQuery
|
||||
{
|
||||
/* Hydration mode constants */
|
||||
/**
|
||||
* Hydrates an object graph. This is the default behavior.
|
||||
*/
|
||||
const HYDRATE_OBJECT = 1;
|
||||
/**
|
||||
* Hydrates an array graph.
|
||||
*/
|
||||
const HYDRATE_ARRAY = 2;
|
||||
/**
|
||||
* Hydrates a flat, rectangular result set with scalar values.
|
||||
*/
|
||||
const HYDRATE_SCALAR = 3;
|
||||
/**
|
||||
* Hydrates a single scalar value.
|
||||
*/
|
||||
const HYDRATE_SINGLE_SCALAR = 4;
|
||||
|
||||
/**
|
||||
* @var array The parameter map of this query.
|
||||
*/
|
||||
protected $_params = array();
|
||||
|
||||
/**
|
||||
* @var array The parameter type map of this query.
|
||||
*/
|
||||
protected $_paramTypes = array();
|
||||
|
||||
/**
|
||||
* @var ResultSetMapping The user-specified ResultSetMapping to use.
|
||||
*/
|
||||
protected $_resultSetMapping;
|
||||
|
||||
/**
|
||||
* @var Doctrine\ORM\EntityManager The entity manager used by this query object.
|
||||
*/
|
||||
protected $_em;
|
||||
|
||||
/**
|
||||
* @var array The map of query hints.
|
||||
*/
|
||||
protected $_hints = array();
|
||||
|
||||
/**
|
||||
* @var integer The hydration mode.
|
||||
*/
|
||||
protected $_hydrationMode = self::HYDRATE_OBJECT;
|
||||
|
||||
/**
|
||||
* The locally set cache driver used for caching result sets of this query.
|
||||
*
|
||||
* @var CacheDriver
|
||||
*/
|
||||
protected $_resultCacheDriver;
|
||||
|
||||
/**
|
||||
* Boolean flag for whether or not to cache the results of this query.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $_useResultCache;
|
||||
|
||||
/**
|
||||
* @var string The id to store the result cache entry under.
|
||||
*/
|
||||
protected $_resultCacheId;
|
||||
|
||||
/**
|
||||
* @var boolean Boolean value that indicates whether or not expire the result cache.
|
||||
*/
|
||||
protected $_expireResultCache = false;
|
||||
|
||||
/**
|
||||
* @var int Result Cache lifetime.
|
||||
*/
|
||||
protected $_resultCacheTTL;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $entityManager
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the associated EntityManager of this Query instance.
|
||||
*
|
||||
* @return Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the resources used by the query object.
|
||||
*
|
||||
* Resets Parameters, Parameter Types and Query Hints.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
$this->_params = array();
|
||||
$this->_paramTypes = array();
|
||||
$this->_hints = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all defined parameters.
|
||||
*
|
||||
* @return array The defined query parameters.
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a query parameter.
|
||||
*
|
||||
* @param mixed $key The key (index or name) of the bound parameter.
|
||||
* @return mixed The value of the bound parameter.
|
||||
*/
|
||||
public function getParameter($key)
|
||||
{
|
||||
return isset($this->_params[$key]) ? $this->_params[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL query that corresponds to this query object.
|
||||
* The returned SQL syntax depends on the connection driver that is used
|
||||
* by this query object at the time of this method call.
|
||||
*
|
||||
* @return string SQL query
|
||||
*/
|
||||
abstract public function getSQL();
|
||||
|
||||
/**
|
||||
* Sets a query parameter.
|
||||
*
|
||||
* @param string|integer $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param string $type The parameter type. If specified, the given value will be run through
|
||||
* the type conversion of this type. This is usually not needed for
|
||||
* strings and numeric types.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setParameter($key, $value, $type = null)
|
||||
{
|
||||
if ($type !== null) {
|
||||
$this->_paramTypes[$key] = $type;
|
||||
}
|
||||
$this->_params[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a collection of query parameters.
|
||||
*
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ResultSetMapping that should be used for hydration.
|
||||
*
|
||||
* @param ResultSetMapping $rsm
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setResultSetMapping(Query\ResultSetMapping $rsm)
|
||||
{
|
||||
$this->_resultSetMapping = $rsm;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a cache driver to be used for caching result sets.
|
||||
*
|
||||
* @param Doctrine\Common\Cache\Cache $driver Cache driver
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setResultCacheDriver($resultCacheDriver = null)
|
||||
{
|
||||
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
|
||||
throw ORMException::invalidResultCacheDriver();
|
||||
}
|
||||
$this->_resultCacheDriver = $resultCacheDriver;
|
||||
if ($resultCacheDriver) {
|
||||
$this->_useResultCache = true;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache driver used for caching result sets.
|
||||
*
|
||||
* @return Doctrine\Common\Cache\Cache Cache driver
|
||||
*/
|
||||
public function getResultCacheDriver()
|
||||
{
|
||||
if ($this->_resultCacheDriver) {
|
||||
return $this->_resultCacheDriver;
|
||||
} else {
|
||||
return $this->_em->getConfiguration()->getResultCacheImpl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to cache the results of this query and if so, for
|
||||
* how long and which ID to use for the cache entry.
|
||||
*
|
||||
* @param boolean $bool
|
||||
* @param integer $timeToLive
|
||||
* @param string $resultCacheId
|
||||
* @return This query instance.
|
||||
*/
|
||||
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
|
||||
{
|
||||
$this->_useResultCache = $bool;
|
||||
if ($timeToLive) {
|
||||
$this->setResultCacheLifetime($timeToLive);
|
||||
}
|
||||
if ($resultCacheId) {
|
||||
$this->_resultCacheId = $resultCacheId;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how long the result cache will be active before expire.
|
||||
*
|
||||
* @param integer $timeToLive How long the cache entry is valid.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setResultCacheLifetime($timeToLive)
|
||||
{
|
||||
if ($timeToLive !== null) {
|
||||
$timeToLive = (int) $timeToLive;
|
||||
}
|
||||
|
||||
$this->_resultCacheTTL = $timeToLive;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the lifetime of resultset cache.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getResultCacheLifetime()
|
||||
{
|
||||
return $this->_resultCacheTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the result cache is active or not.
|
||||
*
|
||||
* @param boolean $expire Whether or not to force resultset cache expiration.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function expireResultCache($expire = true)
|
||||
{
|
||||
$this->_expireResultCache = $expire;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves if the resultset cache is active or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getExpireResultCache()
|
||||
{
|
||||
return $this->_expireResultCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the processing mode to be used during hydration / result set transformation.
|
||||
*
|
||||
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
|
||||
* One of the Query::HYDRATE_* constants.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setHydrationMode($hydrationMode)
|
||||
{
|
||||
$this->_hydrationMode = $hydrationMode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hydration mode currently used by the query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getHydrationMode()
|
||||
{
|
||||
return $this->_hydrationMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of results for the query.
|
||||
*
|
||||
* Alias for execute(array(), $hydrationMode = HYDRATE_OBJECT).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResult($hydrationMode = self::HYDRATE_OBJECT)
|
||||
{
|
||||
return $this->execute(array(), $hydrationMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of results for the query.
|
||||
*
|
||||
* Alias for execute(array(), HYDRATE_ARRAY).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArrayResult()
|
||||
{
|
||||
return $this->execute(array(), self::HYDRATE_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scalar results for the query.
|
||||
*
|
||||
* Alias for execute(array(), HYDRATE_SCALAR).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getScalarResult()
|
||||
{
|
||||
return $this->execute(array(), self::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single result of the query.
|
||||
*
|
||||
* Enforces the presence as well as the uniqueness of the result.
|
||||
*
|
||||
* If the result is not unique, a NonUniqueResultException is thrown.
|
||||
* If there is no result, a NoResultException is thrown.
|
||||
*
|
||||
* @param integer $hydrationMode
|
||||
* @return mixed
|
||||
* @throws NonUniqueResultException If the query result is not unique.
|
||||
* @throws NoResultException If the query returned no result.
|
||||
*/
|
||||
public function getSingleResult($hydrationMode = null)
|
||||
{
|
||||
$result = $this->execute(array(), $hydrationMode);
|
||||
|
||||
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
|
||||
throw new NoResultException;
|
||||
}
|
||||
|
||||
if (is_array($result)) {
|
||||
if (count($result) > 1) {
|
||||
throw new NonUniqueResultException;
|
||||
}
|
||||
return array_shift($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single scalar result of the query.
|
||||
*
|
||||
* Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
|
||||
*
|
||||
* @return mixed
|
||||
* @throws QueryException If the query result is not unique.
|
||||
*/
|
||||
public function getSingleScalarResult()
|
||||
{
|
||||
return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query hint. If the hint name is not recognized, it is silently ignored.
|
||||
*
|
||||
* @param string $name The name of the hint.
|
||||
* @param mixed $value The value of the hint.
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
*/
|
||||
public function setHint($name, $value)
|
||||
{
|
||||
$this->_hints[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
|
||||
*
|
||||
* @param string $name The name of the hint.
|
||||
* @return mixed The value of the hint or FALSE, if the hint name is not recognized.
|
||||
*/
|
||||
public function getHint($name)
|
||||
{
|
||||
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns an IterableResult that can be used to incrementally
|
||||
* iterate over the result.
|
||||
*
|
||||
* @param array $params The query parameters.
|
||||
* @param integer $hydrationMode The hydration mode to use.
|
||||
* @return IterableResult
|
||||
*/
|
||||
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
|
||||
{
|
||||
return $this->_em->newHydrator($this->_hydrationMode)->iterate(
|
||||
$this->_doExecute($params, $hydrationMode), $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query.
|
||||
*
|
||||
* @param string $params Any additional query parameters.
|
||||
* @param integer $hydrationMode Processing mode to be used during the hydration process.
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute($params = array(), $hydrationMode = null)
|
||||
{
|
||||
// If there are still pending insertions in the UnitOfWork we need to flush
|
||||
// in order to guarantee a correct result.
|
||||
//TODO: Think this over. Its tricky. Not doing this can lead to strange results
|
||||
// potentially, but doing it could result in endless loops when querying during
|
||||
// a flush, i.e. inside an event listener.
|
||||
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
if ($hydrationMode !== null) {
|
||||
$this->setHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
if ($params) {
|
||||
$this->setParameters($params);
|
||||
}
|
||||
|
||||
if (isset($this->_params[0])) {
|
||||
throw QueryException::invalidParameterPosition(0);
|
||||
}
|
||||
|
||||
// Check result cache
|
||||
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
|
||||
$id = $this->_getResultCacheId();
|
||||
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($id);
|
||||
|
||||
if ($cached === false) {
|
||||
// Cache miss.
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
|
||||
$cacheDriver->save($id, $result, $this->_resultCacheTTL);
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
// Cache hit.
|
||||
return $cached;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
if (is_numeric($stmt)) {
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the result cache id to use to store the result set cache entry.
|
||||
* If this is not explicitely set by the developer then a hash is automatically
|
||||
* generated for you.
|
||||
*
|
||||
* @param string $id
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setResultCacheId($id)
|
||||
{
|
||||
$this->_resultCacheId = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result cache id to use to store the result set cache entry.
|
||||
* Will return the configured id if it exists otherwise a hash will be
|
||||
* automatically generated for you.
|
||||
*
|
||||
* @return string $id
|
||||
*/
|
||||
protected function _getResultCacheId()
|
||||
{
|
||||
if ($this->_resultCacheId) {
|
||||
return $this->_resultCacheId;
|
||||
} else {
|
||||
$sql = $this->getSql();
|
||||
ksort($this->_hints);
|
||||
return md5(implode(";", (array)$sql) . var_export($this->_params, true) .
|
||||
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns a the resulting Statement object.
|
||||
*
|
||||
* @return Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
|
||||
*/
|
||||
abstract protected function _doExecute();
|
||||
|
||||
/**
|
||||
* Cleanup Query resource when clone is called.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->_params = array();
|
||||
$this->_paramTypes = array();
|
||||
$this->_hints = array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\Common\Cache\Cache,
|
||||
Doctrine\ORM\Mapping\Driver\Driver;
|
||||
|
||||
/**
|
||||
* Configuration container for all configuration options of Doctrine.
|
||||
* It combines all configuration options from DBAL & ORM.
|
||||
*
|
||||
* @since 2.0
|
||||
* @internal When adding a new configuration option just write a getter/setter pair.
|
||||
* @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 Configuration extends \Doctrine\DBAL\Configuration
|
||||
{
|
||||
/**
|
||||
* Sets the directory where Doctrine generates any necessary proxy class files.
|
||||
*
|
||||
* @param string $dir
|
||||
*/
|
||||
public function setProxyDir($dir)
|
||||
{
|
||||
$this->_attributes['proxyDir'] = $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the directory where Doctrine generates any necessary proxy class files.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProxyDir()
|
||||
{
|
||||
return isset($this->_attributes['proxyDir']) ?
|
||||
$this->_attributes['proxyDir'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean flag that indicates whether proxy classes should always be regenerated
|
||||
* during each script execution.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getAutoGenerateProxyClasses()
|
||||
{
|
||||
return isset($this->_attributes['autoGenerateProxyClasses']) ?
|
||||
$this->_attributes['autoGenerateProxyClasses'] : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean flag that indicates whether proxy classes should always be regenerated
|
||||
* during each script execution.
|
||||
*
|
||||
* @param boolean $bool
|
||||
*/
|
||||
public function setAutoGenerateProxyClasses($bool)
|
||||
{
|
||||
$this->_attributes['autoGenerateProxyClasses'] = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace where proxy classes reside.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProxyNamespace()
|
||||
{
|
||||
return isset($this->_attributes['proxyNamespace']) ?
|
||||
$this->_attributes['proxyNamespace'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace where proxy classes reside.
|
||||
*
|
||||
* @param string $ns
|
||||
*/
|
||||
public function setProxyNamespace($ns)
|
||||
{
|
||||
$this->_attributes['proxyNamespace'] = $ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @param Driver $driverImpl
|
||||
* @todo Force parameter to be a Closure to ensure lazy evaluation
|
||||
* (as soon as a metadata cache is in effect, the driver never needs to initialize).
|
||||
*/
|
||||
public function setMetadataDriverImpl(Driver $driverImpl)
|
||||
{
|
||||
$this->_attributes['metadataDriverImpl'] = $driverImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new default annotation driver with a correctly configured annotation reader.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return Mapping\Driver\AnnotationDriver
|
||||
*/
|
||||
public function newDefaultAnnotationDriver($paths = array())
|
||||
{
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
|
||||
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, (array)$paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a namespace under a certain alias.
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $namespace
|
||||
*/
|
||||
public function addEntityNamespace($alias, $namespace)
|
||||
{
|
||||
$this->_attributes['entityNamespaces'][$alias] = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a registered namespace alias to the full namespace.
|
||||
*
|
||||
* @param string $entityNamespaceAlias
|
||||
* @return string
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function getEntityNamespace($entityNamespaceAlias)
|
||||
{
|
||||
if ( ! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) {
|
||||
throw ORMException::unknownEntityNamespace($entityNamespaceAlias);
|
||||
}
|
||||
|
||||
return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entity alias map
|
||||
*
|
||||
* @param array $entityAliasMap
|
||||
* @return void
|
||||
*/
|
||||
public function setEntityNamespaces(array $entityNamespaces)
|
||||
{
|
||||
$this->_attributes['entityNamespaces'] = $entityNamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for the mapping metadata.
|
||||
*
|
||||
* @throws ORMException
|
||||
* @return Mapping\Driver\Driver
|
||||
*/
|
||||
public function getMetadataDriverImpl()
|
||||
{
|
||||
return isset($this->_attributes['metadataDriverImpl']) ?
|
||||
$this->_attributes['metadataDriverImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for query result caching.
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getResultCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['resultCacheImpl']) ?
|
||||
$this->_attributes['resultCacheImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for query result caching.
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setResultCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['resultCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for the query cache (SQL cache).
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getQueryCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['queryCacheImpl']) ?
|
||||
$this->_attributes['queryCacheImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for the query cache (SQL cache).
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setQueryCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['queryCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getMetadataCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['metadataCacheImpl']) ?
|
||||
$this->_attributes['metadataCacheImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setMetadataCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['metadataCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a named DQL query to the configuration.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @param string $dql The DQL query string.
|
||||
*/
|
||||
public function addNamedQuery($name, $dql)
|
||||
{
|
||||
$this->_attributes['namedQueries'][$name] = $dql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a previously registered named DQL query.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @return string The DQL query.
|
||||
*/
|
||||
public function getNamedQuery($name)
|
||||
{
|
||||
if ( ! isset($this->_attributes['namedQueries'][$name])) {
|
||||
throw ORMException::namedQueryNotFound($name);
|
||||
}
|
||||
return $this->_attributes['namedQueries'][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a named native query to the configuration.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @param string $sql The native SQL query string.
|
||||
* @param ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query.
|
||||
*/
|
||||
public function addNamedNativeQuery($name, $sql, Query\ResultSetMapping $rsm)
|
||||
{
|
||||
$this->_attributes['namedNativeQueries'][$name] = array($sql, $rsm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the components of a previously registered named native query.
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
* @return array A tuple with the first element being the SQL string and the second
|
||||
* element being the ResultSetMapping.
|
||||
*/
|
||||
public function getNamedNativeQuery($name)
|
||||
{
|
||||
if ( ! isset($this->_attributes['namedNativeQueries'][$name])) {
|
||||
throw ORMException::namedNativeQueryNotFound($name);
|
||||
}
|
||||
return $this->_attributes['namedNativeQueries'][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that this Configuration instance contains settings that are
|
||||
* suitable for a production environment.
|
||||
*
|
||||
* @throws ORMException If a configuration setting has a value that is not
|
||||
* suitable for a production environment.
|
||||
*/
|
||||
public function ensureProductionSettings()
|
||||
{
|
||||
if ( !$this->getQueryCacheImpl()) {
|
||||
throw ORMException::queryCacheNotConfigured();
|
||||
}
|
||||
if ( !$this->getMetadataCacheImpl()) {
|
||||
throw ORMException::metadataCacheNotConfigured();
|
||||
}
|
||||
if ($this->getAutoGenerateProxyClasses()) {
|
||||
throw ORMException::proxyClassesAlwaysRegenerating();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom DQL function that produces a string value.
|
||||
* Such a function can then be used in any DQL statement in any place where string
|
||||
* functions are allowed.
|
||||
*
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $className
|
||||
*/
|
||||
public function addCustomStringFunction($name, $className)
|
||||
{
|
||||
$this->_attributes['customStringFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom string DQL function.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomStringFunction($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
return isset($this->_attributes['customStringFunctions'][$name]) ?
|
||||
$this->_attributes['customStringFunctions'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of custom DQL string functions.
|
||||
*
|
||||
* Keys must be function names and values the FQCN of the implementing class.
|
||||
* The function names will be case-insensitive in DQL.
|
||||
*
|
||||
* Any previously added string functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL string functions.
|
||||
*/
|
||||
public function setCustomStringFunctions(array $functions)
|
||||
{
|
||||
$this->_attributes['customStringFunctions'] = array_change_key_case($functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom DQL function that produces a numeric value.
|
||||
* Such a function can then be used in any DQL statement in any place where numeric
|
||||
* functions are allowed.
|
||||
*
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $className
|
||||
*/
|
||||
public function addCustomNumericFunction($name, $className)
|
||||
{
|
||||
$this->_attributes['customNumericFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom numeric DQL function.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomNumericFunction($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
return isset($this->_attributes['customNumericFunctions'][$name]) ?
|
||||
$this->_attributes['customNumericFunctions'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of custom DQL numeric functions.
|
||||
*
|
||||
* Keys must be function names and values the FQCN of the implementing class.
|
||||
* The function names will be case-insensitive in DQL.
|
||||
*
|
||||
* Any previously added numeric functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL numeric functions.
|
||||
*/
|
||||
public function setCustomNumericFunctions(array $functions)
|
||||
{
|
||||
$this->_attributes['customNumericFunctions'] = array_change_key_case($functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom DQL function that produces a date/time value.
|
||||
* Such a function can then be used in any DQL statement in any place where date/time
|
||||
* functions are allowed.
|
||||
*
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $className
|
||||
*/
|
||||
public function addCustomDatetimeFunction($name, $className)
|
||||
{
|
||||
$this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom date/time DQL function.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomDatetimeFunction($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
return isset($this->_attributes['customDatetimeFunctions'][$name]) ?
|
||||
$this->_attributes['customDatetimeFunctions'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of custom DQL date/time functions.
|
||||
*
|
||||
* Keys must be function names and values the FQCN of the implementing class.
|
||||
* The function names will be case-insensitive in DQL.
|
||||
*
|
||||
* Any previously added date/time functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL date/time functions.
|
||||
*/
|
||||
public function setCustomDatetimeFunctions(array $functions)
|
||||
{
|
||||
$this->_attributes['customDatetimeFunctions'] = array_change_key_case($functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hydrator class for the given hydration mode name.
|
||||
*
|
||||
* @param string $modeName The hydration mode name.
|
||||
* @return string $hydrator The hydrator class name.
|
||||
*/
|
||||
public function getCustomHydrationMode($modeName)
|
||||
{
|
||||
return isset($this->_attributes['customHydrationModes'][$modeName]) ?
|
||||
$this->_attributes['customHydrationModes'][$modeName] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom hydration mode.
|
||||
*
|
||||
* @param string $modeName The hydration mode name.
|
||||
* @param string $hydrator The hydrator class name.
|
||||
*/
|
||||
public function addCustomHydrationMode($modeName, $hydrator)
|
||||
{
|
||||
$this->_attributes['customHydrationModes'][$modeName] = $hydrator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,715 @@
|
||||
<?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;
|
||||
|
||||
use Closure, Exception,
|
||||
Doctrine\Common\EventManager,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\DBAL\LockMode,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Mapping\ClassMetadataFactory,
|
||||
Doctrine\ORM\Query\ResultSetMapping,
|
||||
Doctrine\ORM\Proxy\ProxyFactory;
|
||||
|
||||
/**
|
||||
* The EntityManager is the central access point to ORM functionality.
|
||||
*
|
||||
* @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 EntityManager
|
||||
{
|
||||
/**
|
||||
* The used Configuration.
|
||||
*
|
||||
* @var Doctrine\ORM\Configuration
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* The database connection used by the EntityManager.
|
||||
*
|
||||
* @var Doctrine\DBAL\Connection
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
/**
|
||||
* The metadata factory, used to retrieve the ORM metadata of entity classes.
|
||||
*
|
||||
* @var Doctrine\ORM\Mapping\ClassMetadataFactory
|
||||
*/
|
||||
private $metadataFactory;
|
||||
|
||||
/**
|
||||
* The EntityRepository instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $repositories = array();
|
||||
|
||||
/**
|
||||
* The UnitOfWork used to coordinate object-level transactions.
|
||||
*
|
||||
* @var Doctrine\ORM\UnitOfWork
|
||||
*/
|
||||
private $unitOfWork;
|
||||
|
||||
/**
|
||||
* The event manager that is the central point of the event system.
|
||||
*
|
||||
* @var Doctrine\Common\EventManager
|
||||
*/
|
||||
private $eventManager;
|
||||
|
||||
/**
|
||||
* The maintained (cached) hydrators. One instance per type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $hydrators = array();
|
||||
|
||||
/**
|
||||
* The proxy factory used to create dynamic proxies.
|
||||
*
|
||||
* @var Doctrine\ORM\Proxy\ProxyFactory
|
||||
*/
|
||||
private $proxyFactory;
|
||||
|
||||
/**
|
||||
* @var ExpressionBuilder The expression builder instance used to generate query expressions.
|
||||
*/
|
||||
private $expressionBuilder;
|
||||
|
||||
/**
|
||||
* Whether the EntityManager is closed or not.
|
||||
*/
|
||||
private $closed = false;
|
||||
|
||||
/**
|
||||
* Creates a new EntityManager that operates on the given database connection
|
||||
* and uses the given Configuration and EventManager implementations.
|
||||
*
|
||||
* @param Doctrine\DBAL\Connection $conn
|
||||
* @param Doctrine\ORM\Configuration $config
|
||||
* @param Doctrine\Common\EventManager $eventManager
|
||||
*/
|
||||
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->config = $config;
|
||||
$this->eventManager = $eventManager;
|
||||
$this->metadataFactory = new ClassMetadataFactory($this);
|
||||
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
|
||||
$this->unitOfWork = new UnitOfWork($this);
|
||||
$this->proxyFactory = new ProxyFactory($this,
|
||||
$config->getProxyDir(),
|
||||
$config->getProxyNamespace(),
|
||||
$config->getAutoGenerateProxyClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database connection object used by the EntityManager.
|
||||
*
|
||||
* @return Doctrine\DBAL\Connection
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the metadata factory used to gather the metadata of classes.
|
||||
*
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadataFactory
|
||||
*/
|
||||
public function getMetadataFactory()
|
||||
{
|
||||
return $this->metadataFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder();
|
||||
* $expr = $em->getExpressionBuilder();
|
||||
* $qb->select('u')->from('User', 'u')
|
||||
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
|
||||
* </code>
|
||||
*
|
||||
* @return ExpressionBuilder
|
||||
*/
|
||||
public function getExpressionBuilder()
|
||||
{
|
||||
if ($this->expressionBuilder === null) {
|
||||
$this->expressionBuilder = new Query\Expr;
|
||||
}
|
||||
return $this->expressionBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a transaction on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.beginTransaction().
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a function in a transaction.
|
||||
*
|
||||
* The function gets passed this EntityManager instance as an (optional) parameter.
|
||||
*
|
||||
* {@link flush} is invoked prior to transaction commit.
|
||||
*
|
||||
* If an exception occurs during execution of the function or flushing or transaction commit,
|
||||
* the transaction is rolled back, the EntityManager closed and the exception re-thrown.
|
||||
*
|
||||
* @param Closure $func The function to execute transactionally.
|
||||
*/
|
||||
public function transactional(Closure $func)
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
try {
|
||||
$func($this);
|
||||
$this->flush();
|
||||
$this->conn->commit();
|
||||
} catch (Exception $e) {
|
||||
$this->close();
|
||||
$this->conn->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a transaction on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.commit().
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->conn->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a rollback on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.rollback().
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
$this->conn->rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ORM metadata descriptor for a class.
|
||||
*
|
||||
* The class name must be the fully-qualified class name without a leading backslash
|
||||
* (as it is returned by get_class($obj)) or an aliased class name.
|
||||
*
|
||||
* Examples:
|
||||
* MyProject\Domain\User
|
||||
* sales:PriceRequest
|
||||
*
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
* @internal Performance-sensitive method.
|
||||
*/
|
||||
public function getClassMetadata($className)
|
||||
{
|
||||
return $this->metadataFactory->getMetadataFor($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Query object.
|
||||
*
|
||||
* @param string The DQL string.
|
||||
* @return Doctrine\ORM\Query
|
||||
*/
|
||||
public function createQuery($dql = "")
|
||||
{
|
||||
$query = new Query($this);
|
||||
if ( ! empty($dql)) {
|
||||
$query->setDql($dql);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Query from a named query.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Doctrine\ORM\Query
|
||||
*/
|
||||
public function createNamedQuery($name)
|
||||
{
|
||||
return $this->createQuery($this->config->getNamedQuery($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a native SQL query.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param ResultSetMapping $rsm The ResultSetMapping to use.
|
||||
* @return NativeQuery
|
||||
*/
|
||||
public function createNativeQuery($sql, ResultSetMapping $rsm)
|
||||
{
|
||||
$query = new NativeQuery($this);
|
||||
$query->setSql($sql);
|
||||
$query->setResultSetMapping($rsm);
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a NativeQuery from a named native query.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Doctrine\ORM\NativeQuery
|
||||
*/
|
||||
public function createNamedNativeQuery($name)
|
||||
{
|
||||
list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
|
||||
return $this->createNativeQuery($sql, $rsm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a QueryBuilder instance
|
||||
*
|
||||
* @return QueryBuilder $qb
|
||||
*/
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new QueryBuilder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes all changes to objects that have been queued up to now to the database.
|
||||
* This effectively synchronizes the in-memory state of managed objects with the
|
||||
* database.
|
||||
*
|
||||
* @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that
|
||||
* makes use of optimistic locking fails.
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an Entity by its identifier.
|
||||
*
|
||||
* This is just a convenient shortcut for getRepository($entityName)->find($id).
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param mixed $identifier
|
||||
* @param int $lockMode
|
||||
* @param int $lockVersion
|
||||
* @return object
|
||||
*/
|
||||
public function find($entityName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
|
||||
{
|
||||
return $this->getRepository($entityName)->find($identifier, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the entity identified by the given type and identifier
|
||||
* without actually loading it, if the entity is not yet loaded.
|
||||
*
|
||||
* @param string $entityName The name of the entity type.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
* @return object The entity reference.
|
||||
*/
|
||||
public function getReference($entityName, $identifier)
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
|
||||
// Check identity map first, if its already in there just return it.
|
||||
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
|
||||
return $entity;
|
||||
}
|
||||
if ($class->subClasses) {
|
||||
$entity = $this->find($entityName, $identifier);
|
||||
} else {
|
||||
if ( ! is_array($identifier)) {
|
||||
$identifier = array($class->identifier[0] => $identifier);
|
||||
}
|
||||
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
|
||||
$this->unitOfWork->registerManaged($entity, $identifier, array());
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a partial reference to the entity identified by the given type and identifier
|
||||
* without actually loading it, if the entity is not yet loaded.
|
||||
*
|
||||
* The returned reference may be a partial object if the entity is not yet loaded/managed.
|
||||
* If it is a partial object it will not initialize the rest of the entity state on access.
|
||||
* Thus you can only ever safely access the identifier of an entity obtained through
|
||||
* this method.
|
||||
*
|
||||
* The use-cases for partial references involve maintaining bidirectional associations
|
||||
* without loading one side of the association or to update an entity without loading it.
|
||||
* Note, however, that in the latter case the original (persistent) entity data will
|
||||
* never be visible to the application (especially not event listeners) as it will
|
||||
* never be loaded in the first place.
|
||||
*
|
||||
* @param string $entityName The name of the entity type.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
* @return object The (partial) entity reference.
|
||||
*/
|
||||
public function getPartialReference($entityName, $identifier)
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
|
||||
// Check identity map first, if its already in there just return it.
|
||||
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
|
||||
return $entity;
|
||||
}
|
||||
if ( ! is_array($identifier)) {
|
||||
$identifier = array($class->identifier[0] => $identifier);
|
||||
}
|
||||
|
||||
$entity = $class->newInstance();
|
||||
$class->setIdentifierValues($entity, $identifier);
|
||||
$this->unitOfWork->registerManaged($entity, $identifier, array());
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached.
|
||||
*
|
||||
* @param string $entityName
|
||||
*/
|
||||
public function clear($entityName = null)
|
||||
{
|
||||
if ($entityName === null) {
|
||||
$this->unitOfWork->clear();
|
||||
} else {
|
||||
//TODO
|
||||
throw new ORMException("EntityManager#clear(\$entityName) not yet implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached. The EntityManager may no longer
|
||||
* be used after it is closed.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->clear();
|
||||
$this->closed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the EntityManager to make an instance managed and persistent.
|
||||
*
|
||||
* The entity will be entered into the database at or before transaction
|
||||
* commit or as a result of the flush operation.
|
||||
*
|
||||
* NOTE: The persist operation always considers entities that are not yet known to
|
||||
* this EntityManager as NEW. Do not pass detached entities to the persist operation.
|
||||
*
|
||||
* @param object $object The instance to make managed and persistent.
|
||||
*/
|
||||
public function persist($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->persist($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entity instance.
|
||||
*
|
||||
* A removed entity will be removed from the database at or before transaction commit
|
||||
* or as a result of the flush operation.
|
||||
*
|
||||
* @param object $entity The entity instance to remove.
|
||||
*/
|
||||
public function remove($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->remove($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the persistent state of an entity from the database,
|
||||
* overriding any local changes that have not yet been persisted.
|
||||
*
|
||||
* @param object $entity The entity to refresh.
|
||||
*/
|
||||
public function refresh($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->refresh($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches an entity from the EntityManager, causing a managed entity to
|
||||
* become detached. Unflushed changes made to the entity if any
|
||||
* (including removal of the entity), will not be synchronized to the database.
|
||||
* Entities which previously referenced the detached entity will continue to
|
||||
* reference it.
|
||||
*
|
||||
* @param object $entity The entity to detach.
|
||||
*/
|
||||
public function detach($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->unitOfWork->detach($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the state of a detached entity into the persistence context
|
||||
* of this EntityManager and returns the managed copy of the entity.
|
||||
* The entity passed to merge will not become associated/managed with this EntityManager.
|
||||
*
|
||||
* @param object $entity The detached entity to merge into the persistence context.
|
||||
* @return object The managed copy of the entity.
|
||||
*/
|
||||
public function merge($entity)
|
||||
{
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
$this->errorIfClosed();
|
||||
return $this->unitOfWork->merge($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given entity. Can create a shallow or a deep copy.
|
||||
*
|
||||
* @param object $entity The entity to copy.
|
||||
* @return object The new entity.
|
||||
* @todo Implementation need. This is necessary since $e2 = clone $e1; throws an E_FATAL when access anything on $e:
|
||||
* Fatal error: Maximum function nesting level of '100' reached, aborting!
|
||||
*/
|
||||
public function copy($entity, $deep = false)
|
||||
{
|
||||
throw new \BadMethodCallException("Not implemented.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lock on the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param int $lockMode
|
||||
* @param int $lockVersion
|
||||
* @throws OptimisticLockException
|
||||
* @throws PessimisticLockException
|
||||
*/
|
||||
public function lock($entity, $lockMode, $lockVersion = null)
|
||||
{
|
||||
$this->unitOfWork->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repository for an entity class.
|
||||
*
|
||||
* @param string $entityName The name of the entity.
|
||||
* @return EntityRepository The repository class.
|
||||
*/
|
||||
public function getRepository($entityName)
|
||||
{
|
||||
$entityName = ltrim($entityName, '\\');
|
||||
if (isset($this->repositories[$entityName])) {
|
||||
return $this->repositories[$entityName];
|
||||
}
|
||||
|
||||
$metadata = $this->getClassMetadata($entityName);
|
||||
$customRepositoryClassName = $metadata->customRepositoryClassName;
|
||||
|
||||
if ($customRepositoryClassName !== null) {
|
||||
$repository = new $customRepositoryClassName($this, $metadata);
|
||||
} else {
|
||||
$repository = new EntityRepository($this, $metadata);
|
||||
}
|
||||
|
||||
$this->repositories[$entityName] = $repository;
|
||||
|
||||
return $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an entity instance is managed in this EntityManager.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
|
||||
*/
|
||||
public function contains($entity)
|
||||
{
|
||||
return $this->unitOfWork->isScheduledForInsert($entity) ||
|
||||
$this->unitOfWork->isInIdentityMap($entity) &&
|
||||
! $this->unitOfWork->isScheduledForDelete($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the EventManager used by the EntityManager.
|
||||
*
|
||||
* @return Doctrine\Common\EventManager
|
||||
*/
|
||||
public function getEventManager()
|
||||
{
|
||||
return $this->eventManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Configuration used by the EntityManager.
|
||||
*
|
||||
* @return Doctrine\ORM\Configuration
|
||||
*/
|
||||
public function getConfiguration()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the EntityManager is closed or currently not active.
|
||||
*
|
||||
* @throws ORMException If the EntityManager is closed.
|
||||
*/
|
||||
private function errorIfClosed()
|
||||
{
|
||||
if ($this->closed) {
|
||||
throw ORMException::entityManagerClosed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
|
||||
*
|
||||
* @return Doctrine\ORM\UnitOfWork
|
||||
*/
|
||||
public function getUnitOfWork()
|
||||
{
|
||||
return $this->unitOfWork;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a hydrator for the given hydration mode.
|
||||
*
|
||||
* This method caches the hydrator instances which is used for all queries that don't
|
||||
* selectively iterate over the result.
|
||||
*
|
||||
* @param int $hydrationMode
|
||||
* @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||
*/
|
||||
public function getHydrator($hydrationMode)
|
||||
{
|
||||
if ( ! isset($this->hydrators[$hydrationMode])) {
|
||||
$this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
|
||||
}
|
||||
|
||||
return $this->hydrators[$hydrationMode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance for the given hydration mode.
|
||||
*
|
||||
* @param int $hydrationMode
|
||||
* @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||
*/
|
||||
public function newHydrator($hydrationMode)
|
||||
{
|
||||
switch ($hydrationMode) {
|
||||
case Query::HYDRATE_OBJECT:
|
||||
$hydrator = new Internal\Hydration\ObjectHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_ARRAY:
|
||||
$hydrator = new Internal\Hydration\ArrayHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SCALAR:
|
||||
$hydrator = new Internal\Hydration\ScalarHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SINGLE_SCALAR:
|
||||
$hydrator = new Internal\Hydration\SingleScalarHydrator($this);
|
||||
break;
|
||||
default:
|
||||
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
|
||||
$hydrator = new $class($this);
|
||||
break;
|
||||
}
|
||||
throw ORMException::invalidHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
return $hydrator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the proxy factory used by the EntityManager to create entity proxies.
|
||||
*
|
||||
* @return ProxyFactory
|
||||
*/
|
||||
public function getProxyFactory()
|
||||
{
|
||||
return $this->proxyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create EntityManager instances.
|
||||
*
|
||||
* @param mixed $conn An array with the connection parameters or an existing
|
||||
* Connection instance.
|
||||
* @param Configuration $config The Configuration instance to use.
|
||||
* @param EventManager $eventManager The EventManager instance to use.
|
||||
* @return EntityManager The created EntityManager.
|
||||
*/
|
||||
public static function create($conn, Configuration $config, EventManager $eventManager = null)
|
||||
{
|
||||
if (!$config->getMetadataDriverImpl()) {
|
||||
throw ORMException::missingMappingDriverImpl();
|
||||
}
|
||||
|
||||
if (is_array($conn)) {
|
||||
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager()));
|
||||
} else if ($conn instanceof Connection) {
|
||||
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
|
||||
throw ORMException::mismatchedEventManager();
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Invalid argument: " . $conn);
|
||||
}
|
||||
|
||||
return new EntityManager($conn, $config, $conn->getEventManager());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Exception thrown when a Proxy fails to retrieve an Entity result.
|
||||
*
|
||||
* @author robo
|
||||
* @since 2.0
|
||||
*/
|
||||
class EntityNotFoundException extends ORMException
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Entity was not found.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
/**
|
||||
* An EntityRepository serves as a repository for entities with generic as well as
|
||||
* business specific methods for retrieving entities.
|
||||
*
|
||||
* This class is designed for inheritance and users can subclass this class to
|
||||
* write their own repositories with business-specific methods to locate entities.
|
||||
*
|
||||
* @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 EntityRepository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_entityName;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $_em;
|
||||
|
||||
/**
|
||||
* @var Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
protected $_class;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>EntityRepository</tt>.
|
||||
*
|
||||
* @param EntityManager $em The EntityManager to use.
|
||||
* @param ClassMetadata $classMetadata The class descriptor.
|
||||
*/
|
||||
public function __construct($em, Mapping\ClassMetadata $class)
|
||||
{
|
||||
$this->_entityName = $class->name;
|
||||
$this->_em = $em;
|
||||
$this->_class = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new QueryBuilder instance that is prepopulated for this entity name
|
||||
*
|
||||
* @param string $alias
|
||||
* @return QueryBuilder $qb
|
||||
*/
|
||||
public function createQueryBuilder($alias)
|
||||
{
|
||||
return $this->_em->createQueryBuilder()
|
||||
->select($alias)
|
||||
->from($this->_entityName, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the repository, causing all managed entities to become detached.
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->_em->clear($this->_class->rootEntityName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an entity by its primary key / identifier.
|
||||
*
|
||||
* @param $id The identifier.
|
||||
* @param int $lockMode
|
||||
* @param int $lockVersion
|
||||
* @return object The entity.
|
||||
*/
|
||||
public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
|
||||
{
|
||||
// Check identity map first
|
||||
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
|
||||
if ($lockMode != LockMode::NONE) {
|
||||
$this->_em->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
return $entity; // Hit!
|
||||
}
|
||||
|
||||
if ( ! is_array($id) || count($id) <= 1) {
|
||||
// @todo FIXME: Not correct. Relies on specific order.
|
||||
$value = is_array($id) ? array_values($id) : array($id);
|
||||
$id = array_combine($this->_class->identifier, $value);
|
||||
}
|
||||
|
||||
if ($lockMode == LockMode::NONE) {
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
|
||||
} else if ($lockMode == LockMode::OPTIMISTIC) {
|
||||
if (!$this->_class->isVersioned) {
|
||||
throw OptimisticLockException::notVersioned($this->_entityName);
|
||||
}
|
||||
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
|
||||
|
||||
$this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
|
||||
|
||||
return $entity;
|
||||
} else {
|
||||
if (!$this->_em->getConnection()->isTransactionActive()) {
|
||||
throw TransactionRequiredException::transactionRequired();
|
||||
}
|
||||
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all entities in the repository.
|
||||
*
|
||||
* @return array The entities.
|
||||
*/
|
||||
public function findAll()
|
||||
{
|
||||
return $this->findBy(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities by a set of criteria.
|
||||
*
|
||||
* @param array $criteria
|
||||
* @return array
|
||||
*/
|
||||
public function findBy(array $criteria)
|
||||
{
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single entity by a set of criteria.
|
||||
*
|
||||
* @param array $criteria
|
||||
* @return object
|
||||
*/
|
||||
public function findOneBy(array $criteria)
|
||||
{
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds support for magic finders.
|
||||
*
|
||||
* @return array|object The found entity/entities.
|
||||
* @throws BadMethodCallException If the method called is an invalid find* method
|
||||
* or no find* method at all and therefore an invalid
|
||||
* method call.
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
if (substr($method, 0, 6) == 'findBy') {
|
||||
$by = substr($method, 6, strlen($method));
|
||||
$method = 'findBy';
|
||||
} else if (substr($method, 0, 9) == 'findOneBy') {
|
||||
$by = substr($method, 9, strlen($method));
|
||||
$method = 'findOneBy';
|
||||
} else {
|
||||
throw new \BadMethodCallException(
|
||||
"Undefined method '$method'. The method name must start with ".
|
||||
"either findBy or findOneBy!"
|
||||
);
|
||||
}
|
||||
|
||||
if ( !isset($arguments[0])) {
|
||||
// we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
|
||||
throw ORMException::findByRequiresParameter($method.$by);
|
||||
}
|
||||
|
||||
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
|
||||
|
||||
if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
|
||||
return $this->$method(array($fieldName => $arguments[0]));
|
||||
} else {
|
||||
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getEntityName()
|
||||
{
|
||||
return $this->_entityName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Mapping\ClassMetadata
|
||||
*/
|
||||
protected function getClassMetadata()
|
||||
{
|
||||
return $this->_class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 60
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Event
|
||||
END
|
||||
OnFlushEventArgs.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 81
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Event/OnFlushEventArgs.php
|
||||
END
|
||||
LifecycleEventArgs.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Event/LifecycleEventArgs.php
|
||||
END
|
||||
LoadClassMetadataEventArgs.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 91
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php
|
||||
END
|
||||
PreUpdateEventArgs.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php
|
||||
END
|
||||
@@ -0,0 +1,164 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine/ORM/Event
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
OnFlushEventArgs.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:43.993323Z
|
||||
399d419cbfefe77070a704559af33280
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2113
|
||||
|
||||
LifecycleEventArgs.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:43.993323Z
|
||||
0d0e130c77fb4b674a57cd31d7d84aa6
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1742
|
||||
|
||||
LoadClassMetadataEventArgs.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:43.993323Z
|
||||
810844e2ae0841a5a19544d66e4f1b22
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
525
|
||||
|
||||
PreUpdateEventArgs.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:43.993323Z
|
||||
5726ec58fa2e7f21552774e923da65e7
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2166
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
/**
|
||||
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
|
||||
* of entities.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.de>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class LifecycleEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
private $_entity;
|
||||
|
||||
public function __construct($entity, $em)
|
||||
{
|
||||
$this->_entity = $entity;
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
public function getEntity()
|
||||
{
|
||||
return $this->_entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
}
|
||||
externo
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
|
||||
/**
|
||||
* Class that holds event arguments for a loadMetadata event.
|
||||
*
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class LoadClassMetadataEventArgs extends EventArgs
|
||||
{
|
||||
private $_classMetadata;
|
||||
|
||||
public function __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata)
|
||||
{
|
||||
$this->_classMetadata = $classMetadata;
|
||||
}
|
||||
|
||||
public function getClassMetadata()
|
||||
{
|
||||
return $this->_classMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?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\Event;
|
||||
|
||||
/**
|
||||
* Provides event arguments for the preFlush 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 OnFlushEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
//private $_entitiesToPersist = array();
|
||||
//private $_entitiesToRemove = array();
|
||||
|
||||
public function __construct($em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/*
|
||||
public function addEntityToPersist($entity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function addEntityToRemove($entity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function addEntityToUpdate($entity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getEntitiesToPersist()
|
||||
{
|
||||
return $this->_entitiesToPersist;
|
||||
}
|
||||
*/
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs,
|
||||
Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Class that holds event arguments for a preInsert/preUpdate event.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
*/
|
||||
class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_entityChangeSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object $entity
|
||||
* @param EntityManager $em
|
||||
* @param array $changeSet
|
||||
*/
|
||||
public function __construct($entity, $em, array &$changeSet)
|
||||
{
|
||||
parent::__construct($entity, $em);
|
||||
$this->_entityChangeSet = &$changeSet;
|
||||
}
|
||||
|
||||
public function getEntityChangeSet()
|
||||
{
|
||||
return $this->_entityChangeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field has a changeset?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChangedField($field)
|
||||
{
|
||||
return isset($this->_entityChangeSet[$field]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old value of the changeset of the changed field.
|
||||
*
|
||||
* @param string $field
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOldValue($field)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
|
||||
return $this->_entityChangeSet[$field][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new value of the changeset of the changed field.
|
||||
*
|
||||
* @param string $field
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNewValue($field)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
|
||||
return $this->_entityChangeSet[$field][1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new value of this field.
|
||||
*
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setNewValue($field, $value)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
|
||||
$this->_entityChangeSet[$field][1] = $value;
|
||||
}
|
||||
|
||||
private function _assertValidField($field)
|
||||
{
|
||||
if (!isset($this->_entityChangeSet[$field])) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Field '".$field."' is not a valid field of the entity ".
|
||||
"'".get_class($this->getEntity())."' in PreInsertUpdateEventArgs."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
/**
|
||||
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
|
||||
* of entities.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.de>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class LifecycleEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
private $_entity;
|
||||
|
||||
public function __construct($entity, $em)
|
||||
{
|
||||
$this->_entity = $entity;
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
public function getEntity()
|
||||
{
|
||||
return $this->_entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
|
||||
/**
|
||||
* Class that holds event arguments for a loadMetadata event.
|
||||
*
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class LoadClassMetadataEventArgs extends EventArgs
|
||||
{
|
||||
private $_classMetadata;
|
||||
|
||||
public function __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata)
|
||||
{
|
||||
$this->_classMetadata = $classMetadata;
|
||||
}
|
||||
|
||||
public function getClassMetadata()
|
||||
{
|
||||
return $this->_classMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?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\Event;
|
||||
|
||||
/**
|
||||
* Provides event arguments for the preFlush 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 OnFlushEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
//private $_entitiesToPersist = array();
|
||||
//private $_entitiesToRemove = array();
|
||||
|
||||
public function __construct($em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/*
|
||||
public function addEntityToPersist($entity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function addEntityToRemove($entity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function addEntityToUpdate($entity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getEntitiesToPersist()
|
||||
{
|
||||
return $this->_entitiesToPersist;
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs,
|
||||
Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Class that holds event arguments for a preInsert/preUpdate event.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
*/
|
||||
class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_entityChangeSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object $entity
|
||||
* @param EntityManager $em
|
||||
* @param array $changeSet
|
||||
*/
|
||||
public function __construct($entity, $em, array &$changeSet)
|
||||
{
|
||||
parent::__construct($entity, $em);
|
||||
$this->_entityChangeSet = &$changeSet;
|
||||
}
|
||||
|
||||
public function getEntityChangeSet()
|
||||
{
|
||||
return $this->_entityChangeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field has a changeset?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChangedField($field)
|
||||
{
|
||||
return isset($this->_entityChangeSet[$field]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old value of the changeset of the changed field.
|
||||
*
|
||||
* @param string $field
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOldValue($field)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
|
||||
return $this->_entityChangeSet[$field][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new value of the changeset of the changed field.
|
||||
*
|
||||
* @param string $field
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNewValue($field)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
|
||||
return $this->_entityChangeSet[$field][1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new value of this field.
|
||||
*
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setNewValue($field, $value)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
|
||||
$this->_entityChangeSet[$field][1] = $value;
|
||||
}
|
||||
|
||||
private function _assertValidField($field)
|
||||
{
|
||||
if (!isset($this->_entityChangeSet[$field])) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Field '".$field."' is not a valid field of the entity ".
|
||||
"'".get_class($this->getEntity())."' in PreInsertUpdateEventArgs."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Container for all ORM events.
|
||||
*
|
||||
* This class cannot be instantiated.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
final class Events
|
||||
{
|
||||
private function __construct() {}
|
||||
/**
|
||||
* The preRemove event occurs for a given entity before the respective
|
||||
* EntityManager remove operation for that entity is executed.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const preRemove = 'preRemove';
|
||||
/**
|
||||
* The postRemove event occurs for an entity after the entity has
|
||||
* been deleted. It will be invoked after the database delete operations.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postRemove = 'postRemove';
|
||||
/**
|
||||
* The prePersist event occurs for a given entity before the respective
|
||||
* EntityManager persist operation for that entity is executed.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const prePersist = 'prePersist';
|
||||
/**
|
||||
* The postPersist event occurs for an entity after the entity has
|
||||
* been made persistent. It will be invoked after the database insert operations.
|
||||
* Generated primary key values are available in the postPersist event.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postPersist = 'postPersist';
|
||||
/**
|
||||
* The preUpdate event occurs before the database update operations to
|
||||
* entity data.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const preUpdate = 'preUpdate';
|
||||
/**
|
||||
* The postUpdate event occurs after the database update operations to
|
||||
* entity data.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postUpdate = 'postUpdate';
|
||||
/**
|
||||
* The postLoad event occurs for an entity after the entity has been loaded
|
||||
* into the current EntityManager from the database or after the refresh operation
|
||||
* has been applied to it.
|
||||
*
|
||||
* Note that the postLoad event occurs for an entity before any associations have been
|
||||
* initialized. Therefore it is not safe to access associations in a postLoad callback
|
||||
* or event handler.
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const postLoad = 'postLoad';
|
||||
/**
|
||||
* The loadClassMetadata event occurs after the mapping metadata for a class
|
||||
* has been loaded from a mapping source (annotations/xml/yaml).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const loadClassMetadata = 'loadClassMetadata';
|
||||
|
||||
/**
|
||||
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
* after any changes to managed entities have been determined but before any
|
||||
* actual database operations are executed. The event is only raised if there is
|
||||
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
|
||||
* the onFlush event is not raised.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const onFlush = 'onFlush';
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 57
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Id
|
||||
END
|
||||
TableGenerator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 76
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Id/TableGenerator.php
|
||||
END
|
||||
SequenceGenerator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 79
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Id/SequenceGenerator.php
|
||||
END
|
||||
IdentityGenerator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 79
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Id/IdentityGenerator.php
|
||||
END
|
||||
AssignedGenerator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 79
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Id/AssignedGenerator.php
|
||||
END
|
||||
AbstractIdGenerator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 81
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Id/AbstractIdGenerator.php
|
||||
END
|
||||
@@ -0,0 +1,198 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine/ORM/Id
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
TableGenerator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.063321Z
|
||||
ce3e39a253194e50859e403954d42efd
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3295
|
||||
|
||||
SequenceGenerator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.063321Z
|
||||
d47f79f138a12fea9514c418de1a23ed
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3330
|
||||
|
||||
IdentityGenerator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.063321Z
|
||||
7f02e69f2b5e95505703035ffe3bd58b
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2091
|
||||
|
||||
AssignedGenerator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.043321Z
|
||||
19b245f5ae46005f9cbda01de3ffa993
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2588
|
||||
|
||||
AbstractIdGenerator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.043321Z
|
||||
7f51c39a4febb70181fc6652213a3421
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1728
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
abstract class AbstractIdGenerator
|
||||
{
|
||||
/**
|
||||
* Generates an identifier for an entity.
|
||||
*
|
||||
* @param Doctrine\ORM\Entity $entity
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function generate(EntityManager $em, $entity);
|
||||
|
||||
/**
|
||||
* Gets whether this generator is a post-insert generator which means that
|
||||
* {@link generate()} must be called after the entity has been inserted
|
||||
* into the database.
|
||||
*
|
||||
* By default, this method returns FALSE. Generators that have this requirement
|
||||
* must override this method and return TRUE.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isPostInsertGenerator()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\ORMException;
|
||||
|
||||
/**
|
||||
* Special generator for application-assigned identifiers (doesnt really generate anything).
|
||||
*
|
||||
* @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 AssignedGenerator extends AbstractIdGenerator
|
||||
{
|
||||
/**
|
||||
* Returns the identifier assigned to the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return mixed
|
||||
* @override
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
$class = $em->getClassMetadata(get_class($entity));
|
||||
$identifier = array();
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idFields = $class->getIdentifierFieldNames();
|
||||
foreach ($idFields as $idField) {
|
||||
$value = $class->getReflectionProperty($idField)->getValue($entity);
|
||||
if (isset($value)) {
|
||||
$identifier[$idField] = $value;
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$idField = $class->identifier[0];
|
||||
$value = $class->reflFields[$idField]->getValue($entity);
|
||||
if (isset($value)) {
|
||||
$identifier[$idField] = $value;
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
}
|
||||
}
|
||||
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
* that automatically get a database-generated, auto-incremented identifier on INSERT.
|
||||
* This generator obtains the last insert id after such an insert.
|
||||
*/
|
||||
class IdentityGenerator extends AbstractIdGenerator
|
||||
{
|
||||
/** @var string The name of the sequence to pass to lastInsertId(), if any. */
|
||||
private $_seqName;
|
||||
|
||||
/**
|
||||
* @param string $seqName The name of the sequence to pass to lastInsertId()
|
||||
* to obtain the last generated identifier within the current
|
||||
* database session/connection, if any.
|
||||
*/
|
||||
public function __construct($seqName = null)
|
||||
{
|
||||
$this->_seqName = $seqName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
return $em->getConnection()->lastInsertId($this->_seqName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPostInsertGenerator()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?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\Id;
|
||||
|
||||
use Serializable, Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Represents an ID generator that uses a database sequence.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
{
|
||||
private $_allocationSize;
|
||||
private $_sequenceName;
|
||||
private $_nextValue = 0;
|
||||
private $_maxValue = null;
|
||||
|
||||
/**
|
||||
* Initializes a new sequence generator.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $em The EntityManager to use.
|
||||
* @param string $sequenceName The name of the sequence.
|
||||
* @param integer $allocationSize The allocation size of the sequence.
|
||||
*/
|
||||
public function __construct($sequenceName, $allocationSize)
|
||||
{
|
||||
$this->_sequenceName = $sequenceName;
|
||||
$this->_allocationSize = $allocationSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an ID for the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return integer|float The generated value.
|
||||
* @override
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
|
||||
// Allocate new values
|
||||
$conn = $em->getConnection();
|
||||
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
|
||||
$this->_nextValue = $conn->fetchColumn($sql);
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
}
|
||||
return $this->_nextValue++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum value of the currently allocated bag of values.
|
||||
*
|
||||
* @return integer|float
|
||||
*/
|
||||
public function getCurrentMaxValue()
|
||||
{
|
||||
return $this->_maxValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next value that will be returned by generate().
|
||||
*
|
||||
* @return integer|float
|
||||
*/
|
||||
public function getNextValue()
|
||||
{
|
||||
return $this->_nextValue;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array(
|
||||
'allocationSize' => $this->_allocationSize,
|
||||
'sequenceName' => $this->_sequenceName
|
||||
));
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$array = unserialize($serialized);
|
||||
$this->_sequenceName = $array['sequenceName'];
|
||||
$this->_allocationSize = $array['allocationSize'];
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Id generator that uses a single-row database table and a hi/lo algorithm.
|
||||
*
|
||||
* @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 TableGenerator extends AbstractIdGenerator
|
||||
{
|
||||
private $_tableName;
|
||||
private $_sequenceName;
|
||||
private $_allocationSize;
|
||||
private $_nextValue;
|
||||
private $_maxValue;
|
||||
|
||||
public function __construct($tableName, $sequenceName = 'default', $allocationSize = 10)
|
||||
{
|
||||
$this->_tableName = $tableName;
|
||||
$this->_sequenceName = $sequenceName;
|
||||
$this->_allocationSize = $allocationSize;
|
||||
}
|
||||
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
|
||||
// Allocate new values
|
||||
$conn = $em->getConnection();
|
||||
if ($conn->getTransactionNestingLevel() == 0) {
|
||||
|
||||
// use select for update
|
||||
$sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName);
|
||||
$currentLevel = $conn->fetchColumn($sql);
|
||||
if ($currentLevel != null) {
|
||||
$this->_nextValue = $currentLevel;
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
|
||||
$updateSql = $conn->getDatabasePlatform()->getTableHiLoUpdateNextValSql(
|
||||
$this->_tableName, $this->_sequenceName, $this->_allocationSize
|
||||
);
|
||||
|
||||
if ($conn->executeUpdate($updateSql, array(1 => $currentLevel, 2 => $currentLevel+1)) !== 1) {
|
||||
// no affected rows, concurrency issue, throw exception
|
||||
}
|
||||
} else {
|
||||
// no current level returned, TableGenerator seems to be broken, throw exception
|
||||
}
|
||||
} else {
|
||||
// only table locks help here, implement this or throw exception?
|
||||
// or do we want to work with table locks exclusively?
|
||||
}
|
||||
}
|
||||
return $this->_nextValue++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
abstract class AbstractIdGenerator
|
||||
{
|
||||
/**
|
||||
* Generates an identifier for an entity.
|
||||
*
|
||||
* @param Doctrine\ORM\Entity $entity
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function generate(EntityManager $em, $entity);
|
||||
|
||||
/**
|
||||
* Gets whether this generator is a post-insert generator which means that
|
||||
* {@link generate()} must be called after the entity has been inserted
|
||||
* into the database.
|
||||
*
|
||||
* By default, this method returns FALSE. Generators that have this requirement
|
||||
* must override this method and return TRUE.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isPostInsertGenerator()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\ORMException;
|
||||
|
||||
/**
|
||||
* Special generator for application-assigned identifiers (doesnt really generate anything).
|
||||
*
|
||||
* @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 AssignedGenerator extends AbstractIdGenerator
|
||||
{
|
||||
/**
|
||||
* Returns the identifier assigned to the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return mixed
|
||||
* @override
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
$class = $em->getClassMetadata(get_class($entity));
|
||||
$identifier = array();
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idFields = $class->getIdentifierFieldNames();
|
||||
foreach ($idFields as $idField) {
|
||||
$value = $class->getReflectionProperty($idField)->getValue($entity);
|
||||
if (isset($value)) {
|
||||
$identifier[$idField] = $value;
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$idField = $class->identifier[0];
|
||||
$value = $class->reflFields[$idField]->getValue($entity);
|
||||
if (isset($value)) {
|
||||
$identifier[$idField] = $value;
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
}
|
||||
}
|
||||
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
* that automatically get a database-generated, auto-incremented identifier on INSERT.
|
||||
* This generator obtains the last insert id after such an insert.
|
||||
*/
|
||||
class IdentityGenerator extends AbstractIdGenerator
|
||||
{
|
||||
/** @var string The name of the sequence to pass to lastInsertId(), if any. */
|
||||
private $_seqName;
|
||||
|
||||
/**
|
||||
* @param string $seqName The name of the sequence to pass to lastInsertId()
|
||||
* to obtain the last generated identifier within the current
|
||||
* database session/connection, if any.
|
||||
*/
|
||||
public function __construct($seqName = null)
|
||||
{
|
||||
$this->_seqName = $seqName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
return $em->getConnection()->lastInsertId($this->_seqName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPostInsertGenerator()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?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\Id;
|
||||
|
||||
use Serializable, Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Represents an ID generator that uses a database sequence.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
{
|
||||
private $_allocationSize;
|
||||
private $_sequenceName;
|
||||
private $_nextValue = 0;
|
||||
private $_maxValue = null;
|
||||
|
||||
/**
|
||||
* Initializes a new sequence generator.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $em The EntityManager to use.
|
||||
* @param string $sequenceName The name of the sequence.
|
||||
* @param integer $allocationSize The allocation size of the sequence.
|
||||
*/
|
||||
public function __construct($sequenceName, $allocationSize)
|
||||
{
|
||||
$this->_sequenceName = $sequenceName;
|
||||
$this->_allocationSize = $allocationSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an ID for the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return integer|float The generated value.
|
||||
* @override
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
|
||||
// Allocate new values
|
||||
$conn = $em->getConnection();
|
||||
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
|
||||
$this->_nextValue = $conn->fetchColumn($sql);
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
}
|
||||
return $this->_nextValue++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum value of the currently allocated bag of values.
|
||||
*
|
||||
* @return integer|float
|
||||
*/
|
||||
public function getCurrentMaxValue()
|
||||
{
|
||||
return $this->_maxValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next value that will be returned by generate().
|
||||
*
|
||||
* @return integer|float
|
||||
*/
|
||||
public function getNextValue()
|
||||
{
|
||||
return $this->_nextValue;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array(
|
||||
'allocationSize' => $this->_allocationSize,
|
||||
'sequenceName' => $this->_sequenceName
|
||||
));
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$array = unserialize($serialized);
|
||||
$this->_sequenceName = $array['sequenceName'];
|
||||
$this->_allocationSize = $array['allocationSize'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Id generator that uses a single-row database table and a hi/lo algorithm.
|
||||
*
|
||||
* @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 TableGenerator extends AbstractIdGenerator
|
||||
{
|
||||
private $_tableName;
|
||||
private $_sequenceName;
|
||||
private $_allocationSize;
|
||||
private $_nextValue;
|
||||
private $_maxValue;
|
||||
|
||||
public function __construct($tableName, $sequenceName = 'default', $allocationSize = 10)
|
||||
{
|
||||
$this->_tableName = $tableName;
|
||||
$this->_sequenceName = $sequenceName;
|
||||
$this->_allocationSize = $allocationSize;
|
||||
}
|
||||
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
|
||||
// Allocate new values
|
||||
$conn = $em->getConnection();
|
||||
if ($conn->getTransactionNestingLevel() == 0) {
|
||||
|
||||
// use select for update
|
||||
$sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName);
|
||||
$currentLevel = $conn->fetchColumn($sql);
|
||||
if ($currentLevel != null) {
|
||||
$this->_nextValue = $currentLevel;
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
|
||||
$updateSql = $conn->getDatabasePlatform()->getTableHiLoUpdateNextValSql(
|
||||
$this->_tableName, $this->_sequenceName, $this->_allocationSize
|
||||
);
|
||||
|
||||
if ($conn->executeUpdate($updateSql, array(1 => $currentLevel, 2 => $currentLevel+1)) !== 1) {
|
||||
// no affected rows, concurrency issue, throw exception
|
||||
}
|
||||
} else {
|
||||
// no current level returned, TableGenerator seems to be broken, throw exception
|
||||
}
|
||||
} else {
|
||||
// only table locks help here, implement this or throw exception?
|
||||
// or do we want to work with table locks exclusively?
|
||||
}
|
||||
}
|
||||
return $this->_nextValue++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 63
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal
|
||||
END
|
||||
CommitOrderCalculator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 89
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php
|
||||
END
|
||||
@@ -0,0 +1,65 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine/ORM/Internal
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
Hydration
|
||||
dir
|
||||
|
||||
CommitOrderCalculator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.193323Z
|
||||
00f23956a125df027fc12ed082376b57
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3651
|
||||
|
||||
externo
+118
@@ -0,0 +1,118 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* The CommitOrderCalculator is used by the UnitOfWork to sort out the
|
||||
* correct order in which changes to entities need to be persisted.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class CommitOrderCalculator
|
||||
{
|
||||
const NOT_VISITED = 1;
|
||||
const IN_PROGRESS = 2;
|
||||
const VISITED = 3;
|
||||
|
||||
private $_nodeStates = array();
|
||||
private $_classes = array(); // The nodes to sort
|
||||
private $_relatedClasses = array();
|
||||
private $_sorted = array();
|
||||
|
||||
/**
|
||||
* Clears the current graph.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->_classes =
|
||||
$this->_relatedClasses = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a valid commit order for all current nodes.
|
||||
*
|
||||
* Uses a depth-first search (DFS) to traverse the graph.
|
||||
* The desired topological sorting is the reverse postorder of these searches.
|
||||
*
|
||||
* @return array The list of ordered classes.
|
||||
*/
|
||||
public function getCommitOrder()
|
||||
{
|
||||
// Check whether we need to do anything. 0 or 1 node is easy.
|
||||
$nodeCount = count($this->_classes);
|
||||
if ($nodeCount == 0) {
|
||||
return array();
|
||||
} else if ($nodeCount == 1) {
|
||||
return array_values($this->_classes);
|
||||
}
|
||||
|
||||
// Init
|
||||
foreach ($this->_classes as $node) {
|
||||
$this->_nodeStates[$node->name] = self::NOT_VISITED;
|
||||
}
|
||||
|
||||
// Go
|
||||
foreach ($this->_classes as $node) {
|
||||
if ($this->_nodeStates[$node->name] == self::NOT_VISITED) {
|
||||
$this->_visitNode($node);
|
||||
}
|
||||
}
|
||||
|
||||
$sorted = array_reverse($this->_sorted);
|
||||
|
||||
$this->_sorted = $this->_nodeStates = array();
|
||||
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
private function _visitNode($node)
|
||||
{
|
||||
$this->_nodeStates[$node->name] = self::IN_PROGRESS;
|
||||
|
||||
if (isset($this->_relatedClasses[$node->name])) {
|
||||
foreach ($this->_relatedClasses[$node->name] as $relatedNode) {
|
||||
if ($this->_nodeStates[$relatedNode->name] == self::NOT_VISITED) {
|
||||
$this->_visitNode($relatedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_nodeStates[$node->name] = self::VISITED;
|
||||
$this->_sorted[] = $node;
|
||||
}
|
||||
|
||||
public function addDependency($fromClass, $toClass)
|
||||
{
|
||||
$this->_relatedClasses[$fromClass->name][] = $toClass;
|
||||
}
|
||||
|
||||
public function hasClass($className)
|
||||
{
|
||||
return isset($this->_classes[$className]);
|
||||
}
|
||||
|
||||
public function addClass($class)
|
||||
{
|
||||
$this->_classes[$class->name] = $class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* The CommitOrderCalculator is used by the UnitOfWork to sort out the
|
||||
* correct order in which changes to entities need to be persisted.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class CommitOrderCalculator
|
||||
{
|
||||
const NOT_VISITED = 1;
|
||||
const IN_PROGRESS = 2;
|
||||
const VISITED = 3;
|
||||
|
||||
private $_nodeStates = array();
|
||||
private $_classes = array(); // The nodes to sort
|
||||
private $_relatedClasses = array();
|
||||
private $_sorted = array();
|
||||
|
||||
/**
|
||||
* Clears the current graph.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->_classes =
|
||||
$this->_relatedClasses = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a valid commit order for all current nodes.
|
||||
*
|
||||
* Uses a depth-first search (DFS) to traverse the graph.
|
||||
* The desired topological sorting is the reverse postorder of these searches.
|
||||
*
|
||||
* @return array The list of ordered classes.
|
||||
*/
|
||||
public function getCommitOrder()
|
||||
{
|
||||
// Check whether we need to do anything. 0 or 1 node is easy.
|
||||
$nodeCount = count($this->_classes);
|
||||
if ($nodeCount == 0) {
|
||||
return array();
|
||||
} else if ($nodeCount == 1) {
|
||||
return array_values($this->_classes);
|
||||
}
|
||||
|
||||
// Init
|
||||
foreach ($this->_classes as $node) {
|
||||
$this->_nodeStates[$node->name] = self::NOT_VISITED;
|
||||
}
|
||||
|
||||
// Go
|
||||
foreach ($this->_classes as $node) {
|
||||
if ($this->_nodeStates[$node->name] == self::NOT_VISITED) {
|
||||
$this->_visitNode($node);
|
||||
}
|
||||
}
|
||||
|
||||
$sorted = array_reverse($this->_sorted);
|
||||
|
||||
$this->_sorted = $this->_nodeStates = array();
|
||||
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
private function _visitNode($node)
|
||||
{
|
||||
$this->_nodeStates[$node->name] = self::IN_PROGRESS;
|
||||
|
||||
if (isset($this->_relatedClasses[$node->name])) {
|
||||
foreach ($this->_relatedClasses[$node->name] as $relatedNode) {
|
||||
if ($this->_nodeStates[$relatedNode->name] == self::NOT_VISITED) {
|
||||
$this->_visitNode($relatedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_nodeStates[$node->name] = self::VISITED;
|
||||
$this->_sorted[] = $node;
|
||||
}
|
||||
|
||||
public function addDependency($fromClass, $toClass)
|
||||
{
|
||||
$this->_relatedClasses[$fromClass->name][] = $toClass;
|
||||
}
|
||||
|
||||
public function hasClass($className)
|
||||
{
|
||||
return isset($this->_classes[$className]);
|
||||
}
|
||||
|
||||
public function addClass($class)
|
||||
{
|
||||
$this->_classes[$class->name] = $class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 73
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration
|
||||
END
|
||||
ArrayHydrator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 91
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php
|
||||
END
|
||||
AbstractHydrator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 94
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
|
||||
END
|
||||
HydrationException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 96
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php
|
||||
END
|
||||
ScalarHydrator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 92
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php
|
||||
END
|
||||
ObjectHydrator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 92
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php
|
||||
END
|
||||
SingleScalarHydrator.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 98
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php
|
||||
END
|
||||
IterableResult.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 92
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php
|
||||
END
|
||||
@@ -0,0 +1,266 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine/ORM/Internal/Hydration
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
ArrayHydrator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.163323Z
|
||||
c6d1bab9a64db92bd0d94d40a2c7e28b
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
9876
|
||||
|
||||
AbstractHydrator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.163323Z
|
||||
cbf1ce2f914306f9087a18b261da2467
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
10702
|
||||
|
||||
HydrationException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.163323Z
|
||||
045ae36ed0ec1736c13c607a020baa42
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
500
|
||||
|
||||
ScalarHydrator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.163323Z
|
||||
808fa26250c13367b3cdb44b1fb72925
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1879
|
||||
|
||||
ObjectHydrator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.163323Z
|
||||
04084fd115371a4b070a52a6a85d3598
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20161
|
||||
|
||||
SingleScalarHydrator.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.193323Z
|
||||
b5a17f0e636375c7cdefa0f0f15d6b82
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1798
|
||||
|
||||
IterableResult.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.163323Z
|
||||
c44febe543d481b9ad9feb993cfa224c
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2593
|
||||
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
<?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,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Base class for all hydrators. A hydrator is a class that provides some form
|
||||
* of transformation of an SQL result set into another structure.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
abstract class AbstractHydrator
|
||||
{
|
||||
/** @var ResultSetMapping The ResultSetMapping. */
|
||||
protected $_rsm;
|
||||
|
||||
/** @var EntityManager The EntityManager instance. */
|
||||
protected $_em;
|
||||
|
||||
/** @var AbstractPlatform The dbms Platform instance */
|
||||
protected $_platform;
|
||||
|
||||
/** @var UnitOfWork The UnitOfWork of the associated EntityManager. */
|
||||
protected $_uow;
|
||||
|
||||
/** @var array The cache used during row-by-row hydration. */
|
||||
protected $_cache = array();
|
||||
|
||||
/** @var Statement The statement that provides the data to hydrate. */
|
||||
protected $_stmt;
|
||||
|
||||
/** @var array The query hints. */
|
||||
protected $_hints;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a class derived from <tt>AbstractHydrator</tt>.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $em The EntityManager to use.
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
$this->_platform = $em->getConnection()->getDatabasePlatform();
|
||||
$this->_uow = $em->getUnitOfWork();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a row-by-row hydration.
|
||||
*
|
||||
* @param object $stmt
|
||||
* @param object $resultSetMapping
|
||||
* @return IterableResult
|
||||
*/
|
||||
public function iterate($stmt, $resultSetMapping, array $hints = array())
|
||||
{
|
||||
$this->_stmt = $stmt;
|
||||
$this->_rsm = $resultSetMapping;
|
||||
$this->_hints = $hints;
|
||||
$this->_prepare();
|
||||
return new IterableResult($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates all rows returned by the passed statement instance at once.
|
||||
*
|
||||
* @param object $stmt
|
||||
* @param object $resultSetMapping
|
||||
* @return mixed
|
||||
*/
|
||||
public function hydrateAll($stmt, $resultSetMapping, array $hints = array())
|
||||
{
|
||||
$this->_stmt = $stmt;
|
||||
$this->_rsm = $resultSetMapping;
|
||||
$this->_hints = $hints;
|
||||
$this->_prepare();
|
||||
$result = $this->_hydrateAll();
|
||||
$this->_cleanup();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates a single row returned by the current statement instance during
|
||||
* row-by-row hydration with {@link iterate()}.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function hydrateRow()
|
||||
{
|
||||
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ( ! $row) {
|
||||
$this->_cleanup();
|
||||
return false;
|
||||
}
|
||||
$result = array();
|
||||
$this->_hydrateRow($row, $this->_cache, $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Excutes one-time preparation tasks, once each time hydration is started
|
||||
* through {@link hydrateAll} or {@link iterate()}.
|
||||
*/
|
||||
protected function _prepare()
|
||||
{}
|
||||
|
||||
/**
|
||||
* Excutes one-time cleanup tasks at the end of a hydration that was initiated
|
||||
* through {@link hydrateAll} or {@link iterate()}.
|
||||
*/
|
||||
protected function _cleanup()
|
||||
{
|
||||
$this->_rsm = null;
|
||||
$this->_stmt->closeCursor();
|
||||
$this->_stmt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates a single row from the current statement instance.
|
||||
*
|
||||
* Template method.
|
||||
*
|
||||
* @param array $data The row data.
|
||||
* @param array $cache The cache to use.
|
||||
* @param mixed $result The result to fill.
|
||||
*/
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
throw new HydrationException("_hydrateRow() not implemented by this hydrator.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates all rows from the current statement instance at once.
|
||||
*/
|
||||
abstract protected function _hydrateAll();
|
||||
|
||||
/**
|
||||
* Processes a row of the result set.
|
||||
* Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY).
|
||||
* Puts the elements of a result row into a new array, grouped by the class
|
||||
* they belong to. The column names in the result set are mapped to their
|
||||
* field names during this procedure as well as any necessary conversions on
|
||||
* the values applied.
|
||||
*
|
||||
* @return array An array with all the fields (name => value) of the data row,
|
||||
* grouped by their component alias.
|
||||
*/
|
||||
protected function _gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
|
||||
{
|
||||
$rowData = array();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// Parse each column name only once. Cache the results.
|
||||
if ( ! isset($cache[$key])) {
|
||||
if (isset($this->_rsm->scalarMappings[$key])) {
|
||||
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
|
||||
$cache[$key]['isScalar'] = true;
|
||||
} else if (isset($this->_rsm->fieldMappings[$key])) {
|
||||
$fieldName = $this->_rsm->fieldMappings[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
} else if (!isset($this->_rsm->metaMappings[$key])) {
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue;
|
||||
} else {
|
||||
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
|
||||
$cache[$key]['isMetaColumn'] = true;
|
||||
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache[$key]['isScalar'])) {
|
||||
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
$dqlAlias = $cache[$key]['dqlAlias'];
|
||||
|
||||
if (isset($cache[$key]['isMetaColumn'])) {
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cache[$key]['isIdentifier']) {
|
||||
$id[$dqlAlias] .= '|' . $value;
|
||||
}
|
||||
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
|
||||
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
|
||||
$nonemptyComponents[$dqlAlias] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a row of the result set.
|
||||
* Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
|
||||
* simply converts column names to field names and properly converts the
|
||||
* values according to their types. The resulting row has the same number
|
||||
* of elements as before.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $cache
|
||||
* @return array The processed row.
|
||||
*/
|
||||
protected function _gatherScalarRowData(&$data, &$cache)
|
||||
{
|
||||
$rowData = array();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// Parse each column name only once. Cache the results.
|
||||
if ( ! isset($cache[$key])) {
|
||||
if (isset($this->_rsm->scalarMappings[$key])) {
|
||||
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
|
||||
$cache[$key]['isScalar'] = true;
|
||||
} else if (isset($this->_rsm->fieldMappings[$key])) {
|
||||
$fieldName = $this->_rsm->fieldMappings[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
} else if (!isset($this->_rsm->metaMappings[$key])) {
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue;
|
||||
} else {
|
||||
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
|
||||
$cache[$key]['isMetaColumn'] = true;
|
||||
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$fieldName = $cache[$key]['fieldName'];
|
||||
|
||||
if (isset($cache[$key]['isScalar'])) {
|
||||
$rowData[$fieldName] = $value;
|
||||
} else if (isset($cache[$key]['isMetaColumn'])) {
|
||||
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
|
||||
} else {
|
||||
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $cache[$key]['type']
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
}
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
}
|
||||
externo
+235
@@ -0,0 +1,235 @@
|
||||
<?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, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* The ArrayHydrator produces a nested array "graph" that is often (not always)
|
||||
* interchangeable with the corresponding object graph for read-only access.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 1.0
|
||||
*/
|
||||
class ArrayHydrator extends AbstractHydrator
|
||||
{
|
||||
private $_ce = array();
|
||||
private $_rootAliases = array();
|
||||
private $_isSimpleQuery = false;
|
||||
private $_identifierMap = array();
|
||||
private $_resultPointers = array();
|
||||
private $_idTemplate = array();
|
||||
private $_resultCounter = 0;
|
||||
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
{
|
||||
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
|
||||
$this->_identifierMap = array();
|
||||
$this->_resultPointers = array();
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_resultPointers[$dqlAlias] = array();
|
||||
$this->_idTemplate[$dqlAlias] = '';
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($data, $cache, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
// 1) Initialize
|
||||
$id = $this->_idTemplate; // initialize the id-memory
|
||||
$nonemptyComponents = array();
|
||||
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
|
||||
|
||||
// Extract scalar values. They're appended at the end.
|
||||
if (isset($rowData['scalars'])) {
|
||||
$scalars = $rowData['scalars'];
|
||||
unset($rowData['scalars']);
|
||||
if (empty($rowData)) {
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Now hydrate the data found in the current row.
|
||||
foreach ($rowData as $dqlAlias => $data) {
|
||||
$index = false;
|
||||
|
||||
if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
|
||||
// It's a joined result
|
||||
|
||||
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
|
||||
$path = $parent . '.' . $dqlAlias;
|
||||
|
||||
// Get a reference to the right element in the result tree.
|
||||
// This element will get the associated element attached.
|
||||
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
|
||||
$first = reset($this->_resultPointers);
|
||||
// TODO: Exception if $key === null ?
|
||||
$baseElement =& $this->_resultPointers[$parent][key($first)];
|
||||
} else if (isset($this->_resultPointers[$parent])) {
|
||||
$baseElement =& $this->_resultPointers[$parent];
|
||||
} else {
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
continue;
|
||||
}
|
||||
|
||||
$relationAlias = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
|
||||
|
||||
// Check the type of the relation (many or single-valued)
|
||||
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
|
||||
$oneToOne = false;
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = array();
|
||||
}
|
||||
|
||||
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
|
||||
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
|
||||
$indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;
|
||||
|
||||
if ( ! $indexExists || ! $indexIsValid) {
|
||||
$element = $data;
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$baseElement[$relationAlias][$element[$field]] = $element;
|
||||
} else {
|
||||
$baseElement[$relationAlias][] = $element;
|
||||
}
|
||||
end($baseElement[$relationAlias]);
|
||||
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] =
|
||||
key($baseElement[$relationAlias]);
|
||||
}
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = array();
|
||||
}
|
||||
} else {
|
||||
$oneToOne = true;
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = null;
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
$coll =& $baseElement[$relationAlias];
|
||||
|
||||
if ($coll !== null) {
|
||||
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
|
||||
}
|
||||
|
||||
} else {
|
||||
// It's a root result element
|
||||
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root
|
||||
|
||||
// Check for an existing element
|
||||
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $rowData[$dqlAlias];
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array($element[$field] => $element);
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[$element[$field]] = $element;
|
||||
}
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array($element);
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[] = $element;
|
||||
}
|
||||
}
|
||||
end($result);
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
|
||||
} else {
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] =& $result[$index];
|
||||
++$this->_resultCounter;
|
||||
}*/
|
||||
}
|
||||
$this->updateResultPointer($result, $index, $dqlAlias, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Append scalar values to mixed result sets
|
||||
if (isset($scalars)) {
|
||||
foreach ($scalars as $name => $value) {
|
||||
$result[$this->_resultCounter - 1][$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the result pointer for an Entity. The result pointers point to the
|
||||
* last seen instance of each Entity type. This is used for graph construction.
|
||||
*
|
||||
* @param array $coll The element.
|
||||
* @param boolean|integer $index Index of the element in the collection.
|
||||
* @param string $dqlAlias
|
||||
* @param boolean $oneToOne Whether it is a single-valued association or not.
|
||||
*/
|
||||
private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne)
|
||||
{
|
||||
if ($coll === null) {
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
return;
|
||||
}
|
||||
if ($index !== false) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[$index];
|
||||
return;
|
||||
} else {
|
||||
if ($coll) {
|
||||
if ($oneToOne) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll;
|
||||
} else {
|
||||
end($coll);
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function _getClassMetadata($className)
|
||||
{
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
return $this->_ce[$className];
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
class HydrationException extends \Doctrine\ORM\ORMException
|
||||
{
|
||||
public static function nonUniqueResult()
|
||||
{
|
||||
return new self("The result returned by the query was not unique.");
|
||||
}
|
||||
|
||||
public static function parentObjectOfRelationNotFound($alias, $parentAlias)
|
||||
{
|
||||
return new self("The parent object of entity result with alias '$alias' was not found."
|
||||
. " The parent alias is '$parentAlias'.");
|
||||
}
|
||||
}
|
||||
externo
+104
@@ -0,0 +1,104 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Represents a result structure that can be iterated over, hydrating row-by-row
|
||||
* during the iteration. An IterableResult is obtained by AbstractHydrator#iterate().
|
||||
*
|
||||
* @author robo
|
||||
* @since 2.0
|
||||
*/
|
||||
class IterableResult implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @var Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||
*/
|
||||
private $_hydrator;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $_rewinded = false;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $_key = -1;
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
private $_current = null;
|
||||
|
||||
/**
|
||||
* @param Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator
|
||||
*/
|
||||
public function __construct($hydrator)
|
||||
{
|
||||
$this->_hydrator = $hydrator;
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
if ($this->_rewinded == true) {
|
||||
throw new HydrationException("Can only iterate a Result once.");
|
||||
} else {
|
||||
$this->_current = $this->next();
|
||||
$this->_rewinded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next set of results.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->_current = $this->_hydrator->hydrateRow();
|
||||
$this->_key++;
|
||||
return $this->_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return ($this->_current!=false);
|
||||
}
|
||||
}
|
||||
externo
+426
@@ -0,0 +1,426 @@
|
||||
<?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,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\PersistentCollection,
|
||||
Doctrine\ORM\Query,
|
||||
Doctrine\Common\Collections\ArrayCollection,
|
||||
Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
* The ObjectHydrator constructs an object graph out of an SQL result set.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @internal Highly performance-sensitive code.
|
||||
*/
|
||||
class ObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
/* Local ClassMetadata cache to avoid going to the EntityManager all the time.
|
||||
* 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;
|
||||
private $_resultCounter;
|
||||
private $_rootAliases = array();
|
||||
private $_initializedCollections = array();
|
||||
private $_existingCollections = array();
|
||||
//private $_createdEntities;
|
||||
|
||||
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
{
|
||||
$this->_identifierMap =
|
||||
$this->_resultPointers =
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_idTemplate[$dqlAlias] = '';
|
||||
$class = $this->_em->getClassMetadata($className);
|
||||
|
||||
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])) {
|
||||
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
|
||||
$sourceClass = $this->_getClassMetadata($sourceClassName);
|
||||
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
|
||||
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
|
||||
if ($sourceClass->subClasses) {
|
||||
foreach ($sourceClass->subClasses as $sourceSubclassName) {
|
||||
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
|
||||
}
|
||||
}
|
||||
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
|
||||
// Mark any non-collection opposite sides as fetched, too.
|
||||
if ($assoc['mappedBy']) {
|
||||
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
|
||||
} else {
|
||||
if ($assoc['inversedBy']) {
|
||||
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
|
||||
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
|
||||
if ($class->subClasses) {
|
||||
foreach ($class->subClasses as $targetSubclassName) {
|
||||
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _cleanup()
|
||||
{
|
||||
parent::_cleanup();
|
||||
$this->_identifierMap =
|
||||
$this->_initializedCollections =
|
||||
$this->_existingCollections =
|
||||
$this->_resultPointers = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($row, $cache, $result);
|
||||
}
|
||||
|
||||
// Take snapshots from all newly initialized collections
|
||||
foreach ($this->_initializedCollections as $coll) {
|
||||
$coll->takeSnapshot();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a related collection.
|
||||
*
|
||||
* @param object $entity The entity to which the collection belongs.
|
||||
* @param string $name The name of the field on the entity that holds the collection.
|
||||
*/
|
||||
private function _initRelatedCollection($entity, $class, $fieldName)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
$relation = $class->associationMappings[$fieldName];
|
||||
|
||||
$value = $class->reflFields[$fieldName]->getValue($entity);
|
||||
if ($value === null) {
|
||||
$value = new ArrayCollection;
|
||||
}
|
||||
|
||||
if ( ! $value instanceof PersistentCollection) {
|
||||
$value = new PersistentCollection(
|
||||
$this->_em,
|
||||
$this->_ce[$relation['targetEntity']],
|
||||
$value
|
||||
);
|
||||
$value->setOwner($entity, $relation);
|
||||
$class->reflFields[$fieldName]->setValue($entity, $value);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
} else if (isset($this->_hints[Query::HINT_REFRESH])) {
|
||||
// Is already PersistentCollection, but REFRESH
|
||||
$value->setDirty(false);
|
||||
$value->setInitialized(true);
|
||||
$value->unwrap()->clear();
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
} else {
|
||||
// Is already PersistentCollection, and DONT REFRESH
|
||||
$this->_existingCollections[$oid . $fieldName] = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entity instance.
|
||||
*
|
||||
* @param $data The instance data.
|
||||
* @param $dqlAlias The DQL alias of the entity's class.
|
||||
* @return object The entity.
|
||||
*/
|
||||
private function _getEntity(array $data, $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]);
|
||||
}
|
||||
return $this->_uow->createEntity($className, $data, $this->_hints);
|
||||
}
|
||||
|
||||
private function _getEntityFromIdentityMap($className, array $data)
|
||||
{
|
||||
$class = $this->_ce[$className];
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idHash = '';
|
||||
foreach ($class->identifier as $fieldName) {
|
||||
$idHash .= $data[$fieldName] . ' ';
|
||||
}
|
||||
return $this->_uow->tryGetByIdHash(rtrim($idHash), $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
|
||||
* local cache.
|
||||
*
|
||||
* @param string $className The name of the class.
|
||||
* @return ClassMetadata
|
||||
*/
|
||||
private function _getClassMetadata($className)
|
||||
{
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
return $this->_ce[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates a single row in an SQL result set.
|
||||
*
|
||||
* @internal
|
||||
* First, the data of the row is split into chunks where each chunk contains data
|
||||
* that belongs to a particular component/class. Afterwards, all these chunks
|
||||
* are processed, one after the other. For each chunk of class data only one of the
|
||||
* following code paths is executed:
|
||||
*
|
||||
* Path A: The data chunk belongs to a joined/associated object and the association
|
||||
* is collection-valued.
|
||||
* Path B: The data chunk belongs to a joined/associated object and the association
|
||||
* is single-valued.
|
||||
* Path C: The data chunk belongs to a root result element/object that appears in the topmost
|
||||
* level of the hydrated result. A typical example are the objects of the type
|
||||
* specified by the FROM clause in a DQL query.
|
||||
*
|
||||
* @param array $data The data of the row to process.
|
||||
* @param array $cache The cache to use.
|
||||
* @param array $result The result array to fill.
|
||||
*/
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
// Initialize
|
||||
$id = $this->_idTemplate; // initialize the id-memory
|
||||
$nonemptyComponents = array();
|
||||
// Split the row data into chunks of class data.
|
||||
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
|
||||
|
||||
// Extract scalar values. They're appended at the end.
|
||||
if (isset($rowData['scalars'])) {
|
||||
$scalars = $rowData['scalars'];
|
||||
unset($rowData['scalars']);
|
||||
if (empty($rowData)) {
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
$parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
|
||||
// we need the $path to save into the identifier map which entities were already
|
||||
// seen for this parent-child relationship
|
||||
$path = $parentAlias . '.' . $dqlAlias;
|
||||
|
||||
// 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);
|
||||
$parentObject = $this->_resultPointers[$parentAlias][key($first)];
|
||||
} else if (isset($this->_resultPointers[$parentAlias])) {
|
||||
$parentObject = $this->_resultPointers[$parentAlias];
|
||||
} else {
|
||||
// Parent object of relation not found, so skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
|
||||
$oid = spl_object_hash($parentObject);
|
||||
$relationField = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relation = $parentClass->associationMappings[$relationField];
|
||||
$reflField = $parentClass->reflFields[$relationField];
|
||||
|
||||
// Check the type of the relation (many or single-valued)
|
||||
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
|
||||
// PATH A: Collection-valued association
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$collKey = $oid . $relationField;
|
||||
if (isset($this->_initializedCollections[$collKey])) {
|
||||
$reflFieldValue = $this->_initializedCollections[$collKey];
|
||||
} 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.
|
||||
if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) {
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
} else {
|
||||
unset($this->_resultPointers[$dqlAlias]);
|
||||
}
|
||||
} else {
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
|
||||
$reflFieldValue->hydrateSet($indexValue, $element);
|
||||
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
|
||||
} else {
|
||||
$reflFieldValue->hydrateAdd($element);
|
||||
$reflFieldValue->last();
|
||||
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
|
||||
}
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
}
|
||||
} else {
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
|
||||
}
|
||||
} else if ( ! $reflField->getValue($parentObject)) {
|
||||
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
|
||||
$reflField->setValue($parentObject, $coll);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
|
||||
}
|
||||
} else {
|
||||
// PATH B: Single-valued association
|
||||
$reflFieldValue = $reflField->getValue($parentObject);
|
||||
if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH])) {
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
$reflField->setValue($parentObject, $element);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
|
||||
$targetClass = $this->_ce[$relation['targetEntity']];
|
||||
if ($relation['isOwningSide']) {
|
||||
//TODO: Just check hints['fetched'] here?
|
||||
// If there is an inverse mapping on the target class its bidirectional
|
||||
if ($relation['inversedBy']) {
|
||||
$inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']];
|
||||
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject);
|
||||
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
|
||||
}
|
||||
} else if ($parentClass === $targetClass && $relation['mappedBy']) {
|
||||
// Special case: bi-directional self-referencing one-one on the same class
|
||||
$targetClass->reflFields[$relationField]->setValue($element, $parentObject);
|
||||
}
|
||||
} else {
|
||||
// For sure bidirectional, as there is no inverse side in unidirectional mappings
|
||||
$targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject);
|
||||
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
|
||||
}
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
}
|
||||
// else leave $reflFieldValue null for single-valued associations
|
||||
} else {
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $reflFieldValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// PATH C: Its a root result element
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
|
||||
|
||||
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array($key => $element);
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[$key] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
|
||||
}
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array(0 => $element);
|
||||
}
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
|
||||
} else {
|
||||
// Update result pointer
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
$this->_resultPointers[$dqlAlias] = $result[$index];
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] = $result[$index];
|
||||
++$this->_resultCounter;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append scalar values to mixed result sets
|
||||
if (isset($scalars)) {
|
||||
foreach ($scalars as $name => $value) {
|
||||
$result[$this->_resultCounter - 1][$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
externo
+50
@@ -0,0 +1,50 @@
|
||||
<?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 Doctrine\DBAL\Connection;
|
||||
|
||||
/**
|
||||
* Hydrator that produces flat, rectangular results of scalar data.
|
||||
* The created result is almost the same as a regular SQL result set, except
|
||||
* that column names are mapped to field names and data type conversions take place.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$result[] = $this->_gatherScalarRowData($data, $cache);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
$result[] = $this->_gatherScalarRowData($data, $cache);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?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 Doctrine\DBAL\Connection;
|
||||
|
||||
/**
|
||||
* Hydrator that hydrates a single scalar value from the result set.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class SingleScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$cache = array();
|
||||
$result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
$num = count($result);
|
||||
|
||||
if ($num == 0) {
|
||||
throw new \Doctrine\ORM\NoResultException;
|
||||
} else if ($num > 1 || count($result[key($result)]) > 1) {
|
||||
throw new \Doctrine\ORM\NonUniqueResultException;
|
||||
}
|
||||
|
||||
$result = $this->_gatherScalarRowData($result[key($result)], $cache);
|
||||
|
||||
return array_shift($result);
|
||||
}
|
||||
}
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
<?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,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Base class for all hydrators. A hydrator is a class that provides some form
|
||||
* of transformation of an SQL result set into another structure.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
abstract class AbstractHydrator
|
||||
{
|
||||
/** @var ResultSetMapping The ResultSetMapping. */
|
||||
protected $_rsm;
|
||||
|
||||
/** @var EntityManager The EntityManager instance. */
|
||||
protected $_em;
|
||||
|
||||
/** @var AbstractPlatform The dbms Platform instance */
|
||||
protected $_platform;
|
||||
|
||||
/** @var UnitOfWork The UnitOfWork of the associated EntityManager. */
|
||||
protected $_uow;
|
||||
|
||||
/** @var array The cache used during row-by-row hydration. */
|
||||
protected $_cache = array();
|
||||
|
||||
/** @var Statement The statement that provides the data to hydrate. */
|
||||
protected $_stmt;
|
||||
|
||||
/** @var array The query hints. */
|
||||
protected $_hints;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a class derived from <tt>AbstractHydrator</tt>.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $em The EntityManager to use.
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
$this->_platform = $em->getConnection()->getDatabasePlatform();
|
||||
$this->_uow = $em->getUnitOfWork();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a row-by-row hydration.
|
||||
*
|
||||
* @param object $stmt
|
||||
* @param object $resultSetMapping
|
||||
* @return IterableResult
|
||||
*/
|
||||
public function iterate($stmt, $resultSetMapping, array $hints = array())
|
||||
{
|
||||
$this->_stmt = $stmt;
|
||||
$this->_rsm = $resultSetMapping;
|
||||
$this->_hints = $hints;
|
||||
$this->_prepare();
|
||||
return new IterableResult($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates all rows returned by the passed statement instance at once.
|
||||
*
|
||||
* @param object $stmt
|
||||
* @param object $resultSetMapping
|
||||
* @return mixed
|
||||
*/
|
||||
public function hydrateAll($stmt, $resultSetMapping, array $hints = array())
|
||||
{
|
||||
$this->_stmt = $stmt;
|
||||
$this->_rsm = $resultSetMapping;
|
||||
$this->_hints = $hints;
|
||||
$this->_prepare();
|
||||
$result = $this->_hydrateAll();
|
||||
$this->_cleanup();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates a single row returned by the current statement instance during
|
||||
* row-by-row hydration with {@link iterate()}.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function hydrateRow()
|
||||
{
|
||||
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ( ! $row) {
|
||||
$this->_cleanup();
|
||||
return false;
|
||||
}
|
||||
$result = array();
|
||||
$this->_hydrateRow($row, $this->_cache, $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Excutes one-time preparation tasks, once each time hydration is started
|
||||
* through {@link hydrateAll} or {@link iterate()}.
|
||||
*/
|
||||
protected function _prepare()
|
||||
{}
|
||||
|
||||
/**
|
||||
* Excutes one-time cleanup tasks at the end of a hydration that was initiated
|
||||
* through {@link hydrateAll} or {@link iterate()}.
|
||||
*/
|
||||
protected function _cleanup()
|
||||
{
|
||||
$this->_rsm = null;
|
||||
$this->_stmt->closeCursor();
|
||||
$this->_stmt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates a single row from the current statement instance.
|
||||
*
|
||||
* Template method.
|
||||
*
|
||||
* @param array $data The row data.
|
||||
* @param array $cache The cache to use.
|
||||
* @param mixed $result The result to fill.
|
||||
*/
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
throw new HydrationException("_hydrateRow() not implemented by this hydrator.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates all rows from the current statement instance at once.
|
||||
*/
|
||||
abstract protected function _hydrateAll();
|
||||
|
||||
/**
|
||||
* Processes a row of the result set.
|
||||
* Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY).
|
||||
* Puts the elements of a result row into a new array, grouped by the class
|
||||
* they belong to. The column names in the result set are mapped to their
|
||||
* field names during this procedure as well as any necessary conversions on
|
||||
* the values applied.
|
||||
*
|
||||
* @return array An array with all the fields (name => value) of the data row,
|
||||
* grouped by their component alias.
|
||||
*/
|
||||
protected function _gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
|
||||
{
|
||||
$rowData = array();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// Parse each column name only once. Cache the results.
|
||||
if ( ! isset($cache[$key])) {
|
||||
if (isset($this->_rsm->scalarMappings[$key])) {
|
||||
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
|
||||
$cache[$key]['isScalar'] = true;
|
||||
} else if (isset($this->_rsm->fieldMappings[$key])) {
|
||||
$fieldName = $this->_rsm->fieldMappings[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
} else if (!isset($this->_rsm->metaMappings[$key])) {
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue;
|
||||
} else {
|
||||
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
|
||||
$cache[$key]['isMetaColumn'] = true;
|
||||
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache[$key]['isScalar'])) {
|
||||
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
$dqlAlias = $cache[$key]['dqlAlias'];
|
||||
|
||||
if (isset($cache[$key]['isMetaColumn'])) {
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cache[$key]['isIdentifier']) {
|
||||
$id[$dqlAlias] .= '|' . $value;
|
||||
}
|
||||
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
|
||||
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
|
||||
$nonemptyComponents[$dqlAlias] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a row of the result set.
|
||||
* Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
|
||||
* simply converts column names to field names and properly converts the
|
||||
* values according to their types. The resulting row has the same number
|
||||
* of elements as before.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $cache
|
||||
* @return array The processed row.
|
||||
*/
|
||||
protected function _gatherScalarRowData(&$data, &$cache)
|
||||
{
|
||||
$rowData = array();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// Parse each column name only once. Cache the results.
|
||||
if ( ! isset($cache[$key])) {
|
||||
if (isset($this->_rsm->scalarMappings[$key])) {
|
||||
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
|
||||
$cache[$key]['isScalar'] = true;
|
||||
} else if (isset($this->_rsm->fieldMappings[$key])) {
|
||||
$fieldName = $this->_rsm->fieldMappings[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
} else if (!isset($this->_rsm->metaMappings[$key])) {
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue;
|
||||
} else {
|
||||
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
|
||||
$cache[$key]['isMetaColumn'] = true;
|
||||
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$fieldName = $cache[$key]['fieldName'];
|
||||
|
||||
if (isset($cache[$key]['isScalar'])) {
|
||||
$rowData[$fieldName] = $value;
|
||||
} else if (isset($cache[$key]['isMetaColumn'])) {
|
||||
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
|
||||
} else {
|
||||
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $cache[$key]['type']
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
}
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<?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, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* The ArrayHydrator produces a nested array "graph" that is often (not always)
|
||||
* interchangeable with the corresponding object graph for read-only access.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 1.0
|
||||
*/
|
||||
class ArrayHydrator extends AbstractHydrator
|
||||
{
|
||||
private $_ce = array();
|
||||
private $_rootAliases = array();
|
||||
private $_isSimpleQuery = false;
|
||||
private $_identifierMap = array();
|
||||
private $_resultPointers = array();
|
||||
private $_idTemplate = array();
|
||||
private $_resultCounter = 0;
|
||||
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
{
|
||||
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
|
||||
$this->_identifierMap = array();
|
||||
$this->_resultPointers = array();
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_resultPointers[$dqlAlias] = array();
|
||||
$this->_idTemplate[$dqlAlias] = '';
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($data, $cache, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
// 1) Initialize
|
||||
$id = $this->_idTemplate; // initialize the id-memory
|
||||
$nonemptyComponents = array();
|
||||
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
|
||||
|
||||
// Extract scalar values. They're appended at the end.
|
||||
if (isset($rowData['scalars'])) {
|
||||
$scalars = $rowData['scalars'];
|
||||
unset($rowData['scalars']);
|
||||
if (empty($rowData)) {
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Now hydrate the data found in the current row.
|
||||
foreach ($rowData as $dqlAlias => $data) {
|
||||
$index = false;
|
||||
|
||||
if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
|
||||
// It's a joined result
|
||||
|
||||
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
|
||||
$path = $parent . '.' . $dqlAlias;
|
||||
|
||||
// Get a reference to the right element in the result tree.
|
||||
// This element will get the associated element attached.
|
||||
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
|
||||
$first = reset($this->_resultPointers);
|
||||
// TODO: Exception if $key === null ?
|
||||
$baseElement =& $this->_resultPointers[$parent][key($first)];
|
||||
} else if (isset($this->_resultPointers[$parent])) {
|
||||
$baseElement =& $this->_resultPointers[$parent];
|
||||
} else {
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
continue;
|
||||
}
|
||||
|
||||
$relationAlias = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
|
||||
|
||||
// Check the type of the relation (many or single-valued)
|
||||
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
|
||||
$oneToOne = false;
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = array();
|
||||
}
|
||||
|
||||
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
|
||||
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
|
||||
$indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;
|
||||
|
||||
if ( ! $indexExists || ! $indexIsValid) {
|
||||
$element = $data;
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$baseElement[$relationAlias][$element[$field]] = $element;
|
||||
} else {
|
||||
$baseElement[$relationAlias][] = $element;
|
||||
}
|
||||
end($baseElement[$relationAlias]);
|
||||
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] =
|
||||
key($baseElement[$relationAlias]);
|
||||
}
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = array();
|
||||
}
|
||||
} else {
|
||||
$oneToOne = true;
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = null;
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
$coll =& $baseElement[$relationAlias];
|
||||
|
||||
if ($coll !== null) {
|
||||
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
|
||||
}
|
||||
|
||||
} else {
|
||||
// It's a root result element
|
||||
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root
|
||||
|
||||
// Check for an existing element
|
||||
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $rowData[$dqlAlias];
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array($element[$field] => $element);
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[$element[$field]] = $element;
|
||||
}
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array($element);
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[] = $element;
|
||||
}
|
||||
}
|
||||
end($result);
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
|
||||
} else {
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] =& $result[$index];
|
||||
++$this->_resultCounter;
|
||||
}*/
|
||||
}
|
||||
$this->updateResultPointer($result, $index, $dqlAlias, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Append scalar values to mixed result sets
|
||||
if (isset($scalars)) {
|
||||
foreach ($scalars as $name => $value) {
|
||||
$result[$this->_resultCounter - 1][$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the result pointer for an Entity. The result pointers point to the
|
||||
* last seen instance of each Entity type. This is used for graph construction.
|
||||
*
|
||||
* @param array $coll The element.
|
||||
* @param boolean|integer $index Index of the element in the collection.
|
||||
* @param string $dqlAlias
|
||||
* @param boolean $oneToOne Whether it is a single-valued association or not.
|
||||
*/
|
||||
private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne)
|
||||
{
|
||||
if ($coll === null) {
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
return;
|
||||
}
|
||||
if ($index !== false) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[$index];
|
||||
return;
|
||||
} else {
|
||||
if ($coll) {
|
||||
if ($oneToOne) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll;
|
||||
} else {
|
||||
end($coll);
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function _getClassMetadata($className)
|
||||
{
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
return $this->_ce[$className];
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
class HydrationException extends \Doctrine\ORM\ORMException
|
||||
{
|
||||
public static function nonUniqueResult()
|
||||
{
|
||||
return new self("The result returned by the query was not unique.");
|
||||
}
|
||||
|
||||
public static function parentObjectOfRelationNotFound($alias, $parentAlias)
|
||||
{
|
||||
return new self("The parent object of entity result with alias '$alias' was not found."
|
||||
. " The parent alias is '$parentAlias'.");
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Represents a result structure that can be iterated over, hydrating row-by-row
|
||||
* during the iteration. An IterableResult is obtained by AbstractHydrator#iterate().
|
||||
*
|
||||
* @author robo
|
||||
* @since 2.0
|
||||
*/
|
||||
class IterableResult implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @var Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||
*/
|
||||
private $_hydrator;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $_rewinded = false;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $_key = -1;
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
private $_current = null;
|
||||
|
||||
/**
|
||||
* @param Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator
|
||||
*/
|
||||
public function __construct($hydrator)
|
||||
{
|
||||
$this->_hydrator = $hydrator;
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
if ($this->_rewinded == true) {
|
||||
throw new HydrationException("Can only iterate a Result once.");
|
||||
} else {
|
||||
$this->_current = $this->next();
|
||||
$this->_rewinded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next set of results.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->_current = $this->_hydrator->hydrateRow();
|
||||
$this->_key++;
|
||||
return $this->_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return ($this->_current!=false);
|
||||
}
|
||||
}
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
<?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,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\PersistentCollection,
|
||||
Doctrine\ORM\Query,
|
||||
Doctrine\Common\Collections\ArrayCollection,
|
||||
Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
* The ObjectHydrator constructs an object graph out of an SQL result set.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @internal Highly performance-sensitive code.
|
||||
*/
|
||||
class ObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
/* Local ClassMetadata cache to avoid going to the EntityManager all the time.
|
||||
* 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;
|
||||
private $_resultCounter;
|
||||
private $_rootAliases = array();
|
||||
private $_initializedCollections = array();
|
||||
private $_existingCollections = array();
|
||||
//private $_createdEntities;
|
||||
|
||||
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
{
|
||||
$this->_identifierMap =
|
||||
$this->_resultPointers =
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_idTemplate[$dqlAlias] = '';
|
||||
$class = $this->_em->getClassMetadata($className);
|
||||
|
||||
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])) {
|
||||
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
|
||||
$sourceClass = $this->_getClassMetadata($sourceClassName);
|
||||
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
|
||||
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
|
||||
if ($sourceClass->subClasses) {
|
||||
foreach ($sourceClass->subClasses as $sourceSubclassName) {
|
||||
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
|
||||
}
|
||||
}
|
||||
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
|
||||
// Mark any non-collection opposite sides as fetched, too.
|
||||
if ($assoc['mappedBy']) {
|
||||
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
|
||||
} else {
|
||||
if ($assoc['inversedBy']) {
|
||||
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
|
||||
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
|
||||
if ($class->subClasses) {
|
||||
foreach ($class->subClasses as $targetSubclassName) {
|
||||
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _cleanup()
|
||||
{
|
||||
parent::_cleanup();
|
||||
$this->_identifierMap =
|
||||
$this->_initializedCollections =
|
||||
$this->_existingCollections =
|
||||
$this->_resultPointers = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($row, $cache, $result);
|
||||
}
|
||||
|
||||
// Take snapshots from all newly initialized collections
|
||||
foreach ($this->_initializedCollections as $coll) {
|
||||
$coll->takeSnapshot();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a related collection.
|
||||
*
|
||||
* @param object $entity The entity to which the collection belongs.
|
||||
* @param string $name The name of the field on the entity that holds the collection.
|
||||
*/
|
||||
private function _initRelatedCollection($entity, $class, $fieldName)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
$relation = $class->associationMappings[$fieldName];
|
||||
|
||||
$value = $class->reflFields[$fieldName]->getValue($entity);
|
||||
if ($value === null) {
|
||||
$value = new ArrayCollection;
|
||||
}
|
||||
|
||||
if ( ! $value instanceof PersistentCollection) {
|
||||
$value = new PersistentCollection(
|
||||
$this->_em,
|
||||
$this->_ce[$relation['targetEntity']],
|
||||
$value
|
||||
);
|
||||
$value->setOwner($entity, $relation);
|
||||
$class->reflFields[$fieldName]->setValue($entity, $value);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
} else if (isset($this->_hints[Query::HINT_REFRESH])) {
|
||||
// Is already PersistentCollection, but REFRESH
|
||||
$value->setDirty(false);
|
||||
$value->setInitialized(true);
|
||||
$value->unwrap()->clear();
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
} else {
|
||||
// Is already PersistentCollection, and DONT REFRESH
|
||||
$this->_existingCollections[$oid . $fieldName] = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entity instance.
|
||||
*
|
||||
* @param $data The instance data.
|
||||
* @param $dqlAlias The DQL alias of the entity's class.
|
||||
* @return object The entity.
|
||||
*/
|
||||
private function _getEntity(array $data, $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]);
|
||||
}
|
||||
return $this->_uow->createEntity($className, $data, $this->_hints);
|
||||
}
|
||||
|
||||
private function _getEntityFromIdentityMap($className, array $data)
|
||||
{
|
||||
$class = $this->_ce[$className];
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idHash = '';
|
||||
foreach ($class->identifier as $fieldName) {
|
||||
$idHash .= $data[$fieldName] . ' ';
|
||||
}
|
||||
return $this->_uow->tryGetByIdHash(rtrim($idHash), $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
|
||||
* local cache.
|
||||
*
|
||||
* @param string $className The name of the class.
|
||||
* @return ClassMetadata
|
||||
*/
|
||||
private function _getClassMetadata($className)
|
||||
{
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
return $this->_ce[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates a single row in an SQL result set.
|
||||
*
|
||||
* @internal
|
||||
* First, the data of the row is split into chunks where each chunk contains data
|
||||
* that belongs to a particular component/class. Afterwards, all these chunks
|
||||
* are processed, one after the other. For each chunk of class data only one of the
|
||||
* following code paths is executed:
|
||||
*
|
||||
* Path A: The data chunk belongs to a joined/associated object and the association
|
||||
* is collection-valued.
|
||||
* Path B: The data chunk belongs to a joined/associated object and the association
|
||||
* is single-valued.
|
||||
* Path C: The data chunk belongs to a root result element/object that appears in the topmost
|
||||
* level of the hydrated result. A typical example are the objects of the type
|
||||
* specified by the FROM clause in a DQL query.
|
||||
*
|
||||
* @param array $data The data of the row to process.
|
||||
* @param array $cache The cache to use.
|
||||
* @param array $result The result array to fill.
|
||||
*/
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
// Initialize
|
||||
$id = $this->_idTemplate; // initialize the id-memory
|
||||
$nonemptyComponents = array();
|
||||
// Split the row data into chunks of class data.
|
||||
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
|
||||
|
||||
// Extract scalar values. They're appended at the end.
|
||||
if (isset($rowData['scalars'])) {
|
||||
$scalars = $rowData['scalars'];
|
||||
unset($rowData['scalars']);
|
||||
if (empty($rowData)) {
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
$parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
|
||||
// we need the $path to save into the identifier map which entities were already
|
||||
// seen for this parent-child relationship
|
||||
$path = $parentAlias . '.' . $dqlAlias;
|
||||
|
||||
// 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);
|
||||
$parentObject = $this->_resultPointers[$parentAlias][key($first)];
|
||||
} else if (isset($this->_resultPointers[$parentAlias])) {
|
||||
$parentObject = $this->_resultPointers[$parentAlias];
|
||||
} else {
|
||||
// Parent object of relation not found, so skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
|
||||
$oid = spl_object_hash($parentObject);
|
||||
$relationField = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relation = $parentClass->associationMappings[$relationField];
|
||||
$reflField = $parentClass->reflFields[$relationField];
|
||||
|
||||
// Check the type of the relation (many or single-valued)
|
||||
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
|
||||
// PATH A: Collection-valued association
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$collKey = $oid . $relationField;
|
||||
if (isset($this->_initializedCollections[$collKey])) {
|
||||
$reflFieldValue = $this->_initializedCollections[$collKey];
|
||||
} 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.
|
||||
if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) {
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
} else {
|
||||
unset($this->_resultPointers[$dqlAlias]);
|
||||
}
|
||||
} else {
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
|
||||
$reflFieldValue->hydrateSet($indexValue, $element);
|
||||
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
|
||||
} else {
|
||||
$reflFieldValue->hydrateAdd($element);
|
||||
$reflFieldValue->last();
|
||||
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
|
||||
}
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
}
|
||||
} else {
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
|
||||
}
|
||||
} else if ( ! $reflField->getValue($parentObject)) {
|
||||
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
|
||||
$reflField->setValue($parentObject, $coll);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
|
||||
}
|
||||
} else {
|
||||
// PATH B: Single-valued association
|
||||
$reflFieldValue = $reflField->getValue($parentObject);
|
||||
if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH])) {
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
$reflField->setValue($parentObject, $element);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
|
||||
$targetClass = $this->_ce[$relation['targetEntity']];
|
||||
if ($relation['isOwningSide']) {
|
||||
//TODO: Just check hints['fetched'] here?
|
||||
// If there is an inverse mapping on the target class its bidirectional
|
||||
if ($relation['inversedBy']) {
|
||||
$inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']];
|
||||
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject);
|
||||
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
|
||||
}
|
||||
} else if ($parentClass === $targetClass && $relation['mappedBy']) {
|
||||
// Special case: bi-directional self-referencing one-one on the same class
|
||||
$targetClass->reflFields[$relationField]->setValue($element, $parentObject);
|
||||
}
|
||||
} else {
|
||||
// For sure bidirectional, as there is no inverse side in unidirectional mappings
|
||||
$targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject);
|
||||
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
|
||||
}
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
}
|
||||
// else leave $reflFieldValue null for single-valued associations
|
||||
} else {
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $reflFieldValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// PATH C: Its a root result element
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
|
||||
|
||||
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array($key => $element);
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[$key] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
|
||||
}
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array(0 => $element);
|
||||
}
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
|
||||
} else {
|
||||
// Update result pointer
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
$this->_resultPointers[$dqlAlias] = $result[$index];
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] = $result[$index];
|
||||
++$this->_resultCounter;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append scalar values to mixed result sets
|
||||
if (isset($scalars)) {
|
||||
foreach ($scalars as $name => $value) {
|
||||
$result[$this->_resultCounter - 1][$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?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 Doctrine\DBAL\Connection;
|
||||
|
||||
/**
|
||||
* Hydrator that produces flat, rectangular results of scalar data.
|
||||
* The created result is almost the same as a regular SQL result set, except
|
||||
* that column names are mapped to field names and data type conversions take place.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$result[] = $this->_gatherScalarRowData($data, $cache);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
{
|
||||
$result[] = $this->_gatherScalarRowData($data, $cache);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?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 Doctrine\DBAL\Connection;
|
||||
|
||||
/**
|
||||
* Hydrator that hydrates a single scalar value from the result set.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class SingleScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$cache = array();
|
||||
$result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
$num = count($result);
|
||||
|
||||
if ($num == 0) {
|
||||
throw new \Doctrine\ORM\NoResultException;
|
||||
} else if ($num > 1 || count($result[key($result)]) > 1) {
|
||||
throw new \Doctrine\ORM\NonUniqueResultException;
|
||||
}
|
||||
|
||||
$result = $this->_gatherScalarRowData($result[key($result)], $cache);
|
||||
|
||||
return array_shift($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 62
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping
|
||||
END
|
||||
MappingException.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/MappingException.php
|
||||
END
|
||||
ClassMetadataFactory.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 87
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
|
||||
END
|
||||
ClassMetadataInfo.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 84
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
|
||||
END
|
||||
ClassMetadata.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 80
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/ClassMetadata.php
|
||||
END
|
||||
@@ -0,0 +1,167 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine/ORM/Mapping
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
MappingException.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.363322Z
|
||||
9a9c3701509d1cd2cb935e3182689326
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
8184
|
||||
|
||||
ClassMetadataFactory.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.363322Z
|
||||
f819a9a54ee9c3eadb0d812bfa1a6f32
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
16579
|
||||
|
||||
ClassMetadataInfo.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.363322Z
|
||||
874133c96ecae5d722b82e141168eb23
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
52601
|
||||
|
||||
Driver
|
||||
dir
|
||||
|
||||
ClassMetadata.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.363322Z
|
||||
b4c5c00534a1665a99419e016d2ada25
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
12240
|
||||
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
<?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\Mapping;
|
||||
|
||||
use ReflectionClass, ReflectionProperty;
|
||||
|
||||
/**
|
||||
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
|
||||
* of an entity and it's associations.
|
||||
*
|
||||
* Once populated, ClassMetadata instances are usually cached in a serialized form.
|
||||
*
|
||||
* <b>IMPORTANT NOTE:</b>
|
||||
*
|
||||
* The fields of this class are only public for 2 reasons:
|
||||
* 1) To allow fast READ access.
|
||||
* 2) To drastically reduce the size of a serialized instance (private/protected members
|
||||
* get the whole class name, namespace inclusive, prepended to every property in
|
||||
* the serialized representation).
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ClassMetadata extends ClassMetadataInfo
|
||||
{
|
||||
/**
|
||||
* The ReflectionProperty instances of the mapped class.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $reflFields = array();
|
||||
|
||||
/**
|
||||
* The prototype from which new instances of the mapped class are created.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $_prototype;
|
||||
|
||||
/**
|
||||
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
|
||||
* metadata of the class with the given name.
|
||||
*
|
||||
* @param string $entityName The name of the entity class the new instance is used for.
|
||||
*/
|
||||
public function __construct($entityName)
|
||||
{
|
||||
parent::__construct($entityName);
|
||||
$this->reflClass = new ReflectionClass($entityName);
|
||||
$this->namespace = $this->reflClass->getNamespaceName();
|
||||
$this->table['name'] = $this->reflClass->getShortName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionPropertys of the mapped class.
|
||||
*
|
||||
* @return array An array of ReflectionProperty instances.
|
||||
*/
|
||||
public function getReflectionProperties()
|
||||
{
|
||||
return $this->reflFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ReflectionProperty for a specific field of the mapped class.
|
||||
*
|
||||
* @param string $name
|
||||
* @return ReflectionProperty
|
||||
*/
|
||||
public function getReflectionProperty($name)
|
||||
{
|
||||
return $this->reflFields[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionProperty for the single identifier field.
|
||||
*
|
||||
* @return ReflectionProperty
|
||||
* @throws BadMethodCallException If the class has a composite identifier.
|
||||
*/
|
||||
public function getSingleIdReflectionProperty()
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
|
||||
}
|
||||
return $this->reflFields[$this->identifier[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the given field mapping.
|
||||
*
|
||||
* @param array $mapping The field mapping to validated & complete.
|
||||
* @return array The validated and completed field mapping.
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
protected function _validateAndCompleteFieldMapping(array &$mapping)
|
||||
{
|
||||
parent::_validateAndCompleteFieldMapping($mapping);
|
||||
|
||||
// Store ReflectionProperty of mapped field
|
||||
$refProp = $this->reflClass->getProperty($mapping['fieldName']);
|
||||
$refProp->setAccessible(true);
|
||||
$this->reflFields[$mapping['fieldName']] = $refProp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the identifier values of an entity of this class.
|
||||
*
|
||||
* For composite identifiers, the identifier values are returned as an array
|
||||
* with the same order as the field order in {@link identifier}.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return array
|
||||
*/
|
||||
public function getIdentifierValues($entity)
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
$id = array();
|
||||
foreach ($this->identifier as $idField) {
|
||||
$value = $this->reflFields[$idField]->getValue($entity);
|
||||
if ($value !== null) {
|
||||
$id[$idField] = $value;
|
||||
}
|
||||
}
|
||||
return $id;
|
||||
} else {
|
||||
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
|
||||
if ($value !== null) {
|
||||
return array($this->identifier[0] => $value);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the entity identifier of an entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param mixed $id
|
||||
* @todo Rename to assignIdentifier()
|
||||
*/
|
||||
public function setIdentifierValues($entity, array $id)
|
||||
{
|
||||
foreach ($id as $idField => $idValue) {
|
||||
$this->reflFields[$idField]->setValue($entity, $idValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified field to the specified value on the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setFieldValue($entity, $field, $value)
|
||||
{
|
||||
$this->reflFields[$field]->setValue($entity, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the specified field's value off the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param string $field
|
||||
*/
|
||||
public function getFieldValue($entity, $field)
|
||||
{
|
||||
return $this->reflFields[$field]->getValue($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the association mapping.
|
||||
*
|
||||
* @param AssociationMapping $assocMapping
|
||||
*/
|
||||
protected function _storeAssociationMapping(array $assocMapping)
|
||||
{
|
||||
parent::_storeAssociationMapping($assocMapping);
|
||||
|
||||
// Store ReflectionProperty of mapped field
|
||||
$sourceFieldName = $assocMapping['fieldName'];
|
||||
|
||||
$refProp = $this->reflClass->getProperty($sourceFieldName);
|
||||
$refProp->setAccessible(true);
|
||||
$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.
|
||||
*
|
||||
* @return string The string representation of this instance.
|
||||
* @todo Construct meaningful string representation.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return __CLASS__ . '@' . spl_object_hash($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which fields get serialized.
|
||||
*
|
||||
* It is only serialized what is necessary for best unserialization performance.
|
||||
* That means any metadata properties that are not set or empty or simply have
|
||||
* their default value are NOT serialized.
|
||||
*
|
||||
* Parts that are also NOT serialized because they can not be properly unserialized:
|
||||
* - reflClass (ReflectionClass)
|
||||
* - reflFields (ReflectionProperty array)
|
||||
*
|
||||
* @return array The names of all the fields that should be serialized.
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
// This metadata is always serialized/cached.
|
||||
$serialized = array(
|
||||
'associationMappings',
|
||||
'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
|
||||
'fieldMappings',
|
||||
'fieldNames',
|
||||
'identifier',
|
||||
'isIdentifierComposite', // TODO: REMOVE
|
||||
'name',
|
||||
'namespace', // TODO: REMOVE
|
||||
'table',
|
||||
'rootEntityName',
|
||||
'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
|
||||
);
|
||||
|
||||
// The rest of the metadata is only serialized if necessary.
|
||||
if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
|
||||
$serialized[] = 'changeTrackingPolicy';
|
||||
}
|
||||
|
||||
if ($this->customRepositoryClassName) {
|
||||
$serialized[] = 'customRepositoryClassName';
|
||||
}
|
||||
|
||||
if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
|
||||
$serialized[] = 'inheritanceType';
|
||||
$serialized[] = 'discriminatorColumn';
|
||||
$serialized[] = 'discriminatorValue';
|
||||
$serialized[] = 'discriminatorMap';
|
||||
$serialized[] = 'parentClasses';
|
||||
$serialized[] = 'subClasses';
|
||||
}
|
||||
|
||||
if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
|
||||
$serialized[] = 'generatorType';
|
||||
if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
|
||||
$serialized[] = 'sequenceGeneratorDefinition';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isMappedSuperclass) {
|
||||
$serialized[] = 'isMappedSuperclass';
|
||||
}
|
||||
|
||||
if ($this->isVersioned) {
|
||||
$serialized[] = 'isVersioned';
|
||||
$serialized[] = 'versionField';
|
||||
}
|
||||
|
||||
if ($this->lifecycleCallbacks) {
|
||||
$serialized[] = 'lifecycleCallbacks';
|
||||
}
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores some state that can not be serialized/unserialized.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
// Restore ReflectionClass and properties
|
||||
$this->reflClass = new ReflectionClass($this->name);
|
||||
|
||||
foreach ($this->fieldMappings as $field => $mapping) {
|
||||
if (isset($mapping['declared'])) {
|
||||
$reflField = new ReflectionProperty($mapping['declared'], $field);
|
||||
} else {
|
||||
$reflField = $this->reflClass->getProperty($field);
|
||||
}
|
||||
$reflField->setAccessible(true);
|
||||
$this->reflFields[$field] = $reflField;
|
||||
}
|
||||
|
||||
foreach ($this->associationMappings as $field => $mapping) {
|
||||
if (isset($mapping['declared'])) {
|
||||
$reflField = new ReflectionProperty($mapping['declared'], $field);
|
||||
} else {
|
||||
$reflField = $this->reflClass->getProperty($field);
|
||||
}
|
||||
|
||||
$reflField->setAccessible(true);
|
||||
$this->reflFields[$field] = $reflField;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the mapped class, without invoking the constructor.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function newInstance()
|
||||
{
|
||||
if ($this->_prototype === null) {
|
||||
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
|
||||
}
|
||||
return clone $this->_prototype;
|
||||
}
|
||||
}
|
||||
externo
+453
@@ -0,0 +1,453 @@
|
||||
<?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\Mapping;
|
||||
|
||||
use ReflectionException,
|
||||
Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\DBAL\Platforms,
|
||||
Doctrine\ORM\Events;
|
||||
|
||||
/**
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
* metadata mapping informations of a class which describes how a class should be mapped
|
||||
* to a relational database.
|
||||
*
|
||||
* @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 ClassMetadataFactory
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @var AbstractPlatform
|
||||
*/
|
||||
private $targetPlatform;
|
||||
|
||||
/**
|
||||
* @var Driver\Driver
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\EventManager
|
||||
*/
|
||||
private $evm;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
private $cacheDriver;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $loadedMetadata = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $initialized = false;
|
||||
|
||||
/**
|
||||
* Creates a new factory instance that uses the given metadata driver implementation.
|
||||
*
|
||||
* @param $driver The metadata driver to use.
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver used by the factory to cache ClassMetadata instances.
|
||||
*
|
||||
* @param Doctrine\Common\Cache\Cache $cacheDriver
|
||||
*/
|
||||
public function setCacheDriver($cacheDriver)
|
||||
{
|
||||
$this->cacheDriver = $cacheDriver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver used by the factory to cache ClassMetadata instances.
|
||||
*
|
||||
* @return Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getCacheDriver()
|
||||
{
|
||||
return $this->cacheDriver;
|
||||
}
|
||||
|
||||
public function getLoadedMetadata()
|
||||
{
|
||||
return $this->loadedMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the factory to load the metadata of all classes known to the underlying
|
||||
* mapping driver.
|
||||
*
|
||||
* @return array The ClassMetadata instances of all mapped classes.
|
||||
*/
|
||||
public function getAllMetadata()
|
||||
{
|
||||
if ( ! $this->initialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
$metadata = array();
|
||||
foreach ($this->driver->getAllClassNames() as $className) {
|
||||
$metadata[] = $this->getMetadataFor($className);
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of this stuff, especially the metadata driver,
|
||||
* since these are not needed at all when a metadata cache is active.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
$this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
|
||||
$this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
|
||||
$this->evm = $this->em->getEventManager();
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class metadata descriptor for a class.
|
||||
*
|
||||
* @param string $className The name of the class.
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
public function getMetadataFor($className)
|
||||
{
|
||||
if ( ! isset($this->loadedMetadata[$className])) {
|
||||
$realClassName = $className;
|
||||
|
||||
// Check for namespace alias
|
||||
if (strpos($className, ':') !== false) {
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $className);
|
||||
$realClassName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
|
||||
if (isset($this->loadedMetadata[$realClassName])) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
|
||||
return $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->cacheDriver) {
|
||||
if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
|
||||
$this->loadedMetadata[$realClassName] = $cached;
|
||||
} else {
|
||||
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
|
||||
$this->cacheDriver->save(
|
||||
"$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->loadMetadata($realClassName);
|
||||
}
|
||||
|
||||
if ($className != $realClassName) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->loadedMetadata[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the factory has the metadata for a class loaded already.
|
||||
*
|
||||
* @param string $className
|
||||
* @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
|
||||
*/
|
||||
public function hasMetadataFor($className)
|
||||
{
|
||||
return isset($this->loadedMetadata[$className]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the metadata descriptor for a specific class.
|
||||
*
|
||||
* NOTE: This is only useful in very special cases, like when generating proxy classes.
|
||||
*
|
||||
* @param string $className
|
||||
* @param ClassMetadata $class
|
||||
*/
|
||||
public function setMetadataFor($className, $class)
|
||||
{
|
||||
$this->loadedMetadata[$className] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of parent classes for the given entity class
|
||||
*
|
||||
* @param string $name
|
||||
* @return array $parentClasses
|
||||
*/
|
||||
protected function getParentClasses($name)
|
||||
{
|
||||
// Collect parent classes, ignoring transient (not-mapped) classes.
|
||||
$parentClasses = array();
|
||||
foreach (array_reverse(class_parents($name)) as $parentClass) {
|
||||
if ( ! $this->driver->isTransient($parentClass)) {
|
||||
$parentClasses[] = $parentClass;
|
||||
}
|
||||
}
|
||||
return $parentClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata of the class in question and all it's ancestors whose metadata
|
||||
* is still not loaded.
|
||||
*
|
||||
* @param string $name The name of the class for which the metadata should get loaded.
|
||||
* @param array $tables The metadata collection to which the loaded metadata is added.
|
||||
*/
|
||||
protected function loadMetadata($name)
|
||||
{
|
||||
if ( ! $this->initialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
$loaded = array();
|
||||
|
||||
$parentClasses = $this->getParentClasses($name);
|
||||
$parentClasses[] = $name;
|
||||
|
||||
// Move down the hierarchy of parent classes, starting from the topmost class
|
||||
$parent = null;
|
||||
$visited = array();
|
||||
foreach ($parentClasses as $className) {
|
||||
if (isset($this->loadedMetadata[$className])) {
|
||||
$parent = $this->loadedMetadata[$className];
|
||||
if ( ! $parent->isMappedSuperclass) {
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = $this->newClassMetadataInstance($className);
|
||||
|
||||
if ($parent) {
|
||||
$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);
|
||||
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
|
||||
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
|
||||
}
|
||||
|
||||
// Invoke driver
|
||||
try {
|
||||
$this->driver->loadMetadataForClass($className, $class);
|
||||
} catch (ReflectionException $e) {
|
||||
throw MappingException::reflectionFailure($className, $e);
|
||||
}
|
||||
|
||||
// Verify & complete identifier mapping
|
||||
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
|
||||
throw MappingException::identifierRequired($className);
|
||||
}
|
||||
if ($parent && ! $parent->isMappedSuperclass) {
|
||||
if ($parent->isIdGeneratorSequence()) {
|
||||
$class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
|
||||
} else if ($parent->isIdGeneratorTable()) {
|
||||
$class->getTableGeneratorDefinition($parent->tableGeneratorDefinition);
|
||||
}
|
||||
if ($parent->generatorType) {
|
||||
$class->setIdGeneratorType($parent->generatorType);
|
||||
}
|
||||
if ($parent->idGenerator) {
|
||||
$class->setIdGenerator($parent->idGenerator);
|
||||
}
|
||||
} else {
|
||||
$this->completeIdGeneratorMapping($class);
|
||||
}
|
||||
|
||||
if ($parent && $parent->isInheritanceTypeSingleTable()) {
|
||||
$class->setPrimaryTable($parent->table);
|
||||
}
|
||||
|
||||
$class->setParentClasses($visited);
|
||||
|
||||
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
|
||||
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class);
|
||||
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
|
||||
$loaded[] = $className;
|
||||
}
|
||||
|
||||
return $loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ClassMetadata instance for the given class name.
|
||||
*
|
||||
* @param string $className
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
protected function newClassMetadataInstance($className)
|
||||
{
|
||||
return new ClassMetadata($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inherited fields to the subclass mapping.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
|
||||
*/
|
||||
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
{
|
||||
foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
|
||||
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
|
||||
$mapping['inherited'] = $parentClass->name;
|
||||
}
|
||||
if ( ! isset($mapping['declared'])) {
|
||||
$mapping['declared'] = $parentClass->name;
|
||||
}
|
||||
$subClass->addInheritedFieldMapping($mapping);
|
||||
}
|
||||
foreach ($parentClass->reflFields as $name => $field) {
|
||||
$subClass->reflFields[$name] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inherited association mappings to the subclass mapping.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
|
||||
*/
|
||||
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
{
|
||||
foreach ($parentClass->associationMappings as $field => $mapping) {
|
||||
if ($parentClass->isMappedSuperclass) {
|
||||
$mapping['sourceEntity'] = $subClass->name;
|
||||
}
|
||||
|
||||
//$subclassMapping = $mapping;
|
||||
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
|
||||
$mapping['inherited'] = $parentClass->name;
|
||||
}
|
||||
if ( ! isset($mapping['declared'])) {
|
||||
$mapping['declared'] = $parentClass->name;
|
||||
}
|
||||
$subClass->addInheritedAssociationMapping($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the ID generator mapping. If "auto" is specified we choose the generator
|
||||
* most appropriate for the targeted database platform.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $class
|
||||
*/
|
||||
private function completeIdGeneratorMapping(ClassMetadataInfo $class)
|
||||
{
|
||||
$idGenType = $class->generatorType;
|
||||
if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
|
||||
if ($this->targetPlatform->prefersSequences()) {
|
||||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
|
||||
} else if ($this->targetPlatform->prefersIdentityColumns()) {
|
||||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
|
||||
} else {
|
||||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Create & assign an appropriate ID generator instance
|
||||
switch ($class->generatorType) {
|
||||
case ClassMetadata::GENERATOR_TYPE_IDENTITY:
|
||||
// For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to
|
||||
// <table>_<column>_seq in PostgreSQL for SERIAL columns.
|
||||
// Not pretty but necessary and the simplest solution that currently works.
|
||||
$seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ?
|
||||
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
|
||||
null;
|
||||
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
|
||||
// If there is no sequence definition yet, create a default definition
|
||||
$definition = $class->sequenceGeneratorDefinition;
|
||||
if ( ! $definition) {
|
||||
$sequenceName = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq';
|
||||
$definition['sequenceName'] = $this->targetPlatform->fixSchemaElementName($sequenceName);
|
||||
$definition['allocationSize'] = 1;
|
||||
$definition['initialValue'] = 1;
|
||||
$class->setSequenceGeneratorDefinition($definition);
|
||||
}
|
||||
$sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator(
|
||||
$definition['sequenceName'],
|
||||
$definition['allocationSize']
|
||||
);
|
||||
$class->setIdGenerator($sequenceGenerator);
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_NONE:
|
||||
$class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_TABLE:
|
||||
throw new ORMException("TableGenerator not yet implemented.");
|
||||
break;
|
||||
default:
|
||||
throw new ORMException("Unknown generator type: " . $class->generatorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1583
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+219
@@ -0,0 +1,219 @@
|
||||
<?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.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* A MappingException indicates that something is wrong with the mapping setup.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class MappingException extends \Doctrine\ORM\ORMException
|
||||
{
|
||||
public static function pathRequired()
|
||||
{
|
||||
return new self("Specifying the paths to your entities is required ".
|
||||
"in the AnnotationDriver to retrieve all class names.");
|
||||
}
|
||||
|
||||
public static function identifierRequired($entityName)
|
||||
{
|
||||
return new self("No identifier/primary key specified for Entity '$entityName'."
|
||||
. " Every Entity must have an identifier/primary key.");
|
||||
}
|
||||
|
||||
public static function invalidInheritanceType($entityName, $type)
|
||||
{
|
||||
return new self("The inheritance type '$type' specified for '$entityName' does not exist.");
|
||||
}
|
||||
|
||||
public static function generatorNotAllowedWithCompositeId()
|
||||
{
|
||||
return new self("Id generators can't be used with a composite id.");
|
||||
}
|
||||
|
||||
public static function missingFieldName()
|
||||
{
|
||||
return new self("The association mapping misses the 'fieldName' attribute.");
|
||||
}
|
||||
|
||||
public static function missingTargetEntity($fieldName)
|
||||
{
|
||||
return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute.");
|
||||
}
|
||||
|
||||
public static function missingSourceEntity($fieldName)
|
||||
{
|
||||
return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute.");
|
||||
}
|
||||
|
||||
public static function mappingFileNotFound($entityName, $fileName)
|
||||
{
|
||||
return new self("No mapping file found named '$fileName' for class '$entityName'.");
|
||||
}
|
||||
|
||||
public static function mappingNotFound($fieldName)
|
||||
{
|
||||
return new self("No mapping found for field '$fieldName'.");
|
||||
}
|
||||
|
||||
public static function oneToManyRequiresMappedBy($fieldName)
|
||||
{
|
||||
return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute.");
|
||||
}
|
||||
|
||||
public static function joinTableRequired($fieldName)
|
||||
{
|
||||
return new self("The mapping of field '$fieldName' requires an the 'joinTable' attribute.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if a required option was not found but is required
|
||||
*
|
||||
* @param string $field which field cannot be processed?
|
||||
* @param string $expectedOption which option is required
|
||||
* @param string $hint Can optionally be used to supply a tip for common mistakes,
|
||||
* e.g. "Did you think of the plural s?"
|
||||
* @return MappingException
|
||||
*/
|
||||
static function missingRequiredOption($field, $expectedOption, $hint = '')
|
||||
{
|
||||
$message = "The mapping of field '{$field}' is invalid: The option '{$expectedOption}' is required.";
|
||||
|
||||
if ( ! empty($hint)) {
|
||||
$message .= ' (Hint: ' . $hint . ')';
|
||||
}
|
||||
|
||||
return new self($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic exception for invalid mappings.
|
||||
*
|
||||
* @param string $fieldName
|
||||
*/
|
||||
public static function invalidMapping($fieldName)
|
||||
{
|
||||
return new self("The mapping of field '$fieldName' is invalid.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception for reflection exceptions - adds the entity name,
|
||||
* because there might be long classnames that will be shortened
|
||||
* within the stacktrace
|
||||
*
|
||||
* @param string $entity The entity's name
|
||||
* @param \ReflectionException $previousException
|
||||
*/
|
||||
public static function reflectionFailure($entity, \ReflectionException $previousException)
|
||||
{
|
||||
return new self('An error occurred in ' . $entity, 0, $previousException);
|
||||
}
|
||||
|
||||
public static function joinColumnMustPointToMappedField($className, $joinColumn)
|
||||
{
|
||||
return new self('The column ' . $joinColumn . ' must be mapped to a field in class '
|
||||
. $className . ' since it is referenced by a join column of another class.');
|
||||
}
|
||||
|
||||
public static function classIsNotAValidEntityOrMappedSuperClass($className)
|
||||
{
|
||||
return new self('Class '.$className.' is not a valid entity or mapped super class.');
|
||||
}
|
||||
|
||||
public static function propertyTypeIsRequired($className, $propertyName)
|
||||
{
|
||||
return new self("The attribute 'type' is required for the column description of property ".$className."::\$".$propertyName.".");
|
||||
}
|
||||
|
||||
public static function tableIdGeneratorNotImplemented($className)
|
||||
{
|
||||
return new self("TableIdGenerator is not yet implemented for use with class ".$className);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $entity The entity's name
|
||||
* @param string $fieldName The name of the field that was already declared
|
||||
*/
|
||||
public static function duplicateFieldMapping($entity, $fieldName) {
|
||||
return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once');
|
||||
}
|
||||
|
||||
public static function duplicateAssociationMapping($entity, $fieldName) {
|
||||
return new self('Property "'.$fieldName.'" 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);
|
||||
}
|
||||
|
||||
public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) {
|
||||
return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") '
|
||||
.'is not supported by Doctrine.'
|
||||
);
|
||||
}
|
||||
|
||||
public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null)
|
||||
{
|
||||
if ( ! empty($path)) {
|
||||
$path = '[' . $path . ']';
|
||||
}
|
||||
|
||||
return new self(
|
||||
'File mapping drivers must have a valid directory path, ' .
|
||||
'however the given path ' . $path . ' seems to be incorrect!'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception that indicates that a class used in a discriminator map does not exist.
|
||||
* An example would be an outdated (maybe renamed) classname.
|
||||
*
|
||||
* @param string $className The class that could not be found
|
||||
* @param string $owningClass The class that declares the discriminator map.
|
||||
* @return self
|
||||
*/
|
||||
public static function invalidClassInDiscriminatorMap($className, $owningClass) {
|
||||
return new self(
|
||||
"Entity class '$className' used in the discriminator map of class '$owningClass' ".
|
||||
"does not exist."
|
||||
);
|
||||
}
|
||||
|
||||
public static function missingDiscriminatorMap($className)
|
||||
{
|
||||
return new self("Entity class '$className' is using inheritance but no discriminator map was defined.");
|
||||
}
|
||||
|
||||
public static function missingDiscriminatorColumn($className)
|
||||
{
|
||||
return new self("Entity class '$className' is using inheritance but no discriminator column was defined.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $columnName
|
||||
* @return self
|
||||
*/
|
||||
public static function duplicateColumnName($className, $columnName)
|
||||
{
|
||||
return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
<?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\Mapping;
|
||||
|
||||
use ReflectionClass, ReflectionProperty;
|
||||
|
||||
/**
|
||||
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
|
||||
* of an entity and it's associations.
|
||||
*
|
||||
* Once populated, ClassMetadata instances are usually cached in a serialized form.
|
||||
*
|
||||
* <b>IMPORTANT NOTE:</b>
|
||||
*
|
||||
* The fields of this class are only public for 2 reasons:
|
||||
* 1) To allow fast READ access.
|
||||
* 2) To drastically reduce the size of a serialized instance (private/protected members
|
||||
* get the whole class name, namespace inclusive, prepended to every property in
|
||||
* the serialized representation).
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ClassMetadata extends ClassMetadataInfo
|
||||
{
|
||||
/**
|
||||
* The ReflectionProperty instances of the mapped class.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $reflFields = array();
|
||||
|
||||
/**
|
||||
* The prototype from which new instances of the mapped class are created.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $_prototype;
|
||||
|
||||
/**
|
||||
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
|
||||
* metadata of the class with the given name.
|
||||
*
|
||||
* @param string $entityName The name of the entity class the new instance is used for.
|
||||
*/
|
||||
public function __construct($entityName)
|
||||
{
|
||||
parent::__construct($entityName);
|
||||
$this->reflClass = new ReflectionClass($entityName);
|
||||
$this->namespace = $this->reflClass->getNamespaceName();
|
||||
$this->table['name'] = $this->reflClass->getShortName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionPropertys of the mapped class.
|
||||
*
|
||||
* @return array An array of ReflectionProperty instances.
|
||||
*/
|
||||
public function getReflectionProperties()
|
||||
{
|
||||
return $this->reflFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ReflectionProperty for a specific field of the mapped class.
|
||||
*
|
||||
* @param string $name
|
||||
* @return ReflectionProperty
|
||||
*/
|
||||
public function getReflectionProperty($name)
|
||||
{
|
||||
return $this->reflFields[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionProperty for the single identifier field.
|
||||
*
|
||||
* @return ReflectionProperty
|
||||
* @throws BadMethodCallException If the class has a composite identifier.
|
||||
*/
|
||||
public function getSingleIdReflectionProperty()
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
|
||||
}
|
||||
return $this->reflFields[$this->identifier[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the given field mapping.
|
||||
*
|
||||
* @param array $mapping The field mapping to validated & complete.
|
||||
* @return array The validated and completed field mapping.
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
protected function _validateAndCompleteFieldMapping(array &$mapping)
|
||||
{
|
||||
parent::_validateAndCompleteFieldMapping($mapping);
|
||||
|
||||
// Store ReflectionProperty of mapped field
|
||||
$refProp = $this->reflClass->getProperty($mapping['fieldName']);
|
||||
$refProp->setAccessible(true);
|
||||
$this->reflFields[$mapping['fieldName']] = $refProp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the identifier values of an entity of this class.
|
||||
*
|
||||
* For composite identifiers, the identifier values are returned as an array
|
||||
* with the same order as the field order in {@link identifier}.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return array
|
||||
*/
|
||||
public function getIdentifierValues($entity)
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
$id = array();
|
||||
foreach ($this->identifier as $idField) {
|
||||
$value = $this->reflFields[$idField]->getValue($entity);
|
||||
if ($value !== null) {
|
||||
$id[$idField] = $value;
|
||||
}
|
||||
}
|
||||
return $id;
|
||||
} else {
|
||||
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
|
||||
if ($value !== null) {
|
||||
return array($this->identifier[0] => $value);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the entity identifier of an entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param mixed $id
|
||||
* @todo Rename to assignIdentifier()
|
||||
*/
|
||||
public function setIdentifierValues($entity, array $id)
|
||||
{
|
||||
foreach ($id as $idField => $idValue) {
|
||||
$this->reflFields[$idField]->setValue($entity, $idValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified field to the specified value on the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setFieldValue($entity, $field, $value)
|
||||
{
|
||||
$this->reflFields[$field]->setValue($entity, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the specified field's value off the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param string $field
|
||||
*/
|
||||
public function getFieldValue($entity, $field)
|
||||
{
|
||||
return $this->reflFields[$field]->getValue($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the association mapping.
|
||||
*
|
||||
* @param AssociationMapping $assocMapping
|
||||
*/
|
||||
protected function _storeAssociationMapping(array $assocMapping)
|
||||
{
|
||||
parent::_storeAssociationMapping($assocMapping);
|
||||
|
||||
// Store ReflectionProperty of mapped field
|
||||
$sourceFieldName = $assocMapping['fieldName'];
|
||||
|
||||
$refProp = $this->reflClass->getProperty($sourceFieldName);
|
||||
$refProp->setAccessible(true);
|
||||
$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.
|
||||
*
|
||||
* @return string The string representation of this instance.
|
||||
* @todo Construct meaningful string representation.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return __CLASS__ . '@' . spl_object_hash($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which fields get serialized.
|
||||
*
|
||||
* It is only serialized what is necessary for best unserialization performance.
|
||||
* That means any metadata properties that are not set or empty or simply have
|
||||
* their default value are NOT serialized.
|
||||
*
|
||||
* Parts that are also NOT serialized because they can not be properly unserialized:
|
||||
* - reflClass (ReflectionClass)
|
||||
* - reflFields (ReflectionProperty array)
|
||||
*
|
||||
* @return array The names of all the fields that should be serialized.
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
// This metadata is always serialized/cached.
|
||||
$serialized = array(
|
||||
'associationMappings',
|
||||
'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
|
||||
'fieldMappings',
|
||||
'fieldNames',
|
||||
'identifier',
|
||||
'isIdentifierComposite', // TODO: REMOVE
|
||||
'name',
|
||||
'namespace', // TODO: REMOVE
|
||||
'table',
|
||||
'rootEntityName',
|
||||
'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
|
||||
);
|
||||
|
||||
// The rest of the metadata is only serialized if necessary.
|
||||
if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
|
||||
$serialized[] = 'changeTrackingPolicy';
|
||||
}
|
||||
|
||||
if ($this->customRepositoryClassName) {
|
||||
$serialized[] = 'customRepositoryClassName';
|
||||
}
|
||||
|
||||
if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
|
||||
$serialized[] = 'inheritanceType';
|
||||
$serialized[] = 'discriminatorColumn';
|
||||
$serialized[] = 'discriminatorValue';
|
||||
$serialized[] = 'discriminatorMap';
|
||||
$serialized[] = 'parentClasses';
|
||||
$serialized[] = 'subClasses';
|
||||
}
|
||||
|
||||
if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
|
||||
$serialized[] = 'generatorType';
|
||||
if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
|
||||
$serialized[] = 'sequenceGeneratorDefinition';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isMappedSuperclass) {
|
||||
$serialized[] = 'isMappedSuperclass';
|
||||
}
|
||||
|
||||
if ($this->isVersioned) {
|
||||
$serialized[] = 'isVersioned';
|
||||
$serialized[] = 'versionField';
|
||||
}
|
||||
|
||||
if ($this->lifecycleCallbacks) {
|
||||
$serialized[] = 'lifecycleCallbacks';
|
||||
}
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores some state that can not be serialized/unserialized.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
// Restore ReflectionClass and properties
|
||||
$this->reflClass = new ReflectionClass($this->name);
|
||||
|
||||
foreach ($this->fieldMappings as $field => $mapping) {
|
||||
if (isset($mapping['declared'])) {
|
||||
$reflField = new ReflectionProperty($mapping['declared'], $field);
|
||||
} else {
|
||||
$reflField = $this->reflClass->getProperty($field);
|
||||
}
|
||||
$reflField->setAccessible(true);
|
||||
$this->reflFields[$field] = $reflField;
|
||||
}
|
||||
|
||||
foreach ($this->associationMappings as $field => $mapping) {
|
||||
if (isset($mapping['declared'])) {
|
||||
$reflField = new ReflectionProperty($mapping['declared'], $field);
|
||||
} else {
|
||||
$reflField = $this->reflClass->getProperty($field);
|
||||
}
|
||||
|
||||
$reflField->setAccessible(true);
|
||||
$this->reflFields[$field] = $reflField;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the mapped class, without invoking the constructor.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function newInstance()
|
||||
{
|
||||
if ($this->_prototype === null) {
|
||||
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
|
||||
}
|
||||
return clone $this->_prototype;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
<?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\Mapping;
|
||||
|
||||
use ReflectionException,
|
||||
Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\DBAL\Platforms,
|
||||
Doctrine\ORM\Events;
|
||||
|
||||
/**
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
* metadata mapping informations of a class which describes how a class should be mapped
|
||||
* to a relational database.
|
||||
*
|
||||
* @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 ClassMetadataFactory
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @var AbstractPlatform
|
||||
*/
|
||||
private $targetPlatform;
|
||||
|
||||
/**
|
||||
* @var Driver\Driver
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\EventManager
|
||||
*/
|
||||
private $evm;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
private $cacheDriver;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $loadedMetadata = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $initialized = false;
|
||||
|
||||
/**
|
||||
* Creates a new factory instance that uses the given metadata driver implementation.
|
||||
*
|
||||
* @param $driver The metadata driver to use.
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver used by the factory to cache ClassMetadata instances.
|
||||
*
|
||||
* @param Doctrine\Common\Cache\Cache $cacheDriver
|
||||
*/
|
||||
public function setCacheDriver($cacheDriver)
|
||||
{
|
||||
$this->cacheDriver = $cacheDriver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver used by the factory to cache ClassMetadata instances.
|
||||
*
|
||||
* @return Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getCacheDriver()
|
||||
{
|
||||
return $this->cacheDriver;
|
||||
}
|
||||
|
||||
public function getLoadedMetadata()
|
||||
{
|
||||
return $this->loadedMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the factory to load the metadata of all classes known to the underlying
|
||||
* mapping driver.
|
||||
*
|
||||
* @return array The ClassMetadata instances of all mapped classes.
|
||||
*/
|
||||
public function getAllMetadata()
|
||||
{
|
||||
if ( ! $this->initialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
$metadata = array();
|
||||
foreach ($this->driver->getAllClassNames() as $className) {
|
||||
$metadata[] = $this->getMetadataFor($className);
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of this stuff, especially the metadata driver,
|
||||
* since these are not needed at all when a metadata cache is active.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
$this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
|
||||
$this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
|
||||
$this->evm = $this->em->getEventManager();
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class metadata descriptor for a class.
|
||||
*
|
||||
* @param string $className The name of the class.
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
public function getMetadataFor($className)
|
||||
{
|
||||
if ( ! isset($this->loadedMetadata[$className])) {
|
||||
$realClassName = $className;
|
||||
|
||||
// Check for namespace alias
|
||||
if (strpos($className, ':') !== false) {
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $className);
|
||||
$realClassName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
|
||||
if (isset($this->loadedMetadata[$realClassName])) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
|
||||
return $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->cacheDriver) {
|
||||
if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
|
||||
$this->loadedMetadata[$realClassName] = $cached;
|
||||
} else {
|
||||
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
|
||||
$this->cacheDriver->save(
|
||||
"$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->loadMetadata($realClassName);
|
||||
}
|
||||
|
||||
if ($className != $realClassName) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->loadedMetadata[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the factory has the metadata for a class loaded already.
|
||||
*
|
||||
* @param string $className
|
||||
* @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
|
||||
*/
|
||||
public function hasMetadataFor($className)
|
||||
{
|
||||
return isset($this->loadedMetadata[$className]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the metadata descriptor for a specific class.
|
||||
*
|
||||
* NOTE: This is only useful in very special cases, like when generating proxy classes.
|
||||
*
|
||||
* @param string $className
|
||||
* @param ClassMetadata $class
|
||||
*/
|
||||
public function setMetadataFor($className, $class)
|
||||
{
|
||||
$this->loadedMetadata[$className] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of parent classes for the given entity class
|
||||
*
|
||||
* @param string $name
|
||||
* @return array $parentClasses
|
||||
*/
|
||||
protected function getParentClasses($name)
|
||||
{
|
||||
// Collect parent classes, ignoring transient (not-mapped) classes.
|
||||
$parentClasses = array();
|
||||
foreach (array_reverse(class_parents($name)) as $parentClass) {
|
||||
if ( ! $this->driver->isTransient($parentClass)) {
|
||||
$parentClasses[] = $parentClass;
|
||||
}
|
||||
}
|
||||
return $parentClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the metadata of the class in question and all it's ancestors whose metadata
|
||||
* is still not loaded.
|
||||
*
|
||||
* @param string $name The name of the class for which the metadata should get loaded.
|
||||
* @param array $tables The metadata collection to which the loaded metadata is added.
|
||||
*/
|
||||
protected function loadMetadata($name)
|
||||
{
|
||||
if ( ! $this->initialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
$loaded = array();
|
||||
|
||||
$parentClasses = $this->getParentClasses($name);
|
||||
$parentClasses[] = $name;
|
||||
|
||||
// Move down the hierarchy of parent classes, starting from the topmost class
|
||||
$parent = null;
|
||||
$visited = array();
|
||||
foreach ($parentClasses as $className) {
|
||||
if (isset($this->loadedMetadata[$className])) {
|
||||
$parent = $this->loadedMetadata[$className];
|
||||
if ( ! $parent->isMappedSuperclass) {
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = $this->newClassMetadataInstance($className);
|
||||
|
||||
if ($parent) {
|
||||
$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);
|
||||
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
|
||||
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
|
||||
}
|
||||
|
||||
// Invoke driver
|
||||
try {
|
||||
$this->driver->loadMetadataForClass($className, $class);
|
||||
} catch (ReflectionException $e) {
|
||||
throw MappingException::reflectionFailure($className, $e);
|
||||
}
|
||||
|
||||
// Verify & complete identifier mapping
|
||||
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
|
||||
throw MappingException::identifierRequired($className);
|
||||
}
|
||||
if ($parent && ! $parent->isMappedSuperclass) {
|
||||
if ($parent->isIdGeneratorSequence()) {
|
||||
$class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
|
||||
} else if ($parent->isIdGeneratorTable()) {
|
||||
$class->getTableGeneratorDefinition($parent->tableGeneratorDefinition);
|
||||
}
|
||||
if ($parent->generatorType) {
|
||||
$class->setIdGeneratorType($parent->generatorType);
|
||||
}
|
||||
if ($parent->idGenerator) {
|
||||
$class->setIdGenerator($parent->idGenerator);
|
||||
}
|
||||
} else {
|
||||
$this->completeIdGeneratorMapping($class);
|
||||
}
|
||||
|
||||
if ($parent && $parent->isInheritanceTypeSingleTable()) {
|
||||
$class->setPrimaryTable($parent->table);
|
||||
}
|
||||
|
||||
$class->setParentClasses($visited);
|
||||
|
||||
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
|
||||
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class);
|
||||
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
|
||||
$loaded[] = $className;
|
||||
}
|
||||
|
||||
return $loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ClassMetadata instance for the given class name.
|
||||
*
|
||||
* @param string $className
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
protected function newClassMetadataInstance($className)
|
||||
{
|
||||
return new ClassMetadata($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inherited fields to the subclass mapping.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
|
||||
*/
|
||||
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
{
|
||||
foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
|
||||
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
|
||||
$mapping['inherited'] = $parentClass->name;
|
||||
}
|
||||
if ( ! isset($mapping['declared'])) {
|
||||
$mapping['declared'] = $parentClass->name;
|
||||
}
|
||||
$subClass->addInheritedFieldMapping($mapping);
|
||||
}
|
||||
foreach ($parentClass->reflFields as $name => $field) {
|
||||
$subClass->reflFields[$name] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inherited association mappings to the subclass mapping.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
|
||||
*/
|
||||
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
{
|
||||
foreach ($parentClass->associationMappings as $field => $mapping) {
|
||||
if ($parentClass->isMappedSuperclass) {
|
||||
$mapping['sourceEntity'] = $subClass->name;
|
||||
}
|
||||
|
||||
//$subclassMapping = $mapping;
|
||||
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
|
||||
$mapping['inherited'] = $parentClass->name;
|
||||
}
|
||||
if ( ! isset($mapping['declared'])) {
|
||||
$mapping['declared'] = $parentClass->name;
|
||||
}
|
||||
$subClass->addInheritedAssociationMapping($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the ID generator mapping. If "auto" is specified we choose the generator
|
||||
* most appropriate for the targeted database platform.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $class
|
||||
*/
|
||||
private function completeIdGeneratorMapping(ClassMetadataInfo $class)
|
||||
{
|
||||
$idGenType = $class->generatorType;
|
||||
if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
|
||||
if ($this->targetPlatform->prefersSequences()) {
|
||||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
|
||||
} else if ($this->targetPlatform->prefersIdentityColumns()) {
|
||||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
|
||||
} else {
|
||||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Create & assign an appropriate ID generator instance
|
||||
switch ($class->generatorType) {
|
||||
case ClassMetadata::GENERATOR_TYPE_IDENTITY:
|
||||
// For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to
|
||||
// <table>_<column>_seq in PostgreSQL for SERIAL columns.
|
||||
// Not pretty but necessary and the simplest solution that currently works.
|
||||
$seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ?
|
||||
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
|
||||
null;
|
||||
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
|
||||
// If there is no sequence definition yet, create a default definition
|
||||
$definition = $class->sequenceGeneratorDefinition;
|
||||
if ( ! $definition) {
|
||||
$sequenceName = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq';
|
||||
$definition['sequenceName'] = $this->targetPlatform->fixSchemaElementName($sequenceName);
|
||||
$definition['allocationSize'] = 1;
|
||||
$definition['initialValue'] = 1;
|
||||
$class->setSequenceGeneratorDefinition($definition);
|
||||
}
|
||||
$sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator(
|
||||
$definition['sequenceName'],
|
||||
$definition['allocationSize']
|
||||
);
|
||||
$class->setIdGenerator($sequenceGenerator);
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_NONE:
|
||||
$class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_TABLE:
|
||||
throw new ORMException("TableGenerator not yet implemented.");
|
||||
break;
|
||||
default:
|
||||
throw new ORMException("Unknown generator type: " . $class->generatorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,65 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 69
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver
|
||||
END
|
||||
Driver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 80
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/Driver.php
|
||||
END
|
||||
StaticPHPDriver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 89
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php
|
||||
END
|
||||
XmlDriver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
|
||||
END
|
||||
DriverChain.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 85
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php
|
||||
END
|
||||
YamlDriver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 84
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
|
||||
END
|
||||
AbstractFileDriver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 92
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php
|
||||
END
|
||||
DatabaseDriver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 88
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php
|
||||
END
|
||||
PHPDriver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php
|
||||
END
|
||||
DoctrineAnnotations.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 93
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
|
||||
END
|
||||
AnnotationDriver.php
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 90
|
||||
/doctrine/doctrine2.git/!svn/ver/4932/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
|
||||
END
|
||||
@@ -0,0 +1,368 @@
|
||||
10
|
||||
|
||||
dir
|
||||
4932
|
||||
http://svn.github.com/doctrine/doctrine2.git/lib/Doctrine/ORM/Mapping/Driver
|
||||
http://svn.github.com/doctrine/doctrine2.git
|
||||
|
||||
|
||||
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27cf1600-089b-f978-97ba-ce57b7db7bff
|
||||
|
||||
Driver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
1164a8c82a6ccd86e1c0edda58a8e1b0
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2006
|
||||
|
||||
StaticPHPDriver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
742c3b5f272cdaccbd5e405f434a9218
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3932
|
||||
|
||||
XmlDriver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
22bcccb9d4b1623ac2c10b404a3371ab
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20650
|
||||
|
||||
DriverChain.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
c0f8f0d9698186a347be595d588e4163
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3683
|
||||
|
||||
YamlDriver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
6acb2d13021cf0da9dcd283153644038
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
18062
|
||||
|
||||
AbstractFileDriver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
7cc68a19effc751c4f4ca706820d43bc
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
6637
|
||||
|
||||
DatabaseDriver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
fa2a8566ec4174a816968a8278d96d25
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
10459
|
||||
|
||||
PHPDriver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
e9e942bc8f189a50963f1fc405126368
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2397
|
||||
|
||||
DoctrineAnnotations.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
a6b799d58cddca5f37d96683765dc3c5
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4343
|
||||
|
||||
AnnotationDriver.php
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-22T02:39:44.333323Z
|
||||
1a41314fee129d566a3f00805f776911
|
||||
2010-10-11T13:15:18.000000Z
|
||||
4932
|
||||
benjamin.eberlei
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20947
|
||||
|
||||
externo
+210
@@ -0,0 +1,210 @@
|
||||
<?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\Mapping\Driver;
|
||||
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
|
||||
/**
|
||||
* Base driver for file-based metadata drivers.
|
||||
*
|
||||
* A file driver operates in a mode where it loads the mapping files of individual
|
||||
* classes on demand. This requires the user to adhere to the convention of 1 mapping
|
||||
* file per class and the file names of the mapping files must correspond to the full
|
||||
* class name, including namespace, with the namespace delimiters '\', replaced by dots '.'.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
abstract class AbstractFileDriver implements Driver
|
||||
{
|
||||
/**
|
||||
* The paths where to look for mapping files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_paths = array();
|
||||
|
||||
/**
|
||||
* The file extension of mapping documents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_fileExtension;
|
||||
|
||||
/**
|
||||
* Initializes a new FileDriver that looks in the given path(s) for mapping
|
||||
* documents and operates in the specified operating mode.
|
||||
*
|
||||
* @param string|array $paths One or multiple paths where mapping documents can be found.
|
||||
*/
|
||||
public function __construct($paths)
|
||||
{
|
||||
$this->addPaths((array) $paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append lookup paths to metadata driver.
|
||||
*
|
||||
* @param array $paths
|
||||
*/
|
||||
public function addPaths(array $paths)
|
||||
{
|
||||
$this->_paths = array_unique(array_merge($this->_paths, $paths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the defined metadata lookup paths.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPaths()
|
||||
{
|
||||
return $this->_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension used to look for mapping files under
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getFileExtension()
|
||||
{
|
||||
return $this->_fileExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file extension used to look for mapping files under
|
||||
*
|
||||
* @param string $fileExtension The file extension to set
|
||||
* @return void
|
||||
*/
|
||||
public function setFileExtension($fileExtension)
|
||||
{
|
||||
$this->_fileExtension = $fileExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element of schema meta data for the class from the mapping file.
|
||||
* This will lazily load the mapping file if it is not loaded yet
|
||||
*
|
||||
* @return array $element The element of schema meta data
|
||||
*/
|
||||
public function getElement($className)
|
||||
{
|
||||
$result = $this->_loadMappingFile($this->_findMappingFile($className));
|
||||
|
||||
return $result[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the class with the specified name should have its metadata loaded.
|
||||
* This is only the case if it is either mapped as an Entity or a
|
||||
* MappedSuperclass.
|
||||
*
|
||||
* @param string $className
|
||||
* @return boolean
|
||||
*/
|
||||
public function isTransient($className)
|
||||
{
|
||||
$fileName = str_replace('\\', '.', $className) . $this->_fileExtension;
|
||||
|
||||
// Check whether file exists
|
||||
foreach ((array) $this->_paths as $path) {
|
||||
if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all mapped classes known to this driver.
|
||||
*
|
||||
* @return array The names of all mapped classes known to this driver.
|
||||
*/
|
||||
public function getAllClassNames()
|
||||
{
|
||||
$classes = array();
|
||||
|
||||
if ($this->_paths) {
|
||||
foreach ((array) $this->_paths as $path) {
|
||||
if ( ! is_dir($path)) {
|
||||
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
|
||||
}
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($path),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: All files found here means classes are not transient!
|
||||
$classes[] = str_replace('.', '\\', $fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the mapping file for the class with the given name by searching
|
||||
* through the configured paths.
|
||||
*
|
||||
* @param $className
|
||||
* @return string The (absolute) file name.
|
||||
* @throws MappingException
|
||||
*/
|
||||
protected function _findMappingFile($className)
|
||||
{
|
||||
$fileName = str_replace('\\', '.', $className) . $this->_fileExtension;
|
||||
|
||||
// Check whether file exists
|
||||
foreach ((array) $this->_paths as $path) {
|
||||
if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) {
|
||||
return $path . DIRECTORY_SEPARATOR . $fileName;
|
||||
}
|
||||
}
|
||||
|
||||
throw MappingException::mappingFileNotFound($className, $fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a mapping file with the given name and returns a map
|
||||
* from class/entity names to their corresponding elements.
|
||||
*
|
||||
* @param string $file The mapping file to load.
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function _loadMappingFile($file);
|
||||
}
|
||||
externo
+489
@@ -0,0 +1,489 @@
|
||||
<?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\Mapping\Driver;
|
||||
|
||||
use Doctrine\Common\Cache\ArrayCache,
|
||||
Doctrine\Common\Annotations\AnnotationReader,
|
||||
Doctrine\ORM\Mapping\ClassMetadataInfo,
|
||||
Doctrine\ORM\Mapping\MappingException;
|
||||
|
||||
require __DIR__ . '/DoctrineAnnotations.php';
|
||||
|
||||
/**
|
||||
* The AnnotationDriver reads the mapping metadata from docblock annotations.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class AnnotationDriver implements Driver
|
||||
{
|
||||
/**
|
||||
* The AnnotationReader.
|
||||
*
|
||||
* @var AnnotationReader
|
||||
*/
|
||||
private $_reader;
|
||||
|
||||
/**
|
||||
* The paths where to look for mapping files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_paths = array();
|
||||
|
||||
/**
|
||||
* The file extension of mapping documents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_fileExtension = '.php';
|
||||
|
||||
/**
|
||||
* @param array
|
||||
*/
|
||||
protected $_classNames;
|
||||
|
||||
/**
|
||||
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
|
||||
* docblock annotations.
|
||||
*
|
||||
* @param $reader The AnnotationReader to use.
|
||||
* @param string|array $paths One or multiple paths where mapping classes can be found.
|
||||
*/
|
||||
public function __construct(AnnotationReader $reader, $paths = null)
|
||||
{
|
||||
$this->_reader = $reader;
|
||||
if ($paths) {
|
||||
$this->addPaths((array) $paths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append lookup paths to metadata driver.
|
||||
*
|
||||
* @param array $paths
|
||||
*/
|
||||
public function addPaths(array $paths)
|
||||
{
|
||||
$this->_paths = array_unique(array_merge($this->_paths, $paths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the defined metadata lookup paths.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPaths()
|
||||
{
|
||||
return $this->_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension used to look for mapping files under
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getFileExtension()
|
||||
{
|
||||
return $this->_fileExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file extension used to look for mapping files under
|
||||
*
|
||||
* @param string $fileExtension The file extension to set
|
||||
* @return void
|
||||
*/
|
||||
public function setFileExtension($fileExtension)
|
||||
{
|
||||
$this->_fileExtension = $fileExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
|
||||
{
|
||||
$class = $metadata->getReflectionClass();
|
||||
|
||||
$classAnnotations = $this->_reader->getClassAnnotations($class);
|
||||
|
||||
// Evaluate Entity annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
|
||||
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
|
||||
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
|
||||
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
|
||||
}
|
||||
|
||||
// Evaluate Table annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) {
|
||||
$tableAnnot = $classAnnotations['Doctrine\ORM\Mapping\Table'];
|
||||
$primaryTable = array(
|
||||
'name' => $tableAnnot->name,
|
||||
'schema' => $tableAnnot->schema
|
||||
);
|
||||
|
||||
if ($tableAnnot->indexes !== null) {
|
||||
foreach ($tableAnnot->indexes as $indexAnnot) {
|
||||
$primaryTable['indexes'][$indexAnnot->name] = array(
|
||||
'columns' => $indexAnnot->columns
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($tableAnnot->uniqueConstraints !== null) {
|
||||
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraint) {
|
||||
$primaryTable['uniqueConstraints'][$uniqueConstraint->name] = array(
|
||||
'columns' => $uniqueConstraint->columns
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$metadata->setPrimaryTable($primaryTable);
|
||||
}
|
||||
|
||||
// Evaluate InheritanceType annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) {
|
||||
$inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType'];
|
||||
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value));
|
||||
|
||||
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
// Evaluate DiscriminatorColumn annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) {
|
||||
$discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'];
|
||||
$metadata->setDiscriminatorColumn(array(
|
||||
'name' => $discrColumnAnnot->name,
|
||||
'type' => $discrColumnAnnot->type,
|
||||
'length' => $discrColumnAnnot->length
|
||||
));
|
||||
} else {
|
||||
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
|
||||
}
|
||||
|
||||
// Evaluate DiscriminatorMap annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) {
|
||||
$discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'];
|
||||
$metadata->setDiscriminatorMap($discrMapAnnot->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Evaluate DoctrineChangeTrackingPolicy annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) {
|
||||
$changeTrackingAnnot = $classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'];
|
||||
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value));
|
||||
}
|
||||
|
||||
// Evaluate annotations on properties/fields
|
||||
foreach ($class->getProperties() as $property) {
|
||||
if ($metadata->isMappedSuperclass && ! $property->isPrivate()
|
||||
||
|
||||
$metadata->isInheritedField($property->name)
|
||||
||
|
||||
$metadata->isInheritedAssociation($property->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mapping = array();
|
||||
$mapping['fieldName'] = $property->getName();
|
||||
|
||||
// Check for JoinColummn/JoinColumns annotations
|
||||
$joinColumns = array();
|
||||
|
||||
if ($joinColumnAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) {
|
||||
$joinColumns[] = array(
|
||||
'name' => $joinColumnAnnot->name,
|
||||
'referencedColumnName' => $joinColumnAnnot->referencedColumnName,
|
||||
'unique' => $joinColumnAnnot->unique,
|
||||
'nullable' => $joinColumnAnnot->nullable,
|
||||
'onDelete' => $joinColumnAnnot->onDelete,
|
||||
'onUpdate' => $joinColumnAnnot->onUpdate,
|
||||
'columnDefinition' => $joinColumnAnnot->columnDefinition,
|
||||
);
|
||||
} else if ($joinColumnsAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) {
|
||||
foreach ($joinColumnsAnnot->value as $joinColumn) {
|
||||
$joinColumns[] = array(
|
||||
'name' => $joinColumn->name,
|
||||
'referencedColumnName' => $joinColumn->referencedColumnName,
|
||||
'unique' => $joinColumn->unique,
|
||||
'nullable' => $joinColumn->nullable,
|
||||
'onDelete' => $joinColumn->onDelete,
|
||||
'onUpdate' => $joinColumn->onUpdate,
|
||||
'columnDefinition' => $joinColumn->columnDefinition,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Field can only be annotated with one of:
|
||||
// @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
|
||||
if ($columnAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Column')) {
|
||||
if ($columnAnnot->type == null) {
|
||||
throw MappingException::propertyTypeIsRequired($className, $property->getName());
|
||||
}
|
||||
|
||||
$mapping['type'] = $columnAnnot->type;
|
||||
$mapping['length'] = $columnAnnot->length;
|
||||
$mapping['precision'] = $columnAnnot->precision;
|
||||
$mapping['scale'] = $columnAnnot->scale;
|
||||
$mapping['nullable'] = $columnAnnot->nullable;
|
||||
$mapping['unique'] = $columnAnnot->unique;
|
||||
if ($columnAnnot->options) {
|
||||
$mapping['options'] = $columnAnnot->options;
|
||||
}
|
||||
|
||||
if (isset($columnAnnot->name)) {
|
||||
$mapping['columnName'] = $columnAnnot->name;
|
||||
}
|
||||
|
||||
if (isset($columnAnnot->columnDefinition)) {
|
||||
$mapping['columnDefinition'] = $columnAnnot->columnDefinition;
|
||||
}
|
||||
|
||||
if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
if ($generatedValueAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) {
|
||||
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
|
||||
}
|
||||
|
||||
if ($versionAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) {
|
||||
$metadata->setVersionMapping($mapping);
|
||||
}
|
||||
|
||||
$metadata->mapField($mapping);
|
||||
|
||||
// Check for SequenceGenerator/TableGenerator definition
|
||||
if ($seqGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\SequenceGenerator')) {
|
||||
$metadata->setSequenceGeneratorDefinition(array(
|
||||
'sequenceName' => $seqGeneratorAnnot->sequenceName,
|
||||
'allocationSize' => $seqGeneratorAnnot->allocationSize,
|
||||
'initialValue' => $seqGeneratorAnnot->initialValue
|
||||
));
|
||||
} else if ($tblGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
|
||||
throw MappingException::tableIdGeneratorNotImplemented($className);
|
||||
}
|
||||
} else if ($oneToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
|
||||
$mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
|
||||
$mapping['joinColumns'] = $joinColumns;
|
||||
$mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
|
||||
$mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
|
||||
$mapping['cascade'] = $oneToOneAnnot->cascade;
|
||||
$mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneAnnot->fetch);
|
||||
$metadata->mapOneToOne($mapping);
|
||||
} else if ($oneToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) {
|
||||
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
|
||||
$mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
|
||||
$mapping['cascade'] = $oneToManyAnnot->cascade;
|
||||
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch);
|
||||
|
||||
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
|
||||
$mapping['orderBy'] = $orderByAnnot->value;
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
} else if ($manyToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) {
|
||||
$mapping['joinColumns'] = $joinColumns;
|
||||
$mapping['cascade'] = $manyToOneAnnot->cascade;
|
||||
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
|
||||
$mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneAnnot->fetch);
|
||||
$metadata->mapManyToOne($mapping);
|
||||
} else if ($manyToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) {
|
||||
$joinTable = array();
|
||||
|
||||
if ($joinTableAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinTable')) {
|
||||
$joinTable = array(
|
||||
'name' => $joinTableAnnot->name,
|
||||
'schema' => $joinTableAnnot->schema
|
||||
);
|
||||
|
||||
foreach ($joinTableAnnot->joinColumns as $joinColumn) {
|
||||
$joinTable['joinColumns'][] = array(
|
||||
'name' => $joinColumn->name,
|
||||
'referencedColumnName' => $joinColumn->referencedColumnName,
|
||||
'unique' => $joinColumn->unique,
|
||||
'nullable' => $joinColumn->nullable,
|
||||
'onDelete' => $joinColumn->onDelete,
|
||||
'onUpdate' => $joinColumn->onUpdate,
|
||||
'columnDefinition' => $joinColumn->columnDefinition,
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
|
||||
$joinTable['inverseJoinColumns'][] = array(
|
||||
'name' => $joinColumn->name,
|
||||
'referencedColumnName' => $joinColumn->referencedColumnName,
|
||||
'unique' => $joinColumn->unique,
|
||||
'nullable' => $joinColumn->nullable,
|
||||
'onDelete' => $joinColumn->onDelete,
|
||||
'onUpdate' => $joinColumn->onUpdate,
|
||||
'columnDefinition' => $joinColumn->columnDefinition,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$mapping['joinTable'] = $joinTable;
|
||||
$mapping['targetEntity'] = $manyToManyAnnot->targetEntity;
|
||||
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
|
||||
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
|
||||
$mapping['cascade'] = $manyToManyAnnot->cascade;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch);
|
||||
|
||||
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
|
||||
$mapping['orderBy'] = $orderByAnnot->value;
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate @HasLifecycleCallbacks annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
|
||||
foreach ($class->getMethods() as $method) {
|
||||
if ($method->isPublic()) {
|
||||
$annotations = $this->_reader->getMethodAnnotations($method);
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PrePersist'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::prePersist);
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PostPersist'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postPersist);
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PreUpdate'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preUpdate);
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PostUpdate'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postUpdate);
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PreRemove'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preRemove);
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PostRemove'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postRemove);
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the class with the specified name is transient. Only non-transient
|
||||
* classes, that is entities and mapped superclasses, should have their metadata loaded.
|
||||
* A class is non-transient if it is annotated with either @Entity or
|
||||
* @MappedSuperclass in the class doc block.
|
||||
*
|
||||
* @param string $className
|
||||
* @return boolean
|
||||
*/
|
||||
public function isTransient($className)
|
||||
{
|
||||
$classAnnotations = $this->_reader->getClassAnnotations(new \ReflectionClass($className));
|
||||
|
||||
return ! isset($classAnnotations['Doctrine\ORM\Mapping\Entity']) &&
|
||||
! isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAllClassNames()
|
||||
{
|
||||
if ($this->_classNames !== null) {
|
||||
return $this->_classNames;
|
||||
}
|
||||
|
||||
if (!$this->_paths) {
|
||||
throw MappingException::pathRequired();
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
$includedFiles = array();
|
||||
|
||||
foreach ($this->_paths as $path) {
|
||||
if ( ! is_dir($path)) {
|
||||
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
|
||||
}
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($path),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sourceFile = realpath($file->getPathName());
|
||||
require_once $sourceFile;
|
||||
$includedFiles[] = $sourceFile;
|
||||
}
|
||||
}
|
||||
|
||||
$declared = get_declared_classes();
|
||||
|
||||
foreach ($declared as $className) {
|
||||
$rc = new \ReflectionClass($className);
|
||||
$sourceFile = $rc->getFileName();
|
||||
if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) {
|
||||
$classes[] = $className;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_classNames = $classes;
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for the Annotation Driver
|
||||
*
|
||||
* @param array|string $paths
|
||||
* @param AnnotationReader $reader
|
||||
* @return AnnotationDriver
|
||||
*/
|
||||
static public function create($paths = array(), AnnotationReader $reader = null)
|
||||
{
|
||||
if ($reader == null) {
|
||||
$reader = new AnnotationReader();
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
}
|
||||
return new self($reader, $paths);
|
||||
}
|
||||
}
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário