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
6e5f2278
Commit
6e5f2278
authored
May 15, 2011
by
Benjamin Eberlei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DBAL-12 - Fully test SQL QueryBuilder
parent
c393e108
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
336 additions
and
17 deletions
+336
-17
Connection.php
lib/Doctrine/DBAL/Connection.php
+0
-1
CompositeExpression.php
lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php
+2
-2
ExpressionBuilder.php
lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php
+1
-2
QueryBuilder.php
lib/Doctrine/DBAL/Query/QueryBuilder.php
+116
-12
QueryBuilderTest.php
tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php
+217
-0
No files found.
lib/Doctrine/DBAL/Connection.php
View file @
6e5f2278
...
...
@@ -33,7 +33,6 @@ use PDO, Closure, Exception,
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
...
...
lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php
View file @
6e5f2278
...
...
@@ -24,7 +24,7 @@ namespace Doctrine\DBAL\Query\Expression;
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.
0
* @since 2.
1
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
...
...
@@ -111,7 +111,7 @@ class CompositeExpression implements \Countable
*/
public
function
__toString
()
{
if
(
$this
->
count
(
)
===
1
)
{
if
(
count
(
$this
->
parts
)
===
1
)
{
return
(
string
)
$this
->
parts
[
0
];
}
...
...
lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php
View file @
6e5f2278
...
...
@@ -26,8 +26,7 @@ use Doctrine\DBAL\Connection;
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @since 2.1
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
...
...
lib/Doctrine/DBAL/Query/QueryBuilder.php
View file @
6e5f2278
...
...
@@ -24,11 +24,17 @@ use Doctrine\DBAL\Query\Expression\CompositeExpression,
/**
* QueryBuilder class is responsible to dynamically create SQL queries.
*
* Important: Verify that every feature you use will work with your database vendor.
* SQL Query Builder does not attempt to validate the generated SQL at all.
*
* The query builder does no validation whatsoever if certain features even work with the
* underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements
* even if some vendors such as MySQL support it.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @since 2.1
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
...
...
@@ -96,6 +102,13 @@ class QueryBuilder
* @var integer The maximum number of results to retrieve.
*/
private
$maxResults
=
null
;
/**
* The counter of bound parameters used with {@see bindValue)
*
* @var int
*/
private
$boundCounter
=
0
;
/**
* Initializes a new <tt>QueryBuilder</tt>.
...
...
@@ -157,6 +170,23 @@ class QueryBuilder
{
return
$this
->
state
;
}
/**
* Execute this query using the bound parameters and their types.
*
* Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
* for insert, update and delete statements.
*
* @return mixed
*/
public
function
execute
()
{
if
(
$this
->
type
==
self
::
SELECT
)
{
return
$this
->
connection
->
executeQuery
(
$this
->
getSQL
(),
$this
->
params
,
$this
->
paramTypes
);
}
else
{
return
$this
->
connection
->
executeUpdate
(
$this
->
getSQL
(),
$this
->
params
,
$this
->
paramTypes
);
}
}
/**
* Get the complete SQL string formed by the current specifications of this QueryBuilder.
...
...
@@ -280,6 +310,7 @@ class QueryBuilder
*/
public
function
setFirstResult
(
$firstResult
)
{
$this
->
state
=
self
::
STATE_DIRTY
;
$this
->
firstResult
=
$firstResult
;
return
$this
;
}
...
...
@@ -303,6 +334,7 @@ class QueryBuilder
*/
public
function
setMaxResults
(
$maxResults
)
{
$this
->
state
=
self
::
STATE_DIRTY
;
$this
->
maxResults
=
$maxResults
;
return
$this
;
}
...
...
@@ -331,20 +363,22 @@ class QueryBuilder
*/
public
function
add
(
$sqlPartName
,
$sqlPart
,
$append
=
false
)
{
$isArray
=
is_array
(
$sqlPart
);
$isMultiple
=
is_array
(
$this
->
sqlParts
[
$sqlPartName
]);
if
(
$isMultiple
&&
!
is_array
(
$sqlPart
)
)
{
if
(
$isMultiple
&&
!
$isArray
)
{
$sqlPart
=
array
(
$sqlPart
);
}
$this
->
state
=
self
::
STATE_DIRTY
;
if
(
$append
)
{
$key
=
key
(
$sqlPart
);
if
(
$sqlPartName
==
'where'
||
$sqlPartName
==
'having'
)
{
$this
->
sqlParts
[
$sqlPartName
]
=
$sqlPart
;
}
else
if
(
is_array
(
$sqlPart
[
$key
]))
{
if
(
$sqlPartName
==
"orderBy"
||
$sqlPartName
==
"groupBy"
||
$sqlPartName
==
"select"
||
$sqlPartName
==
"set"
)
{
foreach
(
$sqlPart
AS
$part
)
{
$this
->
sqlParts
[
$sqlPartName
][]
=
$part
;
}
}
else
if
(
$isArray
&&
is_array
(
$sqlPart
[
key
(
$sqlPart
)]))
{
$key
=
key
(
$sqlPart
);
$this
->
sqlParts
[
$sqlPartName
][
$key
][]
=
$sqlPart
[
$key
];
}
else
if
(
$isMultiple
)
{
$this
->
sqlParts
[
$sqlPartName
][]
=
$sqlPart
;
...
...
@@ -615,7 +649,7 @@ class QueryBuilder
*/
public
function
set
(
$key
,
$value
)
{
return
$this
->
add
(
'set'
,
$
this
->
expr
()
->
eq
(
$key
,
'='
,
$value
)
,
true
);
return
$this
->
add
(
'set'
,
$
key
.
' = '
.
$value
,
true
);
}
/**
...
...
@@ -938,7 +972,7 @@ class QueryBuilder
.
(
$this
->
sqlParts
[
'having'
]
!==
null
?
' HAVING '
.
((
string
)
$this
->
sqlParts
[
'having'
])
:
''
)
.
(
$this
->
sqlParts
[
'orderBy'
]
?
' ORDER BY '
.
implode
(
', '
,
$this
->
sqlParts
[
'orderBy'
])
:
''
);
return
(
$this
->
maxResults
===
null
)
return
(
$this
->
maxResults
===
null
&&
$this
->
firstResult
==
null
)
?
$query
:
$this
->
connection
->
getDatabasePlatform
()
->
modifyLimitQuery
(
$query
,
$this
->
maxResults
,
$this
->
firstResult
);
}
...
...
@@ -950,7 +984,10 @@ class QueryBuilder
*/
private
function
getSQLForUpdate
()
{
$query
=
'UPDATE '
;
$table
=
$this
->
sqlParts
[
'from'
][
'table'
]
.
(
$this
->
sqlParts
[
'from'
][
'alias'
]
?
' '
.
$this
->
sqlParts
[
'from'
][
'alias'
]
:
''
);
$query
=
'UPDATE '
.
$table
.
' SET '
.
implode
(
", "
,
$this
->
sqlParts
[
'set'
])
.
(
$this
->
sqlParts
[
'where'
]
!==
null
?
' WHERE '
.
((
string
)
$this
->
sqlParts
[
'where'
])
:
''
);
return
$query
;
}
...
...
@@ -962,7 +999,8 @@ class QueryBuilder
*/
private
function
getSQLForDelete
()
{
$query
=
'DELETE '
;
$table
=
$this
->
sqlParts
[
'from'
][
'table'
]
.
(
$this
->
sqlParts
[
'from'
][
'alias'
]
?
' '
.
$this
->
sqlParts
[
'from'
][
'alias'
]
:
''
);
$query
=
'DELETE FROM '
.
$table
.
(
$this
->
sqlParts
[
'where'
]
!==
null
?
' WHERE '
.
((
string
)
$this
->
sqlParts
[
'where'
])
:
''
);
return
$query
;
}
...
...
@@ -977,4 +1015,70 @@ class QueryBuilder
{
return
$this
->
getSQL
();
}
/**
* Create a new named parameter and bind the value $value to it.
*
* This method provides a shortcut for PDOStatement::bindValue
* when using prepared statements.
*
* The parameter $value specifies the value that you want to bind. If
* $placeholder is not provided bindValue() will automatically create a
* placeholder for you. An automatic placeholder will be of the name
* ':dcValue1', ':dcValue2' etc.
*
* For more information see {@link http://php.net/pdostatement-bindparam}
*
* Example:
* <code>
* $value = 2;
* $q->eq( 'id', $q->bindValue( $value ) );
* $stmt = $q->executeQuery(); // executed with 'id = 2'
* </code>
*
* @license New BSD License
* @link http://www.zetacomponents.org
* @param mixed $value
* @param mixed $type
* @param string $placeHolder the name to bind with. The string must start with a colon ':'.
* @return string the placeholder name used.
*/
public
function
createNamedParameter
(
$value
,
$type
=
\PDO
::
PARAM_STR
,
$placeHolder
=
null
)
{
if
(
$placeHolder
===
null
)
{
$this
->
boundCounter
++
;
$placeHolder
=
":dcValue"
.
$this
->
boundCounter
;
}
$this
->
setParameter
(
substr
(
$placeHolder
,
1
),
$value
,
$type
);
return
$placeHolder
;
}
/**
* Create a new positional parameter and bind the given value to it.
*
* Attention: If you are using positional parameters with the query builder you have
* to be very careful to bind all parameters in the order they appear in the SQL
* statement , otherwise they get bound in the wrong order which can lead to serious
* bugs in your code.
*
* Example:
* <code>
* $qb = $conn->createQueryBuilder();
* $qb->select('u.*')
* ->from('users', 'u')
* ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR))
* ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR))
* </code>
*
* @param mixed $value
* @param mixed $type
* @return string
*/
public
function
createPositionalParameter
(
$value
,
$type
=
\PDO
::
PARAM_STR
)
{
$this
->
boundCounter
++
;
$this
->
setParameter
(
$this
->
boundCounter
,
$value
,
$type
);
return
"?"
;
}
}
\ No newline at end of file
tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php
View file @
6e5f2278
...
...
@@ -331,4 +331,221 @@ class QueryBuilderTest extends \Doctrine\Tests\DbalTestCase
$this
->
assertEquals
(
'SELECT u.*, p.* FROM users u ORDER BY u.name ASC, u.username DESC'
,
(
string
)
$qb
);
}
public
function
testEmptySelect
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb2
=
$qb
->
select
();
$this
->
assertSame
(
$qb
,
$qb2
);
$this
->
assertEquals
(
QueryBuilder
::
SELECT
,
$qb
->
getType
());
}
public
function
testSelectAddSelect
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$expr
=
$qb
->
expr
();
$qb
->
select
(
'u.*'
)
->
addSelect
(
'p.*'
)
->
from
(
'users'
,
'u'
);
$this
->
assertEquals
(
'SELECT u.*, p.* FROM users u'
,
(
string
)
$qb
);
}
public
function
testEmptyAddSelect
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb2
=
$qb
->
addSelect
();
$this
->
assertSame
(
$qb
,
$qb2
);
$this
->
assertEquals
(
QueryBuilder
::
SELECT
,
$qb
->
getType
());
}
public
function
testSelectMultipleFrom
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$expr
=
$qb
->
expr
();
$qb
->
select
(
'u.*'
)
->
addSelect
(
'p.*'
)
->
from
(
'users'
,
'u'
)
->
from
(
'phonenumbers'
,
'p'
);
$this
->
assertEquals
(
'SELECT u.*, p.* FROM users u, phonenumbers p'
,
(
string
)
$qb
);
}
public
function
testUpdate
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
update
(
'users'
,
'u'
)
->
set
(
'u.foo'
,
'?'
)
->
set
(
'u.bar'
,
'?'
);
$this
->
assertEquals
(
QueryBuilder
::
UPDATE
,
$qb
->
getType
());
$this
->
assertEquals
(
'UPDATE users u SET u.foo = ?, u.bar = ?'
,
(
string
)
$qb
);
}
public
function
testUpdateWithoutAlias
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
update
(
'users'
)
->
set
(
'foo'
,
'?'
)
->
set
(
'bar'
,
'?'
);
$this
->
assertEquals
(
'UPDATE users SET foo = ?, bar = ?'
,
(
string
)
$qb
);
}
public
function
testUpdateWhere
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
update
(
'users'
,
'u'
)
->
set
(
'u.foo'
,
'?'
)
->
where
(
'u.foo = ?'
);
$this
->
assertEquals
(
'UPDATE users u SET u.foo = ? WHERE u.foo = ?'
,
(
string
)
$qb
);
}
public
function
testEmptyUpdate
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb2
=
$qb
->
update
();
$this
->
assertEquals
(
QueryBuilder
::
UPDATE
,
$qb
->
getType
());
$this
->
assertSame
(
$qb2
,
$qb
);
}
public
function
testDelete
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
delete
(
'users'
,
'u'
);
$this
->
assertEquals
(
QueryBuilder
::
DELETE
,
$qb
->
getType
());
$this
->
assertEquals
(
'DELETE FROM users u'
,
(
string
)
$qb
);
}
public
function
testDeleteWithoutAlias
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
delete
(
'users'
);
$this
->
assertEquals
(
QueryBuilder
::
DELETE
,
$qb
->
getType
());
$this
->
assertEquals
(
'DELETE FROM users'
,
(
string
)
$qb
);
}
public
function
testDeleteWhere
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
delete
(
'users'
,
'u'
)
->
where
(
'u.foo = ?'
);
$this
->
assertEquals
(
'DELETE FROM users u WHERE u.foo = ?'
,
(
string
)
$qb
);
}
public
function
testEmptyDelete
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb2
=
$qb
->
delete
();
$this
->
assertEquals
(
QueryBuilder
::
DELETE
,
$qb
->
getType
());
$this
->
assertSame
(
$qb2
,
$qb
);
}
public
function
testGetConnection
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$this
->
assertSame
(
$this
->
conn
,
$qb
->
getConnection
());
}
public
function
testGetState
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$this
->
assertEquals
(
QueryBuilder
::
STATE_CLEAN
,
$qb
->
getState
());
$qb
->
select
(
'u.*'
)
->
from
(
'users'
,
'u'
);
$this
->
assertEquals
(
QueryBuilder
::
STATE_DIRTY
,
$qb
->
getState
());
$sql1
=
$qb
->
getSQL
();
$this
->
assertEquals
(
QueryBuilder
::
STATE_CLEAN
,
$qb
->
getState
());
$this
->
assertEquals
(
$sql1
,
$qb
->
getSQL
());
}
public
function
testSetMaxResults
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
setMaxResults
(
10
);
$this
->
assertEquals
(
QueryBuilder
::
STATE_DIRTY
,
$qb
->
getState
());
$this
->
assertEQuals
(
10
,
$qb
->
getMaxResults
());
}
public
function
testSetFirstResult
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
setFirstResult
(
10
);
$this
->
assertEquals
(
QueryBuilder
::
STATE_DIRTY
,
$qb
->
getState
());
$this
->
assertEQuals
(
10
,
$qb
->
getFirstResult
());
}
public
function
testResetQueryPart
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
select
(
'u.*'
)
->
from
(
'users'
,
'u'
)
->
where
(
'u.name = ?'
);
$this
->
assertEquals
(
'SELECT u.* FROM users u WHERE u.name = ?'
,
(
string
)
$qb
);
$qb
->
resetQueryPart
(
'where'
);
$this
->
assertEquals
(
'SELECT u.* FROM users u'
,
(
string
)
$qb
);
}
public
function
testResetQueryParts
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
select
(
'u.*'
)
->
from
(
'users'
,
'u'
)
->
where
(
'u.name = ?'
)
->
orderBy
(
'u.name'
);
$this
->
assertEquals
(
'SELECT u.* FROM users u WHERE u.name = ? ORDER BY u.name ASC'
,
(
string
)
$qb
);
$qb
->
resetQueryParts
(
array
(
'where'
,
'orderBy'
));
$this
->
assertEquals
(
'SELECT u.* FROM users u'
,
(
string
)
$qb
);
}
public
function
testCreateNamedParameter
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
select
(
'u.*'
)
->
from
(
'users'
,
'u'
)
->
where
(
$qb
->
expr
()
->
eq
(
'u.name'
,
$qb
->
createNamedParameter
(
10
,
\PDO
::
PARAM_INT
))
);
$this
->
assertEquals
(
'SELECT u.* FROM users u WHERE u.name = :dcValue1'
,
(
string
)
$qb
);
$this
->
assertEquals
(
10
,
$qb
->
getParameter
(
'dcValue1'
));
}
public
function
testCreateNamedParameterCustomPlaceholder
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
select
(
'u.*'
)
->
from
(
'users'
,
'u'
)
->
where
(
$qb
->
expr
()
->
eq
(
'u.name'
,
$qb
->
createNamedParameter
(
10
,
\PDO
::
PARAM_INT
,
':test'
))
);
$this
->
assertEquals
(
'SELECT u.* FROM users u WHERE u.name = :test'
,
(
string
)
$qb
);
$this
->
assertEquals
(
10
,
$qb
->
getParameter
(
'test'
));
}
public
function
testCreatePositionalParameter
()
{
$qb
=
new
QueryBuilder
(
$this
->
conn
);
$qb
->
select
(
'u.*'
)
->
from
(
'users'
,
'u'
)
->
where
(
$qb
->
expr
()
->
eq
(
'u.name'
,
$qb
->
createPositionalParameter
(
10
,
\PDO
::
PARAM_INT
))
);
$this
->
assertEquals
(
'SELECT u.* FROM users u WHERE u.name = ?'
,
(
string
)
$qb
);
$this
->
assertEquals
(
10
,
$qb
->
getParameter
(
1
));
}
}
\ No newline at end of file
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