Skip to content

Commit 0aa86fa

Browse files
committed
QBP v2! Support PHP8+. Both QueryBuilders supported!
Either the Eloquent QueryBuilder, or the normal/raw QueryBuilder. You pick.
1 parent 4da9400 commit 0aa86fa

File tree

3 files changed

+68
-26
lines changed

3 files changed

+68
-26
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
},
3030
"minimum-stability": "dev",
3131
"require": {
32-
"illuminate/database": "^9.0||^8.0||^7.0||^6.0||4.1 - 6.0|^10.0"
32+
"illuminate/database": "^9.0||^8.0||^10.0"
3333
},
3434
"suggest": {
3535
"jenssegers/mongodb": "Use QueryBuilderParser with MongoDB"

src/QueryBuilderParser/QBPFunctions.php

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
namespace timgws;
33

4+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
45
use \Illuminate\Database\Query\Builder;
56
use \stdClass;
67
use \Carbon\Carbon;
@@ -214,6 +215,10 @@ protected function appendOperatorIfRequired($requireArray, $value, $sqlOperator)
214215
*/
215216
private function decodeJSON($json)
216217
{
218+
if ($json == null || $json == "null") {
219+
return [];
220+
}
221+
217222
$query = json_decode($json);
218223

219224
if (json_last_error()) {
@@ -264,17 +269,17 @@ private function ensureFieldIsAllowed($fields, $field)
264269
* Some types of SQL Operators (ie, those that deal with lists/arrays) have specific requirements.
265270
* This function enforces those requirements.
266271
*
267-
* @param Builder $query
272+
* @param EloquentBuilder|Builder $query
268273
* @param stdClass $rule
269-
* @param array $sqlOperator
270-
* @param array $value
271-
* @param string $condition
274+
* @param array $sqlOperator
275+
* @param array $value
276+
* @param string $condition
272277
*
278+
* @return Builder
273279
* @throws QBParseException
274280
*
275-
* @return Builder
276281
*/
277-
protected function makeQueryWhenArray(Builder $query, stdClass $rule, array $sqlOperator, array $value, $condition)
282+
protected function makeQueryWhenArray(EloquentBuilder|Builder $query, stdClass $rule, array $sqlOperator, array $value, $condition)
278283
{
279284
if ($sqlOperator['operator'] == 'IN' || $sqlOperator['operator'] == 'NOT IN') {
280285
return $this->makeArrayQueryIn($query, $rule, $sqlOperator['operator'], $value, $condition);
@@ -288,15 +293,15 @@ protected function makeQueryWhenArray(Builder $query, stdClass $rule, array $sql
288293
/**
289294
* Create a 'null' query when required.
290295
*
291-
* @param Builder $query
296+
* @param EloquentBuilder|Builder $query
292297
* @param stdClass $rule
293-
* @param array $sqlOperator
294-
* @param string $condition
298+
* @param array $sqlOperator
299+
* @param string $condition
295300
*
296-
* @throws QBParseException when SQL operator is !null
297301
* @return Builder
302+
* @throws QBParseException when SQL operator is !null
298303
*/
299-
protected function makeQueryWhenNull(Builder $query, stdClass $rule, array $sqlOperator, $condition)
304+
protected function makeQueryWhenNull(EloquentBuilder|Builder $query, stdClass $rule, array $sqlOperator, $condition)
300305
{
301306
if ($sqlOperator['operator'] == 'NULL') {
302307
return $query->whereNull($rule->field, $condition);
@@ -311,14 +316,14 @@ protected function makeQueryWhenNull(Builder $query, stdClass $rule, array $sqlO
311316
* makeArrayQueryIn, when the query is an IN or NOT IN...
312317
*
313318
* @see makeQueryWhenArray
314-
* @param Builder $query
319+
* @param EloquentBuilder|Builder $query
315320
* @param stdClass $rule
316321
* @param string $operator
317322
* @param array $value
318323
* @param string $condition
319324
* @return Builder
320325
*/
321-
private function makeArrayQueryIn(Builder $query, stdClass $rule, $operator, array $value, $condition)
326+
private function makeArrayQueryIn(EloquentBuilder|Builder $query, stdClass $rule, $operator, array $value, $condition)
322327
{
323328
if ($operator == 'NOT IN') {
324329
return $query->whereNotIn($rule->field, $value, $condition);
@@ -332,15 +337,15 @@ private function makeArrayQueryIn(Builder $query, stdClass $rule, $operator, arr
332337
* makeArrayQueryBetween, when the query is a BETWEEN or NOT BETWEEN...
333338
*
334339
* @see makeQueryWhenArray
335-
* @param Builder $query
340+
* @param EloquentBuilder|Builder $query
336341
* @param stdClass $rule
337342
* @param string operator the SQL operator used. [BETWEEN|NOT BETWEEN]
338343
* @param array $value
339344
* @param string $condition
340345
* @throws QBParseException when more then two items given for the between
341346
* @return Builder
342347
*/
343-
private function makeArrayQueryBetween(Builder $query, stdClass $rule, $operator, array $value, $condition)
348+
private function makeArrayQueryBetween(EloquentBuilder|Builder $query, stdClass $rule, $operator, array $value, $condition)
344349
{
345350
if (count($value) !== 2) {
346351
throw new QBParseException("{$rule->field} should be an array with only two items.");

src/QueryBuilderParser/QueryBuilderParser.php

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,25 @@
55
use \Carbon\Carbon;
66
use \stdClass;
77
use \Illuminate\Database\Query\Builder;
8+
use \Illuminate\Database\Eloquent\Builder as EloquentBuilder;
89
use \timgws\QBParseException;
910

1011
class 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

Comments
 (0)