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
ce4ea888
Commit
ce4ea888
authored
May 14, 2007
by
zYne
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
--no commit message
--no commit message
parent
6c517bc1
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
131 additions
and
942 deletions
+131
-942
Query.php
draft/new-core/Query.php
+121
-942
QueryApi.php
draft/new-core/QueryApi.php
+10
-0
No files found.
draft/new-core/Query.php
View file @
ce4ea888
...
@@ -128,242 +128,7 @@ class Doctrine_Query2 extends Doctrine_Hydrate2 implements Countable
...
@@ -128,242 +128,7 @@ class Doctrine_Query2 extends Doctrine_Hydrate2 implements Countable
return
$this
->
isDistinct
;
return
$this
->
isDistinct
;
}
}
/**
* processPendingFields
* the fields in SELECT clause cannot be parsed until the components
* in FROM clause are parsed, hence this method is called everytime a
* specific component is being parsed.
*
* @throws Doctrine_Query_Exception if unknown component alias has been given
* @param string $componentAlias the alias of the component
* @return void
*/
public
function
processPendingFields
(
$componentAlias
)
{
$tableAlias
=
$this
->
getTableAlias
(
$componentAlias
);
$table
=
$this
->
_aliasMap
[
$componentAlias
][
'table'
];
if
(
isset
(
$this
->
pendingFields
[
$componentAlias
]))
{
$fields
=
$this
->
pendingFields
[
$componentAlias
];
// check for wildcards
if
(
in_array
(
'*'
,
$fields
))
{
$fields
=
$table
->
getColumnNames
();
}
else
{
// only auto-add the primary key fields if this query object is not
// a subquery of another query object
if
(
!
$this
->
isSubquery
)
{
$fields
=
array_unique
(
array_merge
(
$table
->
getPrimaryKeys
(),
$fields
));
}
}
}
foreach
(
$fields
as
$name
)
{
$name
=
$table
->
getColumnName
(
$name
);
$this
->
parts
[
'select'
][]
=
$tableAlias
.
'.'
.
$name
.
' AS '
.
$tableAlias
.
'__'
.
$name
;
}
$this
->
neededTables
[]
=
$tableAlias
;
}
/**
* parseSelect
* parses the query select part and
* adds selected fields to pendingFields array
*
* @param string $dql
*/
public
function
parseSelect
(
$dql
)
{
$refs
=
Doctrine_Query
::
bracketExplode
(
$dql
,
','
);
foreach
(
$refs
as
$reference
)
{
if
(
strpos
(
$reference
,
'('
)
!==
false
)
{
if
(
substr
(
$reference
,
0
,
1
)
===
'('
)
{
// subselect found in SELECT part
$this
->
parseSubselect
(
$reference
);
}
else
{
$this
->
parseAggregateFunction2
(
$reference
);
}
}
else
{
$e
=
explode
(
'.'
,
$reference
);
if
(
count
(
$e
)
>
2
)
{
$this
->
pendingFields
[]
=
$reference
;
}
else
{
$this
->
pendingFields
[
$e
[
0
]][]
=
$e
[
1
];
}
}
}
}
/**
* parseSubselect
*
* parses the subquery found in DQL SELECT part and adds the
* parsed form into $pendingSubqueries stack
*
* @param string $reference
* @return void
*/
public
function
parseSubselect
(
$reference
)
{
$e
=
Doctrine_Query
::
bracketExplode
(
$reference
,
' '
);
$alias
=
$e
[
1
];
if
(
count
(
$e
)
>
2
)
{
if
(
strtoupper
(
$e
[
1
])
!==
'AS'
)
{
throw
new
Doctrine_Query_Exception
(
'Syntax error near: '
.
$reference
);
}
$alias
=
$e
[
2
];
}
$subquery
=
substr
(
$e
[
0
],
1
,
-
1
);
$this
->
pendingSubqueries
[]
=
array
(
$subquery
,
$alias
);
}
public
function
parseAggregateFunction2
(
$func
)
{
$e
=
Doctrine_Query
::
bracketExplode
(
$func
,
' '
);
$func
=
$e
[
0
];
$pos
=
strpos
(
$func
,
'('
);
$name
=
substr
(
$func
,
0
,
$pos
);
try
{
$argStr
=
substr
(
$func
,
(
$pos
+
1
),
-
1
);
$args
=
explode
(
','
,
$argStr
);
$func
=
call_user_func_array
(
array
(
$this
->
conn
->
expression
,
$name
),
$args
);
if
(
substr
(
$func
,
0
,
1
)
!==
'('
)
{
$pos
=
strpos
(
$func
,
'('
);
$name
=
substr
(
$func
,
0
,
$pos
);
}
else
{
$name
=
$func
;
}
$e2
=
explode
(
' '
,
$args
[
0
]);
$distinct
=
''
;
if
(
count
(
$e2
)
>
1
)
{
if
(
strtoupper
(
$e2
[
0
])
==
'DISTINCT'
)
$distinct
=
'DISTINCT '
;
$args
[
0
]
=
$e2
[
1
];
}
$parts
=
explode
(
'.'
,
$args
[
0
]);
$owner
=
$parts
[
0
];
$alias
=
(
isset
(
$e
[
1
]))
?
$e
[
1
]
:
$name
;
$e3
=
explode
(
'.'
,
$alias
);
if
(
count
(
$e3
)
>
1
)
{
$alias
=
$e3
[
1
];
$owner
=
$e3
[
0
];
}
// a function without parameters eg. RANDOM()
if
(
$owner
===
''
)
{
$owner
=
0
;
}
$this
->
pendingAggregates
[
$owner
][]
=
array
(
$name
,
$args
,
$distinct
,
$alias
);
}
catch
(
Doctrine_Expression_Exception
$e
)
{
throw
new
Doctrine_Query_Exception
(
'Unknown function '
.
$func
.
'.'
);
}
}
public
function
processPendingSubqueries
()
{
if
(
$this
->
subqueriesProcessed
===
true
)
{
return
false
;
}
foreach
(
$this
->
pendingSubqueries
as
$value
)
{
list
(
$dql
,
$alias
)
=
$value
;
$sql
=
$this
->
createSubquery
()
->
parseQuery
(
$dql
,
false
)
->
getQuery
();
reset
(
$this
->
tableAliases
);
$tableAlias
=
current
(
$this
->
tableAliases
);
reset
(
$this
->
compAliases
);
$componentAlias
=
key
(
$this
->
compAliases
);
$sqlAlias
=
$tableAlias
.
'__'
.
count
(
$this
->
aggregateMap
);
$this
->
parts
[
'select'
][]
=
'('
.
$sql
.
') AS '
.
$sqlAlias
;
$this
->
aggregateMap
[
$alias
]
=
$sqlAlias
;
$this
->
subqueryAggregates
[
$componentAlias
][]
=
$alias
;
}
$this
->
subqueriesProcessed
=
true
;
return
true
;
}
public
function
processPendingAggregates
(
$componentAlias
)
{
$tableAlias
=
$this
->
getTableAlias
(
$componentAlias
);
if
(
!
isset
(
$this
->
tables
[
$tableAlias
]))
{
throw
new
Doctrine_Query_Exception
(
'Unknown component path '
.
$componentAlias
);
}
$root
=
current
(
$this
->
tables
);
$table
=
$this
->
tables
[
$tableAlias
];
$aggregates
=
array
();
if
(
isset
(
$this
->
pendingAggregates
[
$componentAlias
]))
{
$aggregates
=
$this
->
pendingAggregates
[
$componentAlias
];
}
if
(
$root
===
$table
)
{
if
(
isset
(
$this
->
pendingAggregates
[
0
]))
{
$aggregates
+=
$this
->
pendingAggregates
[
0
];
}
}
foreach
(
$aggregates
as
$parts
)
{
list
(
$name
,
$args
,
$distinct
,
$alias
)
=
$parts
;
$arglist
=
array
();
foreach
(
$args
as
$arg
)
{
$e
=
explode
(
'.'
,
$arg
);
if
(
is_numeric
(
$arg
))
{
$arglist
[]
=
$arg
;
}
elseif
(
count
(
$e
)
>
1
)
{
//$tableAlias = $this->getTableAlias($e[0]);
$table
=
$this
->
tables
[
$tableAlias
];
$e
[
1
]
=
$table
->
getColumnName
(
$e
[
1
]);
if
(
!
$table
->
hasColumn
(
$e
[
1
]))
{
throw
new
Doctrine_Query_Exception
(
'Unknown column '
.
$e
[
1
]);
}
$arglist
[]
=
$tableAlias
.
'.'
.
$e
[
1
];
}
else
{
$arglist
[]
=
$e
[
0
];
}
}
$sqlAlias
=
$tableAlias
.
'__'
.
count
(
$this
->
aggregateMap
);
if
(
substr
(
$name
,
0
,
1
)
!==
'('
)
{
$this
->
parts
[
'select'
][]
=
$name
.
'('
.
$distinct
.
implode
(
', '
,
$arglist
)
.
') AS '
.
$sqlAlias
;
}
else
{
$this
->
parts
[
'select'
][]
=
$name
.
' AS '
.
$sqlAlias
;
}
$this
->
aggregateMap
[
$alias
]
=
$sqlAlias
;
$this
->
neededTables
[]
=
$tableAlias
;
}
}
/**
/**
* count
* count
*
*
...
@@ -411,811 +176,225 @@ class Doctrine_Query2 extends Doctrine_Hydrate2 implements Countable
...
@@ -411,811 +176,225 @@ class Doctrine_Query2 extends Doctrine_Hydrate2 implements Countable
return
(
int
)
$this
->
getConnection
()
->
fetchOne
(
$q
,
$params
);
return
(
int
)
$this
->
getConnection
()
->
fetchOne
(
$q
,
$params
);
}
}
/**
/**
* addFrom
* @return boolean
*
* @param strint $from
* @return Doctrine_Query
*/
*/
public
function
addFrom
(
$from
)
public
function
isLimitSubqueryUsed
()
{
{
return
$this
->
limitSubqueryUsed
;
$class
=
'Doctrine_Query_From'
;
$parser
=
new
$class
(
$this
);
$parser
->
parse
(
$from
);
return
$this
;
}
}
/**
/**
* leftJoin
* query
* query the database with DQL (Doctrine Query Language)
*
*
* @param strint $join
* @param string $query DQL query
* @return Doctrine_Query
* @param array $params prepared statement parameters
* @see Doctrine::FETCH_* constants
* @return mixed
*/
*/
public
function
leftJoin
(
$join
)
public
function
query
(
$query
,
$params
=
array
()
)
{
{
$class
=
'Doctrine_Query_From'
;
$this
->
_parser
->
parseQuery
(
$query
);
$parser
=
new
$class
(
$this
);
$parser
->
parse
(
'LEFT JOIN '
.
$join
);
return
$this
;
return
$this
->
execute
(
$params
)
;
}
}
/**
/**
* innerJoin
* getShortAlias
* some database such as Oracle need the identifier lengths to be < ~30 chars
* hence Doctrine creates as short identifier aliases as possible
*
*
* @param strint $join
* this method is used for the creation of short table aliases, its also
* @return Doctrine_Query
* smart enough to check if an alias already exists for given component (componentAlias)
*
* @param string $componentAlias the alias for the query component to search table alias for
* @param string $tableName the table name from which the table alias is being created
* @return string the generated / fetched short alias
*/
*/
public
function
innerJoin
(
$join
)
public
function
getShortAlias
(
$componentAlias
,
$tableName
)
{
{
$class
=
'Doctrine_Query_From'
;
return
$this
->
aliasHandler
->
getShortAlias
(
$componentAlias
,
$tableName
);
$parser
=
new
$class
(
$this
);
$parser
->
parse
(
'INNER JOIN '
.
$join
);
return
$this
;
}
}
/**
/**
* addOrderBy
* addSelect
* adds fields to the SELECT part of the query
*
*
* @param strin
t $orderby
* @param strin
g $select DQL SELECT part
* @return Doctrine_Query
* @return Doctrine_Query
*/
*/
public
function
add
OrderBy
(
$orderby
)
public
function
add
Select
(
$select
)
{
{
if
(
empty
(
$orderby
))
{
return
$this
->
getParser
(
'select'
)
->
parse
(
$select
,
true
);
return
$this
;
}
$class
=
'Doctrine_Query_Orderby'
;
$parser
=
new
$class
(
$this
);
$this
->
parts
[
'orderby'
][]
=
$parser
->
parse
(
$orderby
);
return
$this
;
}
}
/**
/**
* addWhere
* addWhere
* adds conditions to the WHERE part of the query
*
*
* @param string $where
* @param string $where DQL WHERE part
* @param mixed $params
* @param mixed $params an array of parameters or a simple scalar
* @return Doctrine_Query
*/
*/
public
function
addWhere
(
$where
,
$params
=
array
())
public
function
addWhere
(
$where
,
$params
=
array
())
{
{
$class
=
'Doctrine_Query_Where'
;
$parser
=
new
$class
(
$this
);
$this
->
parts
[
'where'
][]
=
$parser
->
parse
(
$where
);
if
(
is_array
(
$params
))
{
if
(
is_array
(
$params
))
{
$this
->
params
=
array_merge
(
$this
->
params
,
$params
);
$this
->
params
=
array_merge
(
$this
->
params
,
$params
);
}
else
{
}
else
{
$this
->
params
[]
=
$params
;
$this
->
params
[]
=
$params
;
}
}
return
$this
->
getParser
(
'where'
)
->
parse
(
$where
,
true
);
return
$this
;
}
}
/**
/**
* addSelect
* addGroupBy
* adds fields to the GROUP BY part of the query
*
*
* @param string $select
* @param string $groupby DQL GROUP BY part
* @return Doctrine_Query
*/
*/
public
function
add
Select
(
$select
)
public
function
add
GroupBy
(
$groupby
)
{
{
$this
->
type
=
self
::
SELECT
;
return
$this
->
getParser
(
'groupby'
)
->
parse
(
$groupby
,
true
);
$this
->
parseSelect
(
$select
);
return
$this
;
}
}
/**
/**
* addHaving
* addHaving
* adds conditions to the HAVING part of the query
*
*
* @param string $having
* @param string $having DQL HAVING part
* @return Doctrine_Query
*/
*/
public
function
addHaving
(
$having
)
public
function
addHaving
(
$having
)
{
{
$class
=
'Doctrine_Query_Having'
;
return
$this
->
getParser
(
'having'
)
->
parse
(
$having
,
true
);
$parser
=
new
$class
(
$this
);
$this
->
parts
[
'having'
][]
=
$parser
->
parse
(
$having
);
return
$this
;
}
}
/**
/**
* sets a query part
* addOrderBy
* adds fields to the ORDER BY part of the query
*
*
* @param string $name
* @param string $orderby DQL ORDER BY part
* @param array $args
* @return Doctrine_Query
* @return void
*/
*/
public
function
__call
(
$name
,
$args
)
public
function
addOrderBy
(
$orderby
)
{
{
$name
=
strtolower
(
$name
);
return
$this
->
getParser
(
'orderby'
)
->
parse
(
$orderby
,
true
);
$method
=
'parse'
.
ucwords
(
$name
);
switch
(
$name
)
{
case
'select'
:
$this
->
type
=
self
::
SELECT
;
if
(
!
isset
(
$args
[
0
]))
{
throw
new
Doctrine_Query_Exception
(
'Empty select part'
);
}
$this
->
parseSelect
(
$args
[
0
]);
break
;
case
'delete'
:
$this
->
type
=
self
::
DELETE
;
break
;
case
'update'
:
$this
->
type
=
self
::
UPDATE
;
$name
=
'from'
;
case
'from'
:
$this
->
parts
[
'from'
]
=
array
();
$this
->
parts
[
'select'
]
=
array
();
$this
->
parts
[
'join'
]
=
array
();
$this
->
joins
=
array
();
$this
->
tables
=
array
();
$this
->
fetchModes
=
array
();
$this
->
tableIndexes
=
array
();
$this
->
tableAliases
=
array
();
$this
->
aliasHandler
->
clear
();
$class
=
"Doctrine_Query_"
.
ucwords
(
$name
);
$parser
=
new
$class
(
$this
);
$parser
->
parse
(
$args
[
0
]);
break
;
case
'where'
:
if
(
isset
(
$args
[
1
]))
{
if
(
is_array
(
$args
[
1
]))
{
$this
->
params
=
$args
[
1
];
}
else
{
$this
->
params
=
array
(
$args
[
1
]);
}
}
case
'having'
:
case
'orderby'
:
case
'groupby'
:
if
(
empty
(
$args
[
0
]))
{
return
$this
;
}
$class
=
'Doctrine_Query_'
.
ucwords
(
$name
);
$parser
=
new
$class
(
$this
);
$this
->
parts
[
$name
]
=
array
(
$parser
->
parse
(
$args
[
0
]));
break
;
case
'limit'
:
case
'offset'
:
if
(
$args
[
0
]
==
null
)
{
$args
[
0
]
=
false
;
}
$this
->
parts
[
$name
]
=
$args
[
0
];
break
;
default
:
$this
->
parts
[
$name
]
=
array
();
if
(
method_exists
(
$this
,
$method
))
{
$this
->
$method
(
$args
[
0
]);
}
throw
new
Doctrine_Query_Exception
(
"Unknown overload method"
);
}
return
$this
;
}
/**
* @return boolean
*/
public
function
isLimitSubqueryUsed
()
{
return
$this
->
limitSubqueryUsed
;
}
}
/**
/**
* getQueryBase
* select
* returns the base of the generated sql query
* sets the SELECT part of the query
* On mysql driver special strategy has to be used for DELETE statements
*
*
* @return string the base of the generated sql query
* @param string $select DQL SELECT part
* @return Doctrine_Query
*/
*/
public
function
getQueryBase
(
)
public
function
select
(
$select
)
{
{
switch
(
$this
->
type
)
{
return
$this
->
getParser
(
'from'
)
->
parse
(
$select
);
case
self
::
DELETE
:
$q
=
'DELETE FROM '
;
break
;
case
self
::
UPDATE
:
$q
=
'UPDATE '
;
break
;
case
self
::
SELECT
:
$distinct
=
(
$this
->
isDistinct
())
?
'DISTINCT '
:
''
;
$q
=
'SELECT '
.
$distinct
.
implode
(
', '
,
$this
->
parts
[
'select'
])
.
' FROM '
;
break
;
}
return
$q
;
}
}
/**
/**
*
builds the sql query from the given parameters and applies things such as
*
from
*
column aggregation inheritance and limit subqueries if needed
*
sets the FROM part of the query
*
*
* @param array $params an array of prepared statement params (needed only in mysql driver
* @param string $from DQL FROM part
* when limit subquery algorithm is used)
* @return Doctrine_Query
* @return string the built sql query
*/
*/
public
function
getQuery
(
$params
=
array
()
)
public
function
from
(
$from
)
{
{
if
(
empty
(
$this
->
parts
[
'select'
])
||
empty
(
$this
->
parts
[
'from'
]))
{
return
$this
->
getParser
(
'from'
)
->
parse
(
$from
);
return
false
;
}
$needsSubQuery
=
false
;
$subquery
=
''
;
$k
=
array_keys
(
$this
->
_aliasMap
);
$table
=
$this
->
_aliasMap
[
$k
[
0
]][
'table'
];
if
(
!
empty
(
$this
->
parts
[
'limit'
])
&&
$this
->
needsSubquery
&&
$table
->
getAttribute
(
Doctrine
::
ATTR_QUERY_LIMIT
)
==
Doctrine
::
LIMIT_RECORDS
)
{
$needsSubQuery
=
true
;
$this
->
limitSubqueryUsed
=
true
;
}
// process all pending SELECT part subqueries
$this
->
processPendingSubqueries
();
// build the basic query
$str
=
''
;
if
(
$this
->
isDistinct
())
{
$str
=
'DISTINCT '
;
}
$q
=
$this
->
getQueryBase
();
foreach
(
$this
->
parts
[
'from'
]
as
$k
=>
$part
)
{
if
(
$k
===
0
)
{
$q
.=
$part
;
continue
;
}
// preserve LEFT JOINs only if needed
if
(
substr
(
$part
,
0
,
9
)
===
'LEFT JOIN'
)
{
$e
=
explode
(
' '
,
$part
);
$aliases
=
array_merge
(
$this
->
subqueryAliases
,
array_keys
(
$this
->
neededTables
));
if
(
!
in_array
(
$e
[
3
],
$aliases
)
&&
!
in_array
(
$e
[
2
],
$aliases
)
&&
!
empty
(
$this
->
pendingFields
))
{
continue
;
}
}
$e
=
explode
(
' ON '
,
$part
);
// we can always be sure that the first join condition exists
$e2
=
explode
(
' AND '
,
$e
[
1
]);
$part
=
$e
[
0
]
.
' ON '
.
array_shift
(
$e2
);
if
(
!
empty
(
$e2
))
{
$parser
=
new
Doctrine_Query_JoinCondition
(
$this
);
$part
.=
' AND '
.
$parser
->
parse
(
implode
(
' AND '
,
$e2
));
}
$q
.=
' '
.
$part
;
}
if
(
!
empty
(
$this
->
parts
[
'set'
]))
{
$q
.=
' SET '
.
implode
(
', '
,
$this
->
parts
[
'set'
]);
}
$string
=
$this
->
applyInheritance
();
if
(
!
empty
(
$string
))
{
$this
->
parts
[
'where'
][]
=
'('
.
$string
.
')'
;
}
$modifyLimit
=
true
;
if
(
!
empty
(
$this
->
parts
[
"limit"
])
||
!
empty
(
$this
->
parts
[
"offset"
]))
{
if
(
$needsSubQuery
)
{
$subquery
=
$this
->
getLimitSubquery
();
switch
(
strtolower
(
$this
->
conn
->
getName
()))
{
case
'mysql'
:
// mysql doesn't support LIMIT in subqueries
$list
=
$this
->
conn
->
execute
(
$subquery
,
$params
)
->
fetchAll
(
PDO
::
FETCH_COLUMN
);
$subquery
=
implode
(
', '
,
$list
);
break
;
case
'pgsql'
:
// pgsql needs special nested LIMIT subquery
$subquery
=
'SELECT doctrine_subquery_alias.'
.
$table
->
getIdentifier
()
.
' FROM ('
.
$subquery
.
') AS doctrine_subquery_alias'
;
break
;
}
$field
=
$this
->
aliasHandler
->
getShortAlias
(
$table
->
getTableName
())
.
'.'
.
$table
->
getIdentifier
();
// only append the subquery if it actually contains something
if
(
$subquery
!==
''
)
array_unshift
(
$this
->
parts
[
'where'
],
$field
.
' IN ('
.
$subquery
.
')'
);
$modifyLimit
=
false
;
}
}
$q
.=
(
!
empty
(
$this
->
parts
[
'where'
]))
?
' WHERE '
.
implode
(
' AND '
,
$this
->
parts
[
'where'
])
:
''
;
$q
.=
(
!
empty
(
$this
->
parts
[
'groupby'
]))
?
' GROUP BY '
.
implode
(
', '
,
$this
->
parts
[
'groupby'
])
:
''
;
$q
.=
(
!
empty
(
$this
->
parts
[
'having'
]))
?
' HAVING '
.
implode
(
' AND '
,
$this
->
parts
[
'having'
])
:
''
;
$q
.=
(
!
empty
(
$this
->
parts
[
'orderby'
]))
?
' ORDER BY '
.
implode
(
', '
,
$this
->
parts
[
'orderby'
])
:
''
;
if
(
$modifyLimit
)
{
$q
=
$this
->
conn
->
modifyLimitQuery
(
$q
,
$this
->
parts
[
'limit'
],
$this
->
parts
[
'offset'
]);
}
// return to the previous state
if
(
!
empty
(
$string
))
{
array_pop
(
$this
->
parts
[
'where'
]);
}
if
(
$needsSubQuery
)
{
array_shift
(
$this
->
parts
[
'where'
]);
}
return
$q
;
}
}
/**
/**
* getLimitSubquery
* innerJoin
* this is method is used by the record limit algorithm
* appends an INNER JOIN to the FROM part of the query
*
* when fetching one-to-many, many-to-many associated data with LIMIT clause
* an additional subquery is needed for limiting the number of returned records instead
* of limiting the number of sql result set rows
*
*
* @return string the limit subquery
* @param string $join DQL INNER JOIN
* @return Doctrine_Query
*/
*/
public
function
getLimitSubquery
(
)
public
function
innerJoin
(
$join
)
{
{
$k
=
array_keys
(
$this
->
tables
);
return
$this
->
getParser
(
'from'
)
->
parse
(
'INNER JOIN '
.
$join
);
$table
=
$this
->
tables
[
$k
[
0
]];
// get short alias
$alias
=
$this
->
aliasHandler
->
getShortAlias
(
$table
->
getTableName
());
$primaryKey
=
$alias
.
'.'
.
$table
->
getIdentifier
();
// initialize the base of the subquery
$subquery
=
'SELECT DISTINCT '
.
$primaryKey
;
if
(
$this
->
conn
->
getDBH
()
->
getAttribute
(
PDO
::
ATTR_DRIVER_NAME
)
==
'pgsql'
)
{
// pgsql needs the order by fields to be preserved in select clause
foreach
(
$this
->
parts
[
'orderby'
]
as
$part
)
{
$e
=
explode
(
' '
,
$part
);
// don't add primarykey column (its already in the select clause)
if
(
$e
[
0
]
!==
$primaryKey
)
{
$subquery
.=
', '
.
$e
[
0
];
}
}
}
$subquery
.=
' FROM '
.
$this
->
conn
->
quoteIdentifier
(
$table
->
getTableName
())
.
' '
.
$alias
;
foreach
(
$this
->
parts
[
'join'
]
as
$parts
)
{
foreach
(
$parts
as
$part
)
{
// preserve LEFT JOINs only if needed
if
(
substr
(
$part
,
0
,
9
)
===
'LEFT JOIN'
)
{
$e
=
explode
(
' '
,
$part
);
if
(
!
in_array
(
$e
[
3
],
$this
->
subqueryAliases
)
&&
!
in_array
(
$e
[
2
],
$this
->
subqueryAliases
))
{
continue
;
}
}
$subquery
.=
' '
.
$part
;
}
}
// all conditions must be preserved in subquery
$subquery
.=
(
!
empty
(
$this
->
parts
[
'where'
]))
?
' WHERE '
.
implode
(
' AND '
,
$this
->
parts
[
'where'
])
:
''
;
$subquery
.=
(
!
empty
(
$this
->
parts
[
'groupby'
]))
?
' GROUP BY '
.
implode
(
', '
,
$this
->
parts
[
'groupby'
])
:
''
;
$subquery
.=
(
!
empty
(
$this
->
parts
[
'having'
]))
?
' HAVING '
.
implode
(
' AND '
,
$this
->
parts
[
'having'
])
:
''
;
$subquery
.=
(
!
empty
(
$this
->
parts
[
'orderby'
]))
?
' ORDER BY '
.
implode
(
', '
,
$this
->
parts
[
'orderby'
])
:
''
;
// add driver specific limit clause
$subquery
=
$this
->
conn
->
modifyLimitQuery
(
$subquery
,
$this
->
parts
[
'limit'
],
$this
->
parts
[
'offset'
]);
$parts
=
self
::
quoteExplode
(
$subquery
,
' '
,
"'"
,
"'"
);
foreach
(
$parts
as
$k
=>
$part
)
{
if
(
strpos
(
$part
,
"'"
)
!==
false
)
{
continue
;
}
if
(
$this
->
aliasHandler
->
hasAliasFor
(
$part
))
{
$parts
[
$k
]
=
$this
->
aliasHandler
->
generateNewAlias
(
$part
);
}
if
(
strpos
(
$part
,
'.'
)
!==
false
)
{
$e
=
explode
(
'.'
,
$part
);
$trimmed
=
ltrim
(
$e
[
0
],
'( '
);
$pos
=
strpos
(
$e
[
0
],
$trimmed
);
$e
[
0
]
=
substr
(
$e
[
0
],
0
,
$pos
)
.
$this
->
aliasHandler
->
generateNewAlias
(
$trimmed
);
$parts
[
$k
]
=
implode
(
'.'
,
$e
);
}
}
$subquery
=
implode
(
' '
,
$parts
);
return
$subquery
;
}
}
/**
/**
* query the database with DQL (Doctrine Query Language)
* leftJoin
* appends a LEFT JOIN to the FROM part of the query
*
*
* @param string $
query DQL query
* @param string $
join DQL LEFT JOIN
* @
param array $params parameters
* @
return Doctrine_Query
*/
*/
public
function
query
(
$query
,
$params
=
array
()
)
public
function
leftJoin
(
$join
)
{
{
$this
->
parseQuery
(
$query
);
return
$this
->
getParser
(
'from'
)
->
parse
(
'LERT JOIN '
.
$join
);
if
(
$this
->
aggregate
)
{
$keys
=
array_keys
(
$this
->
tables
);
$query
=
$this
->
getQuery
();
$stmt
=
$this
->
tables
[
$keys
[
0
]]
->
getConnection
()
->
select
(
$query
,
$this
->
parts
[
"limit"
],
$this
->
parts
[
"offset"
]);
$data
=
$stmt
->
fetch
(
PDO
::
FETCH_ASSOC
);
if
(
count
(
$data
)
==
1
)
{
return
current
(
$data
);
}
else
{
return
$data
;
}
}
else
{
return
$this
->
execute
(
$params
);
}
}
}
/**
/**
* splitQuery
* groupBy
* splits the given dql query into an array where keys
* sets the GROUP BY part of the query
* represent different query part names and values are
* arrays splitted using sqlExplode method
*
*
* example:
* @param string $groupby DQL GROUP BY part
*
* @return Doctrine_Query
* parameter:
* $query = "SELECT u.* FROM User u WHERE u.name LIKE ?"
* returns:
* array('select' => array('u.*'),
* 'from' => array('User', 'u'),
* 'where' => array('u.name', 'LIKE', '?'))
*
* @param string $query DQL query
* @throws Doctrine_Query_Exception if some generic parsing error occurs
* @return array an array containing the query string parts
*/
*/
public
function
splitQuery
(
$quer
y
)
public
function
groupBy
(
$groupb
y
)
{
{
$e
=
self
::
sqlExplode
(
$query
,
' '
);
return
$this
->
getParser
(
'groupby'
)
->
parse
(
$groupby
);
foreach
(
$e
as
$k
=>
$part
)
{
$part
=
trim
(
$part
);
switch
(
strtolower
(
$part
))
{
case
'delete'
:
case
'update'
:
case
'select'
:
case
'set'
:
case
'from'
:
case
'where'
:
case
'limit'
:
case
'offset'
:
case
'having'
:
$p
=
$part
;
$parts
[
$part
]
=
array
();
break
;
case
'order'
:
case
'group'
:
$i
=
(
$k
+
1
);
if
(
isset
(
$e
[
$i
])
&&
strtolower
(
$e
[
$i
])
===
"by"
)
{
$p
=
$part
;
$parts
[
$part
]
=
array
();
}
else
$parts
[
$p
][]
=
$part
;
break
;
case
"by"
:
continue
;
default
:
if
(
!
isset
(
$p
))
throw
new
Doctrine_Query_Exception
(
"Couldn't parse query."
);
$parts
[
$p
][]
=
$part
;
}
}
return
$parts
;
}
}
/**
/**
* DQL PARSER
* where
* parses a DQL query
* sets the WHERE part of the query
* first splits the query in parts and then uses individual
* parsers for each part
*
*
* @param string $query DQL query
* @param string $join DQL WHERE part
* @param boolean $clear whether or not to clear the aliases
* @param mixed $params an array of parameters or a simple scalar
* @throws Doctrine_Query_Exception if some generic parsing error occurs
* @return Doctrine_Query
* @return Doctrine_Query
*/
*/
public
function
parseQuery
(
$query
,
$clear
=
true
)
public
function
where
(
$where
,
$params
=
array
()
)
{
{
if
(
$clear
)
if
(
is_array
(
$params
))
{
$this
->
clear
();
$this
->
params
=
array_merge
(
$this
->
params
,
$params
);
}
else
{
$query
=
trim
(
$query
);
$this
->
params
[]
=
$params
;
$query
=
str_replace
(
"
\n
"
,
' '
,
$query
);
$query
=
str_replace
(
"
\r
"
,
' '
,
$query
);
$parts
=
$this
->
splitQuery
(
$query
);
foreach
(
$parts
as
$k
=>
$part
)
{
$part
=
implode
(
' '
,
$part
);
switch
(
strtolower
(
$k
))
{
case
'create'
:
$this
->
type
=
self
::
CREATE
;
break
;
case
'insert'
:
$this
->
type
=
self
::
INSERT
;
break
;
case
'delete'
:
$this
->
type
=
self
::
DELETE
;
break
;
case
'select'
:
$this
->
type
=
self
::
SELECT
;
$this
->
parseSelect
(
$part
);
break
;
case
'update'
:
$this
->
type
=
self
::
UPDATE
;
$k
=
'FROM'
;
case
'from'
:
$class
=
'Doctrine_Query_'
.
ucwords
(
strtolower
(
$k
));
$parser
=
new
$class
(
$this
);
$parser
->
parse
(
$part
);
break
;
case
'set'
:
$class
=
'Doctrine_Query_'
.
ucwords
(
strtolower
(
$k
));
$parser
=
new
$class
(
$this
);
$this
->
parts
[
'set'
][]
=
$parser
->
parse
(
$part
);
break
;
case
'group'
:
case
'order'
:
$k
.=
'by'
;
case
'where'
:
case
'having'
:
$class
=
'Doctrine_Query_'
.
ucwords
(
strtolower
(
$k
));
$parser
=
new
$class
(
$this
);
$name
=
strtolower
(
$k
);
$this
->
parts
[
$name
][]
=
$parser
->
parse
(
$part
);
break
;
case
'limit'
:
$this
->
parts
[
'limit'
]
=
trim
(
$part
);
break
;
case
'offset'
:
$this
->
parts
[
'offset'
]
=
trim
(
$part
);
break
;
}
}
}
return
$this
->
getParser
(
'where'
)
->
parse
(
$where
);
return
$this
;
}
}
/**
/**
*
DQL ORDER BY PARSER
*
having
*
parses the order by part of the query string
*
sets the HAVING part of the query
*
*
* @param string $str
* @param string $having DQL HAVING part
* @return void
* @param mixed $params an array of parameters or a simple scalar
* @return Doctrine_Query
*/
*/
final
public
function
parseOrderBy
(
$str
)
public
function
having
(
$having
,
$params
)
{
{
$parser
=
new
Doctrine_Query_Part_Orderby
(
$this
);
if
(
is_array
(
$params
))
{
return
$parser
->
parse
(
$str
);
$this
->
params
=
array_merge
(
$this
->
params
,
$params
);
}
else
{
$this
->
params
[]
=
$params
;
}
return
$this
->
getParser
(
'having'
)
->
parse
(
$having
);
}
}
/**
/**
* generateAlias
* orderBy
* sets the ORDER BY part of the query
*
*
* @param string $
tableName
* @param string $
groupby DQL ORDER BY part
* @return
string
* @return
Doctrine_Query
*/
*/
public
function
generateAlias
(
$tableName
)
public
function
orderBy
(
$dql
)
{
{
if
(
isset
(
$this
->
tableIndexes
[
$tableName
]))
{
return
$this
->
getParser
(
'orderby'
)
->
parse
(
$dql
);
return
$tableName
.++
$this
->
tableIndexes
[
$tableName
];
}
else
{
$this
->
tableIndexes
[
$tableName
]
=
1
;
return
$tableName
;
}
}
public
function
load
(
$path
,
$loadFields
=
true
)
{
// parse custom join conditions
$e
=
explode
(
' ON '
,
$path
);
$joinCondition
=
''
;
if
(
count
(
$e
)
>
1
)
{
$joinCondition
=
' AND '
.
$e
[
1
];
$path
=
$e
[
0
];
}
$tmp
=
explode
(
' '
,
$path
);
$componentAlias
=
(
count
(
$tmp
)
>
1
)
?
end
(
$tmp
)
:
false
;
$e
=
preg_split
(
"/[.:]/"
,
$tmp
[
0
],
-
1
);
$fullPath
=
$tmp
[
0
];
$prevPath
=
''
;
$fullLength
=
strlen
(
$fullPath
);
if
(
isset
(
$this
->
_aliasMap
[
$e
[
0
]]))
{
$table
=
$this
->
_aliasMap
[
$e
[
0
]][
'table'
];
$prevPath
=
$parent
=
array_shift
(
$e
);
}
foreach
(
$e
as
$key
=>
$name
)
{
// get length of the previous path
$length
=
strlen
(
$prevPath
);
// build the current component path
$prevPath
=
(
$prevPath
)
?
$prevPath
.
'.'
.
$name
:
$name
;
$delimeter
=
substr
(
$fullPath
,
$length
,
1
);
// if an alias is not given use the current path as an alias identifier
if
(
strlen
(
$prevPath
)
!==
$fullLength
||
!
$componentAlias
)
{
$componentAlias
=
$prevPath
;
}
if
(
!
isset
(
$table
))
{
// process the root of the path
$table
=
$this
->
loadRoot
(
$name
,
$componentAlias
);
}
else
{
$join
=
(
$delimeter
==
':'
)
?
'INNER JOIN '
:
'LEFT JOIN '
;
$relation
=
$table
->
getRelation
(
$name
);
$this
->
_aliasMap
[
$componentAlias
]
=
array
(
'table'
=>
$relation
->
getTable
(),
'parent'
=>
$parent
,
'relation'
=>
$relation
);
if
(
!
$relation
->
isOneToOne
())
{
$this
->
needsSubquery
=
true
;
}
$localAlias
=
$this
->
getShortAlias
(
$parent
,
$table
->
getTableName
());
$foreignAlias
=
$this
->
getShortAlias
(
$componentAlias
,
$relation
->
getTable
()
->
getTableName
());
$localSql
=
$this
->
conn
->
quoteIdentifier
(
$table
->
getTableName
())
.
' '
.
$localAlias
;
$foreignSql
=
$this
->
conn
->
quoteIdentifier
(
$relation
->
getTable
()
->
getTableName
())
.
' '
.
$foreignAlias
;
$map
=
$relation
->
getTable
()
->
inheritanceMap
;
if
(
!
$loadFields
||
!
empty
(
$map
)
||
$joinCondition
)
{
$this
->
subqueryAliases
[]
=
$foreignAlias
;
}
if
(
$relation
instanceof
Doctrine_Relation_Association
)
{
$asf
=
$relation
->
getAssociationFactory
();
$assocTableName
=
$asf
->
getTableName
();
if
(
!
$loadFields
||
!
empty
(
$map
)
||
$joinCondition
)
{
$this
->
subqueryAliases
[]
=
$assocTableName
;
}
$assocPath
=
$prevPath
.
'.'
.
$asf
->
getComponentName
();
$assocAlias
=
$this
->
getShortAlias
(
$assocPath
,
$asf
->
getTableName
());
$queryPart
=
$join
.
$assocTableName
.
' '
.
$assocAlias
.
' ON '
.
$localAlias
.
'.'
.
$table
->
getIdentifier
()
.
' = '
.
$assocAlias
.
'.'
.
$relation
->
getLocal
();
if
(
$relation
instanceof
Doctrine_Relation_Association_Self
)
{
$queryPart
.=
' OR '
.
$localAlias
.
'.'
.
$table
->
getIdentifier
()
.
' = '
.
$assocAlias
.
'.'
.
$relation
->
getForeign
();
}
$this
->
parts
[
'from'
][]
=
$queryPart
;
$queryPart
=
$join
.
$foreignSql
.
' ON '
.
$foreignAlias
.
'.'
.
$relation
->
getTable
()
->
getIdentifier
()
.
' = '
.
$assocAlias
.
'.'
.
$relation
->
getForeign
()
.
$joinCondition
;
if
(
$relation
instanceof
Doctrine_Relation_Association_Self
)
{
$queryPart
.=
' OR '
.
$foreignTable
.
'.'
.
$table
->
getIdentifier
()
.
' = '
.
$assocAlias
.
'.'
.
$relation
->
getLocal
();
}
}
else
{
$queryPart
=
$join
.
$foreignSql
.
' ON '
.
$localAlias
.
'.'
.
$relation
->
getLocal
()
.
' = '
.
$foreignAlias
.
'.'
.
$relation
->
getForeign
()
.
$joinCondition
;
}
$this
->
parts
[
'from'
][]
=
$queryPart
;
}
if
(
$loadFields
)
{
$restoreState
=
false
;
// load fields if necessary
if
(
$loadFields
&&
empty
(
$this
->
pendingFields
))
{
$this
->
pendingFields
[
$componentAlias
]
=
array
(
'*'
);
$restoreState
=
true
;
}
if
(
isset
(
$this
->
pendingFields
[
$componentAlias
]))
{
$this
->
processPendingFields
(
$componentAlias
);
}
if
(
$restoreState
)
{
$this
->
pendingFields
=
array
();
}
if
(
isset
(
$this
->
pendingAggregates
[
$componentAlias
])
||
isset
(
$this
->
pendingAggregates
[
0
]))
{
$this
->
processPendingAggregates
(
$componentAlias
);
}
}
}
}
}
/**
/**
* loadRoot
* limit
* sets the DQL query limit
*
*
* @param
string $name
* @param
integer $limit limit to be used for limiting the query results
* @
param string $componentAlias
* @
return Doctrine_Query
*/
*/
public
function
l
oadRoot
(
$name
,
$componentAlias
)
public
function
l
imit
(
$limit
)
{
{
// get the connection for the component
return
$this
->
getParser
(
'limit'
)
->
parse
(
$dql
);
$this
->
conn
=
Doctrine_Manager
::
getInstance
()
->
getConnectionForComponent
(
$name
);
$table
=
$this
->
conn
->
getTable
(
$name
);
$tableName
=
$table
->
getTableName
();
// get the short alias for this table
$tableAlias
=
$this
->
aliasHandler
->
getShortAlias
(
$componentAlias
,
$tableName
);
// quote table name
$queryPart
=
$this
->
conn
->
quoteIdentifier
(
$tableName
);
if
(
$this
->
type
===
self
::
SELECT
)
{
$queryPart
.=
' '
.
$tableAlias
;
}
$this
->
parts
[
'from'
][]
=
$queryPart
;
$this
->
tableAliases
[
$tableAlias
]
=
$componentAlias
;
$this
->
_aliasMap
[
$componentAlias
]
=
array
(
'table'
=>
$table
);
return
$table
;
}
}
/**
/**
* getShortAlias
* offset
* some database such as Oracle need the identifier lengths to be < ~30 chars
* sets the DQL query offset
* hence Doctrine creates as short identifier aliases as possible
*
*
* this method is used for the creation of short table aliases, its also
* @param integer $offset offset to be used for paginating the query
* smart enough to check if an alias already exists for given component (componentAlias)
* @return Doctrine_Query
*
* @param string $componentAlias the alias for the query component to search table alias for
* @param string $tableName the table name from which the table alias is being created
* @return string the generated / fetched short alias
*/
*/
public
function
getShortAlias
(
$componentAlias
,
$tableName
)
public
function
offset
(
$dql
)
{
{
return
$this
->
aliasHandler
->
getShortAlias
(
$componentAlias
,
$tableName
);
return
$this
->
getParser
(
'offset'
)
->
parse
(
$dql
);
}
}
}
}
draft/new-core/QueryApi.php
View file @
ce4ea888
...
@@ -32,6 +32,16 @@
...
@@ -32,6 +32,16 @@
*/
*/
class
Doctrine_Query
class
Doctrine_Query
{
{
/**
* create
* returns a new Doctrine_Query object
*
* @return Doctrine_Query
*/
public
static
function
create
()
{
return
new
Doctrine_Query
();
}
/**
/**
* addSelect
* addSelect
* adds fields to the SELECT part of the query
* adds fields to the SELECT part of the query
...
...
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