timgws/query-builder-parser
Parse jQuery QueryBuilder rules into safe Laravel query builder constraints. Whitelist allowed fields, then generate SQL (Illuminate/Database) or MongoDB queries (via jenssegers/mongodb). Works well with tools like jQuery DataTables for advanced filtering.
composer require timgws/query-builder-parser
use timgws\QueryBuilderParser;
use Illuminate\Support\Facades\DB;
public function filterData(Request $request)
{
$allowedFields = ['name', 'email', 'status']; // Whitelist columns
$qbp = new QueryBuilderParser($allowedFields);
$query = $qbp->parse($request->input('querybuilder'), DB::table('users'));
return $query->get();
}
querybuilder.getRules()) to your Laravel endpoint.QueryBuilderParser (e.g., ['name', 'email', 'status', 'order_count']).Whitelist Fields:
$qbp = new QueryBuilderParser(['id', 'username', 'created_at']);
Parse Frontend Rules:
$query = $qbp->parse($request->querybuilder, DB::table('users'));
{"condition":"AND","rules":[{"field":"status","operator":"equal","value":"active"}]}
into SQL: WHERE status = 'active'.Extend for Complex Queries:
JoinSupportingQueryBuilderParser for nested relationships:
$joinFields = ['orders' => ['from_table' => 'users', 'to_table' => 'orders', 'from_col' => 'id', 'to_col' => 'user_id']];
$jsqbp = new JoinSupportingQueryBuilderParser(['name'], $joinFields);
DB::table() with DB::collection() for NoSQL queries.DataTables:
Combine with yajra/laravel-datatables for server-side processing:
return Datatable::query($query)
->showColumns(['id', 'name'])
->make();
aoData in DataTables’ fnServerParams.APIs: For React/Vue apps, return filtered data as JSON:
return response()->json($query->get());
Validator before parsing.Caching: Cache parsed queries if rules are static (e.g., admin dashboards):
$cachedQuery = Cache::remember('user_filters', 60, function() use ($qbp) {
return $qbp->parse($request->querybuilder, DB::table('users'));
});
Testing:
Mock QueryBuilderParser in unit tests:
$parser = $this->createMock(QueryBuilderParser::class);
$parser->method('parse')->willReturn(DB::table('users')->where('active', 1));
Field Mismatches:
{"field":"deleted_at"}).$allowed = ['name', 'email'];
$rules = json_decode($request->querybuilder, true);
foreach ($rules['rules'] as $rule) {
if (!in_array($rule['field'], $allowed)) {
throw new \InvalidArgumentException("Field {$rule['field']} not allowed.");
}
}
Operator Quirks:
begins_with/ends_with: Reversed in v1.1.2. Update frontend rules if using older versions.NOT BETWEEN: Supported in v1.5+, but test edge cases (e.g., NULL values).MongoDB Escaping:
$ or \ aren’t escaped.addcslashes() for user input:
$value = addcslashes($request->input('value'), '$\\');
Join Complexity:
JoinSupportingQueryBuilderParser may generate inefficient SQL for deep joins.$query->with(['orders' => function($q) { /* ... */ }]);
Laravel Version Gaps:
composer.json:
"timgws/query-builder-parser": "1.5.3"
Log Raw Rules:
\Log::debug('QueryBuilder Rules:', ['rules' => $request->querybuilder]);
SQL Dump:
$query->toSql(); // Check generated SQL
$query->getBindings(); // Verify parameters
MongoDB Debugging:
Enable MongoDB logging in config/mongodb.php:
'logger' => [
'enabled' => true,
'level' => 'debug',
],
Custom Operators:
Extend QueryBuilderParser to support domain-specific operators (e.g., is_archived):
class CustomQueryBuilderParser extends QueryBuilderParser
{
protected function addCustomOperator($operator, $field, $value, $query)
{
if ($operator === 'is_archived') {
return $query->where('archived_at', '>', now()->subDays(30));
}
}
}
Dynamic Whitelisting: Fetch allowed fields from a database table:
$allowedFields = DB::table('filterable_columns')
->where('table_name', 'users')
->pluck('column_name')
->toArray();
Query Modifiers: Hook into the parsing process to add clauses:
$qbp->parse($rules, $query)->where('deleted_at', null);
Performance:
->paginate(10) to queries for large datasets.Security:
Validator or Str::of($value)->slug() for user-provided values.RateLimiter::hit($request->ip(), 60); // 60 requests/minute
How can I help you explore Laravel packages today?