* @package Mammut\DB\Sql */ class PredicateSet extends StrictObject implements iExprBool, iExprCalc, iExprValue, Countable { use BoolPredicates; protected $predicates = array(); protected $unnest = null; protected $nextPredicateCombineOperator = null; protected $defaultCombination = self::OP_AND; /** * Creates a new predicate set with AND as default combination * * @return \Mammut\DB\Sql\Expression\PredicateSet */ public static function newAndSet() { return new PredicateSet(NULL, self::OP_AND); } /** * Creates a new predicate set with OR as default combination * * @return \Mammut\DB\Sql\Expression\PredicateSet */ public static function newOrSet() { return new PredicateSet(NULL, self::OP_OR); } /** * Constructor * * @param null|array $predicates * @param string $defaultCombination * self::OP_AND or self::OP_OR */ public function __construct(array $predicates = NULL, $defaultCombination = self::OP_AND) { if(!in_array($defaultCombination, [self::OP_AND,self::OP_OR])) throw new IllegalStateException('Invalid combination ' . $defaultCombination); $this->defaultCombination = $defaultCombination; if($predicates) { foreach($predicates as $predicate) $this->addPredicate($predicate); } } /** * Begin nesting predicates (with AND concat) * * @return PredicateSet */ public function nest($defaultCombination = self::OP_AND) { $predicateSet = new PredicateSet(NULL, $defaultCombination); $predicateSet->setUnnest($this); $this->addPredicate($predicateSet, ($this->nextPredicateCombineOperator) ? : $this->defaultCombination); $this->nextPredicateCombineOperator = null; return $predicateSet; } /** * Indicate end of nested predicate * * @return PredicateSet * @throws \RuntimeException */ public function unnest() { if($this->unnest == null) throw new \RuntimeException('Not nested'); $unnset = $this->unnest; $this->unnest = null; return $unnset; } /** * Indicate what predicate will be unnested * * @param PredicateSet $predicate * @return void */ public function setUnnest(PredicateSet $predicate) { $this->unnest = $predicate; } /** * Add predicate to set * * @param iExpression $predicate * @param string $combination * self::OP_AND or self::OP_OR * @return PredicateSet */ public function addPredicate(iExpression $predicate, $combination = NULL) { if(!(is_null($combination) || in_array($combination, [self::OP_AND,self::OP_OR]))) throw new IllegalStateException('Invalid combination ' . $combination); if (empty($combination)) $combination = $this->defaultCombination; if ($combination == self::OP_OR) $this->predicates[] = array(self::OP_OR,$predicate); else $this->predicates[] = array(self::OP_AND,$predicate); return $this; } /** * Add a predicate to the set * @throws InvalidArgumentException if $predicates is empty * @return $this */ public function addPredicates($predicates, $combination = self::OP_AND) { if(empty($predicates)) throw new InvalidArgumentException('Predicate cannot be null'); if($predicates instanceof iExpression) { $this->addPredicate($predicates, $combination); return $this; } if($predicates instanceof \Closure) { $predicates($this); return $this; } if(is_string($predicates)) { // String $predicate should be passed as an expression $predicates = (strpos($predicates, iExpression::PLACEHOLDER) !== false) ? new Parameter($predicates) : new Literal($predicates); $this->addPredicate($predicates, $combination); return $this; } if(is_array($predicates)) { foreach($predicates as $pkey=>$pvalue) { if(is_string($pkey)) { $not = false; if ($pkey[0] == '!') { $not = true; $pkey = substr($pkey,1); } if($pvalue === null) $predicate = $not ? new IsNotNull($pkey, $pvalue) : new IsNull($pkey, $pvalue); elseif(is_array($pvalue)) $predicate = $not ? new NotIn($pkey, $pvalue) : new In($pkey, $pvalue); else $predicate = new Compare($pkey, $not ? self::OP_NE : self::OP_EQ, $pvalue); } elseif($pvalue instanceof iExpression) $predicate = $pvalue; else throw new \Exception("dont know what to do"); $this->addPredicate($predicate, $combination); } } return $this; } /** * Return the predicates * * @return iExpression[] */ public function getPredicates() { return $this->predicates; } /** * Add predicate using OR operator * * @param iExpression $predicate * @return PredicateSet */ public function orPredicate(iExpression $predicate) { $this->predicates[] = array(self::OP_OR,$predicate); return $this; } /** * Add predicate using AND operator * * @param iExpression $predicate * @return PredicateSet */ public function andPredicate(iExpression $predicate) { $this->predicates[] = array(self::OP_AND,$predicate); return $this; } /** * Get predicate parts for where statement * * @return array */ public function getContent() { $parts = array(); for($i = 0, $count = count($this->predicates); $i < $count; $i++) { $o = new \stdClass(); $o->type = 'fragment'; $o->concat = $this->predicates[$i][0]; $o->content = $this->predicates[$i][1]; $parts[] = $o; } return $parts; } /** * Get count of attached predicates * * @return int */ public function count(): int { return count($this->predicates); } }