Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
doctrine-dbal
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Tomáš Trávníček
doctrine-dbal
Commits
0d75147c
Commit
0d75147c
authored
Oct 26, 2006
by
zYne
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Doctrine_Expression classes added, fixes #195, #196, #197, #198
Ticket: 195
parent
ff85f8c6
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
426 additions
and
964 deletions
+426
-964
DataDict.php
lib/Doctrine/DataDict.php
+2
-1
Pgsql.php
lib/Doctrine/DataDict/Pgsql.php
+6
-6
Expression.php
lib/Doctrine/Expression.php
+233
-0
Oracle.php
lib/Doctrine/Expression/Oracle.php
+45
-0
Pgsql.php
lib/Doctrine/Expression/Pgsql.php
+91
-0
Sqlite.php
lib/Doctrine/Expression/Sqlite.php
+49
-0
Filter.php
lib/Doctrine/Filter.php
+0
-14
Session.php
lib/Doctrine/Session.php
+0
-943
No files found.
lib/Doctrine/DataDict.php
View file @
0d75147c
...
...
@@ -30,7 +30,7 @@ class Doctrine_DataDict {
protected
$dbh
;
public
function
__construct
(
PDO
$dbh
)
{
public
function
__construct
(
$dbh
=
null
)
{
$file
=
Doctrine
::
getPath
()
.
DIRECTORY_SEPARATOR
.
"Doctrine"
.
DIRECTORY_SEPARATOR
.
"adodb-hack"
.
DIRECTORY_SEPARATOR
.
"adodb.inc.php"
;
if
(
!
file_exists
(
$file
))
...
...
@@ -39,6 +39,7 @@ class Doctrine_DataDict {
require_once
(
$file
);
$this
->
dbh
=
$dbh
;
if
(
$dbh
)
$this
->
dict
=
NewDataDictionary
(
$dbh
);
}
/**
...
...
lib/Doctrine/DataDict/Pgsql.php
View file @
0d75147c
...
...
@@ -29,7 +29,7 @@
* @version $Id$
*/
class
Doctrine_DataDict_
My
sql
extends
Doctrine_DataDict
{
class
Doctrine_DataDict_
Pg
sql
extends
Doctrine_DataDict
{
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
...
...
@@ -53,7 +53,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict {
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
*/
public
function
get
Typ
eDeclaration
(
array
$field
)
{
public
function
get
Nativ
eDeclaration
(
array
$field
)
{
switch
(
$field
[
'type'
])
{
case
'string'
:
case
'array'
:
...
...
@@ -115,8 +115,8 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict {
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
* @return array containing the various possible types, length, sign, fixed
*/
public
function
mapNativeDatatype
(
$field
)
{
$db_type
=
preg_replace
(
'/\d/'
,
''
,
strtolower
(
$field
[
'type'
])
);
public
function
getDoctrineDeclaration
(
array
$field
)
{
$length
=
$field
[
'length'
];
if
(
$length
==
'-1'
&&
!
empty
(
$field
[
'atttypmod'
]))
{
$length
=
$field
[
'atttypmod'
]
-
4
;
...
...
@@ -126,7 +126,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict {
}
$type
=
array
();
$unsigned
=
$fixed
=
null
;
switch
(
$
db_type
)
{
switch
(
$
field
[
'type'
]
)
{
case
'smallint'
:
case
'int2'
:
$type
[]
=
'integer'
;
...
...
@@ -159,7 +159,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_DataDict {
case
'bool'
:
case
'boolean'
:
$type
[]
=
'boolean'
;
$length
=
null
;
$length
=
1
;
break
;
case
'text'
:
case
'varchar'
:
...
...
lib/Doctrine/Expression.php
0 → 100644
View file @
0d75147c
<?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.phpdoctrine.com>.
*/
/**
* Doctrine_Expression
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class
Doctrine_Expression
{
/**
* @var Doctrine_Connection $connection
*/
protected
$conn
;
/**
* @param Doctrine_Connection $conn
*/
public
function
__construct
(
Doctrine_Connection
$conn
)
{
$this
->
conn
=
$conn
;
}
/**
* Returns the average value of a column
*
* @param string $column the column to use
* @return string generated sql including an AVG aggregate function
*/
public
function
avg
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'AVG('
.
$column
.
')'
;
}
/**
* Returns the number of rows (without a NULL value) of a column
*
* If a '*' is used instead of a column the number of selected rows
* is returned.
*
* @param string|integer $column the column to use
* @return string generated sql including a COUNT aggregate function
*/
public
function
count
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'COUNT('
.
$column
.
')'
;
}
/**
* Returns the highest value of a column
*
* @param string $column the column to use
* @return string generated sql including a MAX aggregate function
*/
public
function
max
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'MAX('
.
$column
.
')'
;
}
/**
* Returns the lowest value of a column
*
* @param string $column the column to use
* @return string
*/
public
function
min
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'MIN('
.
$column
.
')'
;
}
/**
* Returns the total sum of a column
*
* @param string $column the column to use
* @return string
*/
public
function
sum
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'SUM('
.
$column
.
')'
;
}
// scalar functions
/**
* Returns the md5 sum of a field.
*
* Note: Not SQL92, but common functionality
*
* @return string
*/
public
function
md5
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'MD5('
.
$column
.
')'
;
}
/**
* Returns the length of a text field.
*
* @param string $expression1
* @param string $expression2
* @return string
*/
public
function
length
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'LENGTH('
.
$column
.
')'
;
}
/**
* Rounds a numeric field to the number of decimals specified.
*
* @param string $expression1
* @param string $expression2
* @return string
*/
public
function
round
(
$column
,
$decimals
)
{
$column
=
$this
->
getIdentifier
(
$column
);
return
'ROUND('
.
$column
.
', '
.
$decimals
.
')'
;
}
/**
* Returns the remainder of the division operation
* $expression1 / $expression2.
*
* @param string $expression1
* @param string $expression2
* @return string
*/
public
function
mod
(
$expression1
,
$expression2
)
{
$expression1
=
$this
->
getIdentifier
(
$expression1
);
$expression2
=
$this
->
getIdentifier
(
$expression2
);
return
'MOD('
.
$expression1
.
', '
.
$expression2
.
')'
;
}
/**
* ltrim
* returns the string $str with leading space characters removed
*
* @param string $str literal string or column name
* @return string
*/
public
function
ltrim
(
$str
)
{
return
'LTRIM('
.
$str
.
')'
;
}
/**
* upper
* Returns the string $str with all characters changed to uppercase according to the current character set mapping.
*
* @param string $str literal string or column name
* @return string
*/
public
function
upper
(
$str
)
{
return
'UPPER('
.
$str
.
')'
;
}
/**
* lower
* Returns the string $str with all characters changed to lowercase according to the current character set mapping.
*
* @param string $str literal string or column name
* @return string
*/
public
function
lower
(
$str
)
{
return
'LOWER('
.
$str
.
')'
;
}
/**
* locate
* returns the position of the first occurrence of substring $substr in string $str
*
* @param string $substr literal string
* @param string $str literal string
* @return string
*/
public
function
locate
(
$substr
,
$str
)
{
return
'LOCATE('
.
$str
.
', '
.
$substr
.
')'
;
}
/**
* Returns the current system date.
*
* @return string
*/
public
function
now
()
{
return
'NOW()'
;
}
/**
* Returns part of a string.
*
* Note: Not SQL92, but common functionality.
*
* @param string $value the target $value the string or the string column.
* @param int $from extract from this characeter.
* @param int $len extract this amount of characters.
* @return string sql that extracts part of a string.
*/
public
function
subString
(
$value
,
$from
,
$len
=
null
)
{
$value
=
$this
->
getIdentifier
(
$value
);
if
(
$len
===
null
)
return
'SUBSTRING('
.
$value
.
' FROM '
.
$from
.
')'
;
else
{
$len
=
$this
->
getIdentifier
(
$len
);
return
'SUBSTRING('
.
$value
.
' FROM '
.
$from
.
' FOR '
.
$len
.
')'
;
}
}
/**
* Returns a series of strings concatinated
*
* concat() accepts an arbitrary number of parameters. Each parameter
* must contain an expression or an array with expressions.
*
* @param string|array(string) strings that will be concatinated.
*/
public
function
concat
(
$arg1
,
$arg2
)
{
$args
=
func_get_args
();
$cols
=
$this
->
getIdentifiers
(
$cols
);
return
'CONCAT('
.
join
(
', '
,
$cols
)
.
')'
;
}
}
lib/Doctrine/Expression/Oracle.php
0 → 100644
View file @
0d75147c
<?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.phpdoctrine.com>.
*/
Doctrine
::
autoload
(
'Doctrine_Expression'
);
/**
* Doctrine_Expression_Sqlite
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class
Doctrine_Expression_Oracle
extends
Doctrine_Expression
{
/**
* Returns a series of strings concatinated
*
* concat() accepts an arbitrary number of parameters. Each parameter
* must contain an expression
*
* @param string $arg1, $arg2 ... $argN strings that will be concatinated.
* @return string
*/
public
function
concat
(
$arg1
,
$arg2
)
{
$args
=
func_get_args
();
$cols
=
$this
->
getIdentifiers
(
$args
);
return
join
(
' || '
,
$cols
);
}
}
lib/Doctrine/Expression/Pgsql.php
0 → 100644
View file @
0d75147c
/*
* $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.phpdoctrine.com>.
*/
Doctrine
::
autoload
(
'Doctrine_Expression'
);
/**
* Doctrine_Expression_Pgsql
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class
Doctrine_Expression_Pgsql
extends
Doctrine_Expression
{
/**
* Returns the md5 sum of a field.
*
* Note: Not SQL92, but common functionality
*
* md5() works with the default PostgreSQL 8 versions.
*
* If you are using PostgreSQL 7.x or older you need
* to make sure that the digest procedure.
* If you use RPMS (Redhat and Mandrake) install the postgresql-contrib
* package. You must then install the procedure by running this shell command:
* <code>
* psql [dbname] < /usr/share/pgsql/contrib/pgcrypto.sql
* </code>
* You should make sure you run this as the postgres user.
*
* @return string
*/
public
function
md5
(
$column
)
{
$column
=
$this
->
getIdentifier
(
$column
);
if
(
$this
->
version
>
7
)
return
'MD5('
.
$column
.
')'
;
else
return
'encode(digest('
.
$column
.
', md5), hex)'
;
}
/**
* Returns part of a string.
*
* Note: Not SQL92, but common functionality.
*
* @param string $value the target $value the string or the string column.
* @param int $from extract from this characeter.
* @param int $len extract this amount of characters.
* @return string sql that extracts part of a string.
*/
public
function
subString
(
$value
,
$from
,
$len
=
null
)
{
$value
=
$this
->
getIdentifier
(
$value
);
if
(
$len
===
null
)
{
$len
=
$this
->
getIdentifier
(
$len
);
return
'SUBSTR('
.
$value
.
', '
.
$from
.
')'
;
}
else
return
'SUBSTR('
.
$value
.
', '
.
$from
.
', '
.
$len
.
')'
;
}
/**
* Returns a series of strings concatinated
*
* concat() accepts an arbitrary number of parameters. Each parameter
* must contain an expression or an array with expressions.
*
* @param string|array(string) strings that will be concatinated.
* @return string
*/
public
function
concat
(
$arg1
,
$arg2
)
{
$args
=
func_get_args
();
$cols
=
$this
->
getIdentifiers
(
$cols
);
return
join
(
' || '
,
$cols
);
}
}
lib/Doctrine/Expression/Sqlite.php
0 → 100644
View file @
0d75147c
<?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.phpdoctrine.com>.
*/
Doctrine
::
autoload
(
'Doctrine_Expression'
);
/**
* Doctrine_Expression_Sqlite
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class
Doctrine_Expression_Sqlite
extends
Doctrine_Expression
{
/**
* Returns part of a string.
*
* Note: Not SQL92, but common functionality. SQLite only supports the 3
* parameter variant of this function, so we are using 2^30-1 as
* artificial length in that case.
*
* @param string $value the target $value the string or the string column.
* @param int $from extract from this characeter.
* @param int $len extract this amount of characters.
* @return string sql that extracts part of a string.
*/
public
function
subString
(
$value
,
$from
,
$len
=
null
)
{
$value
=
$this
->
getIdentifier
(
$value
);
if
(
$len
===
null
)
$len
=
1073741823
;
return
'SUBSTR('
.
$value
.
', '
.
$from
.
', '
.
$len
.
')'
;
}
}
lib/Doctrine/Filter.php
deleted
100644 → 0
View file @
ff85f8c6
<?php
class
Doctrine_Filter
{
private
$name
;
public
function
__construct
(
$name
)
{
$this
->
name
=
$name
;
}
public
function
getName
()
{
return
$this
->
name
;
}
}
lib/Doctrine/Session.php
deleted
100644 → 0
View file @
ff85f8c6
<?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.phpdoctrine.com>.
*/
/**
* Doctrine_Session
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
abstract
class
Doctrine_Session
extends
Doctrine_Configurable
implements
Countable
,
IteratorAggregate
{
/**
* Doctrine_Session is in open state when it is opened and there are no active transactions
*/
const
STATE_OPEN
=
0
;
/**
* Doctrine_Session is in closed state when it is closed
*/
const
STATE_CLOSED
=
1
;
/**
* Doctrine_Session is in active state when it has one active transaction
*/
const
STATE_ACTIVE
=
2
;
/**
* Doctrine_Session is in busy state when it has multiple active transactions
*/
const
STATE_BUSY
=
3
;
/**
* @var $dbh the database handle
*/
private
$dbh
;
/**
* @see Doctrine_Session::STATE_* constants
* @var boolean $state the current state of the session
*/
private
$state
=
0
;
/**
* @var integer $transaction_level the nesting level of transactions, used by transaction methods
*/
private
$transaction_level
=
0
;
/**
* @var array $tables an array containing all the initialized Doctrine_Table objects
* keys representing Doctrine_Table component names and values as Doctrine_Table objects
*/
protected
$tables
=
array
();
/**
* @var Doctrine_Validator $validator transaction validator
*/
protected
$validator
;
/**
* @var array $update two dimensional pending update list, the records in
* this list will be updated when transaction is committed
*/
protected
$update
=
array
();
/**
* @var array $insert two dimensional pending insert list, the records in
* this list will be inserted when transaction is committed
*/
protected
$insert
=
array
();
/**
* @var array $delete two dimensional pending delete list, the records in
* this list will be deleted when transaction is committed
*/
protected
$delete
=
array
();
/**
* the constructor
*
* @param Doctrine_Manager $manager the manager object
* @param PDO $pdo the database handler
*/
public
function
__construct
(
Doctrine_Manager
$manager
,
PDO
$pdo
)
{
$this
->
dbh
=
$pdo
;
$this
->
state
=
Doctrine_Session
::
STATE_OPEN
;
$this
->
setParent
(
$manager
);
$this
->
dbh
->
setAttribute
(
PDO
::
ATTR_CASE
,
PDO
::
CASE_NATURAL
);
$this
->
dbh
->
setAttribute
(
PDO
::
ATTR_ERRMODE
,
PDO
::
ERRMODE_EXCEPTION
);
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onOpen
(
$this
);
}
/**
* returns the state of this session
*
* @see Doctrine_Session::STATE_* constants
* @return integer the session state
*/
public
function
getState
()
{
return
$this
->
state
;
}
/**
* returns the manager that created this session
*
* @return Doctrine_Manager
*/
public
function
getManager
()
{
return
$this
->
getParent
();
}
/**
* returns the database handler of which this session uses
*
* @return object PDO the database handler
*/
public
function
getDBH
()
{
return
$this
->
dbh
;
}
/**
* query
* queries the database with Doctrine Query Language
*
* @param string $query DQL query
* @param array $params query parameters
*/
final
public
function
query
(
$query
,
array
$params
=
array
())
{
$parser
=
new
Doctrine_Query
(
$this
);
return
$parser
->
query
(
$query
,
$params
);
}
/**
* queries the database with limit and offset
* added to the query and returns a PDOStatement object
*
* @param string $query
* @param integer $limit
* @param integer $offset
* @return PDOStatement
*/
public
function
select
(
$query
,
$limit
=
0
,
$offset
=
0
)
{
if
(
$limit
>
0
||
$offset
>
0
)
$query
=
$this
->
modifyLimitQuery
(
$query
,
$limit
,
$offset
);
return
$this
->
dbh
->
query
(
$query
);
}
/**
* @param string $query sql query
* @param array $params query parameters
*
* @return PDOStatement
*/
public
function
execute
(
$query
,
array
$params
=
array
())
{
if
(
!
empty
(
$params
))
{
$stmt
=
$this
->
dbh
->
prepare
(
$query
);
$stmt
->
execute
(
$params
);
return
$stmt
;
}
else
{
return
$this
->
dbh
->
query
(
$query
);
}
}
/**
* whether or not this session has table $name initialized
*
* @param $mixed $name
* @return boolean
*/
public
function
hasTable
(
$name
)
{
return
isset
(
$this
->
tables
[
$name
]);
}
/**
* returns a table object for given component name
*
* @param string $name component name
* @return object Doctrine_Table
*/
public
function
getTable
(
$name
)
{
if
(
isset
(
$this
->
tables
[
$name
]))
return
$this
->
tables
[
$name
];
$class
=
$name
.
"Table"
;
if
(
class_exists
(
$class
)
&&
in_array
(
"Doctrine_Table"
,
class_parents
(
$class
)))
{
return
new
$class
(
$name
);
}
else
{
return
new
Doctrine_Table
(
$name
);
}
}
/**
* returns an array of all initialized tables
*
* @return array
*/
public
function
getTables
()
{
return
$this
->
tables
;
}
/**
* returns an iterator that iterators through all
* initialized table objects
*
* @return ArrayIterator
*/
public
function
getIterator
()
{
return
new
ArrayIterator
(
$this
->
tables
);
}
/**
* returns the count of initialized table objects
*
* @return integer
*/
public
function
count
()
{
return
count
(
$this
->
tables
);
}
/**
* @param $objTable a Doctrine_Table object to be added into registry
* @return boolean
*/
public
function
addTable
(
Doctrine_Table
$objTable
)
{
$name
=
$objTable
->
getComponentName
();
if
(
isset
(
$this
->
tables
[
$name
]))
return
false
;
$this
->
tables
[
$name
]
=
$objTable
;
return
true
;
}
/**
* creates a record
*
* create creates a record
* @param string $name component name
* @return Doctrine_Record Doctrine_Record object
*/
public
function
create
(
$name
)
{
return
$this
->
getTable
(
$name
)
->
create
();
}
/**
* buildFlushTree
* builds a flush tree that is used in transactions
*
* @return array
*/
public
function
buildFlushTree
(
array
$tables
)
{
$tree
=
array
();
foreach
(
$tables
as
$k
=>
$table
)
{
$k
=
$k
.
$table
;
if
(
!
(
$table
instanceof
Doctrine_Table
))
$table
=
$this
->
getTable
(
$table
);
$nm
=
$table
->
getComponentName
();
$index
=
array_search
(
$nm
,
$tree
);
if
(
$index
===
false
)
{
$tree
[]
=
$nm
;
$index
=
max
(
array_keys
(
$tree
));
//print "$k -- adding <b>$nm</b>...<br \>";
}
$rels
=
$table
->
getRelations
();
// group relations
foreach
(
$rels
as
$key
=>
$rel
)
{
if
(
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
unset
(
$rels
[
$key
]);
array_unshift
(
$rels
,
$rel
);
}
}
foreach
(
$rels
as
$rel
)
{
$name
=
$rel
->
getTable
()
->
getComponentName
();
$index2
=
array_search
(
$name
,
$tree
);
$type
=
$rel
->
getType
();
// skip self-referenced relations
if
(
$name
===
$nm
)
continue
;
if
(
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
if
(
$index2
!==
false
)
{
if
(
$index2
>=
$index
)
continue
;
unset
(
$tree
[
$index
]);
array_splice
(
$tree
,
$index2
,
0
,
$nm
);
$index
=
$index2
;
//print "$k -- pushing $nm into $index2...<br \>";
}
else
{
$tree
[]
=
$name
;
//print "$k -- adding $nm :$name...<br>";
}
}
elseif
(
$rel
instanceof
Doctrine_Relation_LocalKey
)
{
if
(
$index2
!==
false
)
{
if
(
$index2
<=
$index
)
continue
;
unset
(
$tree
[
$index2
]);
array_splice
(
$tree
,
$index
,
0
,
$name
);
//print "$k -- pushing $name into <b>$index</b>...<br \>";
}
else
{
//array_splice($tree, $index, 0, $name);
array_unshift
(
$tree
,
$name
);
$index
++
;
//print "$k -- pushing <b>$name</b> into 0...<br \>";
}
}
elseif
(
$rel
instanceof
Doctrine_Relation_Association
)
{
$t
=
$rel
->
getAssociationFactory
();
$n
=
$t
->
getComponentName
();
if
(
$index2
!==
false
)
unset
(
$tree
[
$index2
]);
array_splice
(
$tree
,
$index
,
0
,
$name
);
$index
++
;
$index3
=
array_search
(
$n
,
$tree
);
if
(
$index3
!==
false
)
{
if
(
$index3
>=
$index
)
continue
;
unset
(
$tree
[
$index
]);
array_splice
(
$tree
,
$index3
,
0
,
$n
);
$index
=
$index2
;
//print "$k -- pushing $nm into $index3...<br \>";
}
else
{
$tree
[]
=
$n
;
//print "$k -- adding $nm :$name...<br>";
}
}
//print_r($tree);
}
//print_r($tree);
}
return
array_values
(
$tree
);
}
/**
* flush
* saves all the records from all tables
* this operation is isolated using a transaction
*
* @return void
*/
public
function
flush
()
{
$this
->
beginTransaction
();
$this
->
saveAll
();
$this
->
commit
();
}
/**
* saveAll
* saves all the records from all tables
*
* @return void
*/
private
function
saveAll
()
{
$tree
=
$this
->
buildFlushTree
(
$this
->
tables
);
foreach
(
$tree
as
$name
)
{
$table
=
$this
->
tables
[
$name
];
foreach
(
$table
->
getRepository
()
as
$record
)
{
$this
->
save
(
$record
);
}
}
foreach
(
$tree
as
$name
)
{
$table
=
$this
->
tables
[
$name
];
foreach
(
$table
->
getRepository
()
as
$record
)
{
$record
->
saveAssociations
();
}
}
}
/**
* clear
* clears all repositories
*
* @return void
*/
public
function
clear
()
{
foreach
(
$this
->
tables
as
$k
=>
$table
)
{
$table
->
getRepository
()
->
evictAll
();
$table
->
clear
();
}
}
/**
* @return void
*/
public
function
evictTables
()
{
$this
->
tables
=
array
();
}
/**
* close
* closes the session
*
* @return void
*/
public
function
close
()
{
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreClose
(
$this
);
$this
->
clear
();
$this
->
state
=
Doctrine_Session
::
STATE_CLOSED
;
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onClose
(
$this
);
}
/**
* get the current transaction nesting level
*
* @return integer
*/
public
function
getTransactionLevel
()
{
return
$this
->
transaction_level
;
}
/**
* beginTransaction
* starts a new transaction
* @return void
*/
public
function
beginTransaction
()
{
if
(
$this
->
transaction_level
==
0
)
{
if
(
$this
->
getAttribute
(
Doctrine
::
ATTR_LOCKMODE
)
==
Doctrine
::
LOCK_PESSIMISTIC
)
{
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreTransactionBegin
(
$this
);
$this
->
dbh
->
beginTransaction
();
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onTransactionBegin
(
$this
);
}
$this
->
state
=
Doctrine_Session
::
STATE_ACTIVE
;
}
else
{
$this
->
state
=
Doctrine_Session
::
STATE_BUSY
;
}
$this
->
transaction_level
++
;
}
/**
* commits the current transaction
* if lockmode is optimistic this method starts a transaction
* and commits it instantly
*
* @return void
*/
public
function
commit
()
{
$this
->
transaction_level
--
;
if
(
$this
->
transaction_level
==
0
)
{
if
(
$this
->
getAttribute
(
Doctrine
::
ATTR_LOCKMODE
)
==
Doctrine
::
LOCK_OPTIMISTIC
)
{
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreTransactionBegin
(
$this
);
$this
->
dbh
->
beginTransaction
();
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onTransactionBegin
(
$this
);
}
if
(
$this
->
getAttribute
(
Doctrine
::
ATTR_VLD
))
$this
->
validator
=
new
Doctrine_Validator
();
try
{
$this
->
bulkInsert
();
$this
->
bulkUpdate
();
$this
->
bulkDelete
();
if
(
$this
->
getAttribute
(
Doctrine
::
ATTR_VLD
))
{
if
(
$this
->
validator
->
hasErrors
())
{
$this
->
rollback
();
throw
new
Doctrine_Validator_Exception
(
$this
->
validator
);
}
}
$this
->
dbh
->
commit
();
}
catch
(
PDOException
$e
)
{
$this
->
rollback
();
throw
new
Doctrine_Exception
(
$e
->
__toString
());
}
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onTransactionCommit
(
$this
);
$this
->
delete
=
array
();
$this
->
state
=
Doctrine_Session
::
STATE_OPEN
;
$this
->
validator
=
null
;
}
elseif
(
$this
->
transaction_level
==
1
)
$this
->
state
=
Doctrine_Session
::
STATE_ACTIVE
;
}
/**
* rollback
* rolls back all transactions
*
* this method also listens to onPreTransactionRollback and onTransactionRollback
* eventlisteners
*
* @return void
*/
public
function
rollback
()
{
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreTransactionRollback
(
$this
);
$this
->
transaction_level
=
0
;
$this
->
dbh
->
rollback
();
$this
->
state
=
Doctrine_Session
::
STATE_OPEN
;
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onTransactionRollback
(
$this
);
}
/**
* bulkInsert
* inserts all the objects in the pending insert list into database
* @return void
*/
public
function
bulkInsert
()
{
if
(
empty
(
$this
->
insert
))
return
false
;
foreach
(
$this
->
insert
as
$name
=>
$inserts
)
{
if
(
!
isset
(
$inserts
[
0
]))
continue
;
$record
=
$inserts
[
0
];
$table
=
$record
->
getTable
();
$seq
=
$table
->
getSequenceName
();
$increment
=
false
;
$keys
=
$table
->
getPrimaryKeys
();
$id
=
null
;
if
(
count
(
$keys
)
==
1
&&
$keys
[
0
]
==
$table
->
getIdentifier
())
{
$increment
=
true
;
}
foreach
(
$inserts
as
$k
=>
$record
)
{
$table
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreSave
(
$record
);
// listen the onPreInsert event
$table
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreInsert
(
$record
);
$this
->
insert
(
$record
);
if
(
$increment
)
{
if
(
$k
==
0
)
{
// record uses auto_increment column
$id
=
$this
->
dbh
->
lastInsertID
();
if
(
!
$id
)
$id
=
$table
->
getMaxIdentifier
();
}
$record
->
assignIdentifier
(
$id
);
$id
++
;
}
else
$record
->
assignIdentifier
(
true
);
// listen the onInsert event
$table
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onInsert
(
$record
);
$table
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onSave
(
$record
);
}
}
$this
->
insert
=
array
();
return
true
;
}
/**
* returns maximum identifier values
*
* @param array $names an array of component names
* @return array
*/
public
function
getMaximumValues
(
array
$names
)
{
$values
=
array
();
foreach
(
$names
as
$name
)
{
$table
=
$this
->
tables
[
$name
];
$keys
=
$table
->
getPrimaryKeys
();
$tablename
=
$table
->
getTableName
();
if
(
count
(
$keys
)
==
1
&&
$keys
[
0
]
==
$table
->
getIdentifier
())
{
// record uses auto_increment column
$sql
=
"SELECT MAX("
.
$table
->
getIdentifier
()
.
") FROM "
.
$tablename
;
$stmt
=
$this
->
dbh
->
query
(
$sql
);
$data
=
$stmt
->
fetch
(
PDO
::
FETCH_NUM
);
$values
[
$tablename
]
=
$data
[
0
];
$stmt
->
closeCursor
();
}
}
return
$values
;
}
/**
* bulkUpdate
* updates all objects in the pending update list
*
* @return void
*/
public
function
bulkUpdate
()
{
foreach
(
$this
->
update
as
$name
=>
$updates
)
{
$ids
=
array
();
foreach
(
$updates
as
$k
=>
$record
)
{
$record
->
getTable
()
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreSave
(
$record
);
// listen the onPreUpdate event
$record
->
getTable
()
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onPreUpdate
(
$record
);
$this
->
update
(
$record
);
// listen the onUpdate event
$record
->
getTable
()
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onUpdate
(
$record
);
$record
->
getTable
()
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
onSave
(
$record
);
}
}
$this
->
update
=
array
();
}
/**
* bulkDelete
* deletes all records from the pending delete list
*
* @return void
*/
public
function
bulkDelete
()
{
foreach
(
$this
->
delete
as
$name
=>
$deletes
)
{
$record
=
false
;
$ids
=
array
();
foreach
(
$deletes
as
$k
=>
$record
)
{
$ids
[]
=
$record
->
getIncremented
();
$record
->
assignIdentifier
(
false
);
}
if
(
$record
instanceof
Doctrine_Record
)
{
$table
=
$record
->
getTable
();
$params
=
substr
(
str_repeat
(
"?, "
,
count
(
$ids
)),
0
,
-
2
);
$query
=
"DELETE FROM "
.
$record
->
getTable
()
->
getTableName
()
.
" WHERE "
.
$table
->
getIdentifier
()
.
" IN("
.
$params
.
")"
;
$this
->
execute
(
$query
,
$ids
);
$record
->
getTable
()
->
getCache
()
->
deleteMultiple
(
$ids
);
}
}
$this
->
delete
=
array
();
}
/**
* saves a collection
*
* @param Doctrine_Collection $coll
* @return void
*/
public
function
saveCollection
(
Doctrine_Collection
$coll
)
{
$this
->
beginTransaction
();
foreach
(
$coll
as
$key
=>
$record
)
:
$record
->
save
();
endforeach
;
$this
->
commit
();
}
/**
* deletes all records from collection
*
* @param Doctrine_Collection $coll
* @return void
*/
public
function
deleteCollection
(
Doctrine_Collection
$coll
)
{
$this
->
beginTransaction
();
foreach
(
$coll
as
$k
=>
$record
)
{
$record
->
delete
();
}
$this
->
commit
();
}
/**
* saves the given record
*
* @param Doctrine_Record $record
* @return void
*/
public
function
save
(
Doctrine_Record
$record
)
{
switch
(
$record
->
getState
())
:
case
Doctrine_Record
::
STATE_TDIRTY
:
$this
->
addInsert
(
$record
);
break
;
case
Doctrine_Record
::
STATE_DIRTY
:
case
Doctrine_Record
::
STATE_PROXY
:
$this
->
addUpdate
(
$record
);
break
;
case
Doctrine_Record
::
STATE_CLEAN
:
case
Doctrine_Record
::
STATE_TCLEAN
:
// do nothing
break
;
endswitch
;
}
/**
* saves all related records to $record
*
* @param Doctrine_Record $record
*/
final
public
function
saveRelated
(
Doctrine_Record
$record
)
{
$saveLater
=
array
();
foreach
(
$record
->
getReferences
()
as
$k
=>
$v
)
{
$fk
=
$record
->
getTable
()
->
getRelation
(
$k
);
if
(
$fk
instanceof
Doctrine_Relation_ForeignKey
||
$fk
instanceof
Doctrine_Relation_LocalKey
)
{
switch
(
$fk
->
getType
())
:
case
Doctrine_Relation
::
ONE_COMPOSITE
:
case
Doctrine_Relation
::
MANY_COMPOSITE
:
$local
=
$fk
->
getLocal
();
$foreign
=
$fk
->
getForeign
();
if
(
$record
->
getTable
()
->
hasPrimaryKey
(
$fk
->
getLocal
()))
{
switch
(
$record
->
getState
())
:
case
Doctrine_Record
::
STATE_TDIRTY
:
case
Doctrine_Record
::
STATE_TCLEAN
:
$saveLater
[
$k
]
=
$fk
;
break
;
case
Doctrine_Record
::
STATE_CLEAN
:
case
Doctrine_Record
::
STATE_DIRTY
:
$v
->
save
();
break
;
endswitch
;
}
else
{
// ONE-TO-ONE relationship
$obj
=
$record
->
get
(
$fk
->
getTable
()
->
getComponentName
());
if
(
$obj
->
getState
()
!=
Doctrine_Record
::
STATE_TCLEAN
)
$obj
->
save
();
}
break
;
endswitch
;
}
elseif
(
$fk
instanceof
Doctrine_Relation_Association
)
{
$v
->
save
();
}
}
return
$saveLater
;
}
/**
* updates the given record
*
* @param Doctrine_Record $record
* @return boolean
*/
private
function
update
(
Doctrine_Record
$record
)
{
$array
=
$record
->
getPrepared
();
if
(
empty
(
$array
))
return
false
;
$set
=
array
();
foreach
(
$array
as
$name
=>
$value
)
:
$set
[]
=
$name
.
" = ?"
;
if
(
$value
instanceof
Doctrine_Record
)
{
switch
(
$value
->
getState
())
:
case
Doctrine_Record
::
STATE_TCLEAN
:
case
Doctrine_Record
::
STATE_TDIRTY
:
$record
->
save
();
default
:
$array
[
$name
]
=
$value
->
getIncremented
();
$record
->
set
(
$name
,
$value
->
getIncremented
());
endswitch
;
}
endforeach
;
if
(
isset
(
$this
->
validator
))
{
if
(
!
$this
->
validator
->
validateRecord
(
$record
))
{
return
false
;
}
}
$params
=
array_values
(
$array
);
$id
=
$record
->
obtainIdentifier
();
if
(
!
is_array
(
$id
))
$id
=
array
(
$id
);
$id
=
array_values
(
$id
);
$params
=
array_merge
(
$params
,
$id
);
$sql
=
"UPDATE "
.
$record
->
getTable
()
->
getTableName
()
.
" SET "
.
implode
(
", "
,
$set
)
.
" WHERE "
.
implode
(
" = ? AND "
,
$record
->
getTable
()
->
getPrimaryKeys
())
.
" = ?"
;
$stmt
=
$this
->
dbh
->
prepare
(
$sql
);
$stmt
->
execute
(
$params
);
$record
->
assignIdentifier
(
true
);
return
true
;
}
/**
* inserts a record into database
*
* @param Doctrine_Record $record
* @return boolean
*/
private
function
insert
(
Doctrine_Record
$record
)
{
$array
=
$record
->
getPrepared
();
if
(
empty
(
$array
))
return
false
;
$seq
=
$record
->
getTable
()
->
getSequenceName
();
if
(
!
empty
(
$seq
))
{
$id
=
$this
->
getNextID
(
$seq
);
$name
=
$record
->
getTable
()
->
getIdentifier
();
$array
[
$name
]
=
$id
;
}
if
(
isset
(
$this
->
validator
))
{
if
(
!
$this
->
validator
->
validateRecord
(
$record
))
{
return
false
;
}
}
$strfields
=
join
(
", "
,
array_keys
(
$array
));
$strvalues
=
substr
(
str_repeat
(
"?, "
,
count
(
$array
)),
0
,
-
2
);
$sql
=
"INSERT INTO "
.
$record
->
getTable
()
->
getTableName
()
.
" ("
.
$strfields
.
") VALUES ("
.
$strvalues
.
")"
;
$stmt
=
$this
->
dbh
->
prepare
(
$sql
);
$stmt
->
execute
(
array_values
(
$array
));
return
true
;
}
/**
* deletes all related composites
* this method is always called internally when a record is deleted
*
* @return void
*/
final
public
function
deleteComposites
(
Doctrine_Record
$record
)
{
foreach
(
$record
->
getTable
()
->
getRelations
()
as
$fk
)
{
switch
(
$fk
->
getType
())
:
case
Doctrine_Relation
::
ONE_COMPOSITE
:
case
Doctrine_Relation
::
MANY_COMPOSITE
:
$obj
=
$record
->
get
(
$record
->
getTable
()
->
getAlias
(
$fk
->
getTable
()
->
getComponentName
()));
$obj
->
delete
();
break
;
endswitch
;
}
}
/**
* deletes this data access object and all the related composites
* this operation is isolated by a transaction
*
* this event can be listened by the onPreDelete and onDelete listeners
*
* @return boolean true on success, false on failure
*/
final
public
function
delete
(
Doctrine_Record
$record
)
{
switch
(
$record
->
getState
())
:
case
Doctrine_Record
::
STATE_PROXY
:
case
Doctrine_Record
::
STATE_CLEAN
:
case
Doctrine_Record
::
STATE_DIRTY
:
$this
->
beginTransaction
();
$this
->
deleteComposites
(
$record
);
$this
->
addDelete
(
$record
);
$this
->
commit
();
return
true
;
break
;
default
:
return
false
;
endswitch
;
}
/**
* adds record into pending insert list
* @param Doctrine_Record $record
*/
public
function
addInsert
(
Doctrine_Record
$record
)
{
$name
=
$record
->
getTable
()
->
getComponentName
();
$this
->
insert
[
$name
][]
=
$record
;
}
/**
* adds record into penging update list
* @param Doctrine_Record $record
*/
public
function
addUpdate
(
Doctrine_Record
$record
)
{
$name
=
$record
->
getTable
()
->
getComponentName
();
$this
->
update
[
$name
][]
=
$record
;
}
/**
* adds record into pending delete list
* @param Doctrine_Record $record
*/
public
function
addDelete
(
Doctrine_Record
$record
)
{
$name
=
$record
->
getTable
()
->
getComponentName
();
$this
->
delete
[
$name
][]
=
$record
;
}
/**
* returns the pending insert list
*
* @return array
*/
public
function
getInserts
()
{
return
$this
->
insert
;
}
/**
* returns the pending update list
*
* @return array
*/
public
function
getUpdates
()
{
return
$this
->
update
;
}
/**
* returns the pending delete list
*
* @return array
*/
public
function
getDeletes
()
{
return
$this
->
delete
;
}
/**
* returns a string representation of this object
* @return string
*/
public
function
__toString
()
{
return
Doctrine_Lib
::
getSessionAsString
(
$this
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment