Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/** $Id: BasePeer.php 1608 2010-03-15 23:09:22Z francois $** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.** This software consists of voluntary contributions made by many individuals* and is licensed under the LGPL. For more information please see* <http://propel.phpdb.org>.*//*** 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: 1608 $* @package propel.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 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');$args = array($type);return call_user_func_array($callable, $args);}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_keys = array();foreach ($criteria as $c) {foreach ($c->getAllTables() as $tableName) {$tableName2 = $criteria->getTableForAlias($tableName);if ($tableName2 !== null) {$tables_keys[$tableName2 . ' ' . $tableName] = true;} else {$tables_keys[$tableName] = true;}}} // foreach criteria->keys()$affectedRows = 0; // initialize this in case the next loop has no iterations.$tables = array_keys($tables_keys);foreach ($tables as $tableName) {$whereClause = array();$selectParams = array();foreach ($dbMap->getTable($tableName)->getColumns() as $colMap) {$key = $tableName . '.' . $colMap->getColumnName();if ($criteria->containsKey($key)) {$sb = "";$criteria->getCriterion($key)->appendPsTo($sb, $selectParams);$whereClause[] = $sb;}}if (empty($whereClause)) {throw new PropelException("Cowardly refusing to delete from table $tableName with empty WHERE clause.");}// Execute the statement.try {$sql = "DELETE FROM " . $tableName . " WHERE " . implode(" AND ", $whereClause);$stmt = $con->prepare($sql);self::populateStmtValues($stmt, $selectParams, $dbMap, $db);$stmt->execute();$affectedRows = $stmt->rowCount();} catch (Exception $e) {Propel::log($e->getMessage(), Propel::LOG_ERR);throw new PropelException("Unable to execute DELETE statement.",$e);}} // for each tablereturn $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);* }* </code>** @param string $tableName The name of the table to empty.* @param PropelPDO $con A PropelPDO connection object.* @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 Creole db driver.* @throws PropelException - wrapping SQLException caught from statement execution.*/public static function doDeleteAll($tableName, PropelPDO $con){try {$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("Unable to perform DELETE ALL operation.", $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 SQLforeach ($qualifiedCols as $qualifiedCol) {$columns[] = substr($qualifiedCol, strrpos($qualifiedCol, '.') + 1);}// add identifiersif ($adapter->useQuoteIdentifier()) {$columns = array_map(array($adapter, 'quoteIdentifier'), $columns);}$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("Unable to execute INSERT statement.", $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 Creole 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();// 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 " . $tableName . " 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 ? charsfor($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);foreach ($columns as $colName) {$sb = "";$selectCriteria->getCriterion($colName)->appendPsTo($sb, $params);$whereClause[] = $sb;}$sql = substr($sql, 0, -2) . " WHERE " . implode(" AND ", $whereClause);$stmt = $con->prepare($sql);// Replace ':p?' with the actual valuesself::populateStmtValues($stmt, $params, $dbMap, $db);$stmt->execute();$affectedRows = $stmt->rowCount();$stmt = null; // close} catch (Exception $e) {if ($stmt) $stmt = null; // closePropel::log($e->getMessage(), Propel::LOG_ERR);throw new PropelException("Unable to execute UPDATE statement.", $e);}} // foreach table in the criteriareturn $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());if ($con === null) {$con = Propel::getConnection($criteria->getDbName(), Propel::CONNECTION_READ);}$stmt = null;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; // closeif ($criteria->isUseTransaction()) $con->rollBack();Propel::log($e->getMessage(), Propel::LOG_ERR);throw new PropelException($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)) {self::turnSelectColumnsToAliases($criteria);}$selectSql = self::createSelectSql($criteria, $params);$sql = 'SELECT COUNT(*) FROM (' . $selectSql . ') AS 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) $stmt = null; // closeif ($criteria->isUseTransaction()) $con->rollBack();Propel::log($e->getMessage(), Propel::LOG_ERR);throw new PropelException($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 ($value === null) {$stmt->bindValue(':p'.$i++, null, PDO::PARAM_NULL);} elseif (isset($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=9919if ($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 formattedif ($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 formattedif ($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 objectsforeach ($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());if ($dbMap === null) {throw new PropelException("\$dbMap is null");}if ($dbMap->getTable($table) === null) {throw new PropelException("\$dbMap->getTable() is null");}$columns = $dbMap->getTable($table)->getColumns();foreach (array_keys($columns) as $key) {if ($columns[$key]->isPrimaryKey()) {$pk = $columns[$key];break;}}}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 requiredreturn 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 backforeach ($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 themforeach ($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());// redundant definition $selectModifiers = array();$selectClause = array();$fromClause = array();$joinClause = array();$joinTables = array();$whereClause = array();$orderByClause = array();// redundant definition $groupByClause = array();$orderBy = $criteria->getOrderByColumns();$groupBy = $criteria->getGroupByColumns();$ignoreCase = $criteria->isIgnoreCase();$select = $criteria->getSelectColumns();$aliases = $criteria->getAsColumns();// simple copy$selectModifiers = $criteria->getSelectModifiers();// get selected columnsforeach ($select 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));// [HL] I think we really only want to worry about adding stuff to// the fromClause if this function has a TABLE.COLUMN in it at all.// e.g. COUNT(*) should not need this treatment -- or there needs to// be special treatment for '*'if ($dotPos !== false) {if ($parenPos === false) { // table.column$tableName = substr($columnName, 0, $dotPos);} else { // FUNC(table.column)$tableName = substr($columnName, $parenPos + 1, $dotPos - ($parenPos + 1));// 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);}}$tableName2 = $criteria->getTableForAlias($tableName);if ($tableName2 !== null) {$fromClause[] = $tableName2 . ' ' . $tableName;} else {$fromClause[] = $tableName;}} // if $dotPost !== null}// set the aliasesforeach ($aliases as $alias => $col) {$selectClause[] = $col . " AS " . $alias;}// add the criteria to WHERE clause// this will also add the table names to the FROM clause if they are not already// invluded via a LEFT JOINforeach ($criteria->keys() as $key) {$criterion = $criteria->getCriterion($key);$someCriteria = $criterion->getAttachedCriterion();$someCriteriaLength = count($someCriteria);$table = null;for ($i=0; $i < $someCriteriaLength; $i++) {$tableName = $someCriteria[$i]->getTable();$table = $criteria->getTableForAlias($tableName);if ($table !== null) {$fromClause[] = $table . ' ' . $tableName;} else {$fromClause[] = $tableName;$table = $tableName;}$ignoreCase =(($criteria->isIgnoreCase()|| $someCriteria[$i]->isIgnoreCase())&& (strpos($dbMap->getTable($table)->getColumn($someCriteria[$i]->getColumn())->getType(), "VARCHAR") !== false));$someCriteria[$i]->setIgnoreCase($ignoreCase);}$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 joinClauseforeach ((array) $criteria->getJoins() as $join) {// The join might have been established using an alias name$leftTable = $join->getLeftTableName();$leftTableAlias = '';if ($realTable = $criteria->getTableForAlias($leftTable)) {$leftTableAlias = " $leftTable";$leftTable = $realTable;}$rightTable = $join->getRightTableName();$rightTableAlias = '';if ($realTable = $criteria->getTableForAlias($rightTable)) {$rightTableAlias = " $rightTable";$rightTable = $realTable;}// 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 joinif (!$fromClause) {$fromClause[] = $leftTable . $leftTableAlias;}$joinTables[] = $rightTable . $rightTableAlias;$joinClause[] = $join->getJoinType() . ' ' . $rightTable . $rightTableAlias . " ON ($condition)";} else {// implicit join, translates to a where$fromClause[] = $leftTable . $leftTableAlias;$fromClause[] = $rightTable . $rightTableAlias;$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 clausesif ($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()) {$orderByClause[] = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias") . $direction;$selectClause[] = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias");} else {$orderByClause[] = $orderByColumn;}}}if (empty($fromClause) && $criteria->getPrimaryTableName()) {$fromClause[] = $criteria->getPrimaryTableName();}// from / join tables quoten if it is necessaryif ($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 = "SELECT ".($selectModifiers ? implode(" ", $selectModifiers) . " " : "").implode(", ", $selectClause)." 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());}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);}}}