12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100 |
- <?php
- /**
- * This file is part of the Propel package.
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- *
- * @license MIT License
- */
- /**
- * This is a utility class for all generated Peer classes in the system.
- *
- * Peer classes are responsible for isolating all of the database access
- * for a specific business object. They execute all of the SQL
- * against the database. Over time this class has grown to include
- * utility methods which ease execution of cross-database queries and
- * the implementation of concrete Peers.
- *
- * @author Hans Lellelid <hans@xmpl.org> (Propel)
- * @author Kaspars Jaudzems <kaspars.jaudzems@inbox.lv> (Propel)
- * @author Heltem <heltem@o2php.com> (Propel)
- * @author Frank Y. Kim <frank.kim@clearink.com> (Torque)
- * @author John D. McNally <jmcnally@collab.net> (Torque)
- * @author Brett McLaughlin <bmclaugh@algx.net> (Torque)
- * @author Stephen Haberman <stephenh@chase3000.com> (Torque)
- * @version $Revision: 1772 $
- * @package propel.runtime.util
- */
- class BasePeer
- {
- /** Array (hash) that contains the cached mapBuilders. */
- private static $mapBuilders = array();
- /** Array (hash) that contains cached validators */
- private static $validatorMap = array();
- /**
- * phpname type
- * e.g. 'AuthorId'
- */
- const TYPE_PHPNAME = 'phpName';
- /**
- * studlyphpname type
- * e.g. 'authorId'
- */
- const TYPE_STUDLYPHPNAME = 'studlyPhpName';
- /**
- * column (peer) name type
- * e.g. 'book.AUTHOR_ID'
- */
- const TYPE_COLNAME = 'colName';
- /**
- * column part of the column peer name
- * e.g. 'AUTHOR_ID'
- */
- const TYPE_RAW_COLNAME = 'rawColName';
- /**
- * column fieldname type
- * e.g. 'author_id'
- */
- const TYPE_FIELDNAME = 'fieldName';
- /**
- * num type
- * simply the numerical array index, e.g. 4
- */
- const TYPE_NUM = 'num';
- static public function getFieldnames ($classname, $type = self::TYPE_PHPNAME) {
- // TODO we should take care of including the peer class here
- $peerclass = 'Base' . $classname . 'Peer'; // TODO is this always true?
- $callable = array($peerclass, 'getFieldnames');
- return call_user_func($callable, $type);
- }
- static public function translateFieldname($classname, $fieldname, $fromType, $toType) {
- // TODO we should take care of including the peer class here
- $peerclass = 'Base' . $classname . 'Peer'; // TODO is this always true?
- $callable = array($peerclass, 'translateFieldname');
- $args = array($fieldname, $fromType, $toType);
- return call_user_func_array($callable, $args);
- }
- /**
- * Method to perform deletes based on values and keys in a
- * Criteria.
- *
- * @param Criteria $criteria The criteria to use.
- * @param PropelPDO $con A PropelPDO connection object.
- * @return int The number of rows affected by last statement execution. For most
- * uses there is only one delete statement executed, so this number
- * will correspond to the number of rows affected by the call to this
- * method. Note that the return value does require that this information
- * is returned (supported) by the PDO driver.
- * @throws PropelException
- */
- public static function doDelete(Criteria $criteria, PropelPDO $con)
- {
- $db = Propel::getDB($criteria->getDbName());
- $dbMap = Propel::getDatabaseMap($criteria->getDbName());
- // Set up a list of required tables (one DELETE statement will
- // be executed per table)
- $tables = $criteria->getTablesColumns();
- if (empty($tables)) {
- throw new PropelException("Cannot delete from an empty Criteria");
- }
- $affectedRows = 0; // initialize this in case the next loop has no iterations.
-
- foreach ($tables as $tableName => $columns) {
- $whereClause = array();
- $params = array();
- $stmt = null;
- try {
- $sql = 'DELETE ';
- if ($queryComment = $criteria->getComment()) {
- $sql .= '/* ' . $queryComment . ' */ ';
- }
- if ($realTableName = $criteria->getTableForAlias($tableName)) {
- if ($db->useQuoteIdentifier()) {
- $realTableName = $db->quoteIdentifierTable($realTableName);
- }
- $sql .= $tableName . ' FROM ' . $realTableName . ' AS ' . $tableName;
- } else {
- if ($db->useQuoteIdentifier()) {
- $tableName = $db->quoteIdentifierTable($tableName);
- }
- $sql .= 'FROM ' . $tableName;
- }
- foreach ($columns as $colName) {
- $sb = "";
- $criteria->getCriterion($colName)->appendPsTo($sb, $params);
- $whereClause[] = $sb;
- }
- $sql .= " WHERE " . implode(" AND ", $whereClause);
- $stmt = $con->prepare($sql);
- self::populateStmtValues($stmt, $params, $dbMap, $db);
- $stmt->execute();
- $affectedRows = $stmt->rowCount();
- } catch (Exception $e) {
- Propel::log($e->getMessage(), Propel::LOG_ERR);
- throw new PropelException(sprintf('Unable to execute DELETE statement [%s]', $sql), $e);
- }
- } // for each table
- return $affectedRows;
- }
- /**
- * Method to deletes all contents of specified table.
- *
- * This method is invoked from generated Peer classes like this:
- * <code>
- * public static function doDeleteAll($con = null)
- * {
- * if ($con === null) $con = Propel::getConnection(self::DATABASE_NAME);
- * BasePeer::doDeleteAll(self::TABLE_NAME, $con, self::DATABASE_NAME);
- * }
- * </code>
- *
- * @param string $tableName The name of the table to empty.
- * @param PropelPDO $con A PropelPDO connection object.
- * @param string $databaseName the name of the database.
- * @return int The number of rows affected by the statement. Note
- * that the return value does require that this information
- * is returned (supported) by the Propel db driver.
- * @throws PropelException - wrapping SQLException caught from statement execution.
- */
- public static function doDeleteAll($tableName, PropelPDO $con, $databaseName = null)
- {
- try {
- $db = Propel::getDB($databaseName);
- if ($db->useQuoteIdentifier()) {
- $tableName = $db->quoteIdentifierTable($tableName);
- }
- $sql = "DELETE FROM " . $tableName;
- $stmt = $con->prepare($sql);
- $stmt->execute();
- return $stmt->rowCount();
- } catch (Exception $e) {
- Propel::log($e->getMessage(), Propel::LOG_ERR);
- throw new PropelException(sprintf('Unable to execute DELETE ALL statement [%s]', $sql), $e);
- }
- }
- /**
- * Method to perform inserts based on values and keys in a
- * Criteria.
- * <p>
- * If the primary key is auto incremented the data in Criteria
- * will be inserted and the auto increment value will be returned.
- * <p>
- * If the primary key is included in Criteria then that value will
- * be used to insert the row.
- * <p>
- * If no primary key is included in Criteria then we will try to
- * figure out the primary key from the database map and insert the
- * row with the next available id using util.db.IDBroker.
- * <p>
- * If no primary key is defined for the table the values will be
- * inserted as specified in Criteria and null will be returned.
- *
- * @param Criteria $criteria Object containing values to insert.
- * @param PropelPDO $con A PropelPDO connection.
- * @return mixed The primary key for the new row if (and only if!) the primary key
- * is auto-generated. Otherwise will return <code>null</code>.
- * @throws PropelException
- */
- public static function doInsert(Criteria $criteria, PropelPDO $con) {
- // the primary key
- $id = null;
- $db = Propel::getDB($criteria->getDbName());
- // Get the table name and method for determining the primary
- // key value.
- $keys = $criteria->keys();
- if (!empty($keys)) {
- $tableName = $criteria->getTableName( $keys[0] );
- } else {
- throw new PropelException("Database insert attempted without anything specified to insert");
- }
- $dbMap = Propel::getDatabaseMap($criteria->getDbName());
- $tableMap = $dbMap->getTable($tableName);
- $keyInfo = $tableMap->getPrimaryKeyMethodInfo();
- $useIdGen = $tableMap->isUseIdGenerator();
- //$keyGen = $con->getIdGenerator();
- $pk = self::getPrimaryKey($criteria);
- // only get a new key value if you need to
- // the reason is that a primary key might be defined
- // but you are still going to set its value. for example:
- // a join table where both keys are primary and you are
- // setting both columns with your own values
- // pk will be null if there is no primary key defined for the table
- // we're inserting into.
- if ($pk !== null && $useIdGen && !$criteria->keyContainsValue($pk->getFullyQualifiedName()) && $db->isGetIdBeforeInsert()) {
- try {
- $id = $db->getId($con, $keyInfo);
- } catch (Exception $e) {
- throw new PropelException("Unable to get sequence id.", $e);
- }
- $criteria->add($pk->getFullyQualifiedName(), $id);
- }
- try {
- $adapter = Propel::getDB($criteria->getDBName());
- $qualifiedCols = $criteria->keys(); // we need table.column cols when populating values
- $columns = array(); // but just 'column' cols for the SQL
- foreach ($qualifiedCols as $qualifiedCol) {
- $columns[] = substr($qualifiedCol, strrpos($qualifiedCol, '.') + 1);
- }
- // add identifiers
- if ($adapter->useQuoteIdentifier()) {
- $columns = array_map(array($adapter, 'quoteIdentifier'), $columns);
- $tableName = $adapter->quoteIdentifierTable($tableName);
- }
- $sql = 'INSERT INTO ' . $tableName
- . ' (' . implode(',', $columns) . ')'
- . ' VALUES (';
- // . substr(str_repeat("?,", count($columns)), 0, -1) .
- for($p=1, $cnt=count($columns); $p <= $cnt; $p++) {
- $sql .= ':p'.$p;
- if ($p !== $cnt) $sql .= ',';
- }
- $sql .= ')';
- $stmt = $con->prepare($sql);
- self::populateStmtValues($stmt, self::buildParams($qualifiedCols, $criteria), $dbMap, $db);
- $stmt->execute();
- } catch (Exception $e) {
- Propel::log($e->getMessage(), Propel::LOG_ERR);
- throw new PropelException(sprintf('Unable to execute INSERT statement [%s]', $sql), $e);
- }
- // If the primary key column is auto-incremented, get the id now.
- if ($pk !== null && $useIdGen && $db->isGetIdAfterInsert()) {
- try {
- $id = $db->getId($con, $keyInfo);
- } catch (Exception $e) {
- throw new PropelException("Unable to get autoincrement id.", $e);
- }
- }
- return $id;
- }
- /**
- * Method used to update rows in the DB. Rows are selected based
- * on selectCriteria and updated using values in updateValues.
- * <p>
- * Use this method for performing an update of the kind:
- * <p>
- * WHERE some_column = some value AND could_have_another_column =
- * another value AND so on.
- *
- * @param $selectCriteria A Criteria object containing values used in where
- * clause.
- * @param $updateValues A Criteria object containing values used in set
- * clause.
- * @param PropelPDO $con The PropelPDO connection object to use.
- * @return int The number of rows affected by last update statement. For most
- * uses there is only one update statement executed, so this number
- * will correspond to the number of rows affected by the call to this
- * method. Note that the return value does require that this information
- * is returned (supported) by the Propel db driver.
- * @throws PropelException
- */
- public static function doUpdate(Criteria $selectCriteria, Criteria $updateValues, PropelPDO $con) {
- $db = Propel::getDB($selectCriteria->getDbName());
- $dbMap = Propel::getDatabaseMap($selectCriteria->getDbName());
- // Get list of required tables, containing all columns
- $tablesColumns = $selectCriteria->getTablesColumns();
- if (empty($tablesColumns)) {
- $tablesColumns = array($selectCriteria->getPrimaryTableName() => array());
- }
- // we also need the columns for the update SQL
- $updateTablesColumns = $updateValues->getTablesColumns();
- $affectedRows = 0; // initialize this in case the next loop has no iterations.
- foreach ($tablesColumns as $tableName => $columns) {
- $whereClause = array();
- $params = array();
- $stmt = null;
- try {
- $sql = 'UPDATE ';
- if ($queryComment = $selectCriteria->getComment()) {
- $sql .= '/* ' . $queryComment . ' */ ';
- }
- // is it a table alias?
- if ($tableName2 = $selectCriteria->getTableForAlias($tableName)) {
- $udpateTable = $tableName2 . ' ' . $tableName;
- $tableName = $tableName2;
- } else {
- $udpateTable = $tableName;
- }
- if ($db->useQuoteIdentifier()) {
- $sql .= $db->quoteIdentifierTable($udpateTable);
- } else {
- $sql .= $udpateTable;
- }
- $sql .= " SET ";
- $p = 1;
- foreach ($updateTablesColumns[$tableName] as $col) {
- $updateColumnName = substr($col, strrpos($col, '.') + 1);
- // add identifiers for the actual database?
- if ($db->useQuoteIdentifier()) {
- $updateColumnName = $db->quoteIdentifier($updateColumnName);
- }
- if ($updateValues->getComparison($col) != Criteria::CUSTOM_EQUAL) {
- $sql .= $updateColumnName . '=:p'.$p++.', ';
- } else {
- $param = $updateValues->get($col);
- $sql .= $updateColumnName . ' = ';
- if (is_array($param)) {
- if (isset($param['raw'])) {
- $raw = $param['raw'];
- $rawcvt = '';
- // parse the $params['raw'] for ? chars
- for($r=0,$len=strlen($raw); $r < $len; $r++) {
- if ($raw{$r} == '?') {
- $rawcvt .= ':p'.$p++;
- } else {
- $rawcvt .= $raw{$r};
- }
- }
- $sql .= $rawcvt . ', ';
- } else {
- $sql .= ':p'.$p++.', ';
- }
- if (isset($param['value'])) {
- $updateValues->put($col, $param['value']);
- }
- } else {
- $updateValues->remove($col);
- $sql .= $param . ', ';
- }
- }
- }
-
- $params = self::buildParams($updateTablesColumns[$tableName], $updateValues);
- $sql = substr($sql, 0, -2);
- if (!empty($columns)) {
- foreach ($columns as $colName) {
- $sb = "";
- $selectCriteria->getCriterion($colName)->appendPsTo($sb, $params);
- $whereClause[] = $sb;
- }
- $sql .= " WHERE " . implode(" AND ", $whereClause);
- }
- $stmt = $con->prepare($sql);
- // Replace ':p?' with the actual values
- self::populateStmtValues($stmt, $params, $dbMap, $db);
- $stmt->execute();
- $affectedRows = $stmt->rowCount();
- $stmt = null; // close
- } catch (Exception $e) {
- if ($stmt) $stmt = null; // close
- Propel::log($e->getMessage(), Propel::LOG_ERR);
- throw new PropelException(sprintf('Unable to execute UPDATE statement [%s]', $sql), $e);
- }
- } // foreach table in the criteria
- return $affectedRows;
- }
- /**
- * Executes query build by createSelectSql() and returns the resultset statement.
- *
- * @param Criteria $criteria A Criteria.
- * @param PropelPDO $con A PropelPDO connection to use.
- * @return PDOStatement The resultset.
- * @throws PropelException
- * @see createSelectSql()
- */
- public static function doSelect(Criteria $criteria, PropelPDO $con = null)
- {
- $dbMap = Propel::getDatabaseMap($criteria->getDbName());
- $db = Propel::getDB($criteria->getDbName());
- $stmt = null;
-
- if ($con === null) {
- $con = Propel::getConnection($criteria->getDbName(), Propel::CONNECTION_READ);
- }
- if ($criteria->isUseTransaction()) {
- $con->beginTransaction();
- }
- try {
- $params = array();
- $sql = self::createSelectSql($criteria, $params);
- $stmt = $con->prepare($sql);
- self::populateStmtValues($stmt, $params, $dbMap, $db);
- $stmt->execute();
- if ($criteria->isUseTransaction()) {
- $con->commit();
- }
- } catch (Exception $e) {
- if ($stmt) {
- $stmt = null; // close
- }
- if ($criteria->isUseTransaction()) {
- $con->rollBack();
- }
- Propel::log($e->getMessage(), Propel::LOG_ERR);
- throw new PropelException(sprintf('Unable to execute SELECT statement [%s]', $sql), $e);
- }
- return $stmt;
- }
- /**
- * Executes a COUNT query using either a simple SQL rewrite or, for more complex queries, a
- * sub-select of the SQL created by createSelectSql() and returns the statement.
- *
- * @param Criteria $criteria A Criteria.
- * @param PropelPDO $con A PropelPDO connection to use.
- * @return PDOStatement The resultset statement.
- * @throws PropelException
- * @see createSelectSql()
- */
- public static function doCount(Criteria $criteria, PropelPDO $con = null)
- {
- $dbMap = Propel::getDatabaseMap($criteria->getDbName());
- $db = Propel::getDB($criteria->getDbName());
- if ($con === null) {
- $con = Propel::getConnection($criteria->getDbName(), Propel::CONNECTION_READ);
- }
- $stmt = null;
- if ($criteria->isUseTransaction()) {
- $con->beginTransaction();
- }
- $needsComplexCount = $criteria->getGroupByColumns()
- || $criteria->getOffset()
- || $criteria->getLimit()
- || $criteria->getHaving()
- || in_array(Criteria::DISTINCT, $criteria->getSelectModifiers());
- try {
- $params = array();
- if ($needsComplexCount) {
- if (self::needsSelectAliases($criteria)) {
- if ($criteria->getHaving()) {
- throw new PropelException('Propel cannot create a COUNT query when using HAVING and duplicate column names in the SELECT part');
- }
- self::turnSelectColumnsToAliases($criteria);
- }
- $selectSql = self::createSelectSql($criteria, $params);
- $sql = 'SELECT COUNT(*) FROM (' . $selectSql . ') propelmatch4cnt';
- } else {
- // Replace SELECT columns with COUNT(*)
- $criteria->clearSelectColumns()->addSelectColumn('COUNT(*)');
- $sql = self::createSelectSql($criteria, $params);
- }
- $stmt = $con->prepare($sql);
- self::populateStmtValues($stmt, $params, $dbMap, $db);
- $stmt->execute();
- if ($criteria->isUseTransaction()) {
- $con->commit();
- }
- } catch (Exception $e) {
- if ($stmt !== null) {
- $stmt = null;
- }
- if ($criteria->isUseTransaction()) {
- $con->rollBack();
- }
- Propel::log($e->getMessage(), Propel::LOG_ERR);
- throw new PropelException(sprintf('Unable to execute COUNT statement [%s]', $sql), $e);
- }
- return $stmt;
- }
- /**
- * Populates values in a prepared statement.
- *
- * This method is designed to work with the createSelectSql() method, which creates
- * both the SELECT SQL statement and populates a passed-in array of parameter
- * values that should be substituted.
- *
- * <code>
- * $params = array();
- * $sql = BasePeer::createSelectSql($criteria, $params);
- * BasePeer::populateStmtValues($stmt, $params, Propel::getDatabaseMap($critera->getDbName()), Propel::getDB($criteria->getDbName()));
- * </code>
- *
- * @param PDOStatement $stmt
- * @param array $params array('column' => ..., 'table' => ..., 'value' => ...)
- * @param DatabaseMap $dbMap
- * @return int The number of params replaced.
- * @see createSelectSql()
- * @see doSelect()
- */
- public static function populateStmtValues(PDOStatement $stmt, array $params, DatabaseMap $dbMap, DBAdapter $db)
- {
- $i = 1;
- foreach ($params as $param) {
- $tableName = $param['table'];
- $columnName = $param['column'];
- $value = $param['value'];
- if (null === $value) {
- $stmt->bindValue(':p'.$i++, null, PDO::PARAM_NULL);
- } elseif (null !== $tableName) {
- $cMap = $dbMap->getTable($tableName)->getColumn($columnName);
- $type = $cMap->getType();
- $pdoType = $cMap->getPdoType();
- // FIXME - This is a temporary hack to get around apparent bugs w/ PDO+MYSQL
- // See http://pecl.php.net/bugs/bug.php?id=9919
- if ($pdoType == PDO::PARAM_BOOL && $db instanceof DBMySQL) {
- $value = (int) $value;
- $pdoType = PDO::PARAM_INT;
- } elseif (is_numeric($value) && $cMap->isEpochTemporal()) { // it's a timestamp that needs to be formatted
- if ($type == PropelColumnTypes::TIMESTAMP) {
- $value = date($db->getTimestampFormatter(), $value);
- } else if ($type == PropelColumnTypes::DATE) {
- $value = date($db->getDateFormatter(), $value);
- } else if ($type == PropelColumnTypes::TIME) {
- $value = date($db->getTimeFormatter(), $value);
- }
- } elseif ($value instanceof DateTime && $cMap->isTemporal()) { // it's a timestamp that needs to be formatted
- if ($type == PropelColumnTypes::TIMESTAMP || $type == PropelColumnTypes::BU_TIMESTAMP) {
- $value = $value->format($db->getTimestampFormatter());
- } else if ($type == PropelColumnTypes::DATE || $type == PropelColumnTypes::BU_DATE) {
- $value = $value->format($db->getDateFormatter());
- } else if ($type == PropelColumnTypes::TIME) {
- $value = $value->format($db->getTimeFormatter());
- }
- } elseif (is_resource($value) && $cMap->isLob()) {
- // we always need to make sure that the stream is rewound, otherwise nothing will
- // get written to database.
- rewind($value);
- }
- $stmt->bindValue(':p'.$i++, $value, $pdoType);
- } else {
- $stmt->bindValue(':p'.$i++, $value);
- }
- } // foreach
- }
- /**
- * Applies any validators that were defined in the schema to the specified columns.
- *
- * @param string $dbName The name of the database
- * @param string $tableName The name of the table
- * @param array $columns Array of column names as key and column values as value.
- */
- public static function doValidate($dbName, $tableName, $columns)
- {
- $dbMap = Propel::getDatabaseMap($dbName);
- $tableMap = $dbMap->getTable($tableName);
- $failureMap = array(); // map of ValidationFailed objects
- foreach ($columns as $colName => $colValue) {
- if ($tableMap->containsColumn($colName)) {
- $col = $tableMap->getColumn($colName);
- foreach ($col->getValidators() as $validatorMap) {
- $validator = BasePeer::getValidator($validatorMap->getClass());
- if ($validator && ($col->isNotNull() || $colValue !== null) && $validator->isValid($validatorMap, $colValue) === false) {
- if (!isset($failureMap[$colName])) { // for now we do one ValidationFailed per column, not per rule
- $failureMap[$colName] = new ValidationFailed($colName, $validatorMap->getMessage(), $validator);
- }
- }
- }
- }
- }
- return (!empty($failureMap) ? $failureMap : true);
- }
- /**
- * Helper method which returns the primary key contained
- * in the given Criteria object.
- *
- * @param Criteria $criteria A Criteria.
- * @return ColumnMap If the Criteria object contains a primary
- * key, or null if it doesn't.
- * @throws PropelException
- */
- private static function getPrimaryKey(Criteria $criteria)
- {
- // Assume all the keys are for the same table.
- $keys = $criteria->keys();
- $key = $keys[0];
- $table = $criteria->getTableName($key);
- $pk = null;
- if (!empty($table)) {
- $dbMap = Propel::getDatabaseMap($criteria->getDbName());
- $pks = $dbMap->getTable($table)->getPrimaryKeys();
- if (!empty($pks)) {
- $pk = array_shift($pks);
- }
- }
- return $pk;
- }
- /**
- * Checks whether the Criteria needs to use column aliasing
- * This is implemented in a service class rather than in Criteria itself
- * in order to avoid doing the tests when it's not necessary (e.g. for SELECTs)
- */
- public static function needsSelectAliases(Criteria $criteria)
- {
- $columnNames = array();
- foreach ($criteria->getSelectColumns() as $fullyQualifiedColumnName) {
- if ($pos = strrpos($fullyQualifiedColumnName, '.')) {
- $columnName = substr($fullyQualifiedColumnName, $pos);
- if (isset($columnNames[$columnName])) {
- // more than one column with the same name, so aliasing is required
- return true;
- }
- $columnNames[$columnName] = true;
- }
- }
- return false;
- }
-
- /**
- * Ensures uniqueness of select column names by turning them all into aliases
- * This is necessary for queries on more than one table when the tables share a column name
- * @see http://propel.phpdb.org/trac/ticket/795
- *
- * @param Criteria $criteria
- *
- * @return Criteria The input, with Select columns replaced by aliases
- */
- public static function turnSelectColumnsToAliases(Criteria $criteria)
- {
- $selectColumns = $criteria->getSelectColumns();
- // clearSelectColumns also clears the aliases, so get them too
- $asColumns = $criteria->getAsColumns();
- $criteria->clearSelectColumns();
- $columnAliases = $asColumns;
- // add the select columns back
- foreach ($selectColumns as $clause) {
- // Generate a unique alias
- $baseAlias = preg_replace('/\W/', '_', $clause);
- $alias = $baseAlias;
- // If it already exists, add a unique suffix
- $i = 0;
- while (isset($columnAliases[$alias])) {
- $i++;
- $alias = $baseAlias . '_' . $i;
- }
- // Add it as an alias
- $criteria->addAsColumn($alias, $clause);
- $columnAliases[$alias] = $clause;
- }
- // Add the aliases back, don't modify them
- foreach ($asColumns as $name => $clause) {
- $criteria->addAsColumn($name, $clause);
- }
-
- return $criteria;
- }
-
- /**
- * Method to create an SQL query based on values in a Criteria.
- *
- * This method creates only prepared statement SQL (using ? where values
- * will go). The second parameter ($params) stores the values that need
- * to be set before the statement is executed. The reason we do it this way
- * is to let the PDO layer handle all escaping & value formatting.
- *
- * @param Criteria $criteria Criteria for the SELECT query.
- * @param array &$params Parameters that are to be replaced in prepared statement.
- * @return string
- * @throws PropelException Trouble creating the query string.
- */
- public static function createSelectSql(Criteria $criteria, &$params)
- {
- $db = Propel::getDB($criteria->getDbName());
- $dbMap = Propel::getDatabaseMap($criteria->getDbName());
- $fromClause = array();
- $joinClause = array();
- $joinTables = array();
- $whereClause = array();
- $orderByClause = array();
- $orderBy = $criteria->getOrderByColumns();
- $groupBy = $criteria->getGroupByColumns();
- $ignoreCase = $criteria->isIgnoreCase();
- // get the first part of the SQL statement, the SELECT part
- $selectSql = self::createSelectSqlPart($criteria, $fromClause);
- // add the criteria to WHERE clause
- // this will also add the table names to the FROM clause if they are not already
- // included via a LEFT JOIN
- foreach ($criteria->keys() as $key) {
- $criterion = $criteria->getCriterion($key);
- $table = null;
- foreach ($criterion->getAttachedCriterion() as $attachedCriterion) {
- $tableName = $attachedCriterion->getTable();
- $table = $criteria->getTableForAlias($tableName);
- if ($table !== null) {
- $fromClause[] = $table . ' ' . $tableName;
- } else {
- $fromClause[] = $tableName;
- $table = $tableName;
- }
- if (($criteria->isIgnoreCase() || $attachedCriterion->isIgnoreCase())
- && $dbMap->getTable($table)->getColumn($attachedCriterion->getColumn())->isText()) {
- $attachedCriterion->setIgnoreCase(true);
- }
- }
- $criterion->setDB($db);
- $sb = '';
- $criterion->appendPsTo($sb, $params);
- $whereClause[] = $sb;
- }
- // Handle joins
- // joins with a null join type will be added to the FROM clause and the condition added to the WHERE clause.
- // joins of a specified type: the LEFT side will be added to the fromClause and the RIGHT to the joinClause
- foreach ($criteria->getJoins() as $join) {
- // The join might have been established using an alias name
- $leftTable = $join->getLeftTableName();
- if ($realTable = $criteria->getTableForAlias($leftTable)) {
- $leftTableForFrom = $realTable . ' ' . $leftTable;
- $leftTable = $realTable;
- } else {
- $leftTableForFrom = $leftTable;
- }
- $rightTable = $join->getRightTableName();
- if ($realTable = $criteria->getTableForAlias($rightTable)) {
- $rightTableForFrom = $realTable . ' ' . $rightTable;
- $rightTable = $realTable;
- } else {
- $rightTableForFrom = $rightTable;
- }
- // determine if casing is relevant.
- if ($ignoreCase = $criteria->isIgnoreCase()) {
- $leftColType = $dbMap->getTable($leftTable)->getColumn($join->getLeftColumnName())->getType();
- $rightColType = $dbMap->getTable($rightTable)->getColumn($join->getRightColumnName())->getType();
- $ignoreCase = ($leftColType == 'string' || $rightColType == 'string');
- }
- // build the condition
- $condition = '';
- foreach ($join->getConditions() as $index => $conditionDesc) {
- if ($ignoreCase) {
- $condition .= $db->ignoreCase($conditionDesc['left']) . $conditionDesc['operator'] . $db->ignoreCase($conditionDesc['right']);
- } else {
- $condition .= implode($conditionDesc);
- }
- if ($index + 1 < $join->countConditions()) {
- $condition .= ' AND ';
- }
- }
- // add 'em to the queues..
- if ($joinType = $join->getJoinType()) {
- // real join
- if (!$fromClause) {
- $fromClause[] = $leftTableForFrom;
- }
- $joinTables[] = $rightTableForFrom;
- $joinClause[] = $join->getJoinType() . ' ' . $rightTableForFrom . " ON ($condition)";
- } else {
- // implicit join, translates to a where
- $fromClause[] = $leftTableForFrom;
- $fromClause[] = $rightTableForFrom;
- $whereClause[] = $condition;
- }
- }
- // Unique from clause elements
- $fromClause = array_unique($fromClause);
- $fromClause = array_diff($fromClause, array(''));
-
- // tables should not exist in both the from and join clauses
- if ($joinTables && $fromClause) {
- foreach ($fromClause as $fi => $ftable) {
- if (in_array($ftable, $joinTables)) {
- unset($fromClause[$fi]);
- }
- }
- }
- // Add the GROUP BY columns
- $groupByClause = $groupBy;
- $having = $criteria->getHaving();
- $havingString = null;
- if ($having !== null) {
- $sb = '';
- $having->appendPsTo($sb, $params);
- $havingString = $sb;
- }
- if (!empty($orderBy)) {
- foreach ($orderBy as $orderByColumn) {
- // Add function expression as-is.
- if (strpos($orderByColumn, '(') !== false) {
- $orderByClause[] = $orderByColumn;
- continue;
- }
- // Split orderByColumn (i.e. "table.column DESC")
- $dotPos = strrpos($orderByColumn, '.');
- if ($dotPos !== false) {
- $tableName = substr($orderByColumn, 0, $dotPos);
- $columnName = substr($orderByColumn, $dotPos + 1);
- } else {
- $tableName = '';
- $columnName = $orderByColumn;
- }
- $spacePos = strpos($columnName, ' ');
- if ($spacePos !== false) {
- $direction = substr($columnName, $spacePos);
- $columnName = substr($columnName, 0, $spacePos);
- } else {
- $direction = '';
- }
- $tableAlias = $tableName;
- if ($aliasTableName = $criteria->getTableForAlias($tableName)) {
- $tableName = $aliasTableName;
- }
- $columnAlias = $columnName;
- if ($asColumnName = $criteria->getColumnForAs($columnName)) {
- $columnName = $asColumnName;
- }
- $column = $tableName ? $dbMap->getTable($tableName)->getColumn($columnName) : null;
- if ($criteria->isIgnoreCase() && $column && $column->isText()) {
- $ignoreCaseColumn = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias");
- $orderByClause[] = $ignoreCaseColumn . $direction;
- $selectSql .= ', ' . $ignoreCaseColumn;
- } else {
- $orderByClause[] = $orderByColumn;
- }
- }
- }
- if (empty($fromClause) && $criteria->getPrimaryTableName()) {
- $fromClause[] = $criteria->getPrimaryTableName();
- }
- // from / join tables quoted if it is necessary
- if ($db->useQuoteIdentifier()) {
- $fromClause = array_map(array($db, 'quoteIdentifierTable'), $fromClause);
- $joinClause = $joinClause ? $joinClause : array_map(array($db, 'quoteIdentifierTable'), $joinClause);
- }
- // build from-clause
- $from = '';
- if (!empty($joinClause) && count($fromClause) > 1) {
- $from .= implode(" CROSS JOIN ", $fromClause);
- } else {
- $from .= implode(", ", $fromClause);
- }
-
- $from .= $joinClause ? ' ' . implode(' ', $joinClause) : '';
- // Build the SQL from the arrays we compiled
- $sql = $selectSql
- ." FROM " . $from
- .($whereClause ? " WHERE ".implode(" AND ", $whereClause) : "")
- .($groupByClause ? " GROUP BY ".implode(",", $groupByClause) : "")
- .($havingString ? " HAVING ".$havingString : "")
- .($orderByClause ? " ORDER BY ".implode(",", $orderByClause) : "");
- // APPLY OFFSET & LIMIT to the query.
- if ($criteria->getLimit() || $criteria->getOffset()) {
- $db->applyLimit($sql, $criteria->getOffset(), $criteria->getLimit(), $criteria);
- }
- return $sql;
- }
- /**
- * Builds the SELECT part of a SQL statement based on a Criteria
- * taking into account select columns and 'as' columns (i.e. columns aliases)
- */
- public static function createSelectSqlPart(Criteria $criteria, &$fromClause, $aliasAll = false)
- {
- $selectClause = array();
-
- if ($aliasAll) {
- self::turnSelectColumnsToAliases($criteria);
- // no select columns after that, they are all aliases
- } else {
- foreach ($criteria->getSelectColumns() as $columnName) {
- // expect every column to be of "table.column" formation
- // it could be a function: e.g. MAX(books.price)
- $tableName = null;
- $selectClause[] = $columnName; // the full column name: e.g. MAX(books.price)
- $parenPos = strrpos($columnName, '(');
- $dotPos = strrpos($columnName, '.', ($parenPos !== false ? $parenPos : 0));
- if ($dotPos !== false) {
- if ($parenPos === false) { // table.column
- $tableName = substr($columnName, 0, $dotPos);
- } else { // FUNC(table.column)
- // functions may contain qualifiers so only take the last
- // word as the table name.
- // COUNT(DISTINCT books.price)
- $lastSpace = strpos($tableName, ' ');
- if ($lastSpace !== false) { // COUNT(DISTINCT books.price)
- $tableName = substr($tableName, $lastSpace + 1);
- } else {
- $tableName = substr($columnName, $parenPos + 1, $dotPos - ($parenPos + 1));
- }
- }
- // is it a table alias?
- $tableName2 = $criteria->getTableForAlias($tableName);
- if ($tableName2 !== null) {
- $fromClause[] = $tableName2 . ' ' . $tableName;
- } else {
- $fromClause[] = $tableName;
- }
- } // if $dotPost !== false
- }
- }
-
- // set the aliases
- foreach ($criteria->getAsColumns() as $alias => $col) {
- $selectClause[] = $col . ' AS ' . $alias;
- }
- $selectModifiers = $criteria->getSelectModifiers();
- $queryComment = $criteria->getComment();
-
- // Build the SQL from the arrays we compiled
- $sql = "SELECT "
- . ($queryComment ? '/* ' . $queryComment . ' */ ' : '')
- . ($selectModifiers ? (implode(' ', $selectModifiers) . ' ') : '')
- . implode(", ", $selectClause);
- return $sql;
- }
- /**
- * Builds a params array, like the kind populated by Criterion::appendPsTo().
- * This is useful for building an array even when it is not using the appendPsTo() method.
- * @param array $columns
- * @param Criteria $values
- * @return array params array('column' => ..., 'table' => ..., 'value' => ...)
- */
- private static function buildParams($columns, Criteria $values)
- {
- $params = array();
- foreach ($columns as $key) {
- if ($values->containsKey($key)) {
- $crit = $values->getCriterion($key);
- $params[] = array('column' => $crit->getColumn(), 'table' => $crit->getTable(), 'value' => $crit->getValue());
- }
- }
- return $params;
- }
- /**
- * This function searches for the given validator $name under propel/validator/$name.php,
- * imports and caches it.
- *
- * @param string $classname The dot-path name of class (e.g. myapp.propel.MyValidator)
- * @return Validator object or null if not able to instantiate validator class (and error will be logged in this case)
- */
- public static function getValidator($classname)
- {
- try {
- $v = isset(self::$validatorMap[$classname]) ? self::$validatorMap[$classname] : null;
- if ($v === null) {
- $cls = Propel::importClass($classname);
- $v = new $cls();
- self::$validatorMap[$classname] = $v;
- }
- return $v;
- } catch (Exception $e) {
- Propel::log("BasePeer::getValidator(): failed trying to instantiate " . $classname . ": ".$e->getMessage(), Propel::LOG_ERR);
- }
- }
- }
|