Location: PHPKode > projects > DIY Blog > diy-blog/lib/propel/runtime-php4/classes/propel/util/BasePeer.php
<?php
/*
 *  $Id: BasePeer.php 536 2007-01-10 14:30:38Z heltem $
 *
 * 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>.
 */

include_once 'propel/adapter/DBAdapter.php';
include_once 'propel/map/ColumnMap.php';
include_once 'propel/map/DatabaseMap.php';
include_once 'propel/map/MapBuilder.php';
include_once 'propel/map/TableMap.php';
include_once 'propel/map/ValidatorMap.php';
include_once 'propel/validator/ValidationFailed.php';

/**
 * 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 <hide@address.com> (Propel)
 * @author     Kaspars Jaudzems <hide@address.com> (Propel)
 * @author     Frank Y. Kim <hide@address.com> (Torque)
 * @author     John D. McNally <hide@address.com> (Torque)
 * @author     Brett McLaughlin <hide@address.com> (Torque)
 * @author     Stephen Haberman <hide@address.com> (Torque)
 * @version    $Revision: 536 $
 * @package    propel.util
 */
class BasePeer
{
  /** Array (hash) that contains the cached mapBuilders. */
  var $mapBuilders = array();
  var $validatorMap = array();

  /**
  * Method to perform deletes based on values and keys in a
  * Criteria.
  *
  * @param      Criteria $criteria The criteria to use.
  * @param      Connection $con A Connection.
  * @return     mixed number of rows affected on success, PropelException on error
  * @access     public
  * @static
  */
  function doDelete(/*Criteria*/ $criteria, /*Connection*/ &$con)
  {
	if (! is_a($criteria, 'Criteria'))
	  return new PropelException (PROPEL_ERROR, "parameter 1 not of type 'Criteria' !");
	if (! is_a($con, 'Connection'))
	  return new PropelException (PROPEL_ERROR, "parameter 2 not of type 'Connection' !");

	$db =& Propel::getDB($criteria->getDbName());
	$dbMap =& Propel::getDatabaseMap($criteria->getDbName());

	if (Propel::isError($dbMap)) {
	  return $dbMap;
	}

	// Set up a list of required tables (one DELETE statement will
	// be executed per table)

	$tables_keys = array();

	for (($it =& $criteria->getIterator()); $it->valid(); $it->next())
	{
	  $c =& $it->current();
	  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()

	$result = 0; // initialize this in case the next loop has no iterations.
	$tables = array_keys($tables_keys);

	foreach($tables as $tableName)
	{
	  $whereClause = array();
	  $selectParams = array();
	  $t =& $dbMap->getTable($tableName);

	  foreach($t->getColumns() as $colMap) {
		  $key = $tableName . '.' . $colMap->getColumnName();
		  if ($criteria->containsKey($key)) {
			$sb = "";
			$c =& $criteria->getCriterion($key);
			$e = $c->appendPsTo($sb, $selectParams);
			if (Propel::isError($e)) {
			  return $e;
			}
			$whereClause[] = $sb;
		  }
	  }

	  if (empty($whereClause)) {
		return new PropelException(PROPEL_ERROR, "Cowardly refusing to delete from table $tableName with empty WHERE clause !");
	  }

	  // Execute the statement.
	  $sqlSnippet = implode(" AND ", $whereClause);

	  if ($criteria->isSingleRecord())
	  {
		$sql = "SELECT COUNT(*) FROM " . $tableName . " WHERE " . $sqlSnippet;
		$stmt = $con->prepareStatement($sql);

		if (Propel::isError($e = BasePeer::populateStmtValues($stmt, $selectParams, $dbMap)))
		  return $e;

		$rs =& $stmt->executeQuery(ResultSet::FETCHMODE_NUM());
		if (Creole::isError($rs)) {
		  Propel::log($rs->getMessage(), PROPEL_LOG_ERR);
		  return new PropelException(PROPEL_ERROR_DB, "Unable to execute DELETE statement.", $rs);
		}
		$rs->next();
		if ($rs->getInt(1) > 1) {
		  $rs->close();
		  return new PropelException(PROPEL_ERROR, "Expecting to delete 1 record, but criteria match multiple.");
		}
		$rs->close();
	  }

	  $sql = "DELETE FROM " . $tableName . " WHERE " .  $sqlSnippet;
	  Propel::log($sql, PROPEL_LOG_DEBUG);
	  $stmt =& $con->prepareStatement($sql);

	  if (Propel::isError($e = BasePeer::populateStmtValues($stmt, $selectParams, $dbMap)))
		return $e;

	  $result = $stmt->executeUpdate();
	  if (Creole::isError($result)) {
		Propel::log($result->getMessage(), PROPEL_LOG_ERR);
		return new PropelException(PROPEL_ERROR_DB, "Unable to execute DELETE statement.", $result);
	  }
	} // for each table

	return $result;
  }

  /**
  * Method to deletes all contents of specified table.
  *
  * This method is invoked from generated Peer classes like this:
  * <code>
  * function doDeleteAll($con = null)
  * {
  *   $con = Param::get($con);
  *   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      Connection $con A Connection.
  *
  * @return     mixed Number of affected rows on success, PropelException on failure.
  */
  function doDeleteAll($tableName, /*Connection*/ &$con)
  {
	$sql = "DELETE FROM " . $tableName;
	Propel::log($sql, PROPEL_LOG_DEBUG);

	$stmt =& $con->prepareStatement($sql);
	$result = $stmt->executeUpdate();

	if (Creole::isError($result)) {
	  Propel::log($result->getMessage(), PROPEL_LOG_ERR);
	  return new PropelException(PROPEL_ERROR_DB, "Unable to perform DELETE ALL operation.", $result);
	}

	return $result;
  }

  /**
  * 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      Connection $con A 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> OR
  *               PropelException on failure.
  */
  function doInsert(/*Criteria*/ $criteria, /*Connection*/ &$con)
  {
	// the primary key
	$id = null;
	// Get the table name and method for determining the primary
	// key value.
	$keys = $criteria->keys();

	if (empty($keys)) {
	  return new PropelException(PROPEL_ERROR, "Database insert attempted without anything specified to insert");
	}

	$tableName = $criteria->getTableName($keys[0]);

	$dbMap =& Propel::getDatabaseMap($criteria->getDbName());

	if (Propel::isError($dbMap))
	  return $dbMap;

	$tableMap =& $dbMap->getTable($tableName);
	$keyInfo =& $tableMap->getPrimaryKeyMethodInfo();
	$useIdGen = $tableMap->isUseIdGenerator();
	$keyGen =& $con->getIdGenerator();

	$pk = BasePeer::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->containsKey($pk->getFullyQualifiedName()))
	{
	  // If the keyMethod is SEQUENCE get the id before the insert.
	  if ($keyGen->isBeforeInsert())
	  {
		$id = $keyGen->getId($keyInfo);
		if (Creole::isError($id)) {
		  return new PropelException(PROPEL_ERROR, "Unable to get sequence id.", $id);
		}
		$criteria->add($pk->getFullyQualifiedName(), $id);
	  }
	}

	$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, strpos($qualifiedCol, '.') + 1);
	}

	$sql = "INSERT INTO " . $tableName
			. " (" . implode(",", $columns) . ")"
			. " VALUES (" . substr(str_repeat("?,", count($columns)), 0, -1) . ")";

	Propel::log($sql, PROPEL_LOG_DEBUG);

	$stmt =& $con->prepareStatement($sql);
	$params =& BasePeer::buildParams($qualifiedCols, $criteria);

	if (Propel::isError($e = BasePeer::populateStmtValues($stmt, $params, $dbMap))) {
	  Propel::log($e->getMessage(), PROPEL_LOG_ERR);
	  return new PropelException("Unable to execute INSERT statement.", $e);
	}

	if (Creole::isError($e = $stmt->executeUpdate())) {
	  Propel::log($e->getMessage(), PROPEL_LOG_ERR);
	  return new PropelException(PROPEL_ERROR_DB, "Unable to execute INSERT statement.", $e);
	}

	// If the primary key column is auto-incremented, get the id
	// now.
	if ($pk !== null && $useIdGen && $keyGen->isAfterInsert())
	{
	  $id = $keyGen->getId($keyInfo);
	  if (Creole::isError($id)) {
		Propel::log("Unable to get autoincrement id: ", $id->getMessage(), PROPEL_LOG_ERR);
		return new PropelException(PROPEL_ERROR_DB, "Unable to get autoincrement id.", $id);
	  }
	}

	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      Criteria $selectCriteria A Criteria object containing values used in where clause.
   * @param      Criteria $updateValues A Criteria object containing values used in set clause.
   * @param      Connection $con A Connection.
   * @return     mixed Number of affected rows on success, PropelException on failure.
   * @static     public
   */
  function doUpdate(/*Criteria*/ &$selectCriteria, /*Criteria*/ &$updateValues, /*Connection*/ &$con)
  {
	Propel::typeHint($con, 'Connection', 'BasePeer', 'doUpdate', 3);

	$db =& Propel::getDB($selectCriteria->getDbName());
	$dbMap =& Propel::getDatabaseMap($selectCriteria->getDbName());

	if (Propel::isError($dbMap))
	  return $dbMap;

	// Get list of required tables, containing all columns
	$tablesColumns = $selectCriteria->getTablesColumns();
	// we also need the columns for the update SQL
	$updateTablesColumns = $updateValues->getTablesColumns();
	// initialize this in case the next loop has no iterations.
	$result = 0;

	foreach($tablesColumns as $tableName => $columns)
	{
	  $whereClause = array();
	  $selectParams = array();

	  foreach($columns as $colName)
	  {
		$sb = "";
		$c =& $selectCriteria->getCriterion($colName);

		if (Propel::isError($e = $c->appendPsTo($sb, $selectParams)))
		  return $e;

		$whereClause[] = $sb;
	  }

	  $rs = null;
	  $stmt = null;

	  $sqlSnippet = implode(" AND ", $whereClause);

	  if ($selectCriteria->isSingleRecord())
	  {
		// Get affected records.
		$sql = "SELECT COUNT(*) FROM " . $tableName . " WHERE " . $sqlSnippet;
		$stmt =& $con->prepareStatement($sql);

		if (Propel::isError($e = BasePeer::populateStmtValues($stmt, $selectParams, $dbMap)))
		  return $e;

		$rs =& $stmt->executeQuery(ResultSet::FETCHMODE_NUM());
		if (Creole::isError($rs)) {
		  return new PropelException(PROPEL_ERROR_DB, "Unable to execute UPDATE statement !", $rs);
		}
		if ($rs) {
		  $rs->next();
		  if ($rs->getInt(1) > 1) {
			$rs->close();
			return new PropelException(PROPEL_ERROR, "Expected to update 1 record, multiple matched.");
		  }
		  $rs->close();
		}
	  }

	  $sql = "UPDATE " . $tableName . " SET ";
	  foreach($updateTablesColumns[$tableName] as $col) {
		$sql .= substr($col, strpos($col, '.') + 1) . " = ?,";
	  }

	  $sql = substr($sql, 0, -1) . " WHERE " . $sqlSnippet;

	  Propel::log($sql, PROPEL_LOG_DEBUG);

	  $stmt =& $con->prepareStatement($sql);

	  // Replace '?' with the actual values
	  $params =& BasePeer::buildParams($updateTablesColumns[$tableName], $updateValues);

	  if (Propel::isError($e = BasePeer::populateStmtValues($stmt, array_merge($params, $selectParams), $dbMap)))
		return $e;

	  if (Creole::isError($result = $stmt->executeUpdate())) {
		if ($rs) $rs->close();
		if ($stmt) $stmt->close();
		Propel::log($result->getMessage(), PROPEL_LOG_ERR);
		return new PropelException(PROPEL_ERROR_DB, "Unable to execute UPDATE statement.", $result);
	  }

	  $stmt->close();
	} // foreach table in the criteria

	return $result;
  }

  /**
  * Executes query build by createSelectSql() and returns a ResultSet.
  *
  * @param      Criteria $criteria A Criteria.
  * @param      Connection $con A connection to use.
  *
  * @return     mixed A ResultSet object on success, PropelException on failure.
  *
  * @see        createSelectSql()
  */
  function & doSelect(/*Criteria*/ &$criteria, $con = null)
  {
	/* [MA] temporarily check */
	Propel::assertParam($con, 'BasePeer', 'doSelect', 2);

	$con =& Param::get($con);

	if ($con === null) {
	  $con =& Propel::getConnection($criteria->getDbName());
	  if (Propel::isError($con)) { return $con; }
	}

	$dbMap = Propel::getDatabaseMap($criteria->getDbName());
	if (Propel::isError($dbMap)) { return $dbMap; }

	$stmt = null;

	// Transaction support exists for (only?) Postgres, which must
	// have SELECT statements that include bytea columns wrapped w/
	// transactions.
	if ($criteria->isUseTransaction())
	{
	  $e = $con->begin();
	  if (Creole::isError($e)) {
		return new PropelException(PROPEL_ERROR_DB, $e);
	  }
	}

	$params = array();
	$sql = BasePeer::createSelectSql($criteria, $params);

	if (Propel::isError($sql)) {
	  if ($criteria->isUseTransaction()) {
		$con->rollback();
	  }
	  return $sql;
	}

	$stmt =& $con->prepareStatement($sql);
	$stmt->setLimit($criteria->getLimit());
	$stmt->setOffset($criteria->getOffset());

	$e = BasePeer::populateStmtValues($stmt, $params, $dbMap);

	if (Propel::isError($e)) {
	  if ($criteria->isUseTransaction()) {
		$con->rollback();
	  }
	  return $e;
	}

	$rs =& $stmt->executeQuery(ResultSet::FETCHMODE_NUM());

	if (Creole::isError($rs)) {
	  $stmt->close();
	  Propel::log($rs->getMessage(), PROPEL_LOG_ERR);

	  if ($criteria->isUseTransaction()) {
		$con->rollback();
	  }

	  return new PropelException(PROPEL_ERROR_DB, "Unable to execute SELECT statement !", $rs);
	}

	if ($criteria->isUseTransaction())
	{
	  $e = $con->commit();
	  if (Creole::isError($e))
	  {
		$stmt->close();
		$con->rollback();
		Propel::log($e->getMessage(), PROPEL_LOG_ERR);
		return new PropelException(PROPEL_ERROR_DB, $e);
	  }
	}

	return $rs;
  }

	/**
	 * 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.
	 */
	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);
		  $vals =& $col->getValidators();
		  for($i = 0, $j = count($vals); $i < $j; $i++) {
			$validatorMap =& $vals[$i];
			$validator =& BasePeer::getValidator($validatorMap->getClass());
			if ($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());
			  }
			}
		  }
		}
	  }
	  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 (Propel::isError($dbMap))
		return $dbMap;


	  if ($dbMap->getTable($table) == null)
		return new PropelException(PROPEL_ERROR, "\$dbMap->getTable() is null");

	  $t =& $dbMap->getTable($table);
	  $columns = $t->getColumns();
	  foreach(array_keys($columns) as $key) {
		if ($columns[$key]->isPrimaryKey()) {
		  $pk = $columns[$key];
		  break;
		}
	  }
	}

	return $pk;
  }

  /**
   * 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 Creole 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.
   */
  function createSelectSql(/*Criteria*/ &$criteria, &$params)
  {
	$db =& Propel::getDB($criteria->getDbName());
	$dbMap =& Propel::getDatabaseMap($criteria->getDbName());

	if (Propel::isError($dbMap))
	  return $dbMap;

	// redundant definition $selectModifiers = array();
	$selectClause = array();
	$fromClause = 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 columns
	foreach($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 = strpos($columnName, '(');
	  $dotPos = strpos($columnName, '.');

	  // [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)
		  // FIXED: see Issue #21
		  $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
	} // foreach

	// set the aliases
	foreach($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 JOIN
	foreach($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;
		}

		$t =& $dbMap->getTable($table);

		/* fix for CriteriaTest.php*/
		if ($t) {
		  $col =& $t->getColumn($someCriteria[$i]->getColumn());
		  $type = $col->getType();
		} else {
		  $type = null;
		}

		$ignoreCase = (
		  ($criteria->isIgnoreCase() || $someCriteria[$i]->isIgnoreCase())
		  && ($type == "string" )
		);

		$someCriteria[$i]->setIgnoreCase($ignoreCase);
	  }

	  $criterion->setDB($db);

	  $sb = "";

	  if (Propel::isError($e = $criterion->appendPsTo($sb, $params)))
		return $e;

	  $whereClause[] = $sb;
	}

	// handle RIGHT (straight) joins
	// This adds tables to the FROM clause and adds WHERE clauses.  Not sure if this shouldn't
	// be changed to use INNER JOIN
	$joins =& $criteria->getJoins();
	if (!empty($joins)) {
	  for ($i=0, $joinSize=count($joins); $i < $joinSize; $i++) {
	  	$join =& $joins[$i];
		$join1 = $join->getLeftColumn();
		$join2 = $join->getRightColumn();

		$tableName = substr($join1, 0, strpos($join1, '.'));
		$table = $criteria->getTableForAlias($tableName);
		if ($table !== null) {
			$fromClause[] = $table . ' ' . $tableName;
		} else {
			$fromClause[] = $tableName;
		}

		$dot = strpos($join2, '.');
		$tableName = substr($join2, 0, $dot);
		$table = $criteria->getTableForAlias($tableName);
		if ($table !== null) {
			$fromClause[] = $table . ' ' . $tableName;
		} else {
			$fromClause[] = $tableName;
			$table = $tableName;
		}

		$t =& $dbMap->getTable($table);
		$col =& $t->getColumn(substr($join2, $dot + 1));
		$type =& $col->getType();

		$ignoreCase = ($criteria->isIgnoreCase() && ($type == "string"));

		if ($ignoreCase) {
			$whereClause[] = $db->ignoreCase($join1) . '=' . $db->ignoreCase($join2);
		} else {
			$whereClause[] = $join1 . '=' . $join2;
		}

		if ($join->getJoinType())
		{
		  $leftTable = $fromClause[count($fromClause) - 2];
		  $rightTable = $fromClause[count($fromClause) - 1];

		  $onClause = $whereClause[count($whereClause) - 1];
		  unset($whereClause[count($whereClause) - 1]);

		  $fromClause [] = $leftTable . ' ' . $join->getJoinType() . ' ' . $rightTable . ' ON ' . $onClause;

		  // remove all references to joinTables made by selectColumns, criteriaColumns
		  for ($i = 0, $fromClauseSize=count($fromClause); $i < $fromClauseSize; $i++) {
		  	if ($fromClause[$i] == $leftTable || $fromClause[$i] == $rightTable) {
			  unset($fromClause[$i]);
			}
		  }
		}
	  }
	}

	// Unique from clause elements
	$fromClause = array_unique($fromClause);

	// Add the GROUP BY columns
	$groupByClause = $groupBy;

	$having = $criteria->getHaving();
	$havingString = null;

	if ($having !== null) {
	  $sb = "";

	  if (Propel::isError($e = $having->appendPsTo($sb, $params)))
		return $e;

	  $havingString = $sb;
	}

	if (!empty($orderBy))
	{
	  foreach($orderBy as $orderByColumn)
	  {
		$dotPos = strpos($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 = null;
		if ($tableName) {
		  $table = $dbMap->getTable($tableName);
		  $column = $table->getColumn($columnName);
		}

		if ($column && $column->getType() == 'string') {
		  $orderByClause[] = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias") . $direction;
		  $selectClause[] = $db->ignoreCaseInOrderBy("$tableAlias.$columnAlias");
		}
		else {
		  $orderByClause[] = $orderByColumn;
		}
	  }
	}

	// Build the SQL from the arrays we compiled
	$sql = "SELECT "
		 .($selectModifiers ? implode(" ", $selectModifiers) . " " : "")
		 .implode(", ", $selectClause)
		 ." FROM ".implode(", ", $fromClause)
		 .($whereClause   ? " WHERE ".implode(" AND ", $whereClause) : "")
		 .($groupByClause ? " GROUP BY ".implode(",", $groupByClause) : "")
		 .($havingString  ? " HAVING ".$havingString : "")
		 .($orderByClause ? " ORDER BY ".implode(",", $orderByClause) : "");

	 Propel::log($sql . ' [LIMIT: ' . $criteria->getLimit() . ', OFFSET: ' . $criteria->getOffset() . ']', PROPEL_LOG_DEBUG);

	 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;
  }

  /**
   * Populates values in a prepared statement.
   *
   * @param      PreparedStatement $stmt
   * @param      array $params array('column' => ..., 'table' => ..., 'value' => ...)
   * @param      DatabaseMap $dbMap
   * @return     int The number of params replaced.
   * @private static
   */
  function populateStmtValues(&$stmt, &$params, /*DatabaseMap*/ &$dbMap)
  {
	$i = 1;
	foreach($params as $param)
	{
	  $tableName = $param['table'];
	  $columnName = $param['column'];
	  $value = $param['value'];

	  if ($value === null) {
		$stmt->setNull($i++);
	  } else {
		$t =& $dbMap->getTable($tableName);
		$cMap = $t->getColumn($columnName);
		$setter = 'set' . CreoleTypes::getAffix($cMap->getCreoleType());

		if (Creole::isError($setter)) {
		  return new PropelException(PROPEL_ERROR_DB, $setter);
		}

		$stmt->$setter($i++, $value);
	  }
	} // foreach
  }

  /**
  * 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 or PropelException if not able to instantiate validator class.
  */
  function & getValidator($classname)
  {
	$self =& BasePeer::getInstance();
	$v = null;

	if (isset($self->validatorMap[$classname])) {
	  $v =& $self->validatorMap[$classname];
	}

	if ($v === null)
	{
	  $cls = Propel::import($classname);
	  if (Propel::isError($cls)) {
		Propel::log("BasePeer::getMapBuilder() failed trying to instantiate: " . $classname . ": " . $cls->getMessage(), PROPEL_LOG_ERR);
		return $cls;
	  }

	  $v =& new $cls();
	  $self->validatorMap[$classname] =& $v;
	}

	return $v;
  }

  /**
   * This method returns the MapBuilder specified in the name
   * parameter.  You should pass in the full dot-path path to the class, ie:
   * myapp.propel.MyMapMapBuilder.  The MapBuilder instances are cached in
   * this class for speed.
   *
   * @param      string $classname The dot-path name of class (e.g. myapp.propel.MyMapBuilder)
   * @return     MapBuilder or PropelException (and logs the error) if the MapBuilder was not found.
   * @todo       -cBasePeer Consider adding app-level caching support for map builders.
   */
  function & getMapBuilder($classname)
  {
	$self =& BasePeer::getInstance();
	$mb = null;

	if (isset($self->mapBuilders[$classname])) {
	  $mb =& $self->mapBuilders[$classname];
	}

	if ($mb === null)
	{
	  $cls = Propel::import($classname);
	  if (Propel::isError($cls)) {
		// Have to catch possible exceptions because method is
		// used in initialization of Peers.  Log the exception and
		// return null.
		Propel::log("BasePeer::getMapBuilder() failed trying to instantiate: " . $classname . ": " . $cls->getMessage(), PROPEL_LOG_ERR);
		return $cls;
	  }

	  $mb =& new $cls();
	  $self->mapBuilders[$classname] =& $mb;
	}

	if (! $mb->isBuilt()) {
	  $mb->doBuild();
	}

	return $mb;
  }

  /*
  * @private
  */
  function & getInstance()
  {
	static $instance;

	if ($instance === null) {
	  $instance = new BasePeer();
	}

	return $instance;
  }

};
Return current item: DIY Blog