PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/runtime/lib/util/BasePeer.php

https://github.com/1989gaurav/Propel
PHP | 874 lines | 498 code | 133 blank | 243 comment | 94 complexity | 0560931b981549ef1e5af0289cad1f95 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. /**
  10. * This is a utility class for all generated Peer classes in the system.
  11. *
  12. * Peer classes are responsible for isolating all of the database access
  13. * for a specific business object. They execute all of the SQL
  14. * against the database. Over time this class has grown to include
  15. * utility methods which ease execution of cross-database queries and
  16. * the implementation of concrete Peers.
  17. *
  18. * @author Hans Lellelid <hans@xmpl.org> (Propel)
  19. * @author Kaspars Jaudzems <kaspars.jaudzems@inbox.lv> (Propel)
  20. * @author Heltem <heltem@o2php.com> (Propel)
  21. * @author Frank Y. Kim <frank.kim@clearink.com> (Torque)
  22. * @author John D. McNally <jmcnally@collab.net> (Torque)
  23. * @author Brett McLaughlin <bmclaugh@algx.net> (Torque)
  24. * @author Stephen Haberman <stephenh@chase3000.com> (Torque)
  25. * @version $Revision$
  26. * @package propel.runtime.util
  27. */
  28. class BasePeer
  29. {
  30. /** Array (hash) that contains the cached mapBuilders. */
  31. private static $mapBuilders = array();
  32. /** Array (hash) that contains cached validators */
  33. private static $validatorMap = array();
  34. /**
  35. * phpname type
  36. * e.g. 'AuthorId'
  37. */
  38. const TYPE_PHPNAME = 'phpName';
  39. /**
  40. * studlyphpname type
  41. * e.g. 'authorId'
  42. */
  43. const TYPE_STUDLYPHPNAME = 'studlyPhpName';
  44. /**
  45. * column (peer) name type
  46. * e.g. 'book.AUTHOR_ID'
  47. */
  48. const TYPE_COLNAME = 'colName';
  49. /**
  50. * column part of the column peer name
  51. * e.g. 'AUTHOR_ID'
  52. */
  53. const TYPE_RAW_COLNAME = 'rawColName';
  54. /**
  55. * column fieldname type
  56. * e.g. 'author_id'
  57. */
  58. const TYPE_FIELDNAME = 'fieldName';
  59. /**
  60. * num type
  61. * simply the numerical array index, e.g. 4
  62. */
  63. const TYPE_NUM = 'num';
  64. static public function getFieldnames ($classname, $type = self::TYPE_PHPNAME) {
  65. // TODO we should take care of including the peer class here
  66. $peerclass = 'Base' . $classname . 'Peer'; // TODO is this always true?
  67. $callable = array($peerclass, 'getFieldnames');
  68. return call_user_func($callable, $type);
  69. }
  70. static public function translateFieldname($classname, $fieldname, $fromType, $toType) {
  71. // TODO we should take care of including the peer class here
  72. $peerclass = 'Base' . $classname . 'Peer'; // TODO is this always true?
  73. $callable = array($peerclass, 'translateFieldname');
  74. $args = array($fieldname, $fromType, $toType);
  75. return call_user_func_array($callable, $args);
  76. }
  77. /**
  78. * Method to perform deletes based on values and keys in a
  79. * Criteria.
  80. *
  81. * @param Criteria $criteria The criteria to use.
  82. * @param PropelPDO $con A PropelPDO connection object.
  83. * @return int The number of rows affected by last statement execution. For most
  84. * uses there is only one delete statement executed, so this number
  85. * will correspond to the number of rows affected by the call to this
  86. * method. Note that the return value does require that this information
  87. * is returned (supported) by the PDO driver.
  88. * @throws PropelException
  89. */
  90. public static function doDelete(Criteria $criteria, PropelPDO $con)
  91. {
  92. $db = Propel::getDB($criteria->getDbName());
  93. $dbMap = Propel::getDatabaseMap($criteria->getDbName());
  94. //join are not supported with DELETE statement
  95. if (count($criteria->getJoins())) {
  96. throw new PropelException('Delete does not support join');
  97. }
  98. // Set up a list of required tables (one DELETE statement will
  99. // be executed per table)
  100. $tables = $criteria->getTablesColumns();
  101. if (empty($tables)) {
  102. throw new PropelException("Cannot delete from an empty Criteria");
  103. }
  104. $affectedRows = 0; // initialize this in case the next loop has no iterations.
  105. foreach ($tables as $tableName => $columns) {
  106. $whereClause = array();
  107. $params = array();
  108. $stmt = null;
  109. try {
  110. $sql = $db->getDeleteFromClause($criteria, $tableName);
  111. foreach ($columns as $colName) {
  112. $sb = "";
  113. $criteria->getCriterion($colName)->appendPsTo($sb, $params);
  114. $whereClause[] = $sb;
  115. }
  116. $sql .= " WHERE " . implode(" AND ", $whereClause);
  117. $stmt = $con->prepare($sql);
  118. $db->bindValues($stmt, $params, $dbMap);
  119. $stmt->execute();
  120. $affectedRows = $stmt->rowCount();
  121. } catch (Exception $e) {
  122. Propel::log($e->getMessage(), Propel::LOG_ERR);
  123. throw new PropelException(sprintf('Unable to execute DELETE statement [%s]', $sql), $e);
  124. }
  125. } // for each table
  126. return $affectedRows;
  127. }
  128. /**
  129. * Method to deletes all contents of specified table.
  130. *
  131. * This method is invoked from generated Peer classes like this:
  132. * <code>
  133. * public static function doDeleteAll($con = null)
  134. * {
  135. * if ($con === null) $con = Propel::getConnection(self::DATABASE_NAME);
  136. * BasePeer::doDeleteAll(self::TABLE_NAME, $con, self::DATABASE_NAME);
  137. * }
  138. * </code>
  139. *
  140. * @param string $tableName The name of the table to empty.
  141. * @param PropelPDO $con A PropelPDO connection object.
  142. * @param string $databaseName the name of the database.
  143. * @return int The number of rows affected by the statement. Note
  144. * that the return value does require that this information
  145. * is returned (supported) by the Propel db driver.
  146. * @throws PropelException - wrapping SQLException caught from statement execution.
  147. */
  148. public static function doDeleteAll($tableName, PropelPDO $con, $databaseName = null)
  149. {
  150. try {
  151. $db = Propel::getDB($databaseName);
  152. if ($db->useQuoteIdentifier()) {
  153. $tableName = $db->quoteIdentifierTable($tableName);
  154. }
  155. $sql = "DELETE FROM " . $tableName;
  156. $stmt = $con->prepare($sql);
  157. $stmt->execute();
  158. return $stmt->rowCount();
  159. } catch (Exception $e) {
  160. Propel::log($e->getMessage(), Propel::LOG_ERR);
  161. throw new PropelException(sprintf('Unable to execute DELETE ALL statement [%s]', $sql), $e);
  162. }
  163. }
  164. /**
  165. * Method to perform inserts based on values and keys in a
  166. * Criteria.
  167. * <p>
  168. * If the primary key is auto incremented the data in Criteria
  169. * will be inserted and the auto increment value will be returned.
  170. * <p>
  171. * If the primary key is included in Criteria then that value will
  172. * be used to insert the row.
  173. * <p>
  174. * If no primary key is included in Criteria then we will try to
  175. * figure out the primary key from the database map and insert the
  176. * row with the next available id using util.db.IDBroker.
  177. * <p>
  178. * If no primary key is defined for the table the values will be
  179. * inserted as specified in Criteria and null will be returned.
  180. *
  181. * @param Criteria $criteria Object containing values to insert.
  182. * @param PropelPDO $con A PropelPDO connection.
  183. * @return mixed The primary key for the new row if (and only if!) the primary key
  184. * is auto-generated. Otherwise will return <code>null</code>.
  185. * @throws PropelException
  186. */
  187. public static function doInsert(Criteria $criteria, PropelPDO $con) {
  188. // the primary key
  189. $id = null;
  190. $db = Propel::getDB($criteria->getDbName());
  191. // Get the table name and method for determining the primary
  192. // key value.
  193. $keys = $criteria->keys();
  194. if (!empty($keys)) {
  195. $tableName = $criteria->getTableName( $keys[0] );
  196. } else {
  197. throw new PropelException("Database insert attempted without anything specified to insert");
  198. }
  199. $dbMap = Propel::getDatabaseMap($criteria->getDbName());
  200. $tableMap = $dbMap->getTable($tableName);
  201. $keyInfo = $tableMap->getPrimaryKeyMethodInfo();
  202. $useIdGen = $tableMap->isUseIdGenerator();
  203. //$keyGen = $con->getIdGenerator();
  204. $pk = self::getPrimaryKey($criteria);
  205. // only get a new key value if you need to
  206. // the reason is that a primary key might be defined
  207. // but you are still going to set its value. for example:
  208. // a join table where both keys are primary and you are
  209. // setting both columns with your own values
  210. // pk will be null if there is no primary key defined for the table
  211. // we're inserting into.
  212. if ($pk !== null && $useIdGen && !$criteria->keyContainsValue($pk->getFullyQualifiedName()) && $db->isGetIdBeforeInsert()) {
  213. try {
  214. $id = $db->getId($con, $keyInfo);
  215. } catch (Exception $e) {
  216. throw new PropelException("Unable to get sequence id.", $e);
  217. }
  218. $criteria->add($pk->getFullyQualifiedName(), $id);
  219. }
  220. try {
  221. $adapter = Propel::getDB($criteria->getDBName());
  222. $qualifiedCols = $criteria->keys(); // we need table.column cols when populating values
  223. $columns = array(); // but just 'column' cols for the SQL
  224. foreach ($qualifiedCols as $qualifiedCol) {
  225. $columns[] = substr($qualifiedCol, strrpos($qualifiedCol, '.') + 1);
  226. }
  227. // add identifiers
  228. if ($adapter->useQuoteIdentifier()) {
  229. $columns = array_map(array($adapter, 'quoteIdentifier'), $columns);
  230. $tableName = $adapter->quoteIdentifierTable($tableName);
  231. }
  232. $sql = 'INSERT INTO ' . $tableName
  233. . ' (' . implode(',', $columns) . ')'
  234. . ' VALUES (';
  235. // . substr(str_repeat("?,", count($columns)), 0, -1) .
  236. for($p=1, $cnt=count($columns); $p <= $cnt; $p++) {
  237. $sql .= ':p'.$p;
  238. if ($p !== $cnt) $sql .= ',';
  239. }
  240. $sql .= ')';
  241. $params = self::buildParams($qualifiedCols, $criteria);
  242. $db->cleanupSQL($sql, $params, $criteria, $dbMap);
  243. $stmt = $con->prepare($sql);
  244. $db->bindValues($stmt, $params, $dbMap, $db);
  245. $stmt->execute();
  246. } catch (Exception $e) {
  247. Propel::log($e->getMessage(), Propel::LOG_ERR);
  248. throw new PropelException(sprintf('Unable to execute INSERT statement [%s]', $sql), $e);
  249. }
  250. // If the primary key column is auto-incremented, get the id now.
  251. if ($pk !== null && $useIdGen && $db->isGetIdAfterInsert()) {
  252. try {
  253. $id = $db->getId($con, $keyInfo);
  254. } catch (Exception $e) {
  255. throw new PropelException("Unable to get autoincrement id.", $e);
  256. }
  257. }
  258. return $id;
  259. }
  260. /**
  261. * Method used to update rows in the DB. Rows are selected based
  262. * on selectCriteria and updated using values in updateValues.
  263. * <p>
  264. * Use this method for performing an update of the kind:
  265. * <p>
  266. * WHERE some_column = some value AND could_have_another_column =
  267. * another value AND so on.
  268. *
  269. * @param $selectCriteria A Criteria object containing values used in where
  270. * clause.
  271. * @param $updateValues A Criteria object containing values used in set
  272. * clause.
  273. * @param PropelPDO $con The PropelPDO connection object to use.
  274. * @return int The number of rows affected by last update statement. For most
  275. * uses there is only one update statement executed, so this number
  276. * will correspond to the number of rows affected by the call to this
  277. * method. Note that the return value does require that this information
  278. * is returned (supported) by the Propel db driver.
  279. * @throws PropelException
  280. */
  281. public static function doUpdate(Criteria $selectCriteria, Criteria $updateValues, PropelPDO $con) {
  282. $db = Propel::getDB($selectCriteria->getDbName());
  283. $dbMap = Propel::getDatabaseMap($selectCriteria->getDbName());
  284. // Get list of required tables, containing all columns
  285. $tablesColumns = $selectCriteria->getTablesColumns();
  286. if (empty($tablesColumns)) {
  287. $tablesColumns = array($selectCriteria->getPrimaryTableName() => array());
  288. }
  289. // we also need the columns for the update SQL
  290. $updateTablesColumns = $updateValues->getTablesColumns();
  291. $affectedRows = 0; // initialize this in case the next loop has no iterations.
  292. foreach ($tablesColumns as $tableName => $columns) {
  293. $whereClause = array();
  294. $params = array();
  295. $stmt = null;
  296. try {
  297. $sql = 'UPDATE ';
  298. if ($queryComment = $selectCriteria->getComment()) {
  299. $sql .= '/* ' . $queryComment . ' */ ';
  300. }
  301. // is it a table alias?
  302. if ($tableName2 = $selectCriteria->getTableForAlias($tableName)) {
  303. $udpateTable = $tableName2 . ' ' . $tableName;
  304. $tableName = $tableName2;
  305. } else {
  306. $udpateTable = $tableName;
  307. }
  308. if ($db->useQuoteIdentifier()) {
  309. $sql .= $db->quoteIdentifierTable($udpateTable);
  310. } else {
  311. $sql .= $udpateTable;
  312. }
  313. $sql .= " SET ";
  314. $p = 1;
  315. foreach ($updateTablesColumns[$tableName] as $col) {
  316. $updateColumnName = substr($col, strrpos($col, '.') + 1);
  317. // add identifiers for the actual database?
  318. if ($db->useQuoteIdentifier()) {
  319. $updateColumnName = $db->quoteIdentifier($updateColumnName);
  320. }
  321. if ($updateValues->getComparison($col) != Criteria::CUSTOM_EQUAL) {
  322. $sql .= $updateColumnName . '=:p'.$p++.', ';
  323. } else {
  324. $param = $updateValues->get($col);
  325. $sql .= $updateColumnName . ' = ';
  326. if (is_array($param)) {
  327. if (isset($param['raw'])) {
  328. $raw = $param['raw'];
  329. $rawcvt = '';
  330. // parse the $params['raw'] for ? chars
  331. for($r=0,$len=strlen($raw); $r < $len; $r++) {
  332. if ($raw{$r} == '?') {
  333. $rawcvt .= ':p'.$p++;
  334. } else {
  335. $rawcvt .= $raw{$r};
  336. }
  337. }
  338. $sql .= $rawcvt . ', ';
  339. } else {
  340. $sql .= ':p'.$p++.', ';
  341. }
  342. if (isset($param['value'])) {
  343. $updateValues->put($col, $param['value']);
  344. }
  345. } else {
  346. $updateValues->remove($col);
  347. $sql .= $param . ', ';
  348. }
  349. }
  350. }
  351. $params = self::buildParams($updateTablesColumns[$tableName], $updateValues);
  352. $sql = substr($sql, 0, -2);
  353. if (!empty($columns)) {
  354. foreach ($columns as $colName) {
  355. $sb = "";
  356. $selectCriteria->getCriterion($colName)->appendPsTo($sb, $params);
  357. $whereClause[] = $sb;
  358. }
  359. $sql .= " WHERE " . implode(" AND ", $whereClause);
  360. }
  361. $db->cleanupSQL($sql, $params, $updateValues, $dbMap);
  362. $stmt = $con->prepare($sql);
  363. // Replace ':p?' with the actual values
  364. $db->bindValues($stmt, $params, $dbMap, $db);
  365. $stmt->execute();
  366. $affectedRows = $stmt->rowCount();
  367. $stmt = null; // close
  368. } catch (Exception $e) {
  369. if ($stmt) $stmt = null; // close
  370. Propel::log($e->getMessage(), Propel::LOG_ERR);
  371. throw new PropelException(sprintf('Unable to execute UPDATE statement [%s]', $sql), $e);
  372. }
  373. } // foreach table in the criteria
  374. return $affectedRows;
  375. }
  376. /**
  377. * Executes query build by createSelectSql() and returns the resultset statement.
  378. *
  379. * @param Criteria $criteria A Criteria.
  380. * @param PropelPDO $con A PropelPDO connection to use.
  381. * @return PDOStatement The resultset.
  382. * @throws PropelException
  383. * @see createSelectSql()
  384. */
  385. public static function doSelect(Criteria $criteria, PropelPDO $con = null)
  386. {
  387. $dbMap = Propel::getDatabaseMap($criteria->getDbName());
  388. $db = Propel::getDB($criteria->getDbName());
  389. $stmt = null;
  390. if ($con === null) {
  391. $con = Propel::getConnection($criteria->getDbName(), Propel::CONNECTION_READ);
  392. }
  393. try {
  394. $params = array();
  395. $sql = self::createSelectSql($criteria, $params);
  396. $stmt = $con->prepare($sql);
  397. $db->bindValues($stmt, $params, $dbMap);
  398. $stmt->execute();
  399. } catch (Exception $e) {
  400. if ($stmt) {
  401. $stmt = null; // close
  402. }
  403. Propel::log($e->getMessage(), Propel::LOG_ERR);
  404. throw new PropelException(sprintf('Unable to execute SELECT statement [%s]', $sql), $e);
  405. }
  406. return $stmt;
  407. }
  408. /**
  409. * Executes a COUNT query using either a simple SQL rewrite or, for more complex queries, a
  410. * sub-select of the SQL created by createSelectSql() and returns the statement.
  411. *
  412. * @param Criteria $criteria A Criteria.
  413. * @param PropelPDO $con A PropelPDO connection to use.
  414. * @return PDOStatement The resultset statement.
  415. * @throws PropelException
  416. * @see createSelectSql()
  417. */
  418. public static function doCount(Criteria $criteria, PropelPDO $con = null)
  419. {
  420. $dbMap = Propel::getDatabaseMap($criteria->getDbName());
  421. $db = Propel::getDB($criteria->getDbName());
  422. if ($con === null) {
  423. $con = Propel::getConnection($criteria->getDbName(), Propel::CONNECTION_READ);
  424. }
  425. $stmt = null;
  426. $needsComplexCount = $criteria->getGroupByColumns()
  427. || $criteria->getOffset()
  428. || $criteria->getLimit()
  429. || $criteria->getHaving()
  430. || in_array(Criteria::DISTINCT, $criteria->getSelectModifiers());
  431. try {
  432. $params = array();
  433. if ($needsComplexCount) {
  434. if (self::needsSelectAliases($criteria)) {
  435. if ($criteria->getHaving()) {
  436. throw new PropelException('Propel cannot create a COUNT query when using HAVING and duplicate column names in the SELECT part');
  437. }
  438. $db->turnSelectColumnsToAliases($criteria);
  439. }
  440. $selectSql = self::createSelectSql($criteria, $params);
  441. $sql = 'SELECT COUNT(*) FROM (' . $selectSql . ') propelmatch4cnt';
  442. } else {
  443. // Replace SELECT columns with COUNT(*)
  444. $criteria->clearSelectColumns()->addSelectColumn('COUNT(*)');
  445. $sql = self::createSelectSql($criteria, $params);
  446. }
  447. $stmt = $con->prepare($sql);
  448. $db->bindValues($stmt, $params, $dbMap);
  449. $stmt->execute();
  450. } catch (Exception $e) {
  451. if ($stmt !== null) {
  452. $stmt = null;
  453. }
  454. Propel::log($e->getMessage(), Propel::LOG_ERR);
  455. throw new PropelException(sprintf('Unable to execute COUNT statement [%s]', $sql), $e);
  456. }
  457. return $stmt;
  458. }
  459. /**
  460. * Applies any validators that were defined in the schema to the specified columns.
  461. *
  462. * @param string $dbName The name of the database
  463. * @param string $tableName The name of the table
  464. * @param array $columns Array of column names as key and column values as value.
  465. */
  466. public static function doValidate($dbName, $tableName, $columns)
  467. {
  468. $dbMap = Propel::getDatabaseMap($dbName);
  469. $tableMap = $dbMap->getTable($tableName);
  470. $failureMap = array(); // map of ValidationFailed objects
  471. foreach ($columns as $colName => $colValue) {
  472. if ($tableMap->containsColumn($colName)) {
  473. $col = $tableMap->getColumn($colName);
  474. foreach ($col->getValidators() as $validatorMap) {
  475. $validator = BasePeer::getValidator($validatorMap->getClass());
  476. if ($validator && ($col->isNotNull() || $colValue !== null) && $validator->isValid($validatorMap, $colValue) === false) {
  477. if (!isset($failureMap[$colName])) { // for now we do one ValidationFailed per column, not per rule
  478. $failureMap[$colName] = new ValidationFailed($colName, $validatorMap->getMessage(), $validator);
  479. }
  480. }
  481. }
  482. }
  483. }
  484. return (!empty($failureMap) ? $failureMap : true);
  485. }
  486. /**
  487. * Helper method which returns the primary key contained
  488. * in the given Criteria object.
  489. *
  490. * @param Criteria $criteria A Criteria.
  491. * @return ColumnMap If the Criteria object contains a primary
  492. * key, or null if it doesn't.
  493. * @throws PropelException
  494. */
  495. private static function getPrimaryKey(Criteria $criteria)
  496. {
  497. // Assume all the keys are for the same table.
  498. $keys = $criteria->keys();
  499. $key = $keys[0];
  500. $table = $criteria->getTableName($key);
  501. $pk = null;
  502. if (!empty($table)) {
  503. $dbMap = Propel::getDatabaseMap($criteria->getDbName());
  504. $pks = $dbMap->getTable($table)->getPrimaryKeys();
  505. if (!empty($pks)) {
  506. $pk = array_shift($pks);
  507. }
  508. }
  509. return $pk;
  510. }
  511. /**
  512. * Checks whether the Criteria needs to use column aliasing
  513. * This is implemented in a service class rather than in Criteria itself
  514. * in order to avoid doing the tests when it's not necessary (e.g. for SELECTs)
  515. */
  516. public static function needsSelectAliases(Criteria $criteria)
  517. {
  518. $columnNames = array();
  519. foreach ($criteria->getSelectColumns() as $fullyQualifiedColumnName) {
  520. if ($pos = strrpos($fullyQualifiedColumnName, '.')) {
  521. $columnName = substr($fullyQualifiedColumnName, $pos);
  522. if (isset($columnNames[$columnName])) {
  523. // more than one column with the same name, so aliasing is required
  524. return true;
  525. }
  526. $columnNames[$columnName] = true;
  527. }
  528. }
  529. return false;
  530. }
  531. /**
  532. * Method to create an SQL query based on values in a Criteria.
  533. *
  534. * This method creates only prepared statement SQL (using ? where values
  535. * will go). The second parameter ($params) stores the values that need
  536. * to be set before the statement is executed. The reason we do it this way
  537. * is to let the PDO layer handle all escaping & value formatting.
  538. *
  539. * @param Criteria $criteria Criteria for the SELECT query.
  540. * @param array &$params Parameters that are to be replaced in prepared statement.
  541. * @return string
  542. * @throws PropelException Trouble creating the query string.
  543. */
  544. public static function createSelectSql(Criteria $criteria, &$params)
  545. {
  546. $db = Propel::getDB($criteria->getDbName());
  547. $dbMap = Propel::getDatabaseMap($criteria->getDbName());
  548. $fromClause = array();
  549. $joinClause = array();
  550. $joinTables = array();
  551. $whereClause = array();
  552. $orderByClause = array();
  553. $orderBy = $criteria->getOrderByColumns();
  554. $groupBy = $criteria->getGroupByColumns();
  555. $ignoreCase = $criteria->isIgnoreCase();
  556. // get the first part of the SQL statement, the SELECT part
  557. $selectSql = $db->createSelectSqlPart($criteria, $fromClause);
  558. // Handle joins
  559. // joins with a null join type will be added to the FROM clause and the condition added to the WHERE clause.
  560. // joins of a specified type: the LEFT side will be added to the fromClause and the RIGHT to the joinClause
  561. foreach ($criteria->getJoins() as $join) {
  562. $join->setDB($db);
  563. // add 'em to the queues..
  564. if (!$fromClause) {
  565. $fromClause[] = $join->getLeftTableWithAlias();
  566. }
  567. $joinTables[] = $join->getRightTableWithAlias();
  568. $joinClause[] = $join->getClause($params);
  569. }
  570. // add the criteria to WHERE clause
  571. // this will also add the table names to the FROM clause if they are not already
  572. // included via a LEFT JOIN
  573. foreach ($criteria->keys() as $key) {
  574. $criterion = $criteria->getCriterion($key);
  575. $table = null;
  576. foreach ($criterion->getAttachedCriterion() as $attachedCriterion) {
  577. $tableName = $attachedCriterion->getTable();
  578. $table = $criteria->getTableForAlias($tableName);
  579. if ($table !== null) {
  580. $fromClause[] = $table . ' ' . $tableName;
  581. } else {
  582. $fromClause[] = $tableName;
  583. $table = $tableName;
  584. }
  585. if (($criteria->isIgnoreCase() || $attachedCriterion->isIgnoreCase())
  586. && $dbMap->getTable($table)->getColumn($attachedCriterion->getColumn())->isText()) {
  587. $attachedCriterion->setIgnoreCase(true);
  588. }
  589. }
  590. $criterion->setDB($db);
  591. $sb = '';
  592. $criterion->appendPsTo($sb, $params);
  593. $whereClause[] = $sb;
  594. }
  595. // Unique from clause elements
  596. $fromClause = array_unique($fromClause);
  597. $fromClause = array_diff($fromClause, array(''));
  598. // tables should not exist in both the from and join clauses
  599. if ($joinTables && $fromClause) {
  600. foreach ($fromClause as $fi => $ftable) {
  601. if (in_array($ftable, $joinTables)) {
  602. unset($fromClause[$fi]);
  603. }
  604. }
  605. }
  606. // Add the GROUP BY columns
  607. $groupByClause = $groupBy;
  608. $having = $criteria->getHaving();
  609. $havingString = null;
  610. if ($having !== null) {
  611. $sb = '';
  612. $having->appendPsTo($sb, $params);
  613. $havingString = $sb;
  614. }
  615. if (!empty($orderBy)) {
  616. foreach ($orderBy as $orderByColumn) {
  617. // Add function expression as-is.
  618. if (strpos($orderByColumn, '(') !== false) {
  619. $orderByClause[] = $orderByColumn;
  620. continue;
  621. }
  622. // Split orderByColumn (i.e. "table.column DESC")
  623. $dotPos = strrpos($orderByColumn, '.');
  624. if ($dotPos !== false) {
  625. $tableName = substr($orderByColumn, 0, $dotPos);
  626. $columnName = substr($orderByColumn, $dotPos + 1);
  627. } else {
  628. $tableName = '';
  629. $columnName = $orderByColumn;
  630. }
  631. $spacePos = strpos($columnName, ' ');
  632. if ($spacePos !== false) {
  633. $direction = substr($columnName, $spacePos);
  634. $columnName = substr($columnName, 0, $spacePos);
  635. } else {
  636. $direction = '';
  637. }
  638. $tableAlias = $tableName;
  639. if ($aliasTableName = $criteria->getTableForAlias($tableName)) {
  640. $tableName = $aliasTableName;
  641. }
  642. $columnAlias = $columnName;
  643. if ($asColumnName = $criteria->getColumnForAs($columnName)) {
  644. $columnName = $asColumnName;
  645. }
  646. $column = $tableName ? $dbMap->getTable($tableName)->getColumn($columnName) : null;
  647. if ($criteria->isIgnoreCase() && $column && $column->isText()) {
  648. $ignoreCaseColumn = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias");
  649. $orderByClause[] = $ignoreCaseColumn . $direction;
  650. $selectSql .= ', ' . $ignoreCaseColumn;
  651. } else {
  652. $orderByClause[] = $orderByColumn;
  653. }
  654. }
  655. }
  656. if (empty($fromClause) && $criteria->getPrimaryTableName()) {
  657. $fromClause[] = $criteria->getPrimaryTableName();
  658. }
  659. // tables should not exist as alias of subQuery
  660. if ($criteria->hasSelectQueries()) {
  661. foreach ($fromClause as $key => $ftable) {
  662. if (strpos($ftable, ' ') !== false) {
  663. list($realtable, $tableName) = explode(' ', $ftable);
  664. } else {
  665. $tableName = $ftable;
  666. }
  667. if ($criteria->hasSelectQuery($tableName)) {
  668. unset($fromClause[$key]);
  669. }
  670. }
  671. }
  672. // from / join tables quoted if it is necessary
  673. if ($db->useQuoteIdentifier()) {
  674. $fromClause = array_map(array($db, 'quoteIdentifierTable'), $fromClause);
  675. $joinClause = $joinClause ? $joinClause : array_map(array($db, 'quoteIdentifierTable'), $joinClause);
  676. }
  677. // add subQuery to From after adding quotes
  678. foreach ($criteria->getSelectQueries() as $subQueryAlias => $subQueryCriteria) {
  679. $fromClause[] = '(' . BasePeer::createSelectSql($subQueryCriteria, $params) . ') AS ' . $subQueryAlias;
  680. }
  681. // build from-clause
  682. $from = '';
  683. if (!empty($joinClause) && count($fromClause) > 1) {
  684. $from .= implode(" CROSS JOIN ", $fromClause);
  685. } else {
  686. $from .= implode(", ", $fromClause);
  687. }
  688. $from .= $joinClause ? ' ' . implode(' ', $joinClause) : '';
  689. // Build the SQL from the arrays we compiled
  690. $sql = $selectSql
  691. ." FROM " . $from
  692. .($whereClause ? " WHERE ".implode(" AND ", $whereClause) : "")
  693. .($groupByClause ? " GROUP BY ".implode(",", $groupByClause) : "")
  694. .($havingString ? " HAVING ".$havingString : "")
  695. .($orderByClause ? " ORDER BY ".implode(",", $orderByClause) : "");
  696. // APPLY OFFSET & LIMIT to the query.
  697. if ($criteria->getLimit() || $criteria->getOffset()) {
  698. $db->applyLimit($sql, $criteria->getOffset(), $criteria->getLimit(), $criteria);
  699. }
  700. return $sql;
  701. }
  702. /**
  703. * Builds a params array, like the kind populated by Criterion::appendPsTo().
  704. * This is useful for building an array even when it is not using the appendPsTo() method.
  705. * @param array $columns
  706. * @param Criteria $values
  707. * @return array params array('column' => ..., 'table' => ..., 'value' => ...)
  708. */
  709. private static function buildParams($columns, Criteria $values)
  710. {
  711. $params = array();
  712. foreach ($columns as $key) {
  713. if ($values->containsKey($key)) {
  714. $crit = $values->getCriterion($key);
  715. $params[] = array('column' => $crit->getColumn(), 'table' => $crit->getTable(), 'value' => $crit->getValue());
  716. }
  717. }
  718. return $params;
  719. }
  720. /**
  721. * This function searches for the given validator $name under propel/validator/$name.php,
  722. * imports and caches it.
  723. *
  724. * @param string $classname The dot-path name of class (e.g. myapp.propel.MyValidator)
  725. * @return Validator object or null if not able to instantiate validator class (and error will be logged in this case)
  726. */
  727. public static function getValidator($classname)
  728. {
  729. try {
  730. $v = isset(self::$validatorMap[$classname]) ? self::$validatorMap[$classname] : null;
  731. if ($v === null) {
  732. $cls = Propel::importClass($classname);
  733. $v = new $cls();
  734. self::$validatorMap[$classname] = $v;
  735. }
  736. return $v;
  737. } catch (Exception $e) {
  738. Propel::log("BasePeer::getValidator(): failed trying to instantiate " . $classname . ": ".$e->getMessage(), Propel::LOG_ERR);
  739. }
  740. }
  741. }