| 1 |
lars |
1 |
<?php
|
|
|
2 |
/*
|
|
|
3 |
* $Id: BasePeer.php 1608 2010-03-15 23:09:22Z francois $
|
|
|
4 |
*
|
|
|
5 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
6 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
7 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
8 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
9 |
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
10 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
11 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
12 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
13 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
14 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
15 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
16 |
*
|
|
|
17 |
* This software consists of voluntary contributions made by many individuals
|
|
|
18 |
* and is licensed under the LGPL. For more information please see
|
|
|
19 |
* <http://propel.phpdb.org>.
|
|
|
20 |
*/
|
|
|
21 |
|
|
|
22 |
/**
|
|
|
23 |
* This is a utility class for all generated Peer classes in the system.
|
|
|
24 |
*
|
|
|
25 |
* Peer classes are responsible for isolating all of the database access
|
|
|
26 |
* for a specific business object. They execute all of the SQL
|
|
|
27 |
* against the database. Over time this class has grown to include
|
|
|
28 |
* utility methods which ease execution of cross-database queries and
|
|
|
29 |
* the implementation of concrete Peers.
|
|
|
30 |
*
|
|
|
31 |
* @author Hans Lellelid <hans@xmpl.org> (Propel)
|
|
|
32 |
* @author Kaspars Jaudzems <kaspars.jaudzems@inbox.lv> (Propel)
|
|
|
33 |
* @author Heltem <heltem@o2php.com> (Propel)
|
|
|
34 |
* @author Frank Y. Kim <frank.kim@clearink.com> (Torque)
|
|
|
35 |
* @author John D. McNally <jmcnally@collab.net> (Torque)
|
|
|
36 |
* @author Brett McLaughlin <bmclaugh@algx.net> (Torque)
|
|
|
37 |
* @author Stephen Haberman <stephenh@chase3000.com> (Torque)
|
|
|
38 |
* @version $Revision: 1608 $
|
|
|
39 |
* @package propel.util
|
|
|
40 |
*/
|
|
|
41 |
class BasePeer
|
|
|
42 |
{
|
|
|
43 |
|
|
|
44 |
/** Array (hash) that contains the cached mapBuilders. */
|
|
|
45 |
private static $mapBuilders = array();
|
|
|
46 |
|
|
|
47 |
/** Array (hash) that contains cached validators */
|
|
|
48 |
private static $validatorMap = array();
|
|
|
49 |
|
|
|
50 |
/**
|
|
|
51 |
* phpname type
|
|
|
52 |
* e.g. 'AuthorId'
|
|
|
53 |
*/
|
|
|
54 |
const TYPE_PHPNAME = 'phpName';
|
|
|
55 |
|
|
|
56 |
/**
|
|
|
57 |
* studlyphpname type
|
|
|
58 |
* e.g. 'authorId'
|
|
|
59 |
*/
|
|
|
60 |
const TYPE_STUDLYPHPNAME = 'studlyPhpName';
|
|
|
61 |
|
|
|
62 |
/**
|
|
|
63 |
* column (peer) name type
|
|
|
64 |
* e.g. 'book.AUTHOR_ID'
|
|
|
65 |
*/
|
|
|
66 |
const TYPE_COLNAME = 'colName';
|
|
|
67 |
|
|
|
68 |
/**
|
|
|
69 |
* column fieldname type
|
|
|
70 |
* e.g. 'author_id'
|
|
|
71 |
*/
|
|
|
72 |
const TYPE_FIELDNAME = 'fieldName';
|
|
|
73 |
|
|
|
74 |
/**
|
|
|
75 |
* num type
|
|
|
76 |
* simply the numerical array index, e.g. 4
|
|
|
77 |
*/
|
|
|
78 |
const TYPE_NUM = 'num';
|
|
|
79 |
|
|
|
80 |
static public function getFieldnames ($classname, $type = self::TYPE_PHPNAME) {
|
|
|
81 |
|
|
|
82 |
// TODO we should take care of including the peer class here
|
|
|
83 |
|
|
|
84 |
$peerclass = 'Base' . $classname . 'Peer'; // TODO is this always true?
|
|
|
85 |
$callable = array($peerclass, 'getFieldnames');
|
|
|
86 |
$args = array($type);
|
|
|
87 |
|
|
|
88 |
return call_user_func_array($callable, $args);
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
static public function translateFieldname($classname, $fieldname, $fromType, $toType) {
|
|
|
92 |
|
|
|
93 |
// TODO we should take care of including the peer class here
|
|
|
94 |
|
|
|
95 |
$peerclass = 'Base' . $classname . 'Peer'; // TODO is this always true?
|
|
|
96 |
$callable = array($peerclass, 'translateFieldname');
|
|
|
97 |
$args = array($fieldname, $fromType, $toType);
|
|
|
98 |
|
|
|
99 |
return call_user_func_array($callable, $args);
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
/**
|
|
|
103 |
* Method to perform deletes based on values and keys in a
|
|
|
104 |
* Criteria.
|
|
|
105 |
*
|
|
|
106 |
* @param Criteria $criteria The criteria to use.
|
|
|
107 |
* @param PropelPDO $con A PropelPDO connection object.
|
|
|
108 |
* @return int The number of rows affected by last statement execution. For most
|
|
|
109 |
* uses there is only one delete statement executed, so this number
|
|
|
110 |
* will correspond to the number of rows affected by the call to this
|
|
|
111 |
* method. Note that the return value does require that this information
|
|
|
112 |
* is returned (supported) by the PDO driver.
|
|
|
113 |
* @throws PropelException
|
|
|
114 |
*/
|
|
|
115 |
public static function doDelete(Criteria $criteria, PropelPDO $con)
|
|
|
116 |
{
|
|
|
117 |
$db = Propel::getDB($criteria->getDbName());
|
|
|
118 |
$dbMap = Propel::getDatabaseMap($criteria->getDbName());
|
|
|
119 |
|
|
|
120 |
// Set up a list of required tables (one DELETE statement will
|
|
|
121 |
// be executed per table)
|
|
|
122 |
|
|
|
123 |
$tables_keys = array();
|
|
|
124 |
foreach ($criteria as $c) {
|
|
|
125 |
foreach ($c->getAllTables() as $tableName) {
|
|
|
126 |
$tableName2 = $criteria->getTableForAlias($tableName);
|
|
|
127 |
if ($tableName2 !== null) {
|
|
|
128 |
$tables_keys[$tableName2 . ' ' . $tableName] = true;
|
|
|
129 |
} else {
|
|
|
130 |
$tables_keys[$tableName] = true;
|
|
|
131 |
}
|
|
|
132 |
}
|
|
|
133 |
} // foreach criteria->keys()
|
|
|
134 |
|
|
|
135 |
$affectedRows = 0; // initialize this in case the next loop has no iterations.
|
|
|
136 |
|
|
|
137 |
$tables = array_keys($tables_keys);
|
|
|
138 |
|
|
|
139 |
foreach ($tables as $tableName) {
|
|
|
140 |
|
|
|
141 |
$whereClause = array();
|
|
|
142 |
$selectParams = array();
|
|
|
143 |
foreach ($dbMap->getTable($tableName)->getColumns() as $colMap) {
|
|
|
144 |
$key = $tableName . '.' . $colMap->getColumnName();
|
|
|
145 |
if ($criteria->containsKey($key)) {
|
|
|
146 |
$sb = "";
|
|
|
147 |
$criteria->getCriterion($key)->appendPsTo($sb, $selectParams);
|
|
|
148 |
$whereClause[] = $sb;
|
|
|
149 |
}
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
if (empty($whereClause)) {
|
|
|
153 |
throw new PropelException("Cowardly refusing to delete from table $tableName with empty WHERE clause.");
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
// Execute the statement.
|
|
|
157 |
try {
|
|
|
158 |
$sql = "DELETE FROM " . $tableName . " WHERE " . implode(" AND ", $whereClause);
|
|
|
159 |
$stmt = $con->prepare($sql);
|
|
|
160 |
self::populateStmtValues($stmt, $selectParams, $dbMap, $db);
|
|
|
161 |
$stmt->execute();
|
|
|
162 |
$affectedRows = $stmt->rowCount();
|
|
|
163 |
} catch (Exception $e) {
|
|
|
164 |
Propel::log($e->getMessage(), Propel::LOG_ERR);
|
|
|
165 |
throw new PropelException("Unable to execute DELETE statement.",$e);
|
|
|
166 |
}
|
|
|
167 |
|
|
|
168 |
} // for each table
|
|
|
169 |
|
|
|
170 |
return $affectedRows;
|
|
|
171 |
}
|
|
|
172 |
|
|
|
173 |
/**
|
|
|
174 |
* Method to deletes all contents of specified table.
|
|
|
175 |
*
|
|
|
176 |
* This method is invoked from generated Peer classes like this:
|
|
|
177 |
* <code>
|
|
|
178 |
* public static function doDeleteAll($con = null)
|
|
|
179 |
* {
|
|
|
180 |
* if ($con === null) $con = Propel::getConnection(self::DATABASE_NAME);
|
|
|
181 |
* BasePeer::doDeleteAll(self::TABLE_NAME, $con);
|
|
|
182 |
* }
|
|
|
183 |
* </code>
|
|
|
184 |
*
|
|
|
185 |
* @param string $tableName The name of the table to empty.
|
|
|
186 |
* @param PropelPDO $con A PropelPDO connection object.
|
|
|
187 |
* @return int The number of rows affected by the statement. Note
|
|
|
188 |
* that the return value does require that this information
|
|
|
189 |
* is returned (supported) by the Creole db driver.
|
|
|
190 |
* @throws PropelException - wrapping SQLException caught from statement execution.
|
|
|
191 |
*/
|
|
|
192 |
public static function doDeleteAll($tableName, PropelPDO $con)
|
|
|
193 |
{
|
|
|
194 |
try {
|
|
|
195 |
$sql = "DELETE FROM " . $tableName;
|
|
|
196 |
$stmt = $con->prepare($sql);
|
|
|
197 |
$stmt->execute();
|
|
|
198 |
return $stmt->rowCount();
|
|
|
199 |
} catch (Exception $e) {
|
|
|
200 |
Propel::log($e->getMessage(), Propel::LOG_ERR);
|
|
|
201 |
throw new PropelException("Unable to perform DELETE ALL operation.", $e);
|
|
|
202 |
}
|
|
|
203 |
}
|
|
|
204 |
|
|
|
205 |
/**
|
|
|
206 |
* Method to perform inserts based on values and keys in a
|
|
|
207 |
* Criteria.
|
|
|
208 |
* <p>
|
|
|
209 |
* If the primary key is auto incremented the data in Criteria
|
|
|
210 |
* will be inserted and the auto increment value will be returned.
|
|
|
211 |
* <p>
|
|
|
212 |
* If the primary key is included in Criteria then that value will
|
|
|
213 |
* be used to insert the row.
|
|
|
214 |
* <p>
|
|
|
215 |
* If no primary key is included in Criteria then we will try to
|
|
|
216 |
* figure out the primary key from the database map and insert the
|
|
|
217 |
* row with the next available id using util.db.IDBroker.
|
|
|
218 |
* <p>
|
|
|
219 |
* If no primary key is defined for the table the values will be
|
|
|
220 |
* inserted as specified in Criteria and null will be returned.
|
|
|
221 |
*
|
|
|
222 |
* @param Criteria $criteria Object containing values to insert.
|
|
|
223 |
* @param PropelPDO $con A PropelPDO connection.
|
|
|
224 |
* @return mixed The primary key for the new row if (and only if!) the primary key
|
|
|
225 |
* is auto-generated. Otherwise will return <code>null</code>.
|
|
|
226 |
* @throws PropelException
|
|
|
227 |
*/
|
|
|
228 |
public static function doInsert(Criteria $criteria, PropelPDO $con) {
|
|
|
229 |
|
|
|
230 |
// the primary key
|
|
|
231 |
$id = null;
|
|
|
232 |
|
|
|
233 |
$db = Propel::getDB($criteria->getDbName());
|
|
|
234 |
|
|
|
235 |
// Get the table name and method for determining the primary
|
|
|
236 |
// key value.
|
|
|
237 |
$keys = $criteria->keys();
|
|
|
238 |
if (!empty($keys)) {
|
|
|
239 |
$tableName = $criteria->getTableName( $keys[0] );
|
|
|
240 |
} else {
|
|
|
241 |
throw new PropelException("Database insert attempted without anything specified to insert");
|
|
|
242 |
}
|
|
|
243 |
|
|
|
244 |
$dbMap = Propel::getDatabaseMap($criteria->getDbName());
|
|
|
245 |
$tableMap = $dbMap->getTable($tableName);
|
|
|
246 |
$keyInfo = $tableMap->getPrimaryKeyMethodInfo();
|
|
|
247 |
$useIdGen = $tableMap->isUseIdGenerator();
|
|
|
248 |
//$keyGen = $con->getIdGenerator();
|
|
|
249 |
|
|
|
250 |
$pk = self::getPrimaryKey($criteria);
|
|
|
251 |
|
|
|
252 |
// only get a new key value if you need to
|
|
|
253 |
// the reason is that a primary key might be defined
|
|
|
254 |
// but you are still going to set its value. for example:
|
|
|
255 |
// a join table where both keys are primary and you are
|
|
|
256 |
// setting both columns with your own values
|
|
|
257 |
|
|
|
258 |
// pk will be null if there is no primary key defined for the table
|
|
|
259 |
// we're inserting into.
|
|
|
260 |
if ($pk !== null && $useIdGen && !$criteria->keyContainsValue($pk->getFullyQualifiedName()) && $db->isGetIdBeforeInsert()) {
|
|
|
261 |
try {
|
|
|
262 |
$id = $db->getId($con, $keyInfo);
|
|
|
263 |
} catch (Exception $e) {
|
|
|
264 |
throw new PropelException("Unable to get sequence id.", $e);
|
|
|
265 |
}
|
|
|
266 |
$criteria->add($pk->getFullyQualifiedName(), $id);
|
|
|
267 |
}
|
|
|
268 |
|
|
|
269 |
try {
|
|
|
270 |
$adapter = Propel::getDB($criteria->getDBName());
|
|
|
271 |
|
|
|
272 |
$qualifiedCols = $criteria->keys(); // we need table.column cols when populating values
|
|
|
273 |
$columns = array(); // but just 'column' cols for the SQL
|
|
|
274 |
foreach ($qualifiedCols as $qualifiedCol) {
|
|
|
275 |
$columns[] = substr($qualifiedCol, strrpos($qualifiedCol, '.') + 1);
|
|
|
276 |
}
|
|
|
277 |
|
|
|
278 |
// add identifiers
|
|
|
279 |
if ($adapter->useQuoteIdentifier()) {
|
|
|
280 |
$columns = array_map(array($adapter, 'quoteIdentifier'), $columns);
|
|
|
281 |
}
|
|
|
282 |
|
|
|
283 |
$sql = 'INSERT INTO ' . $tableName
|
|
|
284 |
. ' (' . implode(',', $columns) . ')'
|
|
|
285 |
. ' VALUES (';
|
|
|
286 |
// . substr(str_repeat("?,", count($columns)), 0, -1) .
|
|
|
287 |
for($p=1, $cnt=count($columns); $p <= $cnt; $p++) {
|
|
|
288 |
$sql .= ':p'.$p;
|
|
|
289 |
if ($p !== $cnt) $sql .= ',';
|
|
|
290 |
}
|
|
|
291 |
$sql .= ')';
|
|
|
292 |
|
|
|
293 |
$stmt = $con->prepare($sql);
|
|
|
294 |
self::populateStmtValues($stmt, self::buildParams($qualifiedCols, $criteria), $dbMap, $db);
|
|
|
295 |
$stmt->execute();
|
|
|
296 |
|
|
|
297 |
} catch (Exception $e) {
|
|
|
298 |
Propel::log($e->getMessage(), Propel::LOG_ERR);
|
|
|
299 |
throw new PropelException("Unable to execute INSERT statement.", $e);
|
|
|
300 |
}
|
|
|
301 |
|
|
|
302 |
// If the primary key column is auto-incremented, get the id now.
|
|
|
303 |
if ($pk !== null && $useIdGen && $db->isGetIdAfterInsert()) {
|
|
|
304 |
try {
|
|
|
305 |
$id = $db->getId($con, $keyInfo);
|
|
|
306 |
} catch (Exception $e) {
|
|
|
307 |
throw new PropelException("Unable to get autoincrement id.", $e);
|
|
|
308 |
}
|
|
|
309 |
}
|
|
|
310 |
|
|
|
311 |
return $id;
|
|
|
312 |
}
|
|
|
313 |
|
|
|
314 |
/**
|
|
|
315 |
* Method used to update rows in the DB. Rows are selected based
|
|
|
316 |
* on selectCriteria and updated using values in updateValues.
|
|
|
317 |
* <p>
|
|
|
318 |
* Use this method for performing an update of the kind:
|
|
|
319 |
* <p>
|
|
|
320 |
* WHERE some_column = some value AND could_have_another_column =
|
|
|
321 |
* another value AND so on.
|
|
|
322 |
*
|
|
|
323 |
* @param $selectCriteria A Criteria object containing values used in where
|
|
|
324 |
* clause.
|
|
|
325 |
* @param $updateValues A Criteria object containing values used in set
|
|
|
326 |
* clause.
|
|
|
327 |
* @param PropelPDO $con The PropelPDO connection object to use.
|
|
|
328 |
* @return int The number of rows affected by last update statement. For most
|
|
|
329 |
* uses there is only one update statement executed, so this number
|
|
|
330 |
* will correspond to the number of rows affected by the call to this
|
|
|
331 |
* method. Note that the return value does require that this information
|
|
|
332 |
* is returned (supported) by the Creole db driver.
|
|
|
333 |
* @throws PropelException
|
|
|
334 |
*/
|
|
|
335 |
public static function doUpdate(Criteria $selectCriteria, Criteria $updateValues, PropelPDO $con) {
|
|
|
336 |
|
|
|
337 |
$db = Propel::getDB($selectCriteria->getDbName());
|
|
|
338 |
$dbMap = Propel::getDatabaseMap($selectCriteria->getDbName());
|
|
|
339 |
|
|
|
340 |
// Get list of required tables, containing all columns
|
|
|
341 |
$tablesColumns = $selectCriteria->getTablesColumns();
|
|
|
342 |
|
|
|
343 |
// we also need the columns for the update SQL
|
|
|
344 |
$updateTablesColumns = $updateValues->getTablesColumns();
|
|
|
345 |
|
|
|
346 |
$affectedRows = 0; // initialize this in case the next loop has no iterations.
|
|
|
347 |
|
|
|
348 |
foreach ($tablesColumns as $tableName => $columns) {
|
|
|
349 |
|
|
|
350 |
$whereClause = array();
|
|
|
351 |
|
|
|
352 |
$params = array();
|
|
|
353 |
|
|
|
354 |
$stmt = null;
|
|
|
355 |
try {
|
|
|
356 |
|
|
|
357 |
$sql = "UPDATE " . $tableName . " SET ";
|
|
|
358 |
$p = 1;
|
|
|
359 |
foreach ($updateTablesColumns[$tableName] as $col) {
|
|
|
360 |
$updateColumnName = substr($col, strrpos($col, '.') + 1);
|
|
|
361 |
// add identifiers for the actual database?
|
|
|
362 |
if ($db->useQuoteIdentifier()) {
|
|
|
363 |
$updateColumnName = $db->quoteIdentifier($updateColumnName);
|
|
|
364 |
}
|
|
|
365 |
if ($updateValues->getComparison($col) != Criteria::CUSTOM_EQUAL) {
|
|
|
366 |
$sql .= $updateColumnName . '=:p'.$p++.', ';
|
|
|
367 |
} else {
|
|
|
368 |
$param = $updateValues->get($col);
|
|
|
369 |
$sql .= $updateColumnName . ' = ';
|
|
|
370 |
if (is_array($param)) {
|
|
|
371 |
if (isset($param['raw'])) {
|
|
|
372 |
$raw = $param['raw'];
|
|
|
373 |
$rawcvt = '';
|
|
|
374 |
// parse the $params['raw'] for ? chars
|
|
|
375 |
for($r=0,$len=strlen($raw); $r < $len; $r++) {
|
|
|
376 |
if ($raw{$r} == '?') {
|
|
|
377 |
$rawcvt .= ':p'.$p++;
|
|
|
378 |
} else {
|
|
|
379 |
$rawcvt .= $raw{$r};
|
|
|
380 |
}
|
|
|
381 |
}
|
|
|
382 |
$sql .= $rawcvt . ', ';
|
|
|
383 |
} else {
|
|
|
384 |
$sql .= ':p'.$p++.', ';
|
|
|
385 |
}
|
|
|
386 |
if (isset($param['value'])) {
|
|
|
387 |
$updateValues->put($col, $param['value']);
|
|
|
388 |
}
|
|
|
389 |
} else {
|
|
|
390 |
$updateValues->remove($col);
|
|
|
391 |
$sql .= $param . ', ';
|
|
|
392 |
}
|
|
|
393 |
}
|
|
|
394 |
}
|
|
|
395 |
|
|
|
396 |
$params = self::buildParams($updateTablesColumns[$tableName], $updateValues);
|
|
|
397 |
|
|
|
398 |
foreach ($columns as $colName) {
|
|
|
399 |
$sb = "";
|
|
|
400 |
$selectCriteria->getCriterion($colName)->appendPsTo($sb, $params);
|
|
|
401 |
$whereClause[] = $sb;
|
|
|
402 |
}
|
|
|
403 |
|
|
|
404 |
$sql = substr($sql, 0, -2) . " WHERE " . implode(" AND ", $whereClause);
|
|
|
405 |
|
|
|
406 |
$stmt = $con->prepare($sql);
|
|
|
407 |
|
|
|
408 |
// Replace ':p?' with the actual values
|
|
|
409 |
self::populateStmtValues($stmt, $params, $dbMap, $db);
|
|
|
410 |
|
|
|
411 |
$stmt->execute();
|
|
|
412 |
|
|
|
413 |
$affectedRows = $stmt->rowCount();
|
|
|
414 |
|
|
|
415 |
$stmt = null; // close
|
|
|
416 |
|
|
|
417 |
} catch (Exception $e) {
|
|
|
418 |
if ($stmt) $stmt = null; // close
|
|
|
419 |
Propel::log($e->getMessage(), Propel::LOG_ERR);
|
|
|
420 |
throw new PropelException("Unable to execute UPDATE statement.", $e);
|
|
|
421 |
}
|
|
|
422 |
|
|
|
423 |
} // foreach table in the criteria
|
|
|
424 |
|
|
|
425 |
return $affectedRows;
|
|
|
426 |
}
|
|
|
427 |
|
|
|
428 |
/**
|
|
|
429 |
* Executes query build by createSelectSql() and returns the resultset statement.
|
|
|
430 |
*
|
|
|
431 |
* @param Criteria $criteria A Criteria.
|
|
|
432 |
* @param PropelPDO $con A PropelPDO connection to use.
|
|
|
433 |
* @return PDOStatement The resultset.
|
|
|
434 |
* @throws PropelException
|
|
|
435 |
* @see createSelectSql()
|
|
|
436 |
*/
|
|
|
437 |
public static function doSelect(Criteria $criteria, PropelPDO $con = null)
|
|
|
438 |
{
|
|
|
439 |
$dbMap = Propel::getDatabaseMap($criteria->getDbName());
|
|
|
440 |
$db = Propel::getDB($criteria->getDbName());
|
|
|
441 |
|
|
|
442 |
if ($con === null) {
|
|
|
443 |
$con = Propel::getConnection($criteria->getDbName(), Propel::CONNECTION_READ);
|
|
|
444 |
}
|
|
|
445 |
|
|
|
446 |
$stmt = null;
|
|
|
447 |
|
|
|
448 |
if ($criteria->isUseTransaction()) $con->beginTransaction();
|
|
|
449 |
|
|
|
450 |
try {
|
|
|
451 |
|
|
|
452 |
$params = array();
|
|
|
453 |
$sql = self::createSelectSql($criteria, $params);
|
|
|
454 |
|
|
|
455 |
$stmt = $con->prepare($sql);
|
|
|
456 |
|
|
|
457 |
self::populateStmtValues($stmt, $params, $dbMap, $db);
|
|
|
458 |
|
|
|
459 |
$stmt->execute();
|
|
|
460 |
|
|
|
461 |
if ($criteria->isUseTransaction()) $con->commit();
|
|
|
462 |
|
|
|
463 |
} catch (Exception $e) {
|
|
|
464 |
if ($stmt) $stmt = null; // close
|
|
|
465 |
if ($criteria->isUseTransaction()) $con->rollBack();
|
|
|
466 |
Propel::log($e->getMessage(), Propel::LOG_ERR);
|
|
|
467 |
throw new PropelException($e);
|
|
|
468 |
}
|
|
|
469 |
|
|
|
470 |
return $stmt;
|
|
|
471 |
}
|
|
|
472 |
|
|
|
473 |
/**
|
|
|
474 |
* Executes a COUNT query using either a simple SQL rewrite or, for more complex queries, a
|
|
|
475 |
* sub-select of the SQL created by createSelectSql() and returns the statement.
|
|
|
476 |
*
|
|
|
477 |
* @param Criteria $criteria A Criteria.
|
|
|
478 |
* @param PropelPDO $con A PropelPDO connection to use.
|
|
|
479 |
* @return PDOStatement The resultset statement.
|
|
|
480 |
* @throws PropelException
|
|
|
481 |
* @see createSelectSql()
|
|
|
482 |
*/
|
|
|
483 |
public static function doCount(Criteria $criteria, PropelPDO $con = null)
|
|
|
484 |
{
|
|
|
485 |
$dbMap = Propel::getDatabaseMap($criteria->getDbName());
|
|
|
486 |
$db = Propel::getDB($criteria->getDbName());
|
|
|
487 |
|
|
|
488 |
if ($con === null) {
|
|
|
489 |
$con = Propel::getConnection($criteria->getDbName(), Propel::CONNECTION_READ);
|
|
|
490 |
}
|
|
|
491 |
|
|
|
492 |
$stmt = null;
|
|
|
493 |
|
|
|
494 |
if ($criteria->isUseTransaction()) $con->beginTransaction();
|
|
|
495 |
|
|
|
496 |
$needsComplexCount = ($criteria->getGroupByColumns() || $criteria->getOffset()
|
|
|
497 |
|| $criteria->getLimit() || $criteria->getHaving() || in_array(Criteria::DISTINCT, $criteria->getSelectModifiers()));
|
|
|
498 |
|
|
|
499 |
try {
|
|
|
500 |
|
|
|
501 |
$params = array();
|
|
|
502 |
|
|
|
503 |
if ($needsComplexCount) {
|
|
|
504 |
if (self::needsSelectAliases($criteria)) {
|
|
|
505 |
self::turnSelectColumnsToAliases($criteria);
|
|
|
506 |
}
|
|
|
507 |
$selectSql = self::createSelectSql($criteria, $params);
|
|
|
508 |
$sql = 'SELECT COUNT(*) FROM (' . $selectSql . ') AS propelmatch4cnt';
|
|
|
509 |
} else {
|
|
|
510 |
// Replace SELECT columns with COUNT(*)
|
|
|
511 |
$criteria->clearSelectColumns()->addSelectColumn('COUNT(*)');
|
|
|
512 |
$sql = self::createSelectSql($criteria, $params);
|
|
|
513 |
}
|
|
|
514 |
|
|
|
515 |
$stmt = $con->prepare($sql);
|
|
|
516 |
self::populateStmtValues($stmt, $params, $dbMap, $db);
|
|
|
517 |
$stmt->execute();
|
|
|
518 |
|
|
|
519 |
if ($criteria->isUseTransaction()) $con->commit();
|
|
|
520 |
|
|
|
521 |
} catch (Exception $e) {
|
|
|
522 |
if ($stmt) $stmt = null; // close
|
|
|
523 |
if ($criteria->isUseTransaction()) $con->rollBack();
|
|
|
524 |
Propel::log($e->getMessage(), Propel::LOG_ERR);
|
|
|
525 |
throw new PropelException($e);
|
|
|
526 |
}
|
|
|
527 |
|
|
|
528 |
return $stmt;
|
|
|
529 |
}
|
|
|
530 |
|
|
|
531 |
/**
|
|
|
532 |
* Populates values in a prepared statement.
|
|
|
533 |
*
|
|
|
534 |
* This method is designed to work with the createSelectSql() method, which creates
|
|
|
535 |
* both the SELECT SQL statement and populates a passed-in array of parameter
|
|
|
536 |
* values that should be substituted.
|
|
|
537 |
*
|
|
|
538 |
* <code>
|
|
|
539 |
* $params = array();
|
|
|
540 |
* $sql = BasePeer::createSelectSql($criteria, $params);
|
|
|
541 |
* BasePeer::populateStmtValues($stmt, $params, Propel::getDatabaseMap($critera->getDbName()), Propel::getDB($criteria->getDbName()));
|
|
|
542 |
* </code>
|
|
|
543 |
*
|
|
|
544 |
* @param PDOStatement $stmt
|
|
|
545 |
* @param array $params array('column' => ..., 'table' => ..., 'value' => ...)
|
|
|
546 |
* @param DatabaseMap $dbMap
|
|
|
547 |
* @return int The number of params replaced.
|
|
|
548 |
* @see createSelectSql()
|
|
|
549 |
* @see doSelect()
|
|
|
550 |
*/
|
|
|
551 |
public static function populateStmtValues(PDOStatement $stmt, array $params, DatabaseMap $dbMap, DBAdapter $db)
|
|
|
552 |
{
|
|
|
553 |
$i = 1;
|
|
|
554 |
foreach ($params as $param) {
|
|
|
555 |
$tableName = $param['table'];
|
|
|
556 |
$columnName = $param['column'];
|
|
|
557 |
$value = $param['value'];
|
|
|
558 |
|
|
|
559 |
if ($value === null) {
|
|
|
560 |
|
|
|
561 |
$stmt->bindValue(':p'.$i++, null, PDO::PARAM_NULL);
|
|
|
562 |
|
|
|
563 |
} elseif (isset($tableName) ) {
|
|
|
564 |
|
|
|
565 |
$cMap = $dbMap->getTable($tableName)->getColumn($columnName);
|
|
|
566 |
$type = $cMap->getType();
|
|
|
567 |
$pdoType = $cMap->getPdoType();
|
|
|
568 |
|
|
|
569 |
// FIXME - This is a temporary hack to get around apparent bugs w/ PDO+MYSQL
|
|
|
570 |
// See http://pecl.php.net/bugs/bug.php?id=9919
|
|
|
571 |
if ($pdoType == PDO::PARAM_BOOL && $db instanceof DBMySQL) {
|
|
|
572 |
$value = (int) $value;
|
|
|
573 |
$pdoType = PDO::PARAM_INT;
|
|
|
574 |
} elseif (is_numeric($value) && $cMap->isEpochTemporal()) { // it's a timestamp that needs to be formatted
|
|
|
575 |
if ($type == PropelColumnTypes::TIMESTAMP) {
|
|
|
576 |
$value = date($db->getTimestampFormatter(), $value);
|
|
|
577 |
} else if ($type == PropelColumnTypes::DATE) {
|
|
|
578 |
$value = date($db->getDateFormatter(), $value);
|
|
|
579 |
} else if ($type == PropelColumnTypes::TIME) {
|
|
|
580 |
$value = date($db->getTimeFormatter(), $value);
|
|
|
581 |
}
|
|
|
582 |
} elseif ($value instanceof DateTime && $cMap->isTemporal()) { // it's a timestamp that needs to be formatted
|
|
|
583 |
if ($type == PropelColumnTypes::TIMESTAMP || $type == PropelColumnTypes::BU_TIMESTAMP) {
|
|
|
584 |
$value = $value->format($db->getTimestampFormatter());
|
|
|
585 |
} else if ($type == PropelColumnTypes::DATE || $type == PropelColumnTypes::BU_DATE) {
|
|
|
586 |
$value = $value->format($db->getDateFormatter());
|
|
|
587 |
} else if ($type == PropelColumnTypes::TIME) {
|
|
|
588 |
$value = $value->format($db->getTimeFormatter());
|
|
|
589 |
}
|
|
|
590 |
} elseif (is_resource($value) && $cMap->isLob()) {
|
|
|
591 |
// we always need to make sure that the stream is rewound, otherwise nothing will
|
|
|
592 |
// get written to database.
|
|
|
593 |
rewind($value);
|
|
|
594 |
}
|
|
|
595 |
|
|
|
596 |
$stmt->bindValue(':p'.$i++, $value, $pdoType);
|
|
|
597 |
} else {
|
|
|
598 |
$stmt->bindValue(':p'.$i++, $value);
|
|
|
599 |
}
|
|
|
600 |
} // foreach
|
|
|
601 |
}
|
|
|
602 |
|
|
|
603 |
/**
|
|
|
604 |
* Applies any validators that were defined in the schema to the specified columns.
|
|
|
605 |
*
|
|
|
606 |
* @param string $dbName The name of the database
|
|
|
607 |
* @param string $tableName The name of the table
|
|
|
608 |
* @param array $columns Array of column names as key and column values as value.
|
|
|
609 |
*/
|
|
|
610 |
public static function doValidate($dbName, $tableName, $columns)
|
|
|
611 |
{
|
|
|
612 |
$dbMap = Propel::getDatabaseMap($dbName);
|
|
|
613 |
$tableMap = $dbMap->getTable($tableName);
|
|
|
614 |
$failureMap = array(); // map of ValidationFailed objects
|
|
|
615 |
foreach ($columns as $colName => $colValue) {
|
|
|
616 |
if ($tableMap->containsColumn($colName)) {
|
|
|
617 |
$col = $tableMap->getColumn($colName);
|
|
|
618 |
foreach ($col->getValidators() as $validatorMap) {
|
|
|
619 |
$validator = BasePeer::getValidator($validatorMap->getClass());
|
|
|
620 |
if ($validator && ($col->isNotNull() || $colValue !== null) && $validator->isValid($validatorMap, $colValue) === false) {
|
|
|
621 |
if (!isset($failureMap[$colName])) { // for now we do one ValidationFailed per column, not per rule
|
|
|
622 |
$failureMap[$colName] = new ValidationFailed($colName, $validatorMap->getMessage(), $validator);
|
|
|
623 |
}
|
|
|
624 |
}
|
|
|
625 |
}
|
|
|
626 |
}
|
|
|
627 |
}
|
|
|
628 |
return (!empty($failureMap) ? $failureMap : true);
|
|
|
629 |
}
|
|
|
630 |
|
|
|
631 |
/**
|
|
|
632 |
* Helper method which returns the primary key contained
|
|
|
633 |
* in the given Criteria object.
|
|
|
634 |
*
|
|
|
635 |
* @param Criteria $criteria A Criteria.
|
|
|
636 |
* @return ColumnMap If the Criteria object contains a primary
|
|
|
637 |
* key, or null if it doesn't.
|
|
|
638 |
* @throws PropelException
|
|
|
639 |
*/
|
|
|
640 |
private static function getPrimaryKey(Criteria $criteria)
|
|
|
641 |
{
|
|
|
642 |
// Assume all the keys are for the same table.
|
|
|
643 |
$keys = $criteria->keys();
|
|
|
644 |
$key = $keys[0];
|
|
|
645 |
$table = $criteria->getTableName($key);
|
|
|
646 |
|
|
|
647 |
$pk = null;
|
|
|
648 |
|
|
|
649 |
if (!empty($table)) {
|
|
|
650 |
|
|
|
651 |
$dbMap = Propel::getDatabaseMap($criteria->getDbName());
|
|
|
652 |
|
|
|
653 |
if ($dbMap === null) {
|
|
|
654 |
throw new PropelException("\$dbMap is null");
|
|
|
655 |
}
|
|
|
656 |
|
|
|
657 |
if ($dbMap->getTable($table) === null) {
|
|
|
658 |
throw new PropelException("\$dbMap->getTable() is null");
|
|
|
659 |
}
|
|
|
660 |
|
|
|
661 |
$columns = $dbMap->getTable($table)->getColumns();
|
|
|
662 |
foreach (array_keys($columns) as $key) {
|
|
|
663 |
if ($columns[$key]->isPrimaryKey()) {
|
|
|
664 |
$pk = $columns[$key];
|
|
|
665 |
break;
|
|
|
666 |
}
|
|
|
667 |
}
|
|
|
668 |
}
|
|
|
669 |
return $pk;
|
|
|
670 |
}
|
|
|
671 |
|
|
|
672 |
/**
|
|
|
673 |
* Checks whether the Criteria needs to use column aliasing
|
|
|
674 |
* This is implemented in a service class rather than in Criteria itself
|
|
|
675 |
* in order to avoid doing the tests when it's not necessary (e.g. for SELECTs)
|
|
|
676 |
*/
|
|
|
677 |
public static function needsSelectAliases(Criteria $criteria)
|
|
|
678 |
{
|
|
|
679 |
$columnNames = array();
|
|
|
680 |
foreach ($criteria->getSelectColumns() as $fullyQualifiedColumnName) {
|
|
|
681 |
if ($pos = strrpos($fullyQualifiedColumnName, '.')) {
|
|
|
682 |
$columnName = substr($fullyQualifiedColumnName, $pos);
|
|
|
683 |
if (isset($columnNames[$columnName])) {
|
|
|
684 |
// more than one column with the same name, so aliasing is required
|
|
|
685 |
return true;
|
|
|
686 |
}
|
|
|
687 |
$columnNames[$columnName] = true;
|
|
|
688 |
}
|
|
|
689 |
}
|
|
|
690 |
return false;
|
|
|
691 |
}
|
|
|
692 |
|
|
|
693 |
/**
|
|
|
694 |
* Ensures uniqueness of select column names by turning them all into aliases
|
|
|
695 |
* This is necessary for queries on more than one table when the tables share a column name
|
|
|
696 |
* @see http://propel.phpdb.org/trac/ticket/795
|
|
|
697 |
*
|
|
|
698 |
* @param Criteria $criteria
|
|
|
699 |
*
|
|
|
700 |
* @return Criteria The input, with Select columns replaced by aliases
|
|
|
701 |
*/
|
|
|
702 |
public static function turnSelectColumnsToAliases(Criteria $criteria)
|
|
|
703 |
{
|
|
|
704 |
$selectColumns = $criteria->getSelectColumns();
|
|
|
705 |
// clearSelectColumns also clears the aliases, so get them too
|
|
|
706 |
$asColumns = $criteria->getAsColumns();
|
|
|
707 |
$criteria->clearSelectColumns();
|
|
|
708 |
$columnAliases = $asColumns;
|
|
|
709 |
// add the select columns back
|
|
|
710 |
foreach ($selectColumns as $clause) {
|
|
|
711 |
// Generate a unique alias
|
|
|
712 |
$baseAlias = preg_replace('/\W/', '_', $clause);
|
|
|
713 |
$alias = $baseAlias;
|
|
|
714 |
// If it already exists, add a unique suffix
|
|
|
715 |
$i = 0;
|
|
|
716 |
while (isset($columnAliases[$alias])) {
|
|
|
717 |
$i++;
|
|
|
718 |
$alias = $baseAlias . '_' . $i;
|
|
|
719 |
}
|
|
|
720 |
// Add it as an alias
|
|
|
721 |
$criteria->addAsColumn($alias, $clause);
|
|
|
722 |
$columnAliases[$alias] = $clause;
|
|
|
723 |
}
|
|
|
724 |
// Add the aliases back, don't modify them
|
|
|
725 |
foreach ($asColumns as $name => $clause) {
|
|
|
726 |
$criteria->addAsColumn($name, $clause);
|
|
|
727 |
}
|
|
|
728 |
|
|
|
729 |
return $criteria;
|
|
|
730 |
}
|
|
|
731 |
|
|
|
732 |
/**
|
|
|
733 |
* Method to create an SQL query based on values in a Criteria.
|
|
|
734 |
*
|
|
|
735 |
* This method creates only prepared statement SQL (using ? where values
|
|
|
736 |
* will go). The second parameter ($params) stores the values that need
|
|
|
737 |
* to be set before the statement is executed. The reason we do it this way
|
|
|
738 |
* is to let the PDO layer handle all escaping & value formatting.
|
|
|
739 |
*
|
|
|
740 |
* @param Criteria $criteria Criteria for the SELECT query.
|
|
|
741 |
* @param array &$params Parameters that are to be replaced in prepared statement.
|
|
|
742 |
* @return string
|
|
|
743 |
* @throws PropelException Trouble creating the query string.
|
|
|
744 |
*/
|
|
|
745 |
public static function createSelectSql(Criteria $criteria, &$params)
|
|
|
746 |
{
|
|
|
747 |
$db = Propel::getDB($criteria->getDbName());
|
|
|
748 |
$dbMap = Propel::getDatabaseMap($criteria->getDbName());
|
|
|
749 |
|
|
|
750 |
// redundant definition $selectModifiers = array();
|
|
|
751 |
$selectClause = array();
|
|
|
752 |
$fromClause = array();
|
|
|
753 |
$joinClause = array();
|
|
|
754 |
$joinTables = array();
|
|
|
755 |
$whereClause = array();
|
|
|
756 |
$orderByClause = array();
|
|
|
757 |
// redundant definition $groupByClause = array();
|
|
|
758 |
|
|
|
759 |
$orderBy = $criteria->getOrderByColumns();
|
|
|
760 |
$groupBy = $criteria->getGroupByColumns();
|
|
|
761 |
$ignoreCase = $criteria->isIgnoreCase();
|
|
|
762 |
$select = $criteria->getSelectColumns();
|
|
|
763 |
$aliases = $criteria->getAsColumns();
|
|
|
764 |
|
|
|
765 |
// simple copy
|
|
|
766 |
$selectModifiers = $criteria->getSelectModifiers();
|
|
|
767 |
|
|
|
768 |
// get selected columns
|
|
|
769 |
foreach ($select as $columnName) {
|
|
|
770 |
|
|
|
771 |
// expect every column to be of "table.column" formation
|
|
|
772 |
// it could be a function: e.g. MAX(books.price)
|
|
|
773 |
|
|
|
774 |
$tableName = null;
|
|
|
775 |
|
|
|
776 |
$selectClause[] = $columnName; // the full column name: e.g. MAX(books.price)
|
|
|
777 |
|
|
|
778 |
$parenPos = strrpos($columnName, '(');
|
|
|
779 |
$dotPos = strrpos($columnName, '.', ($parenPos !== false ? $parenPos : 0));
|
|
|
780 |
|
|
|
781 |
// [HL] I think we really only want to worry about adding stuff to
|
|
|
782 |
// the fromClause if this function has a TABLE.COLUMN in it at all.
|
|
|
783 |
// e.g. COUNT(*) should not need this treatment -- or there needs to
|
|
|
784 |
// be special treatment for '*'
|
|
|
785 |
if ($dotPos !== false) {
|
|
|
786 |
|
|
|
787 |
if ($parenPos === false) { // table.column
|
|
|
788 |
$tableName = substr($columnName, 0, $dotPos);
|
|
|
789 |
} else { // FUNC(table.column)
|
|
|
790 |
$tableName = substr($columnName, $parenPos + 1, $dotPos - ($parenPos + 1));
|
|
|
791 |
// functions may contain qualifiers so only take the last
|
|
|
792 |
// word as the table name.
|
|
|
793 |
// COUNT(DISTINCT books.price)
|
|
|
794 |
$lastSpace = strpos($tableName, ' ');
|
|
|
795 |
if ($lastSpace !== false) { // COUNT(DISTINCT books.price)
|
|
|
796 |
$tableName = substr($tableName, $lastSpace + 1);
|
|
|
797 |
}
|
|
|
798 |
}
|
|
|
799 |
$tableName2 = $criteria->getTableForAlias($tableName);
|
|
|
800 |
if ($tableName2 !== null) {
|
|
|
801 |
$fromClause[] = $tableName2 . ' ' . $tableName;
|
|
|
802 |
} else {
|
|
|
803 |
$fromClause[] = $tableName;
|
|
|
804 |
}
|
|
|
805 |
|
|
|
806 |
} // if $dotPost !== null
|
|
|
807 |
}
|
|
|
808 |
|
|
|
809 |
// set the aliases
|
|
|
810 |
foreach ($aliases as $alias => $col) {
|
|
|
811 |
$selectClause[] = $col . " AS " . $alias;
|
|
|
812 |
}
|
|
|
813 |
|
|
|
814 |
// add the criteria to WHERE clause
|
|
|
815 |
// this will also add the table names to the FROM clause if they are not already
|
|
|
816 |
// invluded via a LEFT JOIN
|
|
|
817 |
foreach ($criteria->keys() as $key) {
|
|
|
818 |
|
|
|
819 |
$criterion = $criteria->getCriterion($key);
|
|
|
820 |
$someCriteria = $criterion->getAttachedCriterion();
|
|
|
821 |
$someCriteriaLength = count($someCriteria);
|
|
|
822 |
$table = null;
|
|
|
823 |
for ($i=0; $i < $someCriteriaLength; $i++) {
|
|
|
824 |
$tableName = $someCriteria[$i]->getTable();
|
|
|
825 |
|
|
|
826 |
$table = $criteria->getTableForAlias($tableName);
|
|
|
827 |
if ($table !== null) {
|
|
|
828 |
$fromClause[] = $table . ' ' . $tableName;
|
|
|
829 |
} else {
|
|
|
830 |
$fromClause[] = $tableName;
|
|
|
831 |
$table = $tableName;
|
|
|
832 |
}
|
|
|
833 |
|
|
|
834 |
$ignoreCase =
|
|
|
835 |
(($criteria->isIgnoreCase()
|
|
|
836 |
|| $someCriteria[$i]->isIgnoreCase())
|
|
|
837 |
&& (strpos($dbMap->getTable($table)->getColumn($someCriteria[$i]->getColumn())->getType(), "VARCHAR") !== false)
|
|
|
838 |
);
|
|
|
839 |
|
|
|
840 |
$someCriteria[$i]->setIgnoreCase($ignoreCase);
|
|
|
841 |
}
|
|
|
842 |
|
|
|
843 |
$criterion->setDB($db);
|
|
|
844 |
|
|
|
845 |
$sb = "";
|
|
|
846 |
$criterion->appendPsTo($sb, $params);
|
|
|
847 |
$whereClause[] = $sb;
|
|
|
848 |
}
|
|
|
849 |
|
|
|
850 |
// Handle joins
|
|
|
851 |
// joins with a null join type will be added to the FROM clause and the condition added to the WHERE clause.
|
|
|
852 |
// joins of a specified type: the LEFT side will be added to the fromClause and the RIGHT to the joinClause
|
|
|
853 |
foreach ((array) $criteria->getJoins() as $join) {
|
|
|
854 |
// The join might have been established using an alias name
|
|
|
855 |
$leftTable = $join->getLeftTableName();
|
|
|
856 |
$leftTableAlias = '';
|
|
|
857 |
if ($realTable = $criteria->getTableForAlias($leftTable)) {
|
|
|
858 |
$leftTableAlias = " $leftTable";
|
|
|
859 |
$leftTable = $realTable;
|
|
|
860 |
}
|
|
|
861 |
|
|
|
862 |
$rightTable = $join->getRightTableName();
|
|
|
863 |
$rightTableAlias = '';
|
|
|
864 |
if ($realTable = $criteria->getTableForAlias($rightTable)) {
|
|
|
865 |
$rightTableAlias = " $rightTable";
|
|
|
866 |
$rightTable = $realTable;
|
|
|
867 |
}
|
|
|
868 |
|
|
|
869 |
// determine if casing is relevant.
|
|
|
870 |
if ($ignoreCase = $criteria->isIgnoreCase()) {
|
|
|
871 |
$leftColType = $dbMap->getTable($leftTable)->getColumn($join->getLeftColumnName())->getType();
|
|
|
872 |
$rightColType = $dbMap->getTable($rightTable)->getColumn($join->getRightColumnName())->getType();
|
|
|
873 |
$ignoreCase = ($leftColType == 'string' || $rightColType == 'string');
|
|
|
874 |
}
|
|
|
875 |
|
|
|
876 |
// build the condition
|
|
|
877 |
$condition = '';
|
|
|
878 |
foreach ($join->getConditions() as $index => $conditionDesc)
|
|
|
879 |
{
|
|
|
880 |
if ($ignoreCase) {
|
|
|
881 |
$condition .= $db->ignoreCase($conditionDesc['left']) . $conditionDesc['operator'] . $db->ignoreCase($conditionDesc['right']);
|
|
|
882 |
} else {
|
|
|
883 |
$condition .= implode($conditionDesc);
|
|
|
884 |
}
|
|
|
885 |
if ($index + 1 < $join->countConditions()) {
|
|
|
886 |
$condition .= ' AND ';
|
|
|
887 |
}
|
|
|
888 |
}
|
|
|
889 |
|
|
|
890 |
// add 'em to the queues..
|
|
|
891 |
if ($joinType = $join->getJoinType()) {
|
|
|
892 |
// real join
|
|
|
893 |
if (!$fromClause) {
|
|
|
894 |
$fromClause[] = $leftTable . $leftTableAlias;
|
|
|
895 |
}
|
|
|
896 |
$joinTables[] = $rightTable . $rightTableAlias;
|
|
|
897 |
$joinClause[] = $join->getJoinType() . ' ' . $rightTable . $rightTableAlias . " ON ($condition)";
|
|
|
898 |
} else {
|
|
|
899 |
// implicit join, translates to a where
|
|
|
900 |
$fromClause[] = $leftTable . $leftTableAlias;
|
|
|
901 |
$fromClause[] = $rightTable . $rightTableAlias;
|
|
|
902 |
$whereClause[] = $condition;
|
|
|
903 |
}
|
|
|
904 |
}
|
|
|
905 |
|
|
|
906 |
// Unique from clause elements
|
|
|
907 |
$fromClause = array_unique($fromClause);
|
|
|
908 |
$fromClause = array_diff($fromClause, array(''));
|
|
|
909 |
|
|
|
910 |
// tables should not exist in both the from and join clauses
|
|
|
911 |
if ($joinTables && $fromClause) {
|
|
|
912 |
foreach ($fromClause as $fi => $ftable) {
|
|
|
913 |
if (in_array($ftable, $joinTables)) {
|
|
|
914 |
unset($fromClause[$fi]);
|
|
|
915 |
}
|
|
|
916 |
}
|
|
|
917 |
}
|
|
|
918 |
|
|
|
919 |
// Add the GROUP BY columns
|
|
|
920 |
$groupByClause = $groupBy;
|
|
|
921 |
|
|
|
922 |
$having = $criteria->getHaving();
|
|
|
923 |
$havingString = null;
|
|
|
924 |
if ($having !== null) {
|
|
|
925 |
$sb = "";
|
|
|
926 |
$having->appendPsTo($sb, $params);
|
|
|
927 |
$havingString = $sb;
|
|
|
928 |
}
|
|
|
929 |
|
|
|
930 |
if (!empty($orderBy)) {
|
|
|
931 |
|
|
|
932 |
foreach ($orderBy as $orderByColumn) {
|
|
|
933 |
|
|
|
934 |
// Add function expression as-is.
|
|
|
935 |
|
|
|
936 |
if (strpos($orderByColumn, '(') !== false) {
|
|
|
937 |
$orderByClause[] = $orderByColumn;
|
|
|
938 |
continue;
|
|
|
939 |
}
|
|
|
940 |
|
|
|
941 |
// Split orderByColumn (i.e. "table.column DESC")
|
|
|
942 |
|
|
|
943 |
$dotPos = strrpos($orderByColumn, '.');
|
|
|
944 |
|
|
|
945 |
if ($dotPos !== false) {
|
|
|
946 |
$tableName = substr($orderByColumn, 0, $dotPos);
|
|
|
947 |
$columnName = substr($orderByColumn, $dotPos+1);
|
|
|
948 |
}
|
|
|
949 |
else {
|
|
|
950 |
$tableName = '';
|
|
|
951 |
$columnName = $orderByColumn;
|
|
|
952 |
}
|
|
|
953 |
|
|
|
954 |
$spacePos = strpos($columnName, ' ');
|
|
|
955 |
|
|
|
956 |
if ($spacePos !== false) {
|
|
|
957 |
$direction = substr($columnName, $spacePos);
|
|
|
958 |
$columnName = substr($columnName, 0, $spacePos);
|
|
|
959 |
}
|
|
|
960 |
else {
|
|
|
961 |
$direction = '';
|
|
|
962 |
}
|
|
|
963 |
|
|
|
964 |
$tableAlias = $tableName;
|
|
|
965 |
if ($aliasTableName = $criteria->getTableForAlias($tableName)) {
|
|
|
966 |
$tableName = $aliasTableName;
|
|
|
967 |
}
|
|
|
968 |
|
|
|
969 |
$columnAlias = $columnName;
|
|
|
970 |
if ($asColumnName = $criteria->getColumnForAs($columnName)) {
|
|
|
971 |
$columnName = $asColumnName;
|
|
|
972 |
}
|
|
|
973 |
|
|
|
974 |
$column = $tableName ? $dbMap->getTable($tableName)->getColumn($columnName) : null;
|
|
|
975 |
|
|
|
976 |
if ($criteria->isIgnoreCase() && $column && $column->isText()) {
|
|
|
977 |
$orderByClause[] = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias") . $direction;
|
|
|
978 |
$selectClause[] = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias");
|
|
|
979 |
} else {
|
|
|
980 |
$orderByClause[] = $orderByColumn;
|
|
|
981 |
}
|
|
|
982 |
}
|
|
|
983 |
}
|
|
|
984 |
|
|
|
985 |
if (empty($fromClause) && $criteria->getPrimaryTableName()) {
|
|
|
986 |
$fromClause[] = $criteria->getPrimaryTableName();
|
|
|
987 |
}
|
|
|
988 |
|
|
|
989 |
// from / join tables quoten if it is necessary
|
|
|
990 |
if ($db->useQuoteIdentifier()) {
|
|
|
991 |
$fromClause = array_map(array($db, 'quoteIdentifierTable'), $fromClause);
|
|
|
992 |
$joinClause = $joinClause ? $joinClause : array_map(array($db, 'quoteIdentifierTable'), $joinClause);
|
|
|
993 |
}
|
|
|
994 |
|
|
|
995 |
// build from-clause
|
|
|
996 |
$from = '';
|
|
|
997 |
if (!empty($joinClause) && count($fromClause) > 1) {
|
|
|
998 |
$from .= implode(" CROSS JOIN ", $fromClause);
|
|
|
999 |
} else {
|
|
|
1000 |
$from .= implode(", ", $fromClause);
|
|
|
1001 |
}
|
|
|
1002 |
|
|
|
1003 |
$from .= $joinClause ? ' ' . implode(' ', $joinClause) : '';
|
|
|
1004 |
|
|
|
1005 |
// Build the SQL from the arrays we compiled
|
|
|
1006 |
$sql = "SELECT "
|
|
|
1007 |
.($selectModifiers ? implode(" ", $selectModifiers) . " " : "")
|
|
|
1008 |
.implode(", ", $selectClause)
|
|
|
1009 |
." FROM " . $from
|
|
|
1010 |
.($whereClause ? " WHERE ".implode(" AND ", $whereClause) : "")
|
|
|
1011 |
.($groupByClause ? " GROUP BY ".implode(",", $groupByClause) : "")
|
|
|
1012 |
.($havingString ? " HAVING ".$havingString : "")
|
|
|
1013 |
.($orderByClause ? " ORDER BY ".implode(",", $orderByClause) : "");
|
|
|
1014 |
|
|
|
1015 |
// APPLY OFFSET & LIMIT to the query.
|
|
|
1016 |
if ($criteria->getLimit() || $criteria->getOffset()) {
|
|
|
1017 |
$db->applyLimit($sql, $criteria->getOffset(), $criteria->getLimit());
|
|
|
1018 |
}
|
|
|
1019 |
|
|
|
1020 |
return $sql;
|
|
|
1021 |
}
|
|
|
1022 |
|
|
|
1023 |
/**
|
|
|
1024 |
* Builds a params array, like the kind populated by Criterion::appendPsTo().
|
|
|
1025 |
* This is useful for building an array even when it is not using the appendPsTo() method.
|
|
|
1026 |
* @param array $columns
|
|
|
1027 |
* @param Criteria $values
|
|
|
1028 |
* @return array params array('column' => ..., 'table' => ..., 'value' => ...)
|
|
|
1029 |
*/
|
|
|
1030 |
private static function buildParams($columns, Criteria $values)
|
|
|
1031 |
{
|
|
|
1032 |
$params = array();
|
|
|
1033 |
foreach ($columns as $key) {
|
|
|
1034 |
if ($values->containsKey($key)) {
|
|
|
1035 |
$crit = $values->getCriterion($key);
|
|
|
1036 |
$params[] = array('column' => $crit->getColumn(), 'table' => $crit->getTable(), 'value' => $crit->getValue());
|
|
|
1037 |
}
|
|
|
1038 |
}
|
|
|
1039 |
return $params;
|
|
|
1040 |
}
|
|
|
1041 |
|
|
|
1042 |
/**
|
|
|
1043 |
* This function searches for the given validator $name under propel/validator/$name.php,
|
|
|
1044 |
* imports and caches it.
|
|
|
1045 |
*
|
|
|
1046 |
* @param string $classname The dot-path name of class (e.g. myapp.propel.MyValidator)
|
|
|
1047 |
* @return Validator object or null if not able to instantiate validator class (and error will be logged in this case)
|
|
|
1048 |
*/
|
|
|
1049 |
public static function getValidator($classname)
|
|
|
1050 |
{
|
|
|
1051 |
try {
|
|
|
1052 |
$v = isset(self::$validatorMap[$classname]) ? self::$validatorMap[$classname] : null;
|
|
|
1053 |
if ($v === null) {
|
|
|
1054 |
$cls = Propel::importClass($classname);
|
|
|
1055 |
$v = new $cls();
|
|
|
1056 |
self::$validatorMap[$classname] = $v;
|
|
|
1057 |
}
|
|
|
1058 |
return $v;
|
|
|
1059 |
} catch (Exception $e) {
|
|
|
1060 |
Propel::log("BasePeer::getValidator(): failed trying to instantiate " . $classname . ": ".$e->getMessage(), Propel::LOG_ERR);
|
|
|
1061 |
}
|
|
|
1062 |
}
|
|
|
1063 |
|
|
|
1064 |
}
|