Index: rdbms/Criteria.class.php =================================================================== --- rdbms/Criteria.class.php (revision 6389) +++ rdbms/Criteria.class.php (working copy) @@ -64,12 +64,17 @@ * @param mixed value * @param string comparison default EQUAL */ - function add($key, $value, $comparison= EQUAL) { + function add($key, $value= NULL, $comparison= EQUAL) { static $nullMapping= array( EQUAL => IS, NOT_EQUAL => IS_NOT ); + if (is('rdbms.criterion.Criterion', $key)) { + $this->conditions[]= &$key; + return; + } + // Automatically convert '= NULL' to 'is NULL', former is not valid ANSI-SQL if (NULL === $value && isset($nullMapping[$comparison])) $comparison= $nullMapping[$comparison]; @@ -145,6 +150,11 @@ if (!empty($this->conditions)) { $sql.= ' where '; foreach ($this->conditions as $condition) { + if (is('rdbms.criterion.Criterion', $condition)) { + $sql.= $condition->asSql($db, $types).' and '; + continue; + } + if (!isset($types[$condition[0]])) { return throw(new SQLStateException('Field "'.$condition[0].'" unknown')); } Index: rdbms/criterion/Restrictions.class.php =================================================================== --- rdbms/criterion/Restrictions.class.php (revision 0) +++ rdbms/criterion/Restrictions.class.php (revision 0) @@ -0,0 +1,205 @@ + Index: rdbms/criterion/SimpleExpression.class.php =================================================================== --- rdbms/criterion/SimpleExpression.class.php (revision 0) +++ rdbms/criterion/SimpleExpression.class.php (revision 0) @@ -0,0 +1,76 @@ + + *
  • IN
  • + *
  • NOT_IN
  • + *
  • LIKE
  • + *
  • EQUAL
  • + *
  • NOT_EQUAL
  • + *
  • LESS_THAN
  • + *
  • GREATER_THAN
  • + *
  • LESS_EQUAL
  • + *
  • GREATER_EQUAL
  • + * + * + * @access public + * @param string field + * @param mixed value + * @param string op default EQUAL + */ + function __construct($field, $value, $op= EQUAL) { + static $nullMapping= array( + EQUAL => IS, + NOT_EQUAL => IS_NOT + ); + + $this->field= $field; + $this->value= &$value; + + // Automatically convert '= NULL' to 'is NULL', former is not valid ANSI-SQL + if (NULL === $value && isset($nullMapping[$op])) { + $op= $nullMapping[$op]; + } + $this->op= $op; + } + + /** + * Returns the fragment SQL + * + * @access public + * @param &rdbms.DBConnection conn + * @param array types + * @return string + * @throws rdbms.SQLStateException + */ + function asSql(&$conn, $types) { + if (!isset($types[$this->field])) { + return throw(new SQLStateException('Field "'.$this->field.'" unknown')); + } + + return $this->field.' '.$conn->prepare( + str_replace('?', $types[$this->field], $this->op), + $this->value + ); + } + + } implements(__FILE__, 'rdbms.criterion.Criterion'); +?> Index: rdbms/criterion/BetweenExpression.class.php =================================================================== --- rdbms/criterion/BetweenExpression.class.php (revision 0) +++ rdbms/criterion/BetweenExpression.class.php (revision 0) @@ -0,0 +1,54 @@ +field= $field; + $this->lo= &$lo; + $this->hi= &$hi; + } + + /** + * Returns the fragment SQL + * + * @access public + * @param &rdbms.DBConnection conn + * @param array types + * @return string + * @throws rdbms.SQLStateException + */ + function asSql(&$conn, $types) { + if (!isset($types[$this->field])) { + return throw(new SQLStateException('Field "'.$this->field.'" unknown')); + } + + return $this->field.' between '.$conn->prepare( + $types[$this->field].' and '.$types[$this->field], + $this->lo, + $this->hi + ); + } + + } implements(__FILE__, 'rdbms.criterion.Criterion'); +?> Index: rdbms/criterion/LogicalExpression.class.php =================================================================== --- rdbms/criterion/LogicalExpression.class.php (revision 0) +++ rdbms/criterion/LogicalExpression.class.php (revision 0) @@ -0,0 +1,54 @@ +left= &$left; + $this->right= &$right; + $this->op= $op; + } + + /** + * Returns the fragment SQL + * + * @access public + * @param &rdbms.DBConnection conn + * @param array types + * @return string + * @throws rdbms.SQLStateException + */ + function asSql(&$conn, $types) { + return $conn->prepare( + '(%c %c %c)', + $this->left->asSql($conn, $types), + $this->op, + $this->right->asSql($conn, $types) + ); + } + + } implements(__FILE__, 'rdbms.criterion.Criterion'); +?> Index: rdbms/criterion/Criterion.class.php =================================================================== --- rdbms/criterion/Criterion.class.php (revision 0) +++ rdbms/criterion/Criterion.class.php (revision 0) @@ -0,0 +1,28 @@ + Index: rdbms/criterion/NegationExpression.class.php =================================================================== --- rdbms/criterion/NegationExpression.class.php (revision 0) +++ rdbms/criterion/NegationExpression.class.php (revision 0) @@ -0,0 +1,40 @@ +criterion= &$criterion; + } + + /** + * Returns the fragment SQL + * + * @access public + * @param &rdbms.DBConnection conn + * @param array types + * @return string + * @throws rdbms.SQLStateException + */ + function asSql(&$conn, $types) { + return $conn->prepare('not (%c)', $this->criterion->asSql($conn, $types)); + } + + } implements(__FILE__, 'rdbms.criterion.Criterion'); +?> Index: rdbms/criterion/Property.class.php =================================================================== --- rdbms/criterion/Property.class.php (revision 0) +++ rdbms/criterion/Property.class.php (revision 0) @@ -0,0 +1,168 @@ +name= $name; + } + + /** + * Retrieve a property instance by name + * + * @model static + * @access public + * @param string name + * @return &rdbms.criterion.Property + */ + function &forName($name) { + static $instances= array(); + + if (!isset($instances[$name])) { + $instances[$name]= &new Property($name); + } + return $instances[$name]; + } + + /** + * Apply an "in" constraint to the named property + * + * @access public + * @param mixed[] values + * @return &rdbms.criterion.SimpleExpression + */ + function &in($values) { + return Restrictions::in($this->name, $values); + } + + /** + * Apply an "not in" constraint to the named property + * + * @access public + * @param mixed[] values + * @return &rdbms.criterion.SimpleExpression + */ + function ¬In($values) { + return Restrictions::notIn($this->name, $values); + } + + /** + * Apply a "like" constraint to the named property + * + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function &like($value) { + return Restrictions::like($this->name, $value); + } + + /** + * Apply a case-insensitive "like" constraint to the named property + * + * @see php://sql_regcase + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function &ilike($value) { + return Restrictions::ilike($this->name, $value); + } + + /** + * Apply an "equal" constraint to the named property + * + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function &equal($value) { + return Restrictions::equal($this->name, $value); + } + + /** + * Apply a "not equal" constraint to the named property + * + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function ¬Equal($value) { + return Restrictions::notEqual($this->name, $value); + } + + /** + * Apply a "less than" constraint to the named property + * + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function &lessThan($value) { + return Restrictions::lessThan($this->name, $value); + } + + /** + * Apply a "greater than" constraint to the named property + * + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function &greaterThan($value) { + return Restrictions::greaterThan($this->name, $value); + } + + /** + * Apply a "less than or equal to" constraint to the named property + * + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function &lessThanOrEqualTo($value) { + return Restrictions::lessThanOrEqualTo($this->name, $value); + } + + /** + * Apply a "greater than or equal to" constraint to the named property + * + * @access public + * @param mixed value + * @return &rdbms.criterion.SimpleExpression + */ + function &greaterThanOrEqualTo($value) { + return Restrictions::greaterThanOrEqualTo($this->name, $value); + } + + /** + * Apply a "between" constraint to the named property + * + * @access public + * @param mixed lo + * @param mixed hi + * @return &rdbms.criterion.SimpleExpression + */ + function &between($lo, $hi) { + return Restrictions::between($this->name, $lo, $hi); + } + } +?> Index: net/xp_framework/unittest/rdbms/CriteriaTest.class.php =================================================================== --- net/xp_framework/unittest/rdbms/CriteriaTest.class.php (revision 6389) +++ net/xp_framework/unittest/rdbms/CriteriaTest.class.php (working copy) @@ -6,6 +6,8 @@ uses( 'rdbms.Criteria', + 'rdbms.criterion.Restrictions', + 'rdbms.criterion.Property', 'rdbms.DriverManager', 'net.xp_framework.unittest.rdbms.dataset.Job', 'util.profiling.unittest.TestCase' @@ -14,6 +16,10 @@ /** * Test criteria class * + * Note we're relying on the connection to be a sybase connection - + * otherwise, quoting and date representation may change and make + * this testcase fail. + * * @see xp://rdbms.Criteria * @purpose Unit Test */ @@ -94,13 +100,34 @@ $c->addOrderBy('valid_from'); } - // Note we're relying on the connection to be a sybase connection - - // otherwise, quoting and date representation may change and make - // this test fail. $this->assertSql( 'where job_id = 1 and valid_from >= "2006-01-01 12:00AM" and title like "Hello%" order by valid_from asc', $c ); } + + /** + * Tests the rdbms.criterion API + * + * @see xp://rdbms.criterion.Property + * @see xp://rdbms.criterion.Restrictions + * @access public + */ + #[@test] + function restrictionsFactory() { + $job_id= &Property::forName('job_id'); + $c= &new Criteria(Restrictions::eitherOf( + Restrictions::not($job_id->in(array(1, 2, 3))), + Restrictions::bothOf( + Restrictions::like('title', 'Hello%'), + Restrictions::greaterThan('valid_from', new Date('2006-01-01')) + ) + )); + + $this->assertSql( + 'where (not (job_id in (1, 2, 3)) or (title like "Hello%" and valid_from > "2006-01-01 12:00AM"))', + $c + ); + } } ?>