|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace PowerLink\Query\Syntax; |
| 4 | + |
| 5 | + |
| 6 | +/** |
| 7 | + * Class Where. |
| 8 | + */ |
| 9 | +class Where |
| 10 | +{ |
| 11 | + const OPERATOR_EQUAL = '='; |
| 12 | + const OPERATOR_NOT_EQUAL = '!='; |
| 13 | + |
| 14 | + const OPERATOR_GREATER_THAN = '>'; |
| 15 | + const OPERATOR_LESS_THAN = '<'; |
| 16 | + const OPERATOR_GREATER_THAN_OR_EQUAL = '>='; |
| 17 | + const OPERATOR_LESS_THAN_OR_EQUAL = '<='; |
| 18 | + |
| 19 | + const CONJUNCTION_AND = 'AND'; |
| 20 | + const CONJUNCTION_OR = 'OR'; |
| 21 | + |
| 22 | + const OPERATOR_START_WITH = 'start-with'; |
| 23 | + const OPERATOR_END_WITH = 'end-with'; |
| 24 | + |
| 25 | + const OPERATOR_NOT_START_WITH = 'not-start-with'; |
| 26 | + const OPERATOR_NOT_END_WITH = 'not-end-with'; |
| 27 | + |
| 28 | + const OPERATOR_IS_NULL = 'is-null'; |
| 29 | + const OPERATOR_IS_NOT_NULL = 'is-not-null'; |
| 30 | + |
| 31 | + /** |
| 32 | + * Add a basic where clause to the query. |
| 33 | + * |
| 34 | + * @param string $field |
| 35 | + * @param mixed $operator |
| 36 | + * @param mixed $value |
| 37 | + * @param string $boolean |
| 38 | + * @return $this |
| 39 | + */ |
| 40 | + public function where($field, $operator = null, $value = null, $boolean = 'and') |
| 41 | + { |
| 42 | + // If the field is an array, we will assume it is an array of key-value pairs |
| 43 | + // and can add them each as a where clause. We will maintain the boolean we |
| 44 | + // received when the method was called and pass it into the nested where. |
| 45 | + if (is_array($field)) { |
| 46 | + return $this->addArrayOfWheres($field, $boolean); |
| 47 | + } |
| 48 | + |
| 49 | + // Here we will make some assumptions about the operator. If only 2 values are |
| 50 | + // passed to the method, we will assume that the operator is an equals sign |
| 51 | + // and keep going. Otherwise, we'll require the operator to be passed in. |
| 52 | + [$value, $operator] = $this->prepareValueAndOperator( |
| 53 | + $value, |
| 54 | + $operator, |
| 55 | + func_num_args() === 2 |
| 56 | + ); |
| 57 | + |
| 58 | + // If the fields is actually a Closure instance, we will assume the developer |
| 59 | + // wants to begin a nested where statement which is wrapped in parenthesis. |
| 60 | + // We'll add that Closure to the query then return back out immediately. |
| 61 | + if ($field instanceof Closure && is_null($operator)) { |
| 62 | + return $this->whereNested($field, $boolean); |
| 63 | + } |
| 64 | + |
| 65 | + // If the field is a Closure instance and there is an operator value, we will |
| 66 | + // assume the developer wants to run a subquery and then compare the result |
| 67 | + // of that subquery with the given value that was provided to the method. |
| 68 | + if ($this->isQueryable($field) && !is_null($operator)) { |
| 69 | + [$sub, $bindings] = $this->createSub($field); |
| 70 | + |
| 71 | + return $this->addBinding($bindings, 'where') |
| 72 | + ->where(new Expression('(' . $sub . ')'), $operator, $value, $boolean); |
| 73 | + } |
| 74 | + |
| 75 | + // If the given operator is not found in the list of valid operators we will |
| 76 | + // assume that the developer is just short-cutting the '=' operators and |
| 77 | + // we will set the operators to '=' and set the values appropriately. |
| 78 | + if ($this->invalidOperator($operator)) { |
| 79 | + [$value, $operator] = [$operator, '=']; |
| 80 | + } |
| 81 | + |
| 82 | + // If the value is a Closure, it means the developer is performing an entire |
| 83 | + // sub-select within the query and we will need to compile the sub-select |
| 84 | + // within the where clause to get the appropriate query record results. |
| 85 | + if ($value instanceof Closure) { |
| 86 | + return $this->whereSub($field, $operator, $value, $boolean); |
| 87 | + } |
| 88 | + |
| 89 | + // If the value is "null", we will just assume the developer wants to add a |
| 90 | + // where null clause to the query. So, we will allow a short-cut here to |
| 91 | + // that method for convenience so the developer doesn't have to check. |
| 92 | + if (is_null($value)) { |
| 93 | + return $this->whereNull($field, $boolean, $operator !== '='); |
| 94 | + } |
| 95 | + |
| 96 | + $type = 'Basic'; |
| 97 | + |
| 98 | + // If the field is making a JSON reference we'll check to see if the value |
| 99 | + // is a boolean. If it is, we'll add the raw boolean string as an actual |
| 100 | + // value to the query to ensure this is properly handled by the query. |
| 101 | + if (Str::contains($field, '->') && is_bool($value)) { |
| 102 | + $value = new Expression($value ? 'true' : 'false'); |
| 103 | + |
| 104 | + if (is_string($field)) { |
| 105 | + $type = 'JsonBoolean'; |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + // Now that we are working with just a simple query we can put the elements |
| 110 | + // in our array and add the query binding to our array of bindings that |
| 111 | + // will be bound to each SQL statements when it is finally executed. |
| 112 | + $this->wheres[] = compact( |
| 113 | + 'type', |
| 114 | + 'field', |
| 115 | + 'operator', |
| 116 | + 'value', |
| 117 | + 'boolean' |
| 118 | + ); |
| 119 | + |
| 120 | + if (!$value instanceof Expression) { |
| 121 | + $this->addBinding($this->flattenValue($value), 'where'); |
| 122 | + } |
| 123 | + |
| 124 | + return $this; |
| 125 | + } |
| 126 | +} |
0 commit comments