/** * Compares string subclauses in this group for potential simplification * * Eg: a.b = 1 OR a.b = 2 OR a.b = 3 => a.b IN (1,2,3) * a.b != 'a' AND a.b != 'b' => a.b NOT IN ('a','b') */ public function simplify($opts) { // For the first item, get the comparison type $cType = null; foreach ($this->items as $subClause) { if (is_string($subClause)) { $column = $op = $value = null; SubclauseParser::parse($subClause, $column, $op, $value); if (!$cType) { $cType = $op; } else { if ($cType != $op) { break; } } } } }
/** * Recursive function to build the code from nested ClauseGroup objects */ private function buildRecursive(ClauseGroup $group, $level, &$cid, $simplifyOpts = null) { $lines = array(); $i = 0; foreach ($group->getSubclauses() as $subClause) { if ($subClause instanceof ClauseGroup) { // Pass the simplification options to the ClauseGroup $subClause->simplify($simplifyOpts); $block = $this->buildRecursive($subClause, $level + 1, $cid, $simplifyOpts); $type = $subClause->getType(); switch ($type) { case ClauseGroup::BOOLEAN_AND: break; case ClauseGroup::BOOLEAN_OR: break; default: throw new Exception('Unrecognised boolean operator found'); } // Copy code into lines foreach ($block as $pieces) { array_push($lines, $pieces); } } else { // Build a criterion statement SubclauseParser::parse($subClause, $col, $op, $value); // This is the name of the criterion object $crit = '$' . $this->cPrefix . $cid; // Modify the value if required $value = $this->valuePostProcess($op, $value); // Loop through the operators we presently know about $line = null; foreach ($this->ops as $thatOp => $syntax) { if ($op == $thatOp) { $cid++; // For the equality operator... if (is_null($syntax)) { $statement = "{$crit} = \$c->getNewCriterion({$col}, {$value});\n"; } elseif (is_null($value)) { $statement = "{$crit} = \$c->getNewCriterion({$col}, null, Criteria::{$syntax});\n"; } else { $statement = "{$crit} = \$c->getNewCriterion({$col}, {$value}, Criteria::{$syntax});\n"; } $line = $this->makeLine($statement, $level, $i == 0, $crit); array_push($lines, $line); break; } } // Moan if the operator is not found, as it was detected by SubclauseParser! if (is_null($line)) { throw new Exception("Unrecognised comparison operator ('{$op}') found"); } } $i++; } // OK, link up the criterions at our current level $crits = ''; $first = null; $code = ''; foreach ($lines as $line) { if ($line['level'] == $level) { $crit = $line['crit']; if (!$first) { $first = $crit; } else { $op = $this->boolOpToPropelOp($group->getType()); $code .= "{$first}->{$op}({$crit});\n"; } $crits .= $line['crit'] . ' '; } } // Trim off the last \n we've given the code block $code = substr($code, 0, strlen($code) - 1); $type = $group->getType(); $str = "{$code}\n"; if ($this->comments) { $str = "// Perform {$type} at level {$level} ({$crits})\n{$str}"; } $str = "\n{$str}"; $line = $this->makeLine($str, $level - 1, false, $first); array_push($lines, $line); // Link into $c if last item if ($level == 0) { // Determine the right select method and loop to use if ($this->returnType == self::RETURN_ARRAY) { $method = 'doSelect'; $loop = "foreach (\$result as \$obj)\n{\n\t//\$val = \$obj->getValue();\n}"; $postOp = ""; } else { // Determine fetch mode $fetchMode = $this->resultSetType; if ($fetchMode == self::RESULTSET_NUM) { $col1 = '1'; $col2 = '2'; } else { $col1 = "'col1'"; $col2 = "'col2'"; } $method = 'doSelectRS'; $loop = "while (\$result->next())\n{\n\t//\$str = \$result->getString({$col1});\n\t//\$int = \$result->getInt({$col2});\n}"; $postOp = "\$result->setFetchMode(ResultSet::{$fetchMode});\n"; } // OK, add the select part... $statement = "\n"; if ($this->comments) { $statement .= "// Remember to change the peer class here for the correct one in your model\n"; } $statement .= "\$c->add({$first});\n\$result = TablePeer::{$method}(\$c);\n{$postOp}\n"; // ... now add a demo loop if ($this->loop) { if ($this->comments) { $statement .= "// This loop will of course need to be edited to work\n"; } $statement .= $loop; } $line = $this->makeLine($statement, -1); array_push($lines, $line); } return $lines; }
<?php require_once dirname(__FILE__) . '/../bootstrap/unit.php'; // Symfony classes $path = realpath(dirname(__FILE__) . '/../../lib'); require_once $path . '/symfony/exception/sfException.class.php'; require_once $path . '/symfony/config/sfConfig.class.php'; require_once $path . '/symfony/util/sfToolkit.class.php'; require_once $path . '/symfony/util/sfInflector.class.php'; $path = dirname(__FILE__) . '/../../plugins/sfPropelQueryBuilderPlugin/modules/sfPropelQueryBuilder/lib'; require_once $path . '/SubclauseParser.class.php'; require_once $path . '/BadSubclause.class.php'; $a = new sfException(); $t = new lime_test(3, new lime_output_color()); $t->diag('SubclauseParser::parse'); // Test numeric equality comparison $col = $op = $value = null; SubclauseParser::parse('table.column = 1', $col, $op, $value); $ok = $col == 'TablePeer::COLUMN' && $op == '=' && $value == '1'; $t->ok($ok, 'decode a column equality comparison'); // Test LIKE $col = $op = $value = null; SubclauseParser::parse("table.column LIKE 'A%'", $col, $op, $value); $ok = $col == 'TablePeer::COLUMN' && $op == 'LIKE' && $value == "'A%'"; $t->ok($ok, 'decode a column string LIKE comparison'); // Test numeric IN $col = $op = $value = null; SubclauseParser::parse("table.column IN (1, 2)", $col, $op, $value); echo "{$col}\n{$op}\n{$value}\n"; $ok = $col == 'TablePeer::COLUMN' && $op == 'IN' && $value == "(1, 2)"; $t->ok($ok, 'decode a column numeric IN test');