Location: PHPKode > projects > DIY Blog > diy-blog/lib/propel/runtime-php4/classes/propel/util/Criteria.php
<?php
/*
 *  $Id: Criteria.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>.
 */

/**
 * This is a utility class for holding criteria information for a query.
 *
 * BasePeer constructs SQL statements based on the values in this class.
 *
 * @author     Kaspars Jaudzems <hide@address.com> (Propel)
 * @author     Hans Lellelid <hide@address.com> (Propel)
 * @author     Michael Aichler <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     Eric Dobbs <hide@address.com> (Torque)
 * @author     Henning P. Schmiedehausen <hide@address.com> (Torque)
 * @author     Sam Joseph <hide@address.com> (Torque)
 * @version    $Revision: 536 $
 * @package    propel.util
 */
class Criteria /*implements IteratorAggregate */
{
  /** Comparison type. */
  function EQUAL() { return "="; }

  /** Comparison type. */
  function NOT_EQUAL() { return "<>"; }

  /** Comparison type. */
  function ALT_NOT_EQUAL() { return "!="; }

  /** Comparison type. */
  function GREATER_THAN() { return ">"; }

  /** Comparison type. */
  function LESS_THAN() { return "<"; }

  /** Comparison type. */
  function GREATER_EQUAL() { return ">="; }

  /** Comparison type. */
  function LESS_EQUAL() { return "<="; }

  /** Comparison type. */
  function LIKE() { return " LIKE "; }

  /** Comparison type. */
  function NOT_LIKE() { return " NOT LIKE "; }

  /** PostgreSQL comparison type */
  function ILIKE() { return " ILIKE "; }

  /** PostgreSQL comparison type */
  function NOT_ILIKE() { return " NOT ILIKE "; }

  /** Comparison type. */
  function CUSTOM() { return "CUSTOM"; }

  /** Comparison type. */
  function DISTINCT() { return "DISTINCT "; }

  /** Comparison type. */
  function IN () { return " IN "; }

  /** Comparison type. */
  function NOT_IN() { return " NOT IN "; }

  /** Comparison type. */
  function ALL() { return "ALL "; }

  /** Comparison type. */
  function JOIN() { return "JOIN"; }

  /** "Order by" qualifier - ascending */
  function ASC() { return "ASC"; }

  /** "Order by" qualifier - descending */
  function DESC() { return "DESC"; }

  /** "IS NULL" null comparison */
  function ISNULL() { return " IS NULL "; }

  /** "IS NOT NULL" null comparison */
  function ISNOTNULL() { return " IS NOT NULL "; }

  /** "CURRENT_DATE" ANSI SQL function */
  function CURRENT_DATE() { return "CURRENT_DATE"; }

  /** "CURRENT_TIME" ANSI SQL function */
  function CURRENT_TIME() { return "CURRENT_TIME"; }

  /** "CURRENT_TIMESTAMP" ANSI SQL function */
  function CURRENT_TIMESTAMP() { return "CURRENT_TIMESTAMP"; }

  /** "LEFT JOIN" SQL statement */
  function LEFT_JOIN() { return "LEFT JOIN"; }

  /** "RIGHT JOIN" SQL statement */
  function RIGHT_JOIN() { return "RIGHT JOIN"; }

  /** "INNER JOIN" SQL statement */
  function INNER_JOIN() { return "INNER JOIN"; }

  var $ignoreCase = false;
  var $singleRecord = false;
  var $selectModifiers = array();
  var $asColumns = array();
  var $selectColumns = array();
  var $orderByColumns = array();
  var $groupByColumns = array();
  var $having = null;
  var $joins = array();

  /** The name of the database. */
  var $dbName;

  /** The name of the database as given in the contructor. */
  var $originalDbName;

  /**
  * To limit the number of rows to return.  <code>0</code> means return all
  * rows.
  */
  var $limit = 0;

  /** To start the results at a row other than the first one. */
  var $offset = 0;

  // flag to note that the criteria involves a blob.
  var $blobFlag = null;

  var $aliases = null;

  var $useTransaction = false;

  /**
  * Primary storage of criteria data.
  * @var        array
  */
  var $map = array();

  /**
  * Creates a new instance with the default capacity which corresponds to
  * the specified database.
  *
  * @param      dbName The dabase name.
  */
  function Criteria($dbName = null)
  {
	$this->setDbName($dbName);
	$this->originalDbName = $dbName;
  }

  /**
  * Implementing SPL IteratorAggregate interface.  This allows
  * you to foreach() over a Criteria object.
  */
  function & getIterator()
  {
	return new CriterionIterator($this);
  }

  /**
  * Get the criteria map.
  *
  * @return     array
  */
  function & getMap()
  {
	return $this->map;
  }

  /**
  * Brings this criteria back to its initial state, so that it
  * can be reused as if it was new. Except if the criteria has grown in
  * capacity, it is left at the current capacity.
  *
  * @return     void
  */
  function clear()
  {
	$this->map = array();
	$this->ignoreCase = false;
	$this->singleRecord = false;
	$this->selectModifiers = array();
	$this->selectColumns = array();
	$this->orderByColumns = array();
	$this->groupByColumns = array();
	$this->having = null;
	$this->asColumns = array();
	$this->joins = array();
	$this->dbName = $this->originalDbName;
	$this->offset = 0;
	$this->limit = -1;
	$this->blobFlag = null;
	$this->aliases = null;
	$this->useTransaction = false;
  }

  /**
  * Add an AS clause to the select columns. Usage:
  *
  * <code>
  * Criteria myCrit = new Criteria();
  * myCrit.addAsColumn("alias", "ALIAS(" . MyPeer::ID() . ")");
  * </code>
  *
  * @param      string $name  wanted Name of the column (alias)
  * @param      string $clause SQL clause to select from the table
  *
  * If the name already exists, it is replaced by the new clause.
  *
  * @return     Criteria A modified Criteria object.
  */
  function & addAsColumn($name, $clause)
  {
	$this->asColumns[$name] = $clause;
	return $this;
  }

  /**
  * Get the column aliases.
  *
  * @return     array A Hashtable which map the column alias names
  * to the alias clauses.
  */
  function & getAsColumns()
  {
	return $this->asColumns;
  }

  /**
  * Returns the column name associated with an alias (AS-column).
  *
  * @param      string $alias
  * @return     string
  */
  function & getColumnForAs($as)
  {
	if(isset($this->asColumns[$as])) {
	  return $this->asColumns[$as];
	}
  }

  /**
  * Allows one to specify an alias for a table that can
  * be used in various parts of the SQL.
  *
  * @param      alias a <code>String</code> value
  * @param      table a <code>String</code> value
  * @return     void
  */
  function addAlias($alias, $table)
  {
	if ($this->aliases === null) {
	  $this->aliases = array();
	}

	$this->aliases[$alias] = $table;
  }

  /**
  * Returns the table name associated with an alias.
  *
  * @param      alias a <code>String</code> value
  * @return     string A <code>String</code> value
  */
  function & getTableForAlias($alias)
  {
	$table = null;

	if (isset($this->aliases[$alias])) {
	  $table = $this->aliases[$alias];
	}

	return $table;
  }

  /**
  * Get the keys for the criteria map.
  * @return     array
  */
  function keys()
  {
	return array_keys($this->map);
  }

  /**
  * Does this Criteria object contain the specified key?
  *
  * @param      string $column [table.]column
  * @return     boolean True if this Criteria object contain the specified key.
  */
  function containsKey($column)
  {
	// must use array_key_exists() because the key could
	// exist but have a NULL value (that'd be valid).
	return array_key_exists($column, $this->map);
  }

  /**
  * Will force the sql represented by this criteria to be executed within
  * a transaction.  This is here primarily to support the oid type in
  * postgresql.  Though it can be used to require any single sql statement
  * to use a transaction.
  * @return     void
  */
  function setUseTransaction($v)
  {
	$this->useTransaction = (boolean) $v;
  }

  /**
  * called by BasePeer to determine whether the sql command specified by
  * this criteria must be wrapped in a transaction.
  *
  * @return     a <code>boolean</code> value
  */
  function isUseTransaction()
  {
	return $this->useTransaction;
  }

  /**
  * Method to return criteria related to columns in a table.
  *
  * @return     A Criterion.
  */
  function & getCriterion($column)
  {
	if (isset($this->map[$column])) {
	  return $this->map[$column];
	}

	return null;
  }

  /**
  * Method to return criterion that is not added automatically
  * to this Criteria.  This can be used to chain the
  * Criterions to form a more complex where clause.
  *
  * @param      column String full name of column (for example TABLE.COLUMN).
  * @param      mixed $value
  * @param      string $comparison
  * @return     A Criterion.
  */
  function & getNewCriterion($column, $value, $comparison = null)
  {
	$nc =& new Criterion($this, $column, $value, $comparison);
	return $nc;
  }

  /**
  * Method to return a String table name.
  *
  * @param      name A String with the name of the key.
  * @return     A String with the value of the object at key.
  */
  function & getColumnName($name)
  {
	$val = null;

	if(isset($this->map[$name])) {
	  $val = $this->map[$name]->getColumn();
	}

	return $val;
  }

  /**
  * Shortcut method to get an array of columns indexed by table.
  * @return     array array(table => array(table.column1, table.column2))
  */
  function & getTablesColumns()
  {
	$tables = array();
	$keys = array_keys($this->map);
	foreach($keys as $key) {
	  $t = substr($key, 0, strpos($key, '.'));
	  // this happens automatically, so if no notices
	  // are raised, then leave it out:
	  // if (!isset($tables[$t])) $tables[$t] = array();
	  $tables[$t][] = $key;
	}
	return $tables;
  }

  /**
  * Method to return a comparison String.
  *
  * @param      string $key String name of the key.
  * @return     string A String with the value of the object at key.
  */
  function & getComparison($key)
  {
	$val = null;

	if (isset($this->map[$key])) {
	  $val = $this->map[$key]->getComparison();
	}

	return $val;
  }

  /**
  * Get the Database(Map) name.
  *
  * @return     string A String with the Database(Map) name.
  */
  function getDbName()
  {
	return $this->dbName;
  }

  /**
  * Set the DatabaseMap name.  If <code>null</code> is supplied, uses value
  * provided by <code>Propel::getDefaultDB()</code>.
  *
  * @param      $dbName A String with the Database(Map) name.
  * @return     void
  */
  function setDbName($dbName = null)
  {
	$this->dbName = ($dbName === null ? Propel::getDefaultDB() : $dbName);
  }

  /**
  * Method to return a String table name.
  *
  * @param      $name A String with the name of the key.
  * @return     string A String with the value of table for criterion at key.
  */
  function & getTableName($name)
  {
	$val = null;

	if (isset($this->map[$name])) {
	  $val = $this->map[$name]->getTable();
	}

	return $val;
  }

  /**
  * Method to return the value that was added to Criteria.
  *
  * @param      string $name A String with the name of the key.
  * @return     mixed The value of object at key.
  */
  function & getValue($name)
  {
	$val = null;

	if (isset($this->map[$name])) {
	  $val = $this->map[$name]->getValue();
	}

	return $val;
  }

  /**
  * An alias to getValue() -- exposing a Hashtable-like interface.
  *
  * @param      string $key An Object.
  * @return     mixed The value within the Criterion (not the Criterion object).
  */
  function & get($key)
  {
	return $this->getValue($key);
  }

  /**
  * Overrides Hashtable put, so that this object is returned
  * instead of the value previously in the Criteria object.
  * The reason is so that it more closely matches the behavior
  * of the add() methods. If you want to get the previous value
  * then you should first Criteria.get() it yourself. Note, if
  * you attempt to pass in an Object that is not a String, it will
  * throw a NPE. The reason for this is that none of the add()
  * methods support adding anything other than a String as a key.
  *
  * @param      string $key
  * @param      string $value
  * @return     Instance of self.
  */
  function & put($key, $value)
  {
	return $this->add($key, $value);
  }

  /**
  * Copies all of the mappings from the specified Map to this Criteria
  * These mappings will replace any mappings that this Criteria had for any
  * of the keys currently in the specified Map.
  *
  * if the map was another Criteria, its attributes are copied to this
  * Criteria, overwriting previous settings.
  *
  * @param      mixed $t Mappings to be stored in this map.
  */
  function putAll($t)
  {
	if (is_array($t))
	{
	  $keys = array_keys($t);
	  foreach ($keys as $key)
	  {
		$val =& $t[$key];
		if (is_a($val, 'Criterion')) {//$val instanceof Criterion) {
		  $this->map[$key] =& $val;
		} else {
		  // put throws an exception ... right?
		  // otherwise there's no difference ... not sure why this is here
		  // %%%
		  $this->put($key, $val);
		}
	  }
	}
	else if (is_a($t, 'Criteria')) //($t instanceof Criteria)
	{
	  $this->joins = $t->joins;
	}
  }

  /**
  * This method adds a new criterion to the list of criterias.
  * If a criterion for the requested column already exists, it is
  * replaced. If is used as follow:
  *
  * <p>
  * <code>
  * Criteria crit = new Criteria();
  * $crit->add(&quot;column&quot;,
  *            &quot;value&quot;
  *            &quot;Criterion.GREATER_THAN()&quot;);
  * </code>
  *
  * Any comparison can be used.
  *
  * The name of the table must be used implicitly in the column name,
  * so the Column name must be something like 'TABLE.id'. If you
  * don't like this, you can use the add(table, column, value) method.
  *
  * @param      mixed $p1 The column to run the comparison on, or a Criterion object.
  * @param      mixed $value
  * @param      string $comparison A String.
  *
  * @return     A modified Criteria object.
  */
  function & add($p1, $value = null, $comparison = null)
  {
	if (is_a($p1, 'Criterion')) {
	  $c =& $p1;
	  $this->map[$c->getTable() . '.' . $c->getColumn()] =& $c;
	} else {
	  $column = $p1;
	  $this->map[$column] =& new Criterion($this, $column, $value, $comparison);
	}

	return $this;
  }

  /**
  * This is the way that you should add a straight (inner) join of two tables.  For
  * example:
  *
  * <p>
  * AND PROJECT.PROJECT_ID=FOO.PROJECT_ID
  * <p>
  *
  * left = PROJECT.PROJECT_ID
  * right = FOO.PROJECT_ID
  *
  * @param      string $left A String with the left side of the join.
  * @param      string $right A String with the right side of the join.
  * @param      string $operator A String with the join operator e.g. LEFT JOIN, ...
  * @return     Criteria A modified Criteria object.
  */
  function & addJoin($left, $right, $operator = null)
  {
	$this->joins [] =& new Join($left, $right, $operator);

	return $this;
  }

  /**
   * Get the List of Joins.  This method is meant to
   * be called by BasePeer.
   * @return     an array which contains objects of type Join,
   *         or an empty array if the criteria does not contains any joins
   */
  function & getJoins()
  {
	return $this->joins;
  }

  /**
  * get one side of the set of possible joins.  This method is meant to
  * be called by BasePeer.
  * @return     array
  * @deprecated This method is no longer used by BasePeer.
  */
  function & getJoinL()
  {
	return new PropelException("getJoinL() in Criteria is no longer supported!");
  }

  /**
  * get one side of the set of possible joins.  This method is meant to
  * be called by BasePeer.
  * @return     array
  * @deprecated This method is no longer used by BasePeer.
  */
  function & getJoinR()
  {
	return new PropelException("getJoinL() in Criteria is no longer supported!");
  }

  /**
  * Adds "ALL " to the SQL statement.
  * @return     void
  */
  function setAll()
  {
	$this->selectModifiers[] = Criteria::ALL();
  }

  /**
  * Adds "DISTINCT " to the SQL statement.
  * @return     void
  */
  function setDistinct()
  {
	$this->selectModifiers[] = Criteria::DISTINCT();
  }

  /**
  * Sets ignore case.
  *
  * @param      boolean $b True if case should be ignored.
  * @return     A modified Criteria object.
  */
  function & setIgnoreCase($b)
  {
	$this->ignoreCase = (boolean) $b;
	return $this;
  }

  /**
  * Is ignore case on or off?
  *
  * @return     boolean True if case is ignored.
  */
  function isIgnoreCase()
  {
	return $this->ignoreCase;
  }

  /**
  * Set single record?  Set this to <code>true</code> if you expect the query
  * to result in only a single result record (the default behaviour is to
  * throw a PropelException if multiple records are returned when the query
  * is executed).  This should be used in situations where returning multiple
  * rows would indicate an error of some sort.  If your query might return
  * multiple records but you are only interested in the first one then you
  * should be using setLimit(1).
  *
  * @param      bool $b Set to <code>true</code> if you expect the query to select just
  * one record.
  * @return     A modified Criteria object.
  */
  function & setSingleRecord($b)
  {
	$this->singleRecord = (boolean) $b;
	return $this;
  }

  /**
  * Is single record?
  *
  * @return     boolean True if a single record is being returned.
  */
  function isSingleRecord()
  {
	return $this->singleRecord;
  }

  /**
  * Set limit.
  *
  * @param      int $limit An int with the value for limit.
  * @return     A modified Criteria object.
  */
  function & setLimit($limit)
  {
	$this->limit = $limit;
	return $this;
  }

  /**
  * Get limit.
  *
  * @return     int An int with the value for limit.
  */
  function getLimit()
  {
	return $this->limit;
  }

  /**
  * Set offset.
  *
  * @param      int $offset An int with the value for offset.
  * @return     A modified Criteria object.
  */
  function & setOffset($offset)
  {
	$this->offset = $offset;
	return $this;
  }

  /**
  * Get offset.
  *
  * @return     An int with the value for offset.
  */
  function getOffset()
  {
	return $this->offset;
  }

  /**
  * Add select column.
  *
  * @param      string $name A String with the name of the select column.
  * @return     A modified Criteria object.
  */
  function & addSelectColumn($name)
  {
	$this->selectColumns[] = $name;
	return $this;
  }

  /**
  * Get select columns.
  *
  * @return     array An array with the name of the select
  * columns.
  */
  function getSelectColumns()
  {
	return $this->selectColumns;
  }

  /**
  * Clears current select columns.
  *
  * @return     Criteria A modified Criteria object.
  */
  function & clearSelectColumns()
  {
	$this->selectColumns = array();
	$this->asColumns = array();
	return $this;
  }

  /**
  * Get select modifiers.
  *
  * @return     An array with the select modifiers.
  */
  function getSelectModifiers()
  {
	return $this->selectModifiers;
  }

  /**
  * Add group by column name.
  *
  * @param      string $groupBy The name of the column to group by.
  * @return     A modified Criteria object.
  */
  function & addGroupByColumn($groupBy)
  {
	$this->groupByColumns[] = $groupBy;
	return $this;
  }

  /**
  * Add order by column name, explicitly specifying ascending.
  *
  * @param      string $name The name of the column to order by.
  * @return     The modified Criteria object.
  */
  function & addAscendingOrderByColumn($name)
  {
	$this->orderByColumns[] = $name . ' ' . Criteria::ASC();
	return $this;
  }

  /**
  * Add order by column name, explicitly specifying descending.
  *
  * @param      string $name The name of the column to order by.
  * @return     The modified Criteria object.
  */
  function & addDescendingOrderByColumn($name)
  {
	$this->orderByColumns[] = $name . ' ' . Criteria::DESC();
	return $this;
  }

  /**
  * Get order by columns.
  *
  * @return     A StringStack with the name of the order columns.
  */
  function getOrderByColumns()
  {
	return $this->orderByColumns;
  }

  /**
  * Clear the order-by columns.
  *
  * @return     The modified Criteria object.
  */
  function & clearOrderByColumns()
  {
	$this->orderByColumns = array();
	return $this;
  }

  /**
  * Clear the group-by columns.
  *
  * @return     The modified Criteria object.
  */
  function & clearGroupByColumns()
  {
	$this->groupByColumns = array();
	return $this;
  }


  /**
  * Get group by columns.
  *
  * @return     array.
  */
  function getGroupByColumns()
  {
	return $this->groupByColumns;
  }

  /**
  * Get Having Criterion.
  *
  * @return     A Criterion that is the having clause.
  */
  function & getHaving()
  {
	return $this->having;
  }

  /**
  * Remove an object from the criteria.
  *
  * @param      string $key A String with the key to be removed.
  * @return     mixed The removed value.
  */
  function & remove($key)
  {
	$c = null;

	if(isset($this->map[$key]))
	{
	  $c = $this->map[$key];
	  unset($this->map[$key]);

	  if (is_a($c, 'Criterion')) {
		return $c->getValue();
	  }
	}

	return $c;
  }

  /**
  * Build a string representation of the Criteria.
  *
  * @return     string A String with the representation of the Criteria.
  */
  function toString()
  {
	$sb  = "Criteria:: ";
	$sb .= "\nCurrent Query SQL (may not be complete or applicable): ";
	$params = array();

	$sql = BasePeer::createSelectSql($this, $params);

	if (Propel::isError($sql)) {
	  $sb .= "(Error: " . $sql->getMessage() . ")";
	}

	$sb .= "\nParameters to replace: " . var_export($params, true);

	return $sb;
  }

  /**
  * Returns the size (count) of this criteria.
  * @return     int
  */
  function size()
  {
	return count($this->map);
  }

  /**
  * This method checks another Criteria to see if they contain
  * the same attributes and hashtable entries.
  * @return     boolean
  */
  function equals(&$crit)
  {
	$isEquiv = false;
	if ($crit === null || ! is_a($crit, 'Criteria')) {
	  $isEquiv = false;
	}
	elseif ($this === $crit) {
	  $isEquiv = true;
	}
	elseif ($this->size() === $crit->size()) {
	  // Important: nested criterion objects are checked
	  $criteria =& $crit; // alias
	  if ($this->offset === $criteria->getOffset()
		  && $this->limit === $criteria->getLimit()
		  && $this->ignoreCase === $criteria->isIgnoreCase()
		  && $this->singleRecord === $criteria->isSingleRecord()
		  && $this->dbName === $criteria->getDbName()
		  && $this->selectModifiers === $criteria->getSelectModifiers()
		  && $this->selectColumns === $criteria->getSelectColumns()
		  && $this->orderByColumns === $criteria->getOrderByColumns()
		 )
	  {
		$isEquiv = true;
		foreach($criteria->keys() as $key) {
		  if ($this->containsKey($key)) {
			$a = $this->getCriterion($key);
			$b = $criteria->getCriterion($key);
			if (!$a->equals($b)) {
			  $isEquiv = false;
			  break;
			}
		  } else {
			$isEquiv = false;
			break;
		  }
		}
	  }
	}

	return $isEquiv;
  }

  /**
  * This method adds a prepared Criterion object to the Criteria as a having clause.
  * You can get a new, empty Criterion object with the
  * getNewCriterion() method.
  *
  * <p>
  * <code>
  * $crit = new Criteria();
  * $c = $crit->getNewCriterion(BasePeer::ID(), 5, Criteria::LESS_THAN());
  * $crit->addHaving($c);
  * </code>
  *
  * @param      having A Criterion object
  *
  * @return     A modified Criteria object.
  */
  function & addHaving(/*Criterion*/ &$having)
  {
	$this->having =& $having;
	return $this;
  }

  /**
  * This method adds a new criterion to the list of criterias.
  * If a criterion for the requested column already exists, it is
  * "AND"ed to the existing criterion.
  *
  * addAnd(column, value, comparison)
  * <code>
  * $crit = $orig_crit->addAnd(&quot;column&quot;,
  *                                      &quot;value&quot;
  *                                      &quot;Criterion::GREATER_THAN&quot;);
  * </code>
  *
  * addAnd(column, value)
  * <code>
  * $crit = $orig_crit->addAnd(&quot;column&quot;, &quot;value&quot;);
  * </code>
  *
  * addAnd(Criterion)
  * <code>
  * $crit = new Criteria();
  * $c = $crit->getNewCriterion(BasePeer::ID, 5, Criteria::LESS_THAN);
  * $crit->addAnd($c);
  * </code>
  *
  * Any comparison can be used, of course.
  *
  * @note As a Criterion object will be passed by value all function calls to that
  *       Criterion must be done before adding it to the Criteria !
  *
  * @param      mixed $p1 Column name or Criterion object.
  * @param      mixed $p2
  * @param      string $p3
  *
  * @return     Criteria A modified Criteria object.
  */
  function & addAnd($p1, $p2 = null, $p3 = null)
  {
	if ($p3 !== null)
	{
	  // addAnd(column, value, comparison)
	  $oc =& $this->getCriterion($p1);
	  $nc =& new Criterion($this, $p1, $p2, $p3);

	  if ($oc === null) {
		$this->map[$p1] =& $nc;
	  } else {
		$oc->addAnd($nc);
	  }
	}
	else if ($p2 !== null)
	{
	  // addAnd(column, value)
	  $this->addAnd($p1, $p2, Criteria::EQUAL());
	}
	else if (is_a($p1, 'Criterion'))
	{
	  // addAnd(Criterion)
	  $c =& $p1;
	  $oc =& $this->getCriterion($c->getTable() . '.' . $c->getColumn());

	  if ($oc === null) {
		$this->add($c);
	  } else {
		$oc->addAnd($c);
	  }
	}

	return $this;
  }

  /**
  * This method adds a new criterion to the list of criterias.
  * If a criterion for the requested column already exists, it is
  * "OR"ed to the existing criterion.
  *
  * Any comparison can be used.
  *
  * Supports a number of different signatures:
  *
  * addOr(column, value, comparison)
  * <code>
  * $crit = $orig_crit->addOr(&quot;column&quot;,
  *                           &quot;value&quot;
  *                           &quot;Criterion::GREATER_THAN&quot;);
  * </code>
  *
  * addOr(column, value)
  * <code>
  * $crit = $orig_crit->addOr(&quot;column&quot;, &quot;value&quot;);
  * </code>
  *
  * addOr(Criterion)
  *
  * @note As a Criterion object will be passed by value all function calls to that
  *       Criterion must be done before adding it to the Criteria !
  *
  * @param      mixed $p1 Column name or Criterion object.
  * @param      mixed $p2
  * @param      string $p3
  *
  * @return     Criteria A modified Criteria object.
  */
  function & addOr($p1, $p2 = null, $p3 = null)
  {
	if ($p3 !== null)
	{
	  // addOr(column, value, comparison)
	  $oc =& $this->getCriterion($p1);
	  $nc =& new Criterion($this, $p1, $p2, $p3);

	  if ($oc === null) {
		$this->map[$p1] =& $nc;
	  } else {
		$oc->addOr($nc);
	  }
	}
	else if ($p2 !== null)
	{
	  // addOr(column, value)
	  $this->addOr($p1, $p2, Criteria::EQUAL());
	}
	else if (is_a($p1, 'Criterion'))
	{
	  // addOr(Criterion)
	  $c =& $p1;
	  $oc =& $this->getCriterion($c->getTable() . '.' . $c->getColumn());
	  if ($oc === null) {
		$this->add($c);
	  } else {
		$oc->addOr($c);
	  }
	}

	return $this;
  }

};

// --------------------------------------------------------------------
// Criterion Iterator class -- allows foreach($criteria as $criterion)
// --------------------------------------------------------------------

/**
 * Class that implements SPL Iterator interface.  This allows foreach() to
 * be used w/ Criteria objects.  Probably there is no performance advantage
 * to doing it this way, but it makes sense -- and simpler code.
 *
 * @author     Hans Lellelid <hide@address.com>
 */
class CriterionIterator
{

  var $idx = 0;
  var $criteria;
  var $criteriaKeys;
  var $criteriaSize;

  function CriterionIterator(&$criteria)
  {
	$this->criteria =& $criteria;
	$this->criteriaKeys = $criteria->keys();
	$this->criteriaSize = count($this->criteriaKeys);
  }

  function rewind()
  {
	$this->idx = 0;
  }

  function valid()
  {
	return $this->idx < $this->criteriaSize;
  }

  function key()
  {
	return $this->criteriaKeys[$this->idx];
  }

  function & current()
  {
	return $this->criteria->getCriterion($this->criteriaKeys[$this->idx]);
  }

  function next()
  {
	$this->idx++;
  }

};

// --------------------------------------------------------------------
// Criterion "inner" class
// --------------------------------------------------------------------

define('Criterion_UND', " AND ");
define('Criterion_ODER'," OR ");

/**
 * This is an "inner" class that describes an object in the criteria.
 *
 * In Torque this is an inner class of the Criteria class.
 *
 * @author     Hans Lellelid <hide@address.com> (Propel)
 */
class Criterion
{
  /** Value of the CO. */
  var $value;

  /** Comparison value.
  * @var        SqlEnum
  */
  var $comparison;

  /** Table name. */
  var $table;

  /** Column name. */
  var $column;

  /** flag to ignore case in comparision */
  var $ignoreStringCase = false;

  /**
  * The DBAdapter adaptor which might be used to get db specific
  * variations of sql.
  */
  var $db;

  /**
  * other connected criteria and their conjunctions.
  */
  var $clauses = array();

  var $conjunctions = array();

  /** "Parent" Criteria class */
  var $parent;

  function UND() { return ' AND '; }

  function ODER() { return ' OR '; }

  /**
  * Create a new instance.
  *
  * @param      Criteria $parent The outer class (this is an "inner" class).
  * @param      string $column TABLE.COLUMN format.
  * @param      mixed $value
  * @param      string $comparison
  */
  function Criterion(/*Criteria*/ &$outer, $column, $value, $comparison = null)
  {
	$this->outer =& $outer;
	list($this->table, $this->column) = explode('.', $column);
	$this->value = $value;
	$this->comparison = ($comparison === null ? Criteria::EQUAL() : $comparison);
  }

  /**
  * Get the column name.
  *
  * @return     string A String with the column name.
  */
  function & getColumn()
  {
	return $this->column;
  }

  /**
  * Set the table name.
  *
  * @param      name A String with the table name.
  * @return     void
  */
  function setTable($name)
  {
	$this->table = $name;
  }

  /**
  * Get the table name.
  *
  * @return     string A String with the table name.
  */
  function & getTable()
  {
	return $this->table;
  }

  /**
  * Get the comparison.
  *
  * @return     string A String with the comparison.
  */
  function & getComparison()
  {
	return $this->comparison;
  }

  /**
  * Get the value.
  *
  * @return     mixed An Object with the value.
  */
  function getValue()
  {
	return $this->value;
  }

  /**
  * Get the value of db.
  * The DBAdapter which might be used to get db specific
  * variations of sql.
  * @return     DBAdapter value of db.
  */
  function & getDB()
  {
	$db = null;
	if ($this->db === null) {
	  // db may not be set if generating preliminary sql for
	  // debugging.
	  if (($db =& Propel::getDB($this->outer->getDbName())) === null) {
		  // we are only doing this to allow easier debugging, so
		  // no need to throw up the exception, just make note of it.
		  Propel::log("Could not get a DB adapter, so sql may be wrong", PROPEL_LOG_ERR);
	  }
	} else {
		$db =& $this->db;
	}

	return $db;
  }

  /**
  * Set the value of db.
  * The DBAdapter adaptor might be used to get db specific
  * variations of sql.
  * @param      v  Value to assign to db.
  * @return     void
  */
  function setDB(/*DBAdapter*/ &$v)
  {
	$this->db =& $v;
	for($i=0, $_i=count($this->clauses); $i < $_i; $i++) {
	  $this->clauses[$i]->setDB($v);
	}
  }

  /**
  * Sets ignore case.
  *
  * @param      boolean $b True if case should be ignored.
  * @return     Criterion A modified Criterion object.
  */
  function setIgnoreCase($b)
  {
	$this->ignoreStringCase = $b;
	return $this;
  }

  /**
  * Is ignore case on or off?
  *
  * @return     boolean True if case is ignored.
  */
  function isIgnoreCase()
  {
	return $this->ignoreStringCase;
  }

  /**
  * Get the list of clauses in this Criterion.
  * @return     array
  * @private
  */
  function & getClauses()
  {
	return $this->clauses;
  }

  /**
  * Get the list of conjunctions in this Criterion
  * @return     array
  * @private
  */
  function & getConjunctions()
  {
	return $this->conjunctions;
  }

  /**
  * Append an AND Criterion onto this Criterion's list.
  */
  function & addAnd(/*Criterion*/ $criterion)
  {
	$this->clauses[] =& $criterion;
	$this->conjunctions[] = Criterion::UND();
	return $this;
  }

  /**
  * Append an OR Criterion onto this Criterion's list.
  * @return     Criterion
  */
  function & addOr(/*Criterion*/ $criterion)
  {
	$this->clauses[] =& $criterion;
	$this->conjunctions[] = Criterion::ODER();
	return $this;
  }

  /**
  * Appends a Prepared Statement representation of the Criterion
  * onto the buffer.
  *
  * @param      string &$sb The stringbuffer that will receive the Prepared Statement
  * @param      array $params A list to which Prepared Statement parameters
  *                      will be appended
  * @return     PropelException If an error occures.
  */
  function appendPsTo(&$sb, &$params)
  {
	if ($this->column === null) {
	  return;
	}

	$db =& $this->getDb();
	$clausesLength = count($this->clauses);

	for($j = 0; $j < $clausesLength; $j++) {
	  $sb .= '(';
	}

	if (Criteria::CUSTOM() === $this->comparison)
	{
	  if ($this->value !== "") {
		$sb .= (string) $this->value;
	  }
	}
	else
	{
	  if  ($this->table === null) {
		$field = $this->column;
	  } else {
		$field = $this->table . '.' . $this->column;
	  }

	  // Check to see if table is an alias & store real name, if so
	  // (real table name is needed for the returned $params array)
	  $realtable = $this->outer->getTableForAlias($this->table);
	  if(!$realtable) $realtable = $this->table;

	  // There are several different types of expressions that need individual handling:
	  // IN/NOT IN, LIKE/NOT LIKE, and traditional expressions.

	  // OPTION 1:  table.column IN (?, ?) or table.column NOT IN (?, ?)
	  if ($this->comparison === Criteria::IN() || $this->comparison === Criteria::NOT_IN())
	  {
		$sb .= $field . $this->comparison;
		$values = (array) $this->value;
		for ($i=0, $valuesLength=count($values); $i < $valuesLength; $i++) {
			$params[] = array('table' => $realtable, 'column' => $this->column, 'value' => $values[$i]);
		}
		$inString = '(' . substr(str_repeat("?,", $valuesLength), 0, -1) . ')';
		$sb .= $inString;
	  }
	  // OPTION 2:  table.column LIKE ? or table.column NOT LIKE ?  (or ILIKE for Postgres)
	  elseif ($this->comparison === Criteria::LIKE() || $this->comparison === Criteria::NOT_LIKE()
		  || $this->comparison === Criteria::ILIKE() || $this->comparison === Criteria::NOT_ILIKE())
	  {
		// Handle LIKE, NOT LIKE (and related ILIKE, NOT ILIKE for Postgres)

		// If selection is case insensitive use ILIKE for PostgreSQL or SQL
		// UPPER() function on column name for other databases.
		if ($this->ignoreStringCase)
		{
		  if (is_a($db, 'DBPostgres'))
		  { // use is_a() because instanceof needs class to have been loaded
			if ($this->comparison === Criteria::LIKE()) {
			  $this->comparison = Criteria::ILIKE();
			}
			elseif ($this->comparison === Criteria::NOT_LIKE()) {
			  $this->comparison = Criteria::NOT_ILIKE();
			}
		  }
		  else {
			$field = $db->ignoreCase($field);
		  }
		}

		$sb .= $field . $this->comparison;

		// If selection is case insensitive use SQL UPPER() function
		// on criteria or, if Postgres we are using ILIKE, so not necessary.
		if ($this->ignoreStringCase && !is_a($db, 'DBPostgres')) {
		  $sb .= $db->ignoreCase('?');
		}
		else {
		  $sb .= '?';
		}

		$params[] = array('table' => $realtable, 'column' => $this->column, 'value' => $this->value);
	  }
	  // OPTION 3:  table.column = ? or table.column >= ? etc. (traditional expressions, the default)
	  else
	  {
		// NULL VALUES need special treatment because the SQL syntax is different
		// i.e. table.column IS NULL rather than table.column = null
		if ($this->value !== null)
		{
		  // ANSI SQL functions get inserted right into SQL (not escaped, etc.)
		  if ($this->value === Criteria::CURRENT_DATE() || $this->value === Criteria::CURRENT_TIME()) {
			  $sb .= $field . $this->comparison . $this->value;
		  }
		  else
		  {
			// default case, it is a normal col = value expression; value
			// will be replaced w/ '?' and will be inserted later using native Creole functions
			if ($this->ignoreStringCase) {
			  $sb .= $db->ignoreCase($field) . $this->comparison . $db->ignoreCase("?");
			} else {
			  $sb .= $field . $this->comparison . "?";
			}

			// need to track the field in params, because
			// we'll need it to determine the correct setter
			// method later on (e.g. field 'review.DATE' => setDate());
			$params[] = array('table' => $realtable, 'column' => $this->column, 'value' => $this->value);
		  }
		}
		else
		{
		  // value is null, which means it was either not specified or specifically
		  // set to null.
		  if ($this->comparison === Criteria::EQUAL() || $this->comparison === Criteria::ISNULL()) {
			  $sb .= $field . Criteria::ISNULL();
		  } elseif ($this->comparison === Criteria::NOT_EQUAL() || $this->comparison === Criteria::ISNOTNULL()) {
			  $sb .= $field . Criteria::ISNOTNULL();
		  } else {
			  // for now throw an exception, because not sure how to interpret this
			  return new PropelException(PROPEL_ERROR_SYNTAX, "Could not build SQL for expression: $field " . $this->comparison . " NULL");
		  }
		}
	  }
	}

	for($i=0; $i < $clausesLength; $i++) {
	  $sb .= $this->conjunctions[$i];
	  $this->clauses[$i]->appendPsTo($sb, $params);
	  $sb .= ')';
	}
  }

  /**
  * Build a string representation of the Criterion.
  *
  * @return     string A String with the representation of the Criterion.
  */
  function toString()
  {
	//
	// it is alright if value == null
	//
	if ($this->column == null) {
	   return "";
	}

	$expr = "";
	$params = array("", "");
	$this->appendPSTo($expr, $params);
	return $expr;
  }

  /**
  * This method checks another Criteria to see if they contain
  * the same attributes and hashtable entries.
  *
  * @return     boolean
  */
  function equals(&$obj)
  {
	if ($this === $obj) {
	  return true;
	}

	if (($obj === null) || !(is_a($obj, 'Criterion'))) {
	  return false;
	}

	$crit =& $obj;

	$isEquiv = ( ( ($this->table === null && $crit->getTable() === null)
			|| ( $this->table !== null && $this->table === $crit->getTable() )
								      )
			&& $this->column === $crit->getColumn()
			&& $this->comparison === $crit->getComparison());


	// check chained criterion

	$clausesLength = count($this->clauses);
	$isEquiv &= (count($crit->getClauses()) == $clausesLength);
	$critConjunctions = $crit->getConjunctions();
	$critClauses = $crit->getClauses();

	for ($i=0; $i < $clausesLength && $isEquiv; $i++) {
	  $isEquiv &= ($this->conjunctions[$i] === $critConjunctions[$i]);
	  $isEquiv &= ($this->clauses[$i] === $critClauses[$i]);
	}

	if($isEquiv) {
	  $isEquiv &= $this->value === $crit->getValue();
	}

	return $isEquiv;
  }

  /**
  * Returns a hash code value for the object.
  */
  function & hashCode()
  {
	$h = crc32(serialize($this->value)) ^ crc32($this->comparison);

	if ($this->table !== null) {
	  $h ^= crc32($this->table);
	}

	if ($this->column !== null) {
	  $h ^= crc32($this->column);
	}

	$clausesLength = count($this->clauses);

	for($i=0; $i < $clausesLength; $i++) {
	  $h ^= crc32($this->clauses[$i]->toString());
	}

	return $h;
  }

  /**
  * Get all tables from nested criterion objects
  * @return     array
  */
  function & getAllTables()
  {
	$tables = array();
	$this->addCriterionTable($this, $tables);
	return $tables;
  }

  /**
  * method supporting recursion through all criterions to give
  * us a string array of tables from each criterion
  * @return     void
  * @private
  */
  function addCriterionTable(/*Criterion*/ $c, &$s)
  {
	$s[] = $c->getTable();
	$clauses = $c->getClauses();

	$clausesLength = count($clauses);

	for($i = 0; $i < $clausesLength; $i++) {
	  $this->addCriterionTable($clauses[$i], $s);
	}
  }

  /**
  * get an array of all criterion attached to this
  * recursing through all sub criterion
  * @return     array Criterion[]
  */
  function & getAttachedCriterion()
  {
	$crits = array();
	$this->traverseCriterion($this, $crits);
	return $crits;
  }

  /**
  * method supporting recursion through all criterions to give
  * us an array of them
  * @param      Criterion $c
  * @param      array &$a
  * @return     void
  * @private
  */
  function traverseCriterion(/*Criterion*/ &$c, &$a)
  {
	$end = count($a);
	$a[$end] =& $c;
	$clauses =& $c->getClauses();

	$clausesLength = count($clauses);

	for($i=0; $i < $clausesLength; $i++) {
	  $this->traverseCriterion($clauses[$i], $a);
	}
  }

};

/**
* Data object to describe a join between two tables, for example
* <pre>
* table_a LEFT JOIN table_b ON table_a.id = table_b.a_id
* </pre>
*/
class Join
{
  /** the left column of the join condition */
  var $leftColumn = null;

  /** the right column of the join condition */
  var $rightColumn = null;

  /** the type of the join (LEFT JOIN, ...), or null */
  var $joinType = null;

  /**
   * Constructor
   * @param      leftColumn the left column of the join condition;
   *        might contain an alias name
   * @param      rightColumn the right column of the join condition
   *        might contain an alias name
   * @param      joinType the type of the join. Valid join types are
   *        null (adding the join condition to the where clause),
   *        Criteria::LEFT_JOIN(), Criteria::RIGHT_JOIN(), and Criteria::INNER_JOIN()
   */
  function Join($leftColumn, $rightColumn, $joinType)
  {
	 $this->leftColumn = $leftColumn;
	 $this->rightColumn = $rightColumn;
	 $this->joinType = $joinType;
  }

  /**
   * @return     the type of the join, i.e. Criteria::LEFT_JOIN(), ...,
   *         or null for adding the join condition to the where Clause
   */
  function getJoinType()
  {
	 return $this->joinType;
  }

  /**
   * @return     the left column of the join condition
   */
  function getLeftColumn()
  {
	 return $this->leftColumn;
  }

  /**
   * @return     the right column of the join condition
   */
  function getRightColumn()
  {
	 return $this->rightColumn;
  }

  /**
   * returns a String representation of the class,
   * mainly for debugging purposes
   * @return     a String representation of the class
   */
  function toString()
  {
	$result = "";
	if ($this->joinType != null)
	{
	  $result .= $this->joinType . " : ";
	}
	$result .= $this->leftColumn . "=" . $this->rightColumn . " (ignoreCase not considered)";

	return $result;
  }
};
Return current item: DIY Blog