55use \Carbon \Carbon ;
66use \stdClass ;
77use \Illuminate \Database \Query \Builder ;
8+ use \Illuminate \Database \Eloquent \Builder as EloquentBuilder ;
89use \timgws \QBParseException ;
910
1011class QueryBuilderParser
1112{
1213 use QBPFunctions;
1314
15+ /**
16+ * The fields (if any) that we allow to filter on using QBP
17+ * @var array|null
18+ */
1419 protected $ fields ;
1520
21+ /**
22+ * A list of all the callbacks that can be called to cleanse provided values from QBP
23+ * @var array
24+ */
25+ private $ cleanFieldCallbacks = [];
26+
1627 /**
1728 * @param array $fields a list of all the fields that are allowed to be filtered by the QueryBuilder
1829 */
@@ -27,13 +38,13 @@ public function __construct(array $fields = null)
2738 * Build a query based on JSON that has been passed into the function, onto the builder passed into the function.
2839 *
2940 * @param $json
30- * @param Builder $querybuilder
41+ * @param EloquentBuilder| Builder $querybuilder
3142 *
3243 * @throws QBParseException
3344 *
3445 * @return Builder
3546 */
36- public function parse ($ json , Builder $ querybuilder )
47+ public function parse ($ json , EloquentBuilder | Builder $ querybuilder )
3748 {
3849 // do a JSON decode (throws exceptions if there is a JSON error...)
3950 $ query = $ this ->decodeJSON ($ json );
@@ -55,14 +66,14 @@ public function parse($json, Builder $querybuilder)
5566 * Called by parse, loops through all the rules to find out if nested or not.
5667 *
5768 * @param array $rules
58- * @param Builder $querybuilder
69+ * @param EloquentBuilder| Builder $querybuilder
5970 * @param string $queryCondition
6071 *
6172 * @throws QBParseException
6273 *
6374 * @return Builder
6475 */
65- protected function loopThroughRules (array $ rules , Builder $ querybuilder , $ queryCondition = 'AND ' )
76+ protected function loopThroughRules (array $ rules , EloquentBuilder | Builder $ querybuilder , $ queryCondition = 'AND ' )
6677 {
6778 foreach ($ rules as $ rule ) {
6879 /*
@@ -99,12 +110,12 @@ protected function isNested($rule)
99110 *
100111 * When a rule is actually a group of rules, we want to build a nested query with the specified condition (AND/OR)
101112 *
102- * @param Builder $querybuilder
113+ * @param EloquentBuilder| Builder $querybuilder
103114 * @param stdClass $rule
104115 * @param string|null $condition
105116 * @return Builder
106117 */
107- protected function createNestedQuery (Builder $ querybuilder , stdClass $ rule , $ condition = null )
118+ protected function createNestedQuery (EloquentBuilder | Builder $ querybuilder , stdClass $ rule , $ condition = null )
108119 {
109120 if ($ condition === null ) {
110121 $ condition = $ rule ->condition ;
@@ -203,15 +214,15 @@ protected function getCorrectValue($operator, stdClass $rule, $value)
203214 * Make sure that all the correct fields are in the rule object then add the expression to
204215 * the query that was given by the user to the QueryBuilder.
205216 *
206- * @param Builder $query
217+ * @param EloquentBuilder| Builder $query
207218 * @param stdClass $rule
208219 * @param string $queryCondition and/or...
209220 *
210221 * @throws QBParseException
211222 *
212223 * @return Builder
213224 */
214- protected function makeQuery (Builder $ query , stdClass $ rule , $ queryCondition = 'AND ' )
225+ protected function makeQuery (EloquentBuilder | Builder $ query , stdClass $ rule , $ queryCondition = 'AND ' )
215226 {
216227 /*
217228 * Ensure that the value is correct for the rule, return query on exception
@@ -227,7 +238,7 @@ protected function makeQuery(Builder $query, stdClass $rule, $queryCondition = '
227238 }
228239
229240 /**
230- * Convert an incomming rule from jQuery QueryBuilder to the Eloquent Querybuilder
241+ * Convert an incoming rule from jQuery QueryBuilder to the Eloquent Querybuilder
231242 *
232243 * (This used to be part of makeQuery, where the name made sense, but I pulled it
233244 * out to reduce some duplicated code inside JoinSupportingQueryBuilder)
@@ -238,7 +249,7 @@ protected function makeQuery(Builder $query, stdClass $rule, $queryCondition = '
238249 * @param string $queryCondition and/or...
239250 * @return Builder
240251 */
241- protected function convertIncomingQBtoQuery (Builder $ query , stdClass $ rule , $ value , $ queryCondition = 'AND ' )
252+ protected function convertIncomingQBtoQuery (EloquentBuilder | Builder $ query , stdClass $ rule , $ value , $ queryCondition = 'AND ' )
242253 {
243254 /*
244255 * Convert the Operator (LIKE/NOT LIKE/GREATER THAN) given to us by QueryBuilder
@@ -257,6 +268,28 @@ protected function convertIncomingQBtoQuery(Builder $query, stdClass $rule, $val
257268 return $ query ->where ($ rule ->field , $ sqlOperator ['operator ' ], $ value , $ condition );
258269 }
259270
271+ /**
272+ * Add a filter for cleaning values that are inputted from a QueryBuilder (eg, for ACL)
273+ * @param $field
274+ * @param callable|null $callback
275+ * @return $this
276+ * @throws \timgws\QBParseException
277+ */
278+ public function clean ($ field , Callable $ callback = null )
279+ {
280+ if (isset ($ this ->cleanFieldCallbacks [$ field ])) {
281+ throw new QBParseException ("Field $ field already has a clean callback set. " );
282+ }
283+
284+ if ($ callback == null ) {
285+ return $ this ;
286+ }
287+
288+ $ this ->cleanFieldCallbacks [$ field ] = $ callback ;
289+
290+ return $ this ;
291+ }
292+
260293 /**
261294 * Ensure that the value is correct for the rule, try and set it if it's not.
262295 *
@@ -272,6 +305,10 @@ protected function getValueForQueryFromRule(stdClass $rule)
272305 /*
273306 * Make sure most of the common fields from the QueryBuilder have been added.
274307 */
308+ if (isset ($ this ->cleanFieldCallbacks [$ rule ->field ])) {
309+ $ rule ->value = call_user_func ($ this ->cleanFieldCallbacks [$ rule ->field ], $ rule ->value );
310+ }
311+
275312 $ value = $ this ->getRuleValue ($ rule );
276313
277314 /*
0 commit comments